1、.Linux 下 TCP 编程框架TCP 网络编程的流程包含服务器和客户端两种模式。服务器模式创建一个服务程序,等待客户端用户的连接,接收到用户的连接请求后,根据用户的请求进行处理;客户端模式则根据目的服务器的地址和端口进行连接,向服务器发送请求并对服务器的响应进行数据处理。 1.服务器端程序包括? 建立套接字( socket())? 套接字与端口的绑定(bind()? 设置服务器的侦听连接(listen())? 接收客户端连接(accept())? 接收和发送数据(send(),recv())? 关闭套接字(close()2.说明1套接字初始化过程中,根据用户对套接字的需求来确定套接字的选项
2、。按照用户定义的网络类型,协议类型和具体的协议标号等参数来定以 socket()函数。系统根据用户的需求生成一个套接字文件描述符供用户使用。2套接字与端口的绑定过程中,将套接字与一个地址结构进行绑定。绑定之后,套接字所代表 IP 地址和端口地址及协议类型等参数按照绑定值进行操作。3由于一个服务器需要满足多个客户端的连接请求,而服务器在某个时间仅能处理有限个数的客户端连接请求,所以服务器需要设置服务器端排队队列的长度。4在客户端发送连接请求之后,服务器需要接收客户端的连接,然后才能进行其他的处理。5在服务器接收客户端请求之后,可以从套接字文件描述符中读取数据或者向文件描述符发送数据。接收数据后服
3、务器按照定义的规则对数据进行处理,并将结果发送给客户端。6当服务器处理完数据,要结束与客户端的通信过程的时候,需要关闭套接字连接2.客户端程序包括? 建立套接字(socket()? 连接服务器(connect()? 读写网络数据(send(),recv()? 关闭套接字(close()3.服务器端和客户端程序的区别客户端程序和服务器端程序不同之处是客户端在建立套接字之后可以不进行地址绑定,而是直接连接服务器端。服务器端有 listen()和 accept()两个函数,而客户端不需要这两个函数。二.基于 Linux 的 TCP 套接字函数1. socket1 函数原型:int socket(in
4、t domain,int type,int protocol)2 函数功能:函数 socket()用于创建一个套接字描述符。3 形参:? domain:用于指定创建套接字所使用的协议族,在头文件中定义。有时候程序中会使用 PF_INET,在头文件中 AF_INET 和PF_INET 的数值是一致的。常见的协议族如下:AF_UNIX:创建只在本机内进行通信的套接字。AF_INET:使用 IPv4TCP/IP 协议AF_INET6:使用 IPv6 TCP/IP 协议说明:AF_UNIX 只能用于单一的 UNIX 系统进程间通信,而 AF_INET 是针对 Interne 的,因而可以允许在远程主机
5、之间通信。一般把它赋为 AF_INET。? type:指明套接子通信的类型,对应的参数如下SOCK_STREAM:创建 TCP 流套接字SOCK_DGRAM:创建 UDP 数据报套接字SOCK_RAW:创建原始套接字? protocol:指定某个协议的特定类型参数 protocol 通常设置为 0,表示通过参数 domain 指定的协议族和参数 type 指定的套接字类型来确定使用的协议。当为原始套接字时,系统无法唯一的确定协议,此时就需要使用使用该参数指定所使用的协议。4 返回值:执行成功后返回一个新创建的套接字;若有错误发生则返回一个-1 ,错误代码存入 errno 中。5 举例:调用 s
6、ocket 函数创建一个 UDP 套接字int sock_fd;sock_fd = socket(AF_INET,SOCK_DGRAM,0);if(sock_fd 函数原型:int bind(int sockfd,struct sockaddr *my_addr,socklen_t addrlen)2 函数功能函数 bind()的作用是将一个套接字文件描述符与地址和端口绑定。3 形参:? sockfd:sockfd 是调用 socket 函数返回的文件描述符;? addrlen 是 sockaddr 结构的长度。? my_addr: 是一个指向 sockaddr 结构的指针,它保存着本地套接字
7、的地址(即端口和 IP地址)信息。不过由于系统兼容性的问题,一般不使用这个结构,而使用另外一个结构(structsockaddr_in)来代替4 套接字地址结构:(1)struct sockaddr:结构 struct sockaddr 定义了一种通用的套接字地址,它在sys/socket.h 中定义。struct sockaddrunsigned short sa_family;/*地址类型,AF_XXX*/char sa_data14;/*14 字节的协议地址*/a. sin_family:表示地址类型,对于使用 TCP/IP 协议进行的网络编程,该值只能是AF_INET.b. sa_da
8、ta:存储具体的协议地址。(2)sockaddr_in每种协议族都有自己的协议地址格式,TCP/IP 协议组的地址格式为结构体 struct sockaddr_in,它在 netinet/in.h 头文件中定义。structsockaddr_inunsigned short sin_family;/*地址类型*/unsigned short sin_port;/*端口号*/struct in_addr sin_addr;/*IP 地址*/unsigned char sin_zero8;/*填充字节,一般赋值为 0*/a. sin_family:表示地址类型,对于使用 TCP/IP 协议进行的网
9、络编程,该值只能是AF_INET.b. sin_port:是端口号c. sin_addr:用来存储 32 位的 IP 地址。d. 数组 sin_zero 为填充字段,一般赋值为 0.e. struct in_addr 的定义如下:structin_addrunsigned long s_addr;结构体 sockaddr 的长度为 16 字节,结构体 sockaddr_in 的长度为 16 字节。可以将参数my_addr 的 sin_addr 设置为 INADDR_ANY 而不是某个确定的 IP 地址就可以绑定到任何网络接口。对于只有一 IP 地址的计算机,INADDR_ANY 对应的就是它的
10、 IP 地址;对于多宿主主机(拥有多个网卡),INADDR_ANY 表示本服务器程序将处理来自所有网络接口上相应端口的连接请求5 返回值:函数成功后返回 0,当有错误发生时则返回-1,错误代码存入 errno 中。6举例:调用 socket 函数创建一个 UDP 套接字struct sockaddr_in addr_serv,addr_client;/*本地的地址信息*/memset(addr_serv.sin_family= AF_INET;/*协议族*/addr_serv.sin_port= htons(SERV_PORT);/*本地端口号*/addr_serv.sin_addr.s_ad
11、dr= htonl(INADDR_ANY); /*任意本地地址*/*套接字绑定*/if(bind(sock_fd,(structsockaddr *)3形参? sockfd: sockfd 是调用 socket 函数返回的文件描述符? backlog:指定该连接队列的最大长度。如果连接队列已经达到最大,之后的连接请求被服务器拒绝。大多数系统的设置为 20,可以将其设置修改为 5 或者 10,根据系统可承受负载或者应用程序的需求来确定。4返回值:当 listen()函数成功运行时,返回值为 0;当运行失败时,它的返回值为-1,错误代码存入 errno 中。5.listen()函数的例子:#def
12、ine SERV_PORT 3000int main(int argc,char *argv)int sock_fd; struct sockaddr_in addr_serv,addr_client;/*本地的地址信息*/sock_fd = socket(AF_INET,SOCK_DGRAM,0);if(sock_fd函数功能:当一个客户端的连接请求到达服务器主机侦听的端口时,此时客户端的连接会在队列中等待,知道使用服务器处理接收请求。函数 accept()成功执行后,会返回一个新的套接口文件描述符来表示客户端的连接,客户端连接的信息可以通过这个新描述符来获得。因此当服务器成功处理客户端的请
13、求连接后,会有两个文件描述符,老的文件描述符表示客户端的连接,函数 send()和 recv()通过新的文件描述符进行数据收发。2函数原型:#include#includeint accept(int sock_fd,struct sockaddr*addr,socklen_t *addrlen);3形参? sock_fd:是由函数 socket 创建,经函数 bind 绑定到本地某一端口上,然后通过函数listen 转化而来的监听套接字。? addr:用来保存发起连接请求的主机的地址和端口。? addrlen 是 addr 所指向的结构体的大小。4返回值:accept()函数的返回值是新连接
14、的客户端套接字文件描述符,与客户端之间的通信是通过 accept()返回的新套接字文件描述符来进行的,而不是通过建立套接字时的文件描述符。如果 accept()函数发生错误,accept()会返回-1,通过 errno 可以得到错误值。5如果参数 sock_fd 所指定的套接字被设置为阻塞方式(Linux 下的默认方式) ,且连接请求队列为空,则 accept()将被阻塞直到有连接请求到此为止;如果参数 s 所指定的套接字被设置为非阻塞方式,如果队列为空,accept 将立即返回-1,errno 被设置为 EAGAIN.6实例:int client_fd;int client_len;stru
15、ct sockaddr_in client_addr;client_len = sizeof(struct sockaddr_in);client_fd = accept(sock_fd,(struct sockaddr *)if(conn_fd函数功能:客户端在建立套接字之后,不需要进行地址绑定,就可以直接连接服务器。连接服务器的函数为 connect(),此函数连接指定参数的服务器,例如 IP 地址,端口号。如果是 TCP 编程,则 connect()函数用于服务器发出连接请求,服务器的 IP 地址和端口号由 参数 serv_addr 指定。如果是 UDP 编程,则 connect 函数并
16、不建立真正的连接,它只是告诉内核与该套接字进行通信的目的地址(由第二个参数指定) ,只有该目的地址发来的数据才会被该 socket 接收。调用 connect 函数的好处是不必在每次发送和接收数据时都指定目的地址。2函数原型:#include#includeint connect(int sock_fd,struct sockaddr *serv_addr,socklen_taddrlen);3形参:? sock_fd:建立套接字时返回的套接字文件描述符,调用 socket()返回的。? serv_addr:是一个指向数据结构 sockaddr 的指针,其中包括客户端需要连接的服务器的目的 I
17、P 地址和端口号。? addrlen:表示了第二了参数的大小,可以使用 sizeof(struct sockaddr)4执行成功后返回 0,有错误发生则返回-1 ,错误代码存入 errno 中。5实例:int sock_fd;struct sockaddr_in serv_addr;if(-1 = (sock_fd = socket(AF_INET,SOCK_STREAM,0)printf(“Error: Unable to createsocket(%i)n”,errno);perror(“sockets”);exit(1);memset(serv_addr.sin_family= AF_I
18、NET;serv_addr.sin_port= htons(DEST_PORT);serv_addr.sin_addr.s_addr= inet(DEST_IP_ADDRESS);if(-1= connect(sock_fd,(struct sockaddr *)perror(“socks”);close(sock_fd);exit(1);6. send(发送数据)1函数功能:函数 send 用来在 TCP 套接字上发送数据,send 只能对处于连接状态的套接字使用。2函数原型#include#includessize_t send(int conn_fd,const void *msg,si
19、ze_t len, int flags);3函数形参:? conn_fd:为已建立好连接的套接字描述符,即调用 accept()函数后返回的套接字描述符。? msg:存放发送数据的缓冲区。? len:发送缓冲区的长度? flags:为控制选项,一般设置为 0,或取以下值:2 MSG_OOB:在指定的套接字上发送带外数据(out-of-band data),该类型的套接字必须支持带外数据(如:SOCK_STREAM ).2 MSG_DONTROUTE:通过最直接的路径发送数据,而忽略下层协议的路由设置。4返回值:执行成功返回实际发送数据的字节数,出错则返回-1,错误代码存入 errno 中。执行
20、成功只是说明数据写入套接字的缓冲区中,并不表示数据已经成功地通过网络发送到目的地。5实例:#define BUFFERSIZE 1500char send_bufBUFFERSIZE;if(send(conn_fd,send_buf,len,0)函数功能:recv()用来 TCP 套接字上接收数据。函数 recv 从指定的套接字描述符上接收数据并保存到指定 buf 中。2函数原型#include#includessize_t recv(int conn_fd,void *buf,size_t len,int flags);3函数形参:? conn_fd: 为已建立好连接的套接字描述符,即调用
21、accept()函数后返回的套接字描述符? buf:接收缓冲区? len:接收缓冲区的大小? flags:为控制选项,一般设置为 0 或取以下数值2 MSG_OOB:请求接收带外数据2 MSG_PEEK:只查看数据而不读出2 MSG_WAITALL:只在接收缓冲区满时才返回。4函数返回值函数执行成功返回接收到的数据字节数,出错返回-1,错误代码存入 errno 中。5实例:#define BUFFERSIZE 1500char recv_bufBUFFERSIZE;if(recv(conn_fd,recv_buf,sizeof(recv_buf),0)函数原型:int close(int fd);2函数功能:函数 close 用来关闭一个套接字描述符。3函数形参:? 参数 fd 为一个套接字描述符。4返回值:执行成功返回 0,出错则返回-1.错误代码存入 errno 中。说明:close()函数的头文件是#include.本篇文章来源于 Linux 公社网站() 原文链接:http:/