LinuxTCP/IP网络编程
通用socket地址
#include<bits/socket.h>
struct sockaddr
{
sa_family_t sa_family;
char sa_data[14];//因为这是端口号和ip地址共用,不知道哪里是端口号,哪里是ip地址,所以一般不用这个结构体
};
一般使用专用结构体
struct sockaddr_in
{
sa_family sin_family;//地址族:AF_INET
u_int16_t sin_port;//端口号,要用网络字节序表示
struct in_addr sin_addr;//IPv4地址结构体
};
struct in_addr
{
u_int32_t s_addr;//IPv4地址,要用网络字节序表示
};
主机字节序和网络字节序之间的转换:
#include<netinet/in.h>
unsigned long int htonl(unsigned long int hostlong);
unsigned short int htons(unsigned short int hostshort);
unsigned long int ntohl(unsigned long int netlong);
unsigned short int ntohs(unsigned short int netshort);
IP地址转换函数:
#include <arpa/inet.h>
in_addr_t inet_addr(const char *strptr);
//用于将点分十进制字符串表示的IPV4地址转换为用网络字节序整数表示的IPv4地址。失败返回INADDR_NONE;
int inet_aton(const char *cp,struct in_addr* inp);//功能和inet_addr一样,但是将转换的结果存储在参数inp指向的地址结构中。成功返回1,失败返回0;
char* inet_ntoa(struct in_addr in);//将用网络字节序整数表示的IPV4地址转换为用点分十进制表示的IPV4地址。(不可重入);
创建socket
socket在linux上是一个文件描述符
#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain, int type, int protocol);
domain参数表示系统使用哪个底层协议族。对于TCP/IP协议族而言,参数应该设置为PF_INET(IPV4)或PF_INET6(IPV6);而对于linux本地域协议族而言,应该为PF_UNIX
type参数表示服务类型:主要有SOCK_STREAM(流服务)和SOCK_UGRAM(数据报)服务。对于TCP/IP协议而言流服务就是TCP协议,数据报服务就是UDP协议。
protocol参数表示在前两个参数构成的协议下,在选择具体的协议。一般都设置为0;
命名socket
在创建socket时我们给定了它一个地址族,但是并没有指定使用那个具体的socket地址。就需要将一个socket矛socket地址绑定在一起,就是为socket命名。
在服务器端,通常需要命名,只有命名后客户端才能知道该如何连接它。客户端不需要命名socket,采用匿名的方式,计时用操作系统自动分配的socket地址。
#include<sys/types.h>
#include<sys/socket.h>
int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen );
bind将my_addr所指的socket地址分配给未命名的sockfd文件描述符,addrlen参数指出了socket地址的长度。
bind成功返回0,失败返回-1;
监听socket
socket被命名之后,还不能马上接受客户连接,需要创建监听队列以存放待处理的客户连接
#include<sys/socket.h>
init listen (int sockfd, int backlog);
sockfd参数指定被监听的socket。backlog参数提示内核监听队列的最大长度。
成功返回0。失败返回-1;
接受连接
从监听队列中接受一个连接
#include<sys/types.h>
#include<sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd为监听的sockfd,addr为用来获取被接受连接的远端socket地址,socket地址长度为addrlen.
函数成功返回一个新连接的socket,该socket唯一的标识了被接受的这个连接。失败返回-1。
发起连接
服务器通过listen调用来被动接受连接,客户端需要通过系统调用来建立连接。
#include<sys/types.h>
#include<sys/socket.h>
int connect (int sockfd, const struct scokaddr *serev_addr , socklen_t addrlen);
sockfd参数由socket系统调用返回一个socket,serv_addr参数是服务器监听的socket地址,addrlen参数指定这个地址长度。
函数成功返回0,一旦连接成功,这个socket就唯一标识这个连接。失败返回-1。
关闭连接
关闭一个连接实际上就是关闭了一个对应的socket,可以通过关闭普通的文件描述符系统调用完成;
#include<unistd.h>
int close (int fd);
可以关闭一个连接,也可以关闭一个通信
读写数据
#include<sys/types.h>
#include<sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
recv读取sockfd上的数据,buf和len参数分别指定度缓冲区的位置和大小,flags参数的含义为数据收发提供额外的控制,一般设置为0;
实现代码:
服务器端:
#include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<string.h> #include<assert.h> #include<netinet/in.h> #include<arpa/inet.h> #include<sys/socket.h> #include<sys/types.h> int main() { int listenfd=socket(PF_INET,SOCK_STREAM,0);//创建一个socket文件描述符 assert(listenfd!=-1); struct sockaddr_in ser; memset(&ser,0,sizeof(ser)); ser.sin_family=AF_INET;//设置协议为tcp协议族 ser.sin_port=htons(6000);//将主机子节序转换为网络子节序 ser.sin_addr.s_addr=inet_addr("127.0.0.1");//将点分十进制字符串转换为网络字节序整数 int res=bind(listenfd, (struct sockaddr*)&ser,sizeof(ser));//命名socket assert(res!=-1); listen(listenfd,5);//监听socket while(1) { struct sockaddr_in cli; int len=sizeof(cli); int c =accept(listenfd,(struct sockaddr*)&cli,&len);//接收连接,没有客户请求时阻塞 if(c==-1) { printf("link is error\n"); break; } while(1) { char data[128]={0}; int n=recv(c,data,127,0);//发送消息 if(n==-1) { printf("recv data error\n"); break; } if(n==0)//断开连接时 { printf("%s:%d unlink\n",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port)); break; } printf("%s: %d ::",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port)); printf("%s \n",data); send(c,"Ok",2,0); } close(c);//关闭连接 } exit(0); }
客户端:
#include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<assert.h> #include<string.h> #include<sys/socket.h> #include<sys/types.h> #include<netinet/in.h> #include<arpa/inet.h> int main() { int sockfd=socket(AF_INET,SOCK_STREAM,0);//创建一个socket文件描述符 assert(sockfd!=-1); struct sockaddr_in ser_addr; memset(&ser_addr,0,sizeof(ser_addr)); ser_addr.sin_family=AF_INET; ser_addr.sin_port=htons(6000); ser_addr.sin_addr.s_addr=inet_addr("127.0.0.1"); int res=connect(sockfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));//创建一个连接 while(1) { printf("please input\n"); char buff[128]={0}; fgets(buff,128,stdin);//从标准输入设备获取 if(strncmp(buff, "bye",3)==0) { break; } send(sockfd,buff,strlen(buff)-1,0);//发送消息 char data[128]={0}; int n=recv(sockfd,data,sizeof(data),0);//接受消息 } close(res); exit(0); }
- TCP/IP网络编程 基于Linux编程_2 --I/O流分离的半关闭问题
- TCP/IP网络编程 基于Linux编程_1 --套接字中标准I/O的使用
- TCP/IP网络编程 基于Linux编程_3 --优于select的epoll
- TCP/IP网络编程 基于Linux编程_1 --套接字中标准I/O的使用
- TCP/IP网络编程 基于Linux编程_3 --优于select的epoll
- 【嵌入式Linux学习七步曲之第七篇 Linux的高级应用编程】TCP/IP网络编程函数解析
- linux下TCP/IP网络编程
- 【读书笔记】TCP/IP网络编程 第17章 Linux epoll
- TCP/IP网络编程 基于Linux编程_4 --多线程服务器端的实现
- Linux下TCP/IP网络编程——结课考试知识总结
- TCP/IP网络编程 基于Linux编程_4 --多线程服务器端的实现
- linux TCP/IP网络编程(多进程)
- Linux下TCP/IP网络编程
- 唯快不破:linux网络编程--TCP/IP网络编程中socket的行为
- 【Linux的高级应用编程】TCP/IP网络编程函数解析
- linux网络编程--TCP/IP网络编程中socket的行为
- 《TCP/IP网络编程》三、基于Linux的编程
- linux网络编程--TCP/IP网络编程中socket的行为
- TCP/IP网络编程 基于Linux编程_2 --I/O流分离的半关闭问题
- linux和windows不同OS环境下VNC的使用方法(20060111)