linux网络编制程序

看了Linux程序设计4粤语版,学习了102线程编制程序和socket编程。本文的次第参考自Linux程序设计四的第35章。

互联网上保有材质都说epoll是高并发、单线程、IO重叠服用的首要推荐架构,比select和poll质量都要好,特别是在有恢宏不活跃接连的意况下。具体原理就不演讲了,上面说说利用。

安慕希组(ip地址,协议,端口)就足以标记互连网的进度

 

1、 传闻102线程落成两个服务器和贰个客户端达成全双工通讯

有着有多少个函数:

 

规划了3个客户端程序,三个服务端程序。使用TCP协议实行数量传输。

劳动器端创造多个线程:三个用于收纳客户端发送过来的新闻;3个用来给客户端发送消息。

#include <sys/epoll.h>

三.一,OSI柒层模型和TCP/IP5层模型

 

OSI7层网络模型由下至上为壹至柒层,分别为:

物理层(Physical layer),数据链路层(Data link layer),网络层(Network
layer),传输层(Transport layer),会话层(Session
layer),表示层(Presentation layer),应用层(Application layer)

1.1 应用层,很简短,正是应用程序。那一层担当分明通讯对象,并确认保障由丰硕的财富用于通讯,那几个本来都以想要通讯的应用程序干的事务。 
1.2 表示层,肩负数据的编码、转化,确认保障应用层的例行干活。那一层,是将我们见到的界面与2进制间互相转化的地点,就是大家的言语与机器语言间的中间转播。数据的压缩、解压,加密、解密都发出在那壹层。那壹层依据差异的选择目的将数据管理为分化的格式,表现出来正是我们看看的异彩纷呈的文本扩展名。 
1.3 会话层,担当建设构造、维护、调整会话,区分不一致的对话,以及提供单工(Simplex)、半双工(Half
duplex)、全双工(Full duplex)三种通讯方式的劳务。大家日常所知的NFS,RPC,X
Windows等都干活在那1层。 
1.4 传输层,肩负分割、组合数据,落成端到端的逻辑连接。数据在上三层是一体化的,到了那壹层初叶被细分,那壹层分割后的数目被誉为段(Segment)。三遍握手(Three-way
handshake),面向连接(Connection-Oriented)或非面向连接(Connectionless-Oriented)的劳动,流控(Flow
control)等都发生在那1层。 
1.5 网络层,担任管理网络地址,定位装置,决定路由。大家所纯熟的IP地址和路由器便是做事在那1层。上层的数据段在那壹层被分割,封装后叫做包(Packet),包有二种,壹种名字为用户数据包(Data
packets),是上层传下来的用户数量;另1种叫路由更新包(Route update
packets),是一向由路由器发出来的,用来和其余路由器进行路由音讯的置换。 
1.6 多少链路层,担当希图物理传输,CPRADOC校验,错误文告,互连网拓扑,流控等。咱们所熟谙的MAC地址和调换机都干活在这一层。上层传下来的包在那壹层被分割封装后叫做帧(Frame)。 
1.7 物理层,正是可信的情理链路,负担将数据以比特流的办法发送、接收,就不多说了。 

 

客户端进度创制了1个客户端应用的socket,叁个socket地址结构体。设置那么些socket地址结构体的端口和地方为要连接的服务端的端口和ip。然后使用客户端的socket尝试连接服务端(connect),若是总是退步直接退出。如若总是成功,则动用这一个一而再成功的socket实行数量传输(send和recv)。首先向服务端发送三个字节字符型数据,然后接受服务端十3个字节字符型数据,不断循环。借使发送和承受出错被擒获到的话(res=-一),关闭socket,退出进度。

客户端也开创五个线程:一个用于吸纳服务器端发送过来的音信;二个用来给服务器端发送消息。

 

3.2,TCP握手

 

 

1、int epoll_create ( int size );

3.二.一 3次握手创设连接

在TCP/IP协议中,TCP协议提供可相信的连接服务,采纳三遍握手建立三个连接。
壹,客户端向服务器发送3个SYN J
二,服务器向客户端响应三个SYN K,并对SYN J举办确认ACK J+一
三,客户端再想服务器发三个承认ACK K+一

图片 1
从图中能够看到,当客户端调用connect时,触发了连接请求,向服务器发送了SYN
J包,那时connect进入阻塞状态;服务器监听到连年请求,即接到SYN
J包,调用accept函数接收请求向客户端发送SYN K ,ACK
J+壹,那时accept进入阻塞状态;客户端收到服务器的SYN K ,ACK
J+一之后,那时connect再次来到,并对SYN K举办确认;服务器收到ACK
K+一时,accept重临,至此一回握手实现,连接创设。

一回握手的目标是确立双向的三番五次,第3次握手是客户端向劳动器端发出请求 
其次次握手是劳务器端告诉客户端,第贰次握手是马到功成的,即能够从客户端发送到客户端, 
其二回握手是客户端告诉服务器端,第一回握手是成功的,即能够从客户端到服务器端 
那般就确认保证了客户端和劳务器端的双向通讯,

 

2、 以身作则代码

size是epoll要监视的fd的框框。

三.二.贰 五回握手释放连接

图片 2

一,有个别应用进度首先调用close主动关闭连接,那时TCP发送1个FIN M;
2,另一端接收到FIN
M之后,实践被动关闭,对那么些FIN进行确认。它的收纳也当作文件截至符传递给应用进程,因为FIN的接受意味着应用进度在对应的总是上再也收到不到额外数据;
三,1段时间之后,接收到文件结束符的利用进程调用close关闭它的socket。那产生它的TCP也发送1个FIN
N;
四,接收到那一个FIN的源发送端TCP对它进行确认。

服务端程序采用了2个sever_socket,并把那些socket与本机的ip和监听的端口进行绑定,并监听那一个socket,accept远端来的延续。Accept成功1遍就能够拿走2个client_socket。那时创设八个新线程,并把这么些这几个client_socket作为线程参数字传送给新线程。3个新线程服务叁个远端连接,管理他们的多少传输请求(不断的收发数据,循环)。

服务器端的代码client.c

 

3.3,socket编程

先从服务器端聊起。服务器端先伊始化Socket,然后与端口绑定(bind),对端口实行监听(listen),调用accept阻塞,等待客户端连接。在这时借使有个客户端早先化三个Socket,然后连接服务器(connect),倘若连接成功,这时客户端与劳动器端的连日就创建了。客户端发送数据请求,服务器端接收请求并拍卖请求,然后把回应数据发送给客户端,客户端读取数据,最终关闭连接,二次交互甘休。

gcc client2.c -o client2 -lpthread

#include <sys/types.h>

2、int epoll_ctl ( int epfd, int op, int fd, struct epoll_event
*event );

3.3.1 AF_INET TCP传输最简易版本

图片 3

 

tcp_client.c

 

[cpp] view
plain copy

 

  1. #include <unistd.h>  
  2. #include <stdio.h>  
  3. #include <stdlib.h>  
  4. #include <string.h>  
  5. #include <errno.h>  
  6. #include <sys/types.h>  
  7. #include <sys/socket.h>  
  8. #include <netinet/in.h>  
  9.   
  10. #define PORT 6666  
  11. #define BUFF_SIZE 1024  
  12. #define MAXLEN 4096  
  13.   
  14. main(int argc, char** argv)  
  15. {  
  16.     if(argc!=2){  
  17.         printf(“usage: ./client <ipaddress>\n”);  
  18.         exit(0);  
  19.     }  
  20.       
  21.     char sendline[4096];  
  22.       
  23.     //socket()建立socket  
  24.     int sockfd = socket(AF_INET,SOCK_STREAM,0);  
  25.   
  26.     //要连接的服务器地址  
  27.     struct sockaddr_in sliaddr;  
  28.     sliaddr.sin_family = AF_INET;  
  29.     sliaddr.sin_port = htons(PORT);  
  30.     inet_pton(AF_INET, argv[1], &sliaddr.sin_addr);  
  31.   
  32.     //connect()发送请求(ip=argv[1],protocal=TCP,port=6666)  
  33.     connect(sockfd, (struct sockaddr*)&sliaddr, sizeof(sliaddr));  
  34.   
  35.     //recv sth  
  36.     recv_len = recv(sockfd, buff, sizeof(buff), 0);  
  37.     buff[recv_len] = ‘\0’;  
  38.     printf(” %s “, buff);  
  39.    
  40.     //interactive  
  41.     while (1)  
  42.     {  
  43.         printf(“Enter string to send: “);  
  44.         scanf(“%s”, buff);  
  45.         if (!strcmp(buff, “quit”))  
  46.             break;  
  47.            
  48.         send_len = send(sockfd, buff, strlen(buff), 0);  
  49.         recv_len = recv(sockfd, buff, BUFF_SIZE, 0);  
  50.         buff[recv_len] = ‘\0’;  
  51.         printf(”    received: %s \n”, buff);  
  52.     }  
  53.   
  54.     //close()关闭连接  
  55.     close(sockfd);  
  56. }  

 

tcp_server.c

 

[cpp] view
plain copy

 

  1. #include <unistd.h>  
  2. #include <stdio.h>  
  3. #include <stdlib.h>  
  4. #include <string.h>  
  5. #include <errno.h>  
  6. #include <sys/types.h>  
  7. #include <sys/socket.h>  
  8. #include <netinet/in.h>  
  9.   
  10. #define PORT 6666  
  11. #define WAIT_QUEUE_LEN 5  
  12. #define BUFF_SIZE 1024  
  13. #define WELCOME “Welcome to my server ^_^!\n”  
  14.   
  15. main()  
  16. {  
  17.     int connfd;  
  18.     char buff[MAXLEN];  
  19.     int len;  
  20.   
  21.     //socket() 建立socket,其中SOCK_STREAM表示tcp连接  
  22.     int sockfd = socket(AF_INET,SOCK_STREAM,0);  
  23.       
  24.     struct sockaddr_in servaddr;  
  25.     servaddr.sin_family = AF_INET;  
  26.     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  
  27.     servaddr.sin_port = htons(PORT);  
  28.       
  29.     //bind()绑定三个socket(ip=all,protocal=TCP,port=666六)  
  30.     bind(sockfd,(struct sockaddr *) &servaddr, sizeof(servaddr));  
  31.   
  32.     //listen()监听  
  33.     listen(sockfd, WAIT_QUEUE_LEN);  
  34.   
  35.     //accept() & close()  
  36.     printf(“======waiting for client’s request======\n”);  
  37.     while(1){  
  38.         c_addrlen = sizeof(struct sockaddr_in);  
  39.         connfd = accept(serverfd, (struct sockaddr *)&caddr, &c_addrlen);  
  40.         printf(“client: ip=%s,port=%s\n”, cliaddr.sin_addr.s_addr,cliaddr.sin_port);  
  41.           
  42.         //send a welcome  
  43.         send(connfd, WELCOME, strlen(WELCOME), 0);  
  44.        
  45.         //阻塞模式下recv==0表示客户端已断开连接  
  46.         while ((len = recv(connfd, buff, BUFF_SIZE, 0)) > 0)  
  47.         {  
  48.             buff[len] = ‘\0’;  
  49.             printf(“recv msg is : %s \n”, buff);  
  50.             send(connfd, buff, len, 0);  
  51.         }  
  52.           
  53.         close(connfd);  
  54.     }  
  55.   
  56.     //close()关闭连接  
  57.     close(sockfd);  
  58. }  

卡住与非阻塞recv重回值未有区分,都以
<0 出错
=0 连接关闭
>0 接收到数码大小,

 

makefile

 

[plain] view
plain copy

 

  1. .PHONY : main  
  2. main : server client  
  3. server : server.o  
  4.         gcc -g -o server server.o   
  5. client : client.o  
  6.         gcc -g -o client client.o   
  7. server.o : server.c  
  8.         gcc -g -c server.c  
  9. client.o : client.c  
  10.         gcc -g -c client.c  
  11. clean :   
  12.         rm -rf *.o  
  13. ser :  
  14.         ./server  
  15. cli :  
  16.         ./client  

 

gcc server2.c -o server2 -lpthread

#include <sys/socket.h>

(1)epfd:epoll_create的重临值。

三.三.二 到场重临值检查和IP地址

tcp_client.c

 

[cpp] view
plain copy

 

  1. #include <unistd.h>  
  2. #include <stdio.h>  
  3. #include <stdlib.h>  
  4. #include <string.h>  
  5. #include <errno.h>  
  6. #include <sys/types.h>  
  7. #include <sys/socket.h>  
  8. #include <netinet/in.h>  
  9.   
  10. #define MAXLEN 4096  
  11.   
  12. main(int argc, char** argv)  
  13. {  
  14.     if(argc!=2){  
  15.         printf(“usage: ./client <ipaddress>\n”);  
  16.         exit(0);  
  17.     }  
  18.       
  19.     char sendline[4096];  
  20.       
  21.     //socket()建立socket  
  22.     int sockfd;  
  23.     if((sockfd=socket(AF_INET,SOCK_STREAM,0)) ==-1){  
  24.         printf(“create socket error: %s(errno: %d)\n”, strerror(errno),errno);  
  25.     }  
  26.   
  27.     struct sockaddr_in cliaddr;  
  28.     cliaddr.sin_family = AF_INET;  
  29.     cliaddr.sin_port = htons(6666);  
  30.     if(inet_pton(AF_INET, argv[1], &cliaddr.sin_addr)==-1){  
  31.         printf(“inet_pton error for %s\n”,argv[1]);  
  32.         exit(0);  
  33.     }  
  34.   
  35.     //connect()发送请求(ip=argv[1],protocal=TCP,port=6666)  
  36.     if(connect(sockfd, (struct sockaddr*)&cliaddr, sizeof(cliaddr))==-1){  
  37.         printf(“connect error: %s(errno: %d)\n”,strerror(errno),errno);  
  38.         exit(0);  
  39.     }  
  40.   
  41.     printf(“send msg to server: \n”);  
  42.     fgets(sendline, 4096, stdin);  
  43.   
  44.     //send()发送数据  
  45.     if(send(sockfd, sendline, strlen(sendline), 0)==-1){  
  46.         printf(“send msg error: %s(errno: %d)\n”, strerror(errno), errno);  
  47.         exit(0);  
  48.     }  
  49.   
  50.     //close()关闭连接  
  51.     close(sockfd);  
  52. }  

tcp_server.c

 

 

[cpp] view
plain copy

 

  1. #include <unistd.h>  
  2. #include <stdio.h>  
  3. #include <stdlib.h>  
  4. #include <string.h>  
  5. #include <errno.h>  
  6. #include <sys/types.h>  
  7. #include <sys/socket.h>  
  8. #include <netinet/in.h>  
  9.   
  10. #define MAXLEN 4096  
  11.   
  12. main()  
  13. {  
  14.     int connfd;  
  15.     char buff[MAXLEN];  
  16.     int n;  
  17.   
  18.     //socket()建立socket  
  19.     int sockfd;  
  20.     if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){  
  21.         printf(“create socket error: %s(errno:%d)\n”,strerror(errno),errno);  
  22.         exit(0);  
  23.     }  
  24.       
  25.     struct sockaddr_in servaddr;  
  26.     servaddr.sin_family = AF_INET;  
  27.     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  
  28.     servaddr.sin_port = htons(6666);  
  29.       
  30.     //bind()绑定三个socket(ip=all,protocal=TCP,port=666六)  
  31.     if(bind(sockfd,(struct sockaddr *) &servaddr, sizeof(servaddr))==-1){  
  32.         printf(“bind socket error: %s(errno: %d)\n”,strerror(errno),errno);  
  33.         exit(0);  
  34.     }  
  35.   
  36.     //listen()监听  
  37.     if(listen(sockfd,10)==-1){  
  38.         printf(“listen socket error: %s(errno: %d)\n”,strerror(errno),errno);  
  39.         exit(0);  
  40.     }  
  41.   
  42.     //accept() & close()  
  43.     printf(“======waiting for client’s request======\n”);  
  44.     while(1){  
  45.         if((connfd=accept(sockfd, (struct sockaddr *)NULL,NULL))==-1){  
  46.             printf(“accept socket error: %s(errno: %d)”,strerror(errno),errno);  
  47.             continue;  
  48.         }  
  49.         struct sockaddr_in serv, guest;  
  50.         char serv_ip[20];  
  51.         char guest_ip[20];  
  52.         int serv_len = sizeof(serv);  
  53.         int guest_len = sizeof(guest);  
  54.         getsockname(connfd, (struct sockaddr *)&serv, &serv_len);  
  55.         getpeername(connfd, (struct sockaddr *)&guest, &guest_len);  
  56.         inet_ntop(AF_INET, &serv.sin_addr, serv_ip, sizeof(serv_ip));  
  57.         inet_ntop(AF_INET, &guest.sin_addr, guest_ip, sizeof(guest_ip));  
  58.         printf(“host %s:%d guest %s:%d\n”, serv_ip, ntohs(serv.sin_port), guest_ip, ntohs(guest.sin_port));  
  59.         n = recv(connfd, buff, MAXLEN,0);  
  60.         buff[n] = ‘\0’;  
  61.         printf(“recv msg from client: %s\n”, buff);  
  62.         close(connfd);  
  63.     }  
  64.   
  65.     //close()关闭连接  
  66.     close(sockfd);  
  67. }  

 

 

#include <stdio.h>

(二)op  钦定操作类型:

3.3.3 AF_INET UDP传输最轻便易行版本

图片 4

udp_client.c

 

[cpp] view
plain copy

 

  1. /** 
  2. *   @file: udpclient.c 
  3. *   @brief: A simple Udp server 
  4. *   @author: ToakMa <mchgloak1120@163.com> 
  5. *   @date:  2014/10/09 
  6. */  
  7.    
  8. #include <stdio.h>  
  9. #include <stdlib.h>  
  10. #include <string.h>  
  11. #include <strings.h>  
  12. #include <sys/types.h>  
  13. #include <sys/socket.h>  
  14. #include <netinet/in.h>  
  15. #include <arpa/inet.h>  
  16.    
  17. #define BUFF_SIZE 1024  
  18. #define PORT     9988  
  19.    
  20. int main(int argc, char *argv[])  
  21. {  
  22.     int sockfd;  
  23.     struct sockaddr_in remote_addr;  
  24.     int len;  
  25.     char buff[BUFF_SIZE];  
  26.    
  27.     //1. create a socket  
  28.     sockfd = socket(AF_INET, SOCK_DGRAM, 0);  
  29.     if (-1 == sockfd)  
  30.     {  
  31.         perror(“udp client socket: “);  
  32.         return -1;  
  33.     }  
  34.        
  35.     //2. prepare ip and port  
  36.     memset(&remote_addr, 0, sizeof(remote_addr));  
  37.     remote_addr.sin_family = AF_INET;  
  38.     remote_addr.sin_port   = htons(PORT);  
  39.     remote_addr.sin_addr.s_addr = inet_addr(argv[1]);  
  40.     bzero(&(remote_addr.sin_zero), 8);  
  41.        
  42.     //3. sendto  
  43.     strcpy(buff, “this a test\n”);  
  44.     printf(“sending : %s\n”, buff);  
  45.     len = sendto(sockfd, buff, strlen(buff), 0, (struct sockaddr *)&remote_addr, sizeof(remote_addr));  
  46.     if (len < 0)  
  47.     {  
  48.         perror(“udp client sendto :”);  
  49.         return -1;  
  50.     }  
  51.        
  52.     //4. close  
  53.     close(sockfd);  
  54.    
  55.     return 0;  
  56. }  

 

 

udp_server.c

 

[cpp] view
plain copy

 

  1. /** 
  2. *   @file: udpserver.c 
  3. *   @brief: A simple Udp server 
  4. *   @author: ToakMa <mchgloak1120@163.com> 
  5. *   @date:  2014/10/09 
  6. */  
  7. #include <stdlib.h>  
  8. #include <stdio.h>  
  9. #include <string.h>  
  10. #include <strings.h>  
  11. #include <sys/types.h>  
  12. #include <sys/socket.h>  
  13. #include <netinet/in.h>  
  14. #include <arpa/inet.h>  
  15.    
  16. #define PORT 9988  
  17. #define BUFF_SIZE 1024  
  18.    
  19. int main(int argc, char *argv[])  
  20. {  
  21.     int sockfd;  
  22.     int sin_len;  
  23.     struct sockaddr_in saddr;  
  24.     struct sockaddr_in remote_addr;  
  25.     char buff[BUFF_SIZE];     
  26.     int res, len;  
  27.    
  28.     //1. create socket  
  29.     sockfd = socket(AF_INET, SOCK_DGRAM, 0);  
  30.     if (-1 == sockfd)  
  31.     {  
  32.         perror(“Udp server socket: “);  
  33.         return -1;  
  34.     }  
  35.     printf(“Udp server socket create succ!\n”);  
  36.    
  37.     //2. prepare IP and port  
  38.     memset(&saddr, 0, sizeof(saddr));  
  39.     saddr.sin_family = AF_INET;  
  40.     saddr.sin_port   = htons(PORT);  
  41.     saddr.sin_addr.s_addr = INADDR_ANY;  
  42.     bzero(saddr.sin_zero, 8);  
  43.    
  44.     //3. bind  
  45.     res = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));  
  46.     if (-1 == res)  
  47.     {  
  48.         perror(“udp server bind: “);  
  49.         return -1;  
  50.     }  
  51.        
  52.     //4. recvfrom  
  53.     printf(“Wait for a packet …\n”);  
  54.     sin_len = sizeof(struct sockaddr_in);  
  55.     len = recvfrom(sockfd, buff, BUFF_SIZE, 0, (struct sockaddr *)&remote_addr, &sin_len);  
  56.     if (-1 == len)  
  57.     {  
  58.         perror(“udp server recvform: “);  
  59.         return -1;  
  60.     }  
  61.     buff[len] = ‘\0’;  
  62.    
  63.     printf(“Recived packet from %s, contents is: %s \n”, \  
  64.         inet_ntoa(remote_addr.sin_addr), buff);  
  65.    
  66.    
  67.     //5. close  
  68.     close(sockfd);  
  69.    
  70.     return 0;  
  71. }  

 

这几个客户和服务端程序有一个很奇怪的气象,正是服务端运转之后,接收客户端的连年,每一种连接创设三个新线程实行服务。当有四个客户端程序连接服务端(张开八个顶峰窗口开程序),全数的客户端都在疯狂与服务端进行数量传输时,把其中一个客户端程序(Ctrl+C)掉,那时服务端的长河也会立即停下,而服务端的甘休会有关把任何正在传输数据的客户端进度一齐终止掉。对应的失误的客户端和服务端的代码为如下所示。(PS:停止掉客户端不是每一回都让服务端挂掉,有时候会使得劳动端recv出错重回-一,被捕获,然后安全退出线程,进度依然安全。)

#include <stdlib.h>

     EPOLL_CTL_ADD:过去的事情件表中注册fd上的风浪

3.3.4 AF_INET UNIX本地传输最简易版本

unix_client.c

 

[cpp] view
plain copy

 

  1. #include <sys/types.h>  
  2. #include <sys/socket.h>  
  3. #include <stdio.h>  
  4. #include <sys/un.h>  
  5. #include <unistd.h>  
  6. #include <errno.h>   
  7.   
  8. int main()  
  9. {  
  10.       
  11.     int result;  
  12.     char ch = ‘A’;  
  13.   
  14.     //创建socket  
  15.     int sockfd;  
  16.     if((sockfd = socket(AF_UNIX,SOCK_STREAM,0) == -1){  
  17.         printf(“create socket error: %s(errno: %d)\n”, strerror(errno),errno);  
  18.     }  
  19.   
  20.     struct sockaddr_un address;  
  21.     address.sun_family = AF_UNIX;  
  22.     strcpy(address.sun_path,”server_socket”);  
  23.     int len = sizeof(address);  
  24.   
  25.     //connect()  
  26.     int result;  
  27.     if((result = connect(socket,(struct sockaddr*)&address,len)==-1){  
  28.         printf(“connect error: %s(errno: %d)\n”,strerror(errno),errno);    
  29.         exit(0);    
  30.     }  
  31.   
  32.     //read & write  
  33.     write(sockfd,&ch,1);  
  34.     read(sockfd,&ch,1);  
  35.     printf(“char from server = %c\n”,ch);  
  36.   
  37.     //close()  
  38.     close(sockfd);  
  39.     exit(0);  
  40. }  

unix_server.c

 

 

[cpp] view
plain copy

 

  1. #include <sys/types.h>  
  2. #include <sys/socket.h>  
  3. #include <stdio.h>  
  4. #include <sys/un.h>  
  5. #include <unistd.h>  
  6. #include <errno.h>   
  7.   
  8. int main()  
  9. {  
  10.     int server_len,client_len;  
  11.     struct socket_un server_address;  
  12.     struct socket_un client_address;  
  13.   
  14.     //删除在此以前套接字  
  15.     unlink(“server_socket”);  
  16.   
  17.     //socket  
  18.     int server_sockfd = socket(AF_UNIX,SOCK_STREAM,0);  
  19.   
  20.     server_address.sun_family = AF_UNIX;  
  21.     strcpy(server_address.sun_path,”server_socket”);  
  22.     server_len = sizeof(server_address);  
  23.   
  24.     //bind()  
  25.     bind(server_sockfd,(struct sockaddr*)&server_address,server_len);  
  26.   
  27.     //listen()  
  28.     listen(server_sockfd,5);  
  29.     while(1){  
  30.         char ch;  
  31.         printf(“server waiting\n”);  
  32.         client_len = sizeof(client_address);  
  33.         client_sockfd = accept(server_sockfd,(struct sockaddr*)&client_address,&client_len);  
  34.   
  35.         //read & write  
  36.         read(client_sockfd,&ch,1);  
  37.         ch++;  
  38.         write(client_sockfd,&ch,1);  
  39.         close(client_sockfd);  
  40.     }  
  41. }  

 

 

#include <errno.h>

     EPOLL_CTL_MOD:修改fd上的挂号事件

三.4 网络编制程序函数

Unix/Linux基本管理学之壹便是“一切皆文件”,都得以用“展开open –>
读写write/read –> 关闭close”

 

[plain] view
plain copy

 

  1. #include<sys/types.h>  
  2. #include<sys/socket.h>  

 

 

#include <string.h>

     EPOLL_CTL_DEL:删除fd上的注册事件

3.4.1 函数socket

成立套接字:socket函数对应于普通文书的开荒操作。普通文书的开荒操作再次来到2个文书讲述字,而socket()用于创造二个socket描述符(socket
descriptor),它唯1标志三个socket。

[plain] view
plain copy

 

  1. 函数原型:  
  2. int socket(int domain, int type, int protocol);  
  3. 参数表明:  
  4. domain:即协议域。常用的协议族有,AF_INET(ipv4)、AF_INET6(ipv6)、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。  
  5. type:指定socket类型。常用的socket类型有,SOCK_STREAM(TCP)、SOCK_DGRAM(UDP)、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。  
  6. protocol:内定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等。  
  7. int:重临值为-一意味着出错  

留意:并不是上边的type和protocol能够率性组合的,如SOCK_STREAM不得以跟IPPROTO_UDP组合。当protocol为0时,会活动选择type类型对应的暗中同意协议。

 

本着那一个情形本身做了有的测试。1、把客户端改为只收,服务端改为只发。

#include <unistd.h>

(叁)fd:要操作的文件讲述符(socket)

3.4.2 函数bind

命名套接字:bind()函数把3个地址族中的特定地点赋给socket。比方对应AF_INET、AF_INET陆就是把2个ipv肆或ipv陆地址和端口号组合赋给socket。

 

[plain] view
plain copy

 

  1. 函数原型:  
  2. int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);  
  3. 参数表明:  
  4. sockfd:是由socket()调用重回的套接口文件讲述符。  
  5. addr:传入数据结构sockaddr的指针,包含(IP,protocol,port)供给转移为通用地址类型struct sockaddr*  
  6. addrlen:以设置成sizeof(struct sockaddr)  
  7. int:再次回到值,-一代表出错  

 

[plain] view
plain copy

 

  1. ipv四地址结构【AF_INET】:  
  2. struct sockaddr_in {  
  3.     sa_family_t    sin_family; /* address family: AF_INET */  
  4.     in_port_t      sin_port;   /* port in network byte order */  
  5.     struct in_addr sin_addr;   /* internet address */  
  6. };  
  7.   
  8. /* Internet address. */  
  9. struct in_addr {  
  10.     uint32_t       s_addr;     /* address in network byte order */  
  11. };  

ipv六地址结构【AF_INET6】:

[plain] view
plain copy

 

  1. struct sockaddr_in6 {   
  2.     sa_family_t     sin6_family;   /* AF_INET6 */   
  3.     in_port_t       sin6_port;     /* port number */   
  4.     uint32_t        sin6_flowinfo; /* IPv6 flow information */   
  5.     struct in6_addr sin6_addr;     /* IPv6 address */   
  6.     uint32_t        sin6_scope_id; /* Scope ID (new in 2.4) */   
  7. };  
  8.   
  9. struct in6_addr {   
  10.     unsigned char   s6_addr[16];   /* IPv6 address */   
  11. };  

Unix域地址结构【AF_UNIX】:

[plain] view
plain copy

 

  1. #define UNIX_PATH_MAX    108  
  2.   
  3. struct sockaddr_un {   
  4.     sa_family_t sun_family;               /* AF_UNIX */   
  5.     char        sun_path[UNIX_PATH_MAX];  /* pathname */ 使用strcpy(address.sun_path,”server_socket”)   
  6. };  

平凡服务器在起步的时候都会绑定三个醒目的地点(如ip地址+端口号),用于提供劳动,客户就足以透过它来连接服务器;所以服务器端在listen在此之前会调用bind()。

而客户端就无须钦定,有系统活动分配二个端口号和本人的ip地址组合,在connect()时由系统随机生成三个。由于客户端没有必要一定的端口号,由此不必调用bind(),客户端的端口号由基本自动分配。注意,客户端不是不允许调用bind(),只是没有供给调用
bind()固定三个端口号,服务器也不是必须调用bind(),但若是服务器不调用bind(),内核会自动给服务器分配监听端口,每一次运营服务器时端口号都不平等,客户端要连接服务器就能够遭逢麻烦。

 

对server端的addr的初步化如下所示:

 

[cpp] view
plain copy

 

  1. memset(&servaddr, 0, sizeof(servaddr));  
  2. servaddr.sin_family = AF_INET;  
  3. servaddr.sin_port = htons(5188);  
  4. servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  

首先将总体结构体清零(也得以用bzero函数),然后设置地方类型为AF_INET,网络地址为INADD景逸SUV_ANY,那个宏表示本地的大肆IP地址,因为服务器恐怕有多个网卡,每种网卡也大概绑定三个IP地址,那样设置能够在富有的IP地址上监听,直到与有些客户端创建了连接时才规定下来到底用哪些IP地址,端口号为518八。

 

(客户端崩掉会招致服务端的send的res=-壹,百分之百被擒获到,从而安全退出线程,不会恐吓任何经过。

#include <netinet/in.h>

 (四)event:内定要监听fd的哪些业务。它是epoll_event结构指针类型:

3.4.3 函数listen

创办套接字队列

[plain] view
plain copy

 

  1. 函数原型:  
  2. int listen(int sockfd, int backlog);  
  3. 参数表达:  
  4. sockfd:即为要监听的socket描述字  
  5. backlog:相应socket能够排队的最罗安达接个数  

其次个参数是跻身队列中允许的连天的个数。进入的连天请求在运用系统调用accept()应答以前要在进入队列中伺机。那些值是队列中最多能够有所的伸手的个数。大诸多连串的缺省设置为20。你能够安装为伍要么拾。当出错时,listen()将会回来-1值。

其壹函数对于backlog的解说《unix互联网编制程序》P85是说已造成连接队列(ESTABLISHED)和未成功连接队列(SYN_KoleosCVD)之和(即等待连接数而非连接数)。服务器调用的accept()重临并收受这么些接二连三,纵然有雅量的客户端发起连接而服务器来比不上管理,尚未accept的客户端就处在连接等待情状,listen()表明sockfd处于监听状态,并且最多允许有backlog个客户端处于连接等待景况,借使收到到越来越多的三番五次请求就忽略。在管理器早期由于网络连接的处理速度比非常的慢,多少个冒出的伸手就可能导致系统管理不回复而引发错误,以往这一个数字的意思已经比较小了,今后软硬件质量差不离能担保这么些队列不容许满。连接的意况变为ESTABLISHED之后(实际是接2连3由从半接连队列转移到了成功握手的到位队列)才表示能够被accpet()管理。

服务端崩掉会招致客户端recv出错为0,但未曾被抓获,使得客户端在收不到数据的动静下直接死循环。客户端从未阻塞,recv的再次回到值是0。)

#define SERVPORT 3333

struct epoll_event

3.4.4 函数accept

收受连接

[plain] view
plain copy

 

  1. 函数原型:  
  2. int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);  
  3. 参数表达:  
  4. sockfd:是由socket()调用再次回到的套接口文件讲述符。  
  5. addr:传出连接客户的sockaddr指针,蕴涵(IP,protocol,port)  
  6. addrlen:传入内定客户结构addr的长短,重回时该值传出为连日来客户地址addr的实际上尺寸  
  7. int:重临值,-1象征出错  

调用accept()之后,将会回去一个全新的套接口文件讲述符来管理那些单个的接连。那样,对于同一个接连来讲,你就有了四个文本讲述符。原先的八个文书讲述符正在监听你钦点的端口,新的文本讲述符能够用来调用send()和recv()。

能够透过对套接字文件讲述符设置O_NONBLOCK来改换其是或不是封堵:

 

[cpp] view
plain copy

 

  1. int flags = fcntl(socket, F_GETFL,0);  
  2. fcntl(socket, F_SETFL, O_NONBLOCK| flags);  

 

 

 

#define BACKLOG
10                 //请求队列中的最大请求数

{

3.4.5 函数connect

[plain] view
plain copy

 

  1. 函数原型:  
  2. int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);  

客户端连接服务器,addr为流传参数,表示目标服务器的(ip,protocal,port)。

 

二、把客户端改为只发,服务端只收。

#define MAX_CONNECTED_NO 10  //最大是12八台Computer连续

     __unit32_t events;    // epoll事件

3.4.6 函数read与write

read()/write()
recv()/send()
readv()/writev()
recvmsg()/sendmsg()
recvfrom()/sendto()

 

[plain] view
plain copy

 

  1. #include <unistd.h>  
  2. ssize_t read(int fd, void *buf, size_t count);  
  3. ssize_t write(int fd, const void *buf, size_t count);  
  4.   
  5. #include <sys/types.h>  
  6. #include <sys/socket.h>  
  7. ssize_t send(int sockfd, const void *buf, size_t len, int flags);  
  8. ssize_t recv(int sockfd, void *buf, size_t len, int flags);  
  9.   
  10. ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,  
  11.                const struct sockaddr *dest_addr, socklen_t addrlen);  
  12. ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,  
  13.                struct sockaddr *src_addr, socklen_t *addrlen);  
  14.   
  15. ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);  
  16. ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);  

(客户端崩掉会促成服务端recv出错为0,但绝非被破获,从而多少个线程不能够退出,在死循环。

#define MAXDATASIZE 100

     epoll_data_t data;     // 用户数量

3.4.7 函数close

 

[plain] view
plain copy

 

  1. 函数原型:  
  2. int close(int fd);  

在意:close操作只是使相应socket描述字的引用计数-1,只有当引用计数为0的时候,才会触发TCP客户端向服务器发送终止连接请求。

服务端崩掉会产生客户端send出错为-壹,被抓走,从而安全退出。)

 

};

3.4.8 函数getsockname

函数重返与套接口关联的本土协议地址。

 

[plain] view
plain copy

 

  1. 函数原型:  
  2. int getsockname(int sockfd, struct sockaddr * localaddr, socken_t * addrlen);  

 

 

void pthread_recv(void *arg);

events:描述事件类型。events能够是以下多少个宏的集纳:

3.4.9 函数getpeername

函数重回与套接口关联的中距离协议地址。

 

[plain] view
plain copy

 

  1. 函数原型:  
  2. int getpeername(int sockfd,struct sockaddr* peeraddr,int* addrlen);  
  3. 参数表明:  
  4. sockfd:是由socket()调用再次回到的套接口文件讲述符。  
  5. peeraddr:传出数据结构sockaddr的指针,包含(IP,protocol,port)  
  6. addrlen:传出结构大小的指针  
  7. int:重临值,-一代表出错  

 

据此地点的奇特现象现身的来由是:

void Pthread_send(void * arg);

 EPOLLIN :表示对应的文本讲述符能够读(包蕴对端SOCKET不荒谬关闭);
EPOLLOUT:表示对应的文书讲述符能够写;

在对端崩溃之后,send出错只会回去-1,而recv出错有时回来-一,大多数时候再次回到0。只有在recv出错为0未曾被擒获,而又持续send,才会导致进程马上病逝(此时不也许捕获send的荒谬)。

 

 EPOLLP本田CR-VI:表示对应的文书讲述符有紧迫的数额可读(这里应该代表有带外数据来临);
EPOLLERubicon哈弗:表示对应的文书讲述符产生错误;

三.伍 互连网字节序与主机字节序转变

将主机字节序调换为互联网字节序(制止三头小端难题)

#include <netinet/in.h>

消除办法是:

int main()

EPOLLHUP:表示对应的公文讲述符被挂断;

3.5.1 函数htonl

 

[plain] view
plain copy

 

  1. uint32_t htonl(uint32_t hostlong);  

 

两端的程序扩张捕获recv的出错为0的图景,具体代码是if((res==-一)||(res==0))。那样不管哪端崩掉,对端都能够捕获出错的情况,处理失误的情形,不会冒出进度突然死去现象。

{

 EPOLLET: 将EPOLL设为边缘触发(艾德ge
Triggered)格局,那是周旋于水平触发(Level Triggered)来讲的。

3.5.2 函数htons

 

 

[plain] view
plain copy

 

  1. uint16_t htons(uint16_t hostshort);  

 

 

         struct sockaddr_in
server_sockaddr,client_sockaddr;

EPOLLONESHOT:只监听1遍事件,当监听完此次事件今后,若是还需求一连监听那么些socket的话,需求重新把那么些socket加入到EPOLL队列里。

3.5.3 函数ntohl

 

[plain] view
plain copy

 

  1. uint32_t ntohl(uint32_t netlong);  

 

 

 

         int sockfd,client_fd,sin_size,ret;

data成员:当中data.fd常用来装要操作的fd。

3.5.4 函数ntohs

[plain] view
plain copy

 

  1. uint16_t ntohs(uint16_t netshort);  

 

Client2.C程序:

         pthread_t id1,id2;

 

三.6 IP地址与主机字节序调换

 

#include <sys/types.h>

         sin_size =sizeof(struct sockaddr);

typedef union epoll_data

3.6.1 函数inet_pton

[将“点分十进制” -> “整数”]

 

[plain] view
plain copy

 

  1. 函数原型:  
  2. int inet_pton(int af, const char *src, void *dst);  
  3. 参数表达:  
  4. af:地址族,AF_INET为ipv4地址,AF_INET6为ipv6地址  
  5. src:为ip地址(ipv4例如1.1.1.1)  
  6. dst:函数将该地方转变为in_addr的结构体,并复制在*dst中  
  7. int:重返值:假诺函数出错将回来二个负值,并将errno设置为EAFNOSUPPORT,若是参数af钦命的地址族和src格式不对,函数将重返0。  

 

#include <sys/socket.h>

         //struct sockaddr_in
server_sockaddr,client_sockaddr;

{

3.6.2 函数inet_ntop

 

[将“整数” -> “点分拾进制”]

[plain] view
plain copy

 

  1. 函数原型:  
  2. const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);  

 

#include <stdio.h>

         //创立2个socket连接

     void *ptr;

三.柒 主机数据库函数

#include <netinet/in.h>

     //目前都是AF_INET(ipv4);SOCK_STREAM(TCP),如果是UDP,则SOCK_DGRAM

      int fd;

3.7.1 函数gethostname

它回到(本地)Computer的名字,存款和储蓄在hostname中,大小为size

 

[plain] view
plain copy

 

  1. 函数原型:  
  2. int gethostname(char*hostname,size_tsize);  
  3. 参数表达:  
  4. int:重返值gethostname将再次回到0。假若败北,它将再次回到-1。   

#include <arpa/inet.h>

         if((sockfd =
socket(AF_INET,SOCK_STREAM,0))==-1){

      __uint32_t u32;

3.7.2 函数gethostbyname

基于域名依然主机名获取新闻

 

[cpp] view
plain copy

 

  1. 函数原型:  
  2. struct hostent *gethostbyname(const char *name);  
  3. 参数表达:  
  4. name:主机名,如”www.baidu.com”  
  5. hostend:再次来到值。若是函数调用退步,将重回NULL。  

 

hostend的协会如下:

 

[cpp] view
plain copy

 

  1. struct hostent   
  2. {  
  3.   char  *h_name;            //表示的是主机的规范名,举例www.google.com的规范名其实是www.l.google.com  
  4.   char  **h_aliases;        //表示的是主机的外号  
  5.   int   h_addrtype;         //IP地址的品类  
  6.   int   h_length;           //IP地址的尺寸  
  7.   char  **h_addr_list;      //主机的ip地址,注意那是以互联网字节顺序保存的三个值,用inet_ntop恢复  
  8. };  

 

以身作则代码:

 

[cpp] view
plain copy

 

  1. #include <netdb.h>  
  2. #include <sys/socket.h>  
  3. #include <stdio.h>  
  4. #include <unistd.h>  
  5.   
  6. int main(int argc, char **argv)  
  7. {  
  8.     char *host,**names;  
  9.     struct hostent *hostinfo;  
  10.     cha str[32];  
  11.     /* 赚取命令后率先个参数,即要解析的域名或主机名 */  
  12.     if(argc==1){  
  13.         char myname[256];  
  14.         gethostname(myname,255);  
  15.         host=myname;  
  16.     }  
  17.     else{  
  18.         host=argv[1];  
  19.     }  
  20.     /* 调用gethostbyname()。调用结果都留存hostinfo中 */  
  21.     if( (hostinfo = gethostbyname(host) ) == NULL )  
  22.     {  
  23.         printf(“gethostbyname error for host:%s/n”, host);  
  24.         exit(1); /* 假如调用gethostbyname产生错误,重回一 */  
  25.     }  
  26.     /* 将主机的标准名打出来 */  
  27.     printf(“official hostname:%s/n”,hostinfo->h_name);  
  28.     /* 主机只怕有多少个小名,将有着外号分别打出去 */  
  29.     for(names = hostinfo->h_aliases; *names != NULL; names++)  
  30.         printf(” alias:%s/n”,*names);  
  31.     /* 总局址类型,将地点打出去 */  
  32.     switch(hostinfo->h_addrtype)  
  33.     {  
  34.     case AF_INET:  
  35.     case AF_INET6:  
  36.         names=hostinfo->h_addr_list;  
  37.         /* 将刚刚得到的全部地方都打出来。当中调用了inet_ntop()函数 */  
  38.         for(;*names!=NULL;names++)  
  39.             printf(” address:%s/n”, inet_ntop(hostinfo->h_addrtype, *names, str, sizeof(str)));  
  40.         break;  
  41.     default:  
  42.         printf(“unknown address type/n”);  
  43.         break;  
  44.     }  
  45.     exit(0);  
  46. }   

 

#include <unistd.h>

                   perror(“socket”);

      __uint64_t u64;

3.7.3 函数gethostbyaddr

依据ip地址(互联网字节序)获取音讯

 

[cpp] view
plain copy

 

  1. 函数原型:  
  2. struct hostent *gethostbyaddr(const char *name,int len,int type)  
  3. 参数表明:  
  4. name:ip地址,例如: inet_addr(“192.168.4.111”)  
  5. len:  
  6. type:  
  7. hostend:重回值。倘若函数调用退步,将回到NULL。  

 

 

#include <stdlib.h>

                   exit(1);

  } epoll_data_t;

3.7.4 函数getservbyname

 

用来依据给定的名字来搜寻相应的服务器,再次回到对应于给定服务名和协议名的连带服务音讯

 

[cpp] view
plain copy

 

  1. 函数原型:  
  2. struct servernt *gerservbyname(const char *servname,const char *protoname)  
  3. 参数表明:  
  4. servname:例如smtp  
  5. protoname:例如tcp  

 

servent结构如下:

 

[cpp] view
plain copy

 

  1. struct servent{  
  2.     char *s_name;    /*服务的正式名字*/  
  3.     char **s_aliases;/*别称列表*/  
  4.     int s_port;      /*服务的端口号*/  
  5.     char *s_proto;   /*劳务的协商,如tcp或udp*/  
  6. }  

 

 

         }

 

3.7.5 函数getservbyport

回去与给定服务名对应的盈盈名字和服务号消息的servent结构指针(注意参数port的值必须是互连网字节序类型的,所以在行使的时候要求运用函数htons(port)来开始展览转变)。

 

[cpp] view
plain copy

 

  1. 函数原型:  
  2. struct servent *getservbyport(int port,const char *proroname);  
  3. 函数示例:  
  4. sptr=getservbyport(htons(53),”udp”);  

示范代码:

 

[cpp] view
plain copy

 

  1. #收获任性已知主机的日期和时间  
  2. #include <netdb.h>  
  3. #include <sys/socket.h>  
  4. #include <netinet/in.h>  
  5. #include <stdio.h>  
  6. #include <unistd.h>  
  7.   
  8. int main(int argc, char **argv)  
  9. {  
  10.     char *host;  
  11.     struct hostent * hostinfo;  
  12.     struct servent * servinfo;  
  13.     /* 取得命令后首先个参数,即要解析的域名或主机名 */  
  14.     if(argc==1){  
  15.         host=”localhost”;  
  16.     }  
  17.     else{  
  18.         host=argv[1];  
  19.     }  
  20.     /* 调用gethostbyname()。调用结果都留存hostinfo中 */  
  21.     if( (hostinfo = gethostbyname(host) ) == NULL )  
  22.     {  
  23.         printf(“gethostbyname error for host:%s/n”, host);  
  24.         exit(1); /* 要是调用gethostbyname发生错误,重回1 */  
  25.     }  
  26.     if ( (servinfo = getservbyname(“daytime”,”tcp”)) );  
  27.     {  
  28.         printf(“no daytime service/n”);  
  29.         exit(1); /* 假若调用getservbyname产生错误,再次回到1 */  
  30.     }  
  31.     /* 将daytime的端口打字与印刷出来*/  
  32.     printf(“daytime port is %d/n”,ntohs(servinfo -> s_port));  
  33.   
  34.     int sockfd = socket(AF_INET, SOCK_STREAM,0);  
  35.     struct sockaddr_in address;  
  36.     address.sin_family = AF_INET;  
  37.     address.sin_port = servinfo -> s_port;  
  38.     address.sin_addr= *(struct in_addr*)*hostinfo->h_addr_list;  
  39.     int len = sizeof(address);  
  40.   
  41.     int result;  
  42.     if((result = connect(socket,(struct sockaddr*)&address,len))==-1){  
  43.         printf(“connect error: %s(errno: %d)\n”,strerror(errno),errno);    
  44.         exit(0);   
  45.     }  
  46.   
  47.     char buffer[128];  
  48.     result = read(sockfd,buffer,sizeof(buffer));  
  49.     buffer[result]=’\0′;  
  50.     printf(“read %d bytes: %s”,result,buffer);  
  51.   
  52.     close(sockfd);  
  53.     exit(0);  
  54. }   

int main(int argc,char* argv[])                         

         printf(“socket
success!,sockfd=%d\n”,sockfd);

 

三.伍 多客户编制程序

{  
                                       //测试方法,把client2和server2放在同样

         //bind用于地点IP地址和端口的绑定

3、int epoll_wait ( int epfd, struct epoll_event* events, int
maxevents, int timeout );

三.伍.一 阻塞与非阻塞

(1)阻塞block
   
所谓阻塞方式block,顾名思义,正是经过也许线程实行到这一个函数时务必等待有些事件的发生,假使事件尚未发生,进程或线程就被打断(进度Sleep),函数无法及时赶回。
    举例socket编制程序中connect、accept、recv、recvfrom这样的短路程序。
   
再如大部分的函数调用、语句实施,严峻来讲,他们都以以堵塞格局推行的。
(2)非阻塞non-block
   
所谓非阻塞方式non-block,就是经过或线程试行此函数时不必非要等待事件的发出,一旦实施一定回来,以重临值的不等来展现函数的实施景况,纵然事件发生则与阻塞格局一样,若事件尚未发生则赶回叁个代码来告诉事件未生出,而经过或线程继续实践,所以效用较高。
 
 非阻塞I/O有3个弱点,若是持有设备都一贯尚未数量到达,调用者必要频仍查询做无用功,假使打断在那边,操作系统能够调治其余进度实行,就不会做无用功了。?????

int sockfd;                              //恐怕差异文件夹下都得以

         server_sockaddr.sin_family=AF_INET;

epoll_wait的行事流程是:等待,如若有epoll事件发生立时回到,不然等待timeout微秒。重返时拷贝要拍卖的轩然大波到events指向的数组,重返就绪的文书讲述符的个数,失利时再次回到-一并安装errno。 

3.5.2 函数select

 

[cpp] view
plain copy

 

  1. 函数原型:  
  2. int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);    
  3. 参数表达:  
  4. nfds:为文件讲述符集结中的最大值+1,幸免函数检查fd_set的所有1024位  
  5. readfds:被监督是或不是能够读   
  6. writefds:被监督是还是不是能够写   
  7. exceptfds:被监督是或不是产生尤其   
  8. timeout:超时时间,Linux重回时会被修改成剩余的时刻。  
  9. int:再次来到值,再次回到状态爆发变化的描述符的总数,错误重临-壹,超时重临0。  

 

关于timeout:

 

[cpp] view
plain copy

 

  1. struct timeval结构如下:  
  2. struct timeval  
  3. {  
  4.        long tv_sec;  //seconds  
  5.        long tv_usec; //microseconds  
  6. };  

 

一,若timeout设置为t>0,表示等待固定时间,有1个fd位被置为一恐怕时间耗尽,函数均重临。
二,若timeout设置为t=0,表示非阻塞,函数检查完每一种fd后即时赶回。
叁,若timeout设置为t=”NULL”,表示阻塞,直到有一个fd位被置为一函数才回去。

[cpp] view
plain copy

 

  1. 相关操作:    
  2. FD_ZERO(fd_set *set);         //fd_set繁多系统贯彻为bit arrays,将具有的公文讲述符从fd_set中清空    
  3. FD_CLR(int fd, fd_set *set);  //从set中清除fd      
  4. FD_SET(int fd, fd_set *set);  //将fd添加到set中     
  5. FD_ISSET(int fd, fd_set *set);//判别描述符fd是还是不是在给定的叙述符集set中,经常协作select函数使用,由于select函数成功再次回到时会将未筹算好的叙说符位清零。平日大家使用FD_ISSET是为了检查在select函数再次回到后,有个别描述符是不是计划好,以便举行接下去的管理操作。       

常用代码:

 

[cpp] view
plain copy

 

  1. fd_set  rdfds;  
  2. struct timeval tv;  
  3. tv.tv_sec = 1;  
  4. tv.tv_uses = 500;  
  5. int ret;  
  6. FD_ZERO(&rdfds);  
  7. FD_SET(socket, &rdfds);  
  8. ret = select (socket + 1, %rdfds, NULL, NULL, &tv);  
  9. if(ret < 0)   
  10.  perror (“select”);  
  11. else if (ret == 0)   
  12.  printf(“time out”);  
  13. else {  
  14.        printf(“ret = %d/n”,ret);  
  15.        if(FD_ISSET(socket, &rdfds)){  
  16.        /* 读取socket句柄里的数据 */  
  17.        recv( );  
  18.        }  
  19. }  

         int len,i,res;                            
//八个终端运营./client二  别的二个终端

         server_sockaddr.sin_port=htons(SERVPORT);

timeout:钦点epoll的晚点时间,单位是纳秒。当timeout为-1是,epoll_wait调用将恒久阻塞,直到有些事件时有产生。当timeout为0时,epoll_wait调用将随即回去。 

3.5.3 函数pselect

 

[cpp] view
plain copy

 

  1. 函数原型:  
  2. #include <sys/select.h>  
  3. int pselect(int maxfdp1, fd_set *restrict readfds, fd_set *restrict writefds,fd_set *restrict exceptfds, const struct timespec *restrict tsptr,const sigset_t *restrict sigmask);  
  4. 参数表明  
  5. 返回值:Returns: count of ready descriptors, 0 on timeout, -1 on error  

它与select的分别在于:
1,pselect使用timespec结构钦点超时值。timespec结构以秒和飞秒表示时间,而非秒和阿秒。
二,pselect的超时值被声称为const,那保险了调用pselect不会改换timespec结构。
3,pselect可采用三个可挑选的频限信号屏蔽字。在调用pselect时,以原子操作的章程安装该时域信号屏蔽字,在回到时回涨原先的频域信号屏蔽字。

 

         struct sockaddr_in address;                //运转  ./server贰就能够观望结果了

         server_sockaddr.sin_addr.s_addr=INADDR_ANY;
        //htonl(INADDR_ANY)

maxevents:钦命最多监听多少个事件。即使events指向的是十多个单元的构造体数组,那么就设置maxevents为20。 

3.5.4 函数poll

[cpp] view
plain copy

 

  1. 函数原型:  
  2. #include <sys/poll.h>  
  3. int poll (struct pollfd *fds, unsigned int nfds, int timeout);  
  4. 参数表达:  
  5. timeout:【值=INFTIM】表示永恒等待【值=0】表示立刻回去,不封堵进度【值>0】等待内定数量的皮秒数。  

pollfd结构表达:

 

[cpp] view
plain copy

 

  1. struct pollfd {  
  2.      int fd; /* 文件讲述符 */  
  3.      short events; /* 等待的轩然大波 */  
  4.      short revents; /* 发生的风浪 */  
  5. };  
  6. poll函数可用的测试值:  
  7. 常量  说明  
  8. POLLIN  普通或先期级带多少可读  
  9. POLLRubiconDNO本田UR-VM  普通数据可读  
  10. POLLPAJERODBAND  优先级带多少可读  
  11. POLLPSportageI 高优先级数据可读  
  12. POLLOUT 普通数据可写  
  13. POLLWSportageNOLX570M  普通数据可写  
  14. POLLWRBAND  优先级带多少可写  
  15. POLLE景逸SUV奥迪Q7 产生错误  
  16. POLLHUP 发生挂起  
  17. POLLNVAL    描述字不是三个展开的公文  
  18. 在意:后多个只好当做描述字的归来结果存款和储蓄在revents中,而无法当做测试条件用于events中。  

 

 

         int result;

     bzero(&(server_sockaddr.sin_zero),8);

events:
events指向的数组师长拷贝全部就绪的轩然大波,从根本领件表中。events的成员是struct
epoll_event类型,一般包涵events(其值是如:EPOLLIN、EPOLLOUT等)。还有3个是data.fd,包蕴拷贝的事件对应的socket,如若是服务器监听socket,表明是有用户连接到那个socket,纵然是别的已经几次三番好的socket,表明是有数量要发送或然吸收。

3.5.5 函数epoll

总结有七个函数:

 

[cpp] view
plain copy

 

  1. int epoll_create(int size);  

开创三个epoll的句柄,size用来报告内核那么些监听的数码一共有多大。那个参数区别于select()中的第一个参数,给出最大监听的fd+1的值。必要小心的是,当创立好epoll句柄后,它便是会占有2个fd值,在linux下1旦翻开/proc/进度id/fd/,是能够见到那个fd的,所以在利用完epoll后,必须调用close()关闭,不然恐怕导致fd被耗尽。

[cpp] view
plain copy

 

  1. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);  

epoll的风浪注册函数,它区别与select()是在监听事件时告诉内核要监听什么类型的风浪,而是在那边先注册要监听的风浪类型。第三个参数是epoll_create()的重回值,第3个参数表示动作,用多少个宏来表示:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中除去2个fd;
其两个参数是索要监听的fd,第多少个参数是报告内核须要监听什么事。
struct epoll_event结构如下:

[cpp] view
plain copy

 

  1. struct epoll_event {  
  2.   __uint32_t events;  /* Epoll events */  
  3.   epoll_data_t data;  /* User data variable */  
  4. };  

events能够是以下多少个宏的会集:
EPOLLIN :表示对应的文书讲述符能够读(包蕴对端SOCKET符合规律关闭);
EPOLLOUT:表示对应的文书讲述符可以写;
EPOLLPPRADOI:表示对应的文件讲述符有急切的数码可读(这里应该代表有带外数据来临);
EPOLLE本田CR-V中华V:表示对应的文件讲述符产生错误;
EPOLLHUP:表示对应的公文讲述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(艾德ge
Triggered)形式,这是对峙于水平触发(Level Triggered)来讲的。
EPOLLONESHOT:只监听三次事件,当监听完本次风云过后,即使还亟需继续监听那些socket的话,要求再一次把这几个socket参与到EPOLL队列里

[cpp] view
plain copy

 

  1. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);  

等候事件的发出,类似于select()调用。参数events用来从水源获得事件的集合,maxevents告之根本那么些events有多大,这么些maxevents的值不可能超过创立epoll_create()时的size,参数timeout是过期时间(微秒,0会马上赶回,-一将不明确,也有
说法说是恒久阻塞)。该函数重临必要管理的事件数量,如重临0表示已逾期。

 

 

 

在前边讲过的客户/服务器程序中,服务器只好管理贰个客户端的央求,如何同时服务三个客户端呢?在未讲到select/poll/epoll等高端IO在此以前,相比较老土的不二诀倘若利用fork来促成。网络服务器一般用fork来还要服务八个客户端,父进程专责监听端口,每一回accept三个新的客户端连接就fork出一个子经过专门服务那几个客户端。但是子进度退出时会产生僵尸进度,父进程要专注管理SIGCHLD时域信号和调用wait清理僵尸进度,最简便的主意正是一向忽略SIGCHLD信号。

1,这里为什么会有僵尸进程?

2,实现P2P

3,signal(SIGCHLD, SIG_IGN);

         char ch = ‘A’;                       
//古怪,三个client运行八个本子,每一回获得的

         //把本端的新闻绑定

若果事件数组中的数据得到管理,那么内核事件表中的事件就能够被去除,下次wait就从未有过那多少个socket的风云了。 

         char buff[1024];                    
//居然是同贰个socket。改个名字也非凡

         if(bind(sockfd,(struct sockaddr
*)&server_sockaddr,sizeof(struct sockaddr))==-1)

 

        

      {              

实例代码:

         sockfd = socket(AF_INET,SOCK_STREAM,0);              //

           perror(“bind”);

epoll.c

         printf(“socket is %d\n”,sockfd);

               exit(1);

#include <stdio.h>

         address.sin_family = AF_INET;

         }

#include <sys/epoll.h>

         address.sin_addr.s_addr = inet_addr(“192.168.131.129”);

         printf(“bind success!\n”);

#include <sys/socket.h>

         address.sin_port = htons(9734);

         if(listen(sockfd,BACKLOG)==-1)

#include <stdlib.h>

         len = sizeof(address);

     {

#include <netinet/in.h>                 //包含sockaddr_in定义

        

                   perror(“listen”);

#include <errno.h>

         result = connect(sockfd,(struct sockaddr*)&address,len);

                   exit(1);

#include <string.h>                     //包含memset strncpy

         if(result == -1)

         }

 

         {

         printf(“listening….\n”);

 

                   perror(“oops:client1”);

     //接受并保留客户端的音讯

int main(int argc,char* argv[])   //主函数

                   exit(-1);

         if((client_fd=accept(sockfd,(struct
sockaddr *)&client_sockaddr,&sin_size))==-1)

{

         }

     {                

 

        

perror(“accept”);

      int epfd1;int result;

         memset(buff,0,1024);

                exit(1);

      int server_len,client_len;

         i = 0;

         }

      int server_sockfd,client_sockfd;

         //for(i=0;i<100;i++)

         printf(“client_fd=%d”,client_fd);

      struct sockaddr_in server_address;       //定义在
<netinet/in.h>

         for(;;)

         pthread_create(&id1,NULL,(void *)
pthread_recv,(void*)&client_fd);

      struct sockaddr_in client_address;

         {

         if(ret != 0)

      struct epoll_event ev1;

                  

                   perror(“pthread_recv
creat”);

      struct epoll_event ev[20];

                  

         ret=pthread_create(&id2,NULL,(void
*)Pthread_send,(void*)&client_fd);

      int epollreturn;

                   res = send(sockfd,&ch,1,0);

         if(ret != 0)

      int i,j,res;

                   if(res==-1)

                   perror(“Pthread_send
creat”);

      int sockfd;

                   {

         pthread_join(id1,NULL);

      char ch = ‘0’;

发表评论

电子邮件地址不会被公开。 必填项已用*标注