UNIX环境高级编程学习之第十六章网络IPC:套接字 - 非阻塞的Socket通信EPoll模型(多路复用), 实用Socket通信模板


 

[cpp]  view plain copy
  1. /* User:Lixiujie          
  2. * Date:20101207 
  3. * Desc:Unix(Linux)非阻塞的Socket通信EPoll模型,多路复用,TCP服务器端, 向客户端发送响应信息。 
  4. * File:tcp_server_epoll.c   
  5. * System:Ubuntu 64bit   
  6. * gcc tcp_server_epoll.c -o  tcp_server_epoll 
  7. * tcp_server_epoll 7878 
  8. * 
  9. EPoll 函数介绍 
  10. epoll是Linux内核为处理大批量句柄而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。 
  11.  
  12. EPoll 优点 
  13. 1、它保留了poll的两个相对与select的优点 
  14. 2、epoll_wait的参数events作为出参,直接返回了有事件发生的fd,epoll_wait的返回值既是发生事件的个数,省略了poll中返回之后的循环操作。 
  15. 3、不再象select、poll一样将标识符局限于fd,epoll中可以将标识符扩大为指针,大大增加了epoll模型下的灵活性。 
  16.  
  17. EPoll 使用说明事项 
  18. 1、如果fd被注册到两个epoll中时,如果有时间发生则两个epoll都会触发事件。 
  19. 2、如果注册到epoll中的fd被关闭,则其会自动被清除出epoll监听列表。注意:关闭自动清除,不用手机清除 
  20. 3、如果多个事件同时触发epoll,则多个事件会被联合在一起返回。 
  21. 4、epoll_wait会一直监听epollhup事件发生,所以其不需要添加到events中。 
  22. 5、为了避免大数据量io时,et模式下只处理一个fd,其他fd被饿死的情况发生。linux建议可以在fd联系到的结构中增加ready位,然后epoll_wait触发事件之后仅将其置位为ready模式,然后在下边轮询ready fd列表。 
  23. 6、epoll_ctl epoll的事件注册函数,其events参数可以是以下宏的集合: 
  24.         EPOLLIN:    表示对应的文件描述符可以读(包括对端SOCKET正常关闭); 
  25.         EPOLLOUT:   表示对应的文件描述符可以写; 
  26.         EPOLLPRI:   表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来); 
  27.         EPOLLERR:   表示对应的文件描述符发生错误;写已关闭socket pipe broken  
  28.         EPOLLHUP:   表示对应的文件描述符被挂断;譬如收到RST包。在注册事件的时候这个事件是默认添加。 
  29.         EPOLLRDHUP: 表示对应的文件描述符对端socket关闭事件,主要应用于ET模式下。 
  30.         在水平触发模式下,如果对端socket关闭,则会一直触发epollin事件,驱动去处理client socket。 
  31.         在边沿触发模式下,如果client首先发送协议然后shutdown写端。则会触发epollin事件。但是如果处理程序只进行一次recv操作时,根据recv收取到得数据长度来判读后边是否还有需要处理的协议时,将丢失客户端关闭事件。 
  32.  
  33.         EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。 
  34.         EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里 
  35.  
  36. EPoll 工作模式 
  37. 1、水平触发Level Triggered (LT) 是EPoll的缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你 的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表. 
  38.  
  39. 2、边缘触发Edge  Triggered (ET) 是EPoll的高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述 符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致 了一个EWOULDBLOCK 错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once),不过在TCP协议中,ET模式的加速效用仍需要更多的benchmark确认。 
  40. 内核的读buffer有内核态主动变化时,内核会通知你, 无需再去mod。写事件是给用户使用的,最开始add之后,内核都不会通知你了,你可以强制写数据(直到EAGAIN或者实际字节数小于 需要写的字节数),当然你可以主动mod OUT,此时如果句柄可以写了(send buffer有空间),内核就通知你。  
  41. 这里内核态主动的意思是:内核从网络接收了数据放入了读buffer(会通知用户IN事件,即用户可以recv数据)  
  42. 并且这种通知只会通知一次,如果这次处理(recv)没有到刚才说的两种情况(EAGIN或者实际字节数小于 需要读写的字节数),则该事件会被丢弃,直到下次buffer发生变化。  
  43. 与LT的差别就在这里体现,LT在这种情况下,事件不会丢弃,而是只要读buffer里面有数据可以让用户读,则不断的通知你。 
  44.  
  45. EPoll 函数使用介绍 
  46. 1、EPoll 创建epoll句柄函数。 
  47. int epoll_create(int size); 
  48.         参数size:用来告诉内核要监听的数目一共有多少个。 
  49.         返回值:成功时,返回一个非负整数的文件描述符,作为创建好的epoll句柄。调用失败时,返回-1,错误信息可以通过errno获得。 
  50.     说明:创建一个epoll句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。 
  51.  
  52. 2、EPoll 注册修改删除文件描述符到epoll句柄函数 
  53. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 
  54.     参数epfd:epoll_create()函数返回的epoll句柄。 
  55.         参数op:操作选项。 
  56.         参数fd:要进行操作的目标文件描述符。 
  57.         参数event:struct epoll_event结构指针,将fd和要进行的操作关联起来。 
  58.         返回值:成功时,返回0,作为创建好的epoll句柄。调用失败时,返回-1,错误信息可以通过errno获得。 
  59.         说明:epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。 
  60.         参数op的可选值有以下3个: 
  61.         EPOLL_CTL_ADD:注册新的fd到epfd中; 
  62.         EPOLL_CTL_MOD:修改已经注册的fd的监听事件; 
  63.         EPOLL_CTL_DEL:从epfd中删除一个fd; 
  64.  
  65.         events可以是以下几个宏的集合: 
  66.         EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭); 
  67.         EPOLLOUT:表示对应的文件描述符可以写; 
  68.         EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来); 
  69.         EPOLLERR:表示对应的文件描述符发生错误; 
  70.         EPOLLHUP:表示对应的文件描述符被挂断; 
  71.         EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。 
  72.         EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里 
  73.  
  74. 3、EPoll 等待事件的产生函数 
  75. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout); 
  76.         参数epfd:epoll_create()函数返回的epoll句柄。 
  77.         参数events:struct epoll_event结构指针,用来从内核得到事件的集合。 
  78.         参数 maxevents:告诉内核这个events有多大 
  79.         参数 timeout: 等待时的超时时间,以毫秒为单位。 
  80.         返回值:成功时,返回需要处理的事件数目。调用失败时,返回0,表示等待超时。 
  81.         说明:等待事件的产生。 
  82.         timeout值 说明  
  83.         -1 永远等待  
  84.         0 立即返回,不阻塞进程  
  85.         >0 等待指定数目的毫秒数  
  86.  
  87. EPoll 注意事项 
  88.         建立连接的时候epoll_add IN和OUT事件, 后面就不需要管了  
  89.         每次read/write的时候,到两种情况下结束:  
  90.         1 发生EAGAIN  
  91.         2 read/write的实际字节数小于 需要读写的字节数  
  92.         对于第二点需要注意两点:  
  93.         A:如果是UDP服务,处理就不完全是这样,必须要recv到发生EAGAIN为止,否则就丢失事件了  
  94.         因为UDP和TCP不同,是有边界的,每次接收一定是一个完整的UDP包,当然recv的buffer需要至少大于一个UDP包的大小  
  95.         随便再说一下,一个UDP包到底应该多大?  
  96.         对于internet,由于MTU的限制,UDP包的大小不要超过576个字节,否则容易被分包,对于公司的IDC环境,建议不要超过1472,否则也比较容易分包。  
  97.  
  98.         B 如果发送方发送完数据以后,就close连接,这个时候如果recv到数据是实际字节数小于读写字节数,根据开始所述就认为到EAGIN了从而直接返回,等待下一次事件,这样是有问题的,close事件丢失了!  
  99.         因此如果依赖这种关闭逻辑的服务,必须接收数据到EAGIN为止。 
  100.  
  101.  
  102. */  
  103. #include <stdio.h>  
  104. #include <string.h>  
  105. #include <stdlib.h>  
  106. #include <errno.h>  
  107.   
  108. #include <sys/types.h>   
  109. #include <sys/socket.h> /* socket bind listen connect accept send recv */  
  110. #include <arpa/inet.h>  /* htons ntohs htonl ntohl inet_addr inet_ntoa */  
  111. #include <netinet/in.h> /* sockaddr_in */  
  112.   
  113. #include <sys/epoll.h>  
  114. #include <unistd.h>  
  115. #include <fcntl.h>  
  116.   
  117.   
  118. #define BUFLEN 1024  
  119. #define QLEN 20  
  120.   
  121. /* 传送信息结构体 */  
  122. typedef struct _MyMsg{  
  123.         char szCmd[16];/* message command  
  124.                                         * RE_LINK:test link request 
  125.                                         * RESP_LINK:test link response 
  126.                                         * RE_EXIT: exit request 
  127.                                         * RESP_TEXT: exit response 
  128.                                         * RE_DATA: data request 
  129.                                         * RESP_DATA: data response 
  130.                                         */  
  131.         int  iLen;     /* message data length*/  
  132.         char szData[0];/* message data */  
  133. }MyMsg;  
  134.   
  135. /* 信息处理 */  
  136. MyMsg * MsgProcess(MyMsg *pMsgIn){  
  137.         char szBuf[BUFLEN] = { 0x00 };  
  138.         char szTmp[BUFLEN] = { 0x00 };  
  139.         MyMsg *pMsg = NULL;  
  140.         FILE *fp;  
  141.         if (strcmp(pMsgIn->szCmd, "RE_LINK") == 0){  
  142.                 pMsg = (MyMsg *)malloc(sizeof(MyMsg));  
  143.                 memset(pMsg, 0, sizeof(MyMsg));  
  144.                 strcpy(pMsg->szCmd, "RESP_LINK");  
  145.         }else if (strcmp(pMsgIn->szCmd, "RESP_LINK") == 0){  
  146.         }else if (strcmp(pMsgIn->szCmd, "RE_EXIT") == 0){  
  147.                 pMsg = (MyMsg *)malloc(sizeof(MyMsg));  
  148.                 memset(pMsg, 0, sizeof(MyMsg));  
  149.                 strcpy(pMsg->szCmd, "RESP_TEXT");  
  150.         }else if (strcmp(pMsgIn->szCmd, "RESP_TEXT") == 0){  
  151.         }else if (strcmp(pMsgIn->szCmd, "RE_DATA") == 0){  
  152.                 memset(szBuf, 0, BUFLEN);  
  153.                 strncpy(szBuf, pMsgIn->szData, pMsgIn->iLen);  
  154.                 if ((fp = popen(szBuf, "r")) == NULL){  
  155.                         memset(szBuf, 0, BUFLEN);  
  156.                         sprintf(szBuf, "error: %s\n", strerror(errno));  
  157.                 }else{  
  158.                         memset(szTmp, 0, BUFLEN);  
  159.                         while (fgets(szTmp, BUFLEN, fp) != NULL){  
  160.                                 strcat(szBuf, szTmp);  
  161.                                 memset(szTmp, 0, BUFLEN);  
  162.                         }  
  163.                         pclose(fp);  
  164.                 }  
  165.                 pMsg = (MyMsg *)malloc(sizeof(MyMsg)+ strlen(szBuf)+1);  
  166.                 memset(pMsg, 0, sizeof(MyMsg));  
  167.                 strcpy(pMsg->szCmd, "RESP_DATA");  
  168.                 pMsg->iLen = strlen(szBuf)+1;  
  169.                 strcpy(pMsg->szData, szBuf);  
  170.         }else if (strcmp(pMsgIn->szCmd, "RESP_DATA") == 0){  
  171.         }  
  172.         return pMsg;  
  173. }  
  174.   
  175. /* Socket结构体 */  
  176. typedef struct _SockNode{  
  177.         int sock;  
  178.         struct sockaddr_in addr;  
  179.         struct _SockNode *pNext;  
  180. }SockNode;  
  181.   
  182. /* create SockNode */  
  183. SockNode* createSockNode(int sock, struct sockaddr_in *pAddr){  
  184.         SockNode *p = NULL;  
  185.         if ((p = (SockNode *)malloc(sizeof(SockNode))) != NULL){  
  186.                 p->sock = sock;  
  187.                 memcpy(&(p->addr), pAddr, sizeof(struct sockaddr_in));  
  188.                 p->pNext = NULL;  
  189.         }  
  190.         return p;  
  191. }  
  192.   
  193. /* add SockNode from list */  
  194. void addSockNodeList(SockNode *head, SockNode *node){  
  195.         SockNode *p = head;  
  196.         while (p->pNext != NULL){  
  197.                 p = p->pNext;  
  198.         }  
  199.         p->pNext = node;  
  200. }  
  201.   
  202. /* delete SockNode from list  
  203. * return head 
  204. */  
  205. SockNode* deleteSockNodeList(SockNode *head, int sock){  
  206.         SockNode *p = head, *pPrevious = p;  
  207.         while (p != NULL){  
  208.                 if (p->sock == sock){  
  209.                         if (p != pPrevious){  
  210.                                 pPrevious->pNext = p->pNext;  
  211.                         }else{  
  212.                                 head = p->pNext;  
  213.                         }  
  214.                         free(p);  
  215.                         break;  
  216.                 }  
  217.                 pPrevious = p;  
  218.                 p = p->pNext;  
  219.         }  
  220.         return head;  
  221. }  
  222. /* select SockNode from list  
  223. * return head 
  224. */  
  225. SockNode* selectSockNodeList(SockNode *head, int sock){  
  226.         SockNode *p = head, *pPrevious = p;  
  227.         while (p != NULL){  
  228.                 if (p->sock == sock){  
  229.                         return p;  
  230.                 }  
  231.                 p = p->pNext;  
  232.         }  
  233.         return NULL;  
  234. }  
  235.   
  236. /* maximumly sock from list */  
  237. int maxSockNodeList(SockNode *head){  
  238.         SockNode *p = head;  
  239.         int maxsock = -1;  
  240.         while (p != NULL){  
  241.                 maxsock = maxsock > p->sock ? maxsock : p->sock;  
  242.                 p = p->pNext;  
  243.         }  
  244.         return maxsock;  
  245. }  
  246.   
  247. /* create tcp server */  
  248. int initserver(int type, const struct sockaddr *addr, socklen_t alen, int qlen){  
  249.         int fd;  
  250.         int err = 0, iSockAttrOn = 1;  
  251.           
  252.         /* 创建 */  
  253.         if ((fd = socket(addr->sa_family, type, 0)) < 0){  
  254.                 return -1;  
  255.         }  
  256.         /* 端口复用 */  
  257.         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &iSockAttrOn, sizeof(iSockAttrOn) ) < 0){  
  258.                 err = errno;  
  259.                 goto errout;  
  260.         }  
  261.         /* 绑定 */  
  262.         if (bind(fd, addr, alen) < 0){  
  263.                 err = errno;  
  264.                 goto errout;  
  265.         }  
  266.         /* 监听数 */  
  267.         if (SOCK_STREAM == type || SOCK_SEQPACKET == type){  
  268.                 if (listen(fd, qlen) < 0) {  
  269.                         err = errno;  
  270.                         goto errout;  
  271.                 }  
  272.         }  
  273.         return fd;  
  274. errout:  
  275.         close(fd);  
  276.         errno = err;  
  277.         return -1;  
  278. }  
  279. /* 设置为非阻塞模式 */  
  280. void setnonblocking(int sock){     
  281.     int opts;     
  282.     opts=fcntl(sock,F_GETFL);     
  283.     if(opts<0){     
  284.         perror("fcntl(sock,GETFL)");     
  285.         exit(1);     
  286.     }     
  287.     opts = opts|O_NONBLOCK;     
  288.     if(fcntl(sock,F_SETFL,opts)<0){     
  289.         perror("fcntl(sock,SETFL,opts)");     
  290.         exit(1);     
  291.     }     
  292. }  
  293. /* EPoll ET模式下读取数据函数, 保证 szBuf空间足够大, 否则数据丢失 */  
  294. int epoolRecv(int fd, char *szBuf, int nBuflen){  
  295.         int recvTotalLen = 0, recvLen = 0;  
  296.         char szTmp[1024] = { 0x00 };  
  297.         while (1) {  
  298.                 memset(szTmp, 0x00, sizeof(szTmp));  
  299.                 recvLen = recv(fd, szTmp, sizeof(szTmp), 0);  
  300.                 if (recvLen < 0){  
  301.                         if (EAGAIN == errno) {  
  302.                                 return recvTotalLen; /* 读取结束,正常返回 */  
  303.                         } else {  
  304.                                 perror("Err:epoolRecv recv err!");  
  305.                                 return -1;  
  306.                         }  
  307.                 }else if (0 == recvLen) {  
  308.                         return 0; /* 对方Socket正常关闭 */  
  309.                 }  
  310.                 if (recvTotalLen + recvLen <= nBuflen){  
  311.                         memcpy(szBuf + recvTotalLen, szTmp, recvLen);  
  312.                 }  
  313.                 recvTotalLen += recvLen;  
  314.                 if (recvLen < sizeof(szTmp)){ /* TCP 可以用这种模式, UDP 可能报EAGAIN才能结束 */  
  315.                         return recvTotalLen; /* 读取结束,正常返回 */  
  316.                 }  
  317.         }  
  318.         return recvTotalLen;  
  319. }  
  320. /* EPoll ET模式下发送数据函数 */  
  321. int epollSend(int fd, const char *szBuf, int nBuflen){  
  322.         int sendTotalLen = 0, sendLen = 0;  
  323.         char szTmp[1024] = { 0x00 };  
  324.         while (1) {  
  325.                 sendLen = send(fd, szBuf + sendTotalLen, nBuflen - sendTotalLen, 0);  
  326.                 if (sendLen < 0) {  
  327.                         /* 当socket是非阻塞时,如返回此错误,表示写缓冲队列已满,在这里做延时后再重试. */  
  328.                         if (errno == EAGAIN) {  
  329.                                 usleep(1000);  
  330.                                 continue;  
  331.                         }  
  332.                         return -1;  
  333.                 }else if (0 == sendLen) {  
  334.                         return 0;  
  335.                 }  
  336.                 sendTotalLen += sendLen;  
  337.                 if(sendTotalLen == nBuflen)  
  338.                         return sendTotalLen;  
  339.         }  
  340.         return sendTotalLen;  
  341. }   
  342.   
  343. int main(int argc, char *argv[]){  
  344.         if (argc != 2){  
  345.                 printf("arg err!\n");  
  346.                 return -1;  
  347.         }  
  348.         int sefd, clfd, ret, len;  
  349.         char szBuf[BUFLEN];  
  350.         SockNode *head = NULL,*node = NULL; /* socket 监听链表 */  
  351.   
  352.         struct sockaddr_in se_addr,cl_addr;  
  353.         socklen_t alen = sizeof(struct sockaddr);  
  354.         /* 设置服务IP和端口 */  
  355.         memset((void *)&se_addr, 0, alen);  
  356.         se_addr.sin_family = AF_INET;  
  357.         se_addr.sin_addr.s_addr = INADDR_ANY;// inet_addr("0.0.0.0");  
  358.         se_addr.sin_port = htons(atoi(argv[1]));  
  359.   
  360.         if ((sefd = initserver(SOCK_STREAM, (struct sockaddr *)&se_addr, alen, QLEN)) < 0){  
  361.                 printf("initserver err=%s!\n", strerror(errno));  
  362.                 return -1;  
  363.         }  
  364.         printf("initserver OK !\n");  
  365.         head = createSockNode(sefd, &se_addr);  
  366.   
  367.         int epfd = epoll_create(256);/* 创建一个epoll句柄,size用来告诉内核这个监听的数目一共有多大 */  
  368.         struct epoll_event ev, events[QLEN];/* 声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件 */  
  369.         /* 注册Server socket事件到EPoll */  
  370.         ev.data.fd = sefd;  
  371.         ev.events = EPOLLIN|EPOLLET; /* 读事件、ET模式  */  
  372.         epoll_ctl(epfd, EPOLL_CTL_ADD, sefd, &ev); /* 注册epoll事件 */  
  373.         int i, nevs;  
  374.         while (1){  
  375.                 printf("epoll_wait before OK !\n");  
  376.                 nevs = epoll_wait(epfd, events, QLEN, -1);   
  377.                 printf("epoll_wait after OK !ret = %d\n", ret);  
  378.                 if (nevs < 0){  
  379.                     if(errno == EINTR && epfd > 0){  
  380.                         usleep(10*1000);  
  381.                         continue;  
  382.                     }  
  383.                     printf("epoll_wait err=%s!\n", strerror(errno));  
  384.                     while (head != NULL){  
  385.                             node = head;  
  386.                             head = head->pNext;  
  387.                             close(node->sock);  
  388.                             free(node);  
  389.                     }  
  390.                     return -1;  
  391.                 }else if (0 == nevs) { /* 不可能出现  */  
  392.                         printf("epoll_wait timeout!\n");  
  393.                         sleep(1);  
  394.                         continue;  
  395.                 }  
  396.                 for (i = 0; i < nevs; i++){          
  397.                         if (events[i].data.fd == sefd){ /* Server Socket */  
  398.                                 alen = sizeof(struct sockaddr);  
  399.                                 memset((void *)&cl_addr, 0 , alen);  
  400.                                 clfd = accept(events[i].data.fd, (struct sockaddr*)&cl_addr, &alen);  
  401.                                 if (clfd < 0){  
  402.                                         printf("accept err=%s!\n", strerror(errno));  
  403.                                         while (head != NULL){  
  404.                                                 node = head;  
  405.                                                 head = head->pNext;  
  406.                                                 close(node->sock);  
  407.                                                 free(node);  
  408.                                         }  
  409.                                         return -1;  
  410.                                 }  
  411.                                 printf("Client connect:ip=%s, port=%d \n", inet_ntoa(cl_addr.sin_addr),   
  412.                                         ntohs(cl_addr.sin_port));  
  413.                                 addSockNodeList(head, createSockNode(clfd, &cl_addr));  
  414.   
  415.                                 setnonblocking(clfd); /* 设置客户端为非阻塞模式 */  
  416.                                 ev.data.fd = clfd;  
  417.                                 ev.events = EPOLLIN|EPOLLET; /* 读事件、ET模式  */  
  418.                                 epoll_ctl(epfd, EPOLL_CTL_ADD, clfd, &ev); /* 注册epoll事件 */  
  419.   
  420.                         }else if (events[i].events | EPOLLIN){  
  421.                                 node = selectSockNodeList(head, events[i].data.fd);  
  422.                                 if (NULL == node){  
  423.                                         continue;  
  424.                                 }  
  425.                                 memset(szBuf, 0, BUFLEN);  
  426.                                 /* ret = recv(node->sock, szBuf, BUFLEN, 0); */  
  427.                                 ret = epoolRecv(node->sock, szBuf, BUFLEN);  
  428.                                 if (ret < 0){  
  429.                                         printf("epoolRecv Client err=%s, ip=%s, port=%d!\n", strerror(errno),   
  430.                                                 inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port));  
  431.                                         close(node->sock);  
  432.                                         events[i].data.fd = -1;     
  433.                                         head =  deleteSockNodeList(head, node->sock);  
  434.                                 } else if (0 == ret){  
  435.                                         printf("epoolRecv Client exit, ip=%s, port=%d!\n",   
  436.                                                 inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port));  
  437.                                         close(node->sock);  
  438.                                         events[i].data.fd = -1;  
  439.                                         head = deleteSockNodeList(head, node->sock);  
  440.   
  441.                                 } else {  
  442.                                         MyMsg *msgRecv = (MyMsg *)szBuf;  
  443.                                         msgRecv->iLen = ntohl(msgRecv->iLen);/* 转换成本机字节序 */  
  444.                                         MyMsg *msgSend = NULL;  
  445.                                         /* 预处理 */  
  446.                                         if (strcmp(msgRecv->szCmd, "RE_LINK") == 0){  
  447.                                                 printf("epoolRecv Client RE_LINK, ip=%s, port=%d!\n",   
  448.                                                         inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port));  
  449.                                                 msgSend = MsgProcess(msgRecv); /* 实际处理 */  
  450.                                                 if (msgSend != NULL){  
  451.                                                         len = msgSend->iLen;  
  452.                                                         msgSend->iLen = htonl(msgSend->iLen); /* 转换成网络字节序 */  
  453.                                                         memset(szBuf, 0, BUFLEN);  
  454.                                                         memcpy(szBuf, msgSend, sizeof(MyMsg) + len);  
  455.                                                         epollSend(node->sock, szBuf, sizeof(MyMsg) + len);  
  456.                                                         printf("epollSend Client %s, ip=%s, port=%d!\n", msgSend->szCmd,   
  457.                                                                 inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port));  
  458.                                                         free(msgSend);  
  459.                                                         msgSend = NULL;  
  460.                                                 }  
  461.                                         }else if (strcmp(msgRecv->szCmd, "RESP_LINK") == 0){  
  462.                                                 printf("epoolRecv Client RESP_LINK, ip=%s, port=%d!\n",   
  463.                                                         inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port));  
  464.                                         }else if (strcmp(msgRecv->szCmd, "RE_EXIT") == 0){  
  465.                                                 printf("epoolRecv Client RE_EXIT, ip=%s, port=%d!\n",   
  466.                                                         inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port));  
  467.                                                 msgSend = MsgProcess(msgRecv); /* 实际处理 */  
  468.                                                 if (msgSend != NULL){  
  469.                                                         len = msgSend->iLen;  
  470.                                                         msgSend->iLen = htonl(msgSend->iLen); /* 转换成网络字节序 */  
  471.                                                         memset(szBuf, 0, BUFLEN);  
  472.                                                         memcpy(szBuf, msgSend, sizeof(MyMsg) + len);  
  473.                                                         epollSend(node->sock, szBuf, sizeof(MyMsg) + len);  
  474.                                                         printf("epollSend Client %s, ip=%s, port=%d!\n", msgSend->szCmd,   
  475.                                                                 inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port));  
  476.                                                         free(msgSend);  
  477.                                                         msgSend = NULL;  
  478.                                                           
  479.                                                 }  
  480.                                                 close(node->sock);  
  481.                                                 events[i].data.fd = -1;  
  482.                                                 head = deleteSockNodeList(head, node->sock);  
  483.                                         }else if (strcmp(msgRecv->szCmd, "RESP_TEXT") == 0){  
  484.                                                 printf("epoolRecv Client RESP_TEXT, ip=%s, port=%d!\n",   
  485.                                                         inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port));  
  486.                                                 close(node->sock);  
  487.                                                 events[i].data.fd = -1;  
  488.                                                 head = deleteSockNodeList(head, node->sock);  
  489.                                         }else if (strcmp(msgRecv->szCmd, "RE_DATA") == 0){  
  490.                                                 printf("epoolRecv Client RE_DATA, ip=%s, port=%d, data:%s!\n",   
  491.                                                         inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port), msgRecv->szData);  
  492.                                                 msgSend = MsgProcess(msgRecv); /* 实际处理 */  
  493.                                                 if (msgSend != NULL){  
  494.                                                         len = msgSend->iLen;  
  495.                                                         msgSend->iLen = htonl(msgSend->iLen); /* 转换成网络字节序 */  
  496.                                                         memset(szBuf, 0, BUFLEN);  
  497.                                                         memcpy(szBuf, msgSend, sizeof(MyMsg) + len);  
  498.                                                         epollSend(node->sock, szBuf, sizeof(MyMsg) + len);  
  499.                                                         printf("epollSend Client %s, ip=%s, port=%d, data:%s!\n", msgSend->szCmd,   
  500.                                                                 inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port),   
  501.                                                                 len > 0 ? msgSend->szData : "");  
  502.                                                         free(msgSend);  
  503.                                                         msgSend = NULL;  
  504.                                                 }  
  505.                                         }else if (strcmp(msgRecv->szCmd, "RESP_DATA") == 0){  
  506.                                                 printf("epoolRecv Client RESP_DATA, ip=%s, port=%d, data:%s!\n",   
  507.                                                         inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port), msgRecv->szData);  
  508.                                         }  
  509.                                 }/* recv */  
  510.                         }/* if i = 0 */  
  511.                 }/*for */  
  512.         }/* while 1 */  
  513.   
  514.         while (head != NULL){  
  515.                 node = head;  
  516.                 head = head->pNext;  
  517.                 close(node->sock);  
  518.                 free(node);  
  519.         }  
  520.         return 0;  
  521. }  

[cpp]  view plain copy
  1. /* User:Lixiujie          
  2. * Date:20101207 
  3. * Desc:Unix(Linux)非阻塞的Socket通信EPoll模型,多路复用,TCP客户端, 向服务端发送请求信息,接收响应信息。 
  4. * 可以在发送ls uptime pwd 等简单的显示命令 
  5. * File:tcp_client_epoll.c   
  6. * System:Ubuntu 64bit   
  7. * gcc tcp_client_epoll.c -o  tcp_client_epoll 
  8. * tcp_client_epoll 127.0.0.1 7878 
  9. */  
  10. #include <stdio.h>  
  11. #include <string.h>  
  12. #include <stdlib.h>  
  13. #include <errno.h>  
  14.   
  15. #include <sys/types.h>   
  16. #include <sys/socket.h> /* socket bind listen connect accept send recv */  
  17. #include <arpa/inet.h>  /* htons ntohs htonl ntohl inet_addr inet_ntoa */  
  18. #include <netinet/in.h> /* sockaddr_in */  
  19.   
  20. #include <pthread.h> /* multithreading  */  
  21.   
  22. #define BUFLEN 1024  
  23.   
  24. /* 传送信息结构体 */  
  25. typedef struct _MyMsg{  
  26.         char szCmd[16];/* message command  
  27.                                         * RE_LINK:test link request 
  28.                                         * RESP_LINK:test link response 
  29.                                         * RE_EXIT: exit request 
  30.                                         * RESP_TEXT: exit response 
  31.                                         * RE_DATA: data request 
  32.                                         * RESP_DATA: data response 
  33.                                         */  
  34.         int  iLen;     /* message data length*/  
  35.         char szData[0];/* message data */  
  36. }MyMsg;  
  37.   
  38. /* 信息处理 */  
  39. MyMsg * MsgProcess(MyMsg *pMsgIn){  
  40.         char szBuf[BUFLEN] = { 0x00 };  
  41.         char szTmp[BUFLEN] = { 0x00 };  
  42.         MyMsg *pMsg = NULL;  
  43.         FILE *fp;  
  44.         if (strcmp(pMsgIn->szCmd, "RE_LINK") == 0){  
  45.                 pMsg = (MyMsg *)malloc(sizeof(MyMsg));  
  46.                 memset(pMsg, 0, sizeof(MyMsg));  
  47.                 strcpy(pMsg->szCmd, "RESP_LINK");  
  48.         }else if (strcmp(pMsgIn->szCmd, "RESP_LINK") == 0){  
  49.         }else if (strcmp(pMsgIn->szCmd, "RE_EXIT") == 0){  
  50.                 pMsg = (MyMsg *)malloc(sizeof(MyMsg));  
  51.                 memset(pMsg, 0, sizeof(MyMsg));  
  52.                 strcpy(pMsg->szCmd, "RESP_TEXT");  
  53.         }else if (strcmp(pMsgIn->szCmd, "RESP_TEXT") == 0){  
  54.         }else if (strcmp(pMsgIn->szCmd, "RE_DATA") == 0){  
  55.                 memset(szBuf, 0, BUFLEN);  
  56.                 strncpy(szBuf, pMsgIn->szData, pMsgIn->iLen);  
  57.                 if ((fp = popen(szBuf, "r")) == NULL){  
  58.                         memset(szBuf, 0, BUFLEN);  
  59.                         sprintf(szBuf, "error: %s\n", strerror(errno));  
  60.                 }else{  
  61.                         memset(szTmp, 0, BUFLEN);  
  62.                         while (fgets(szTmp, BUFLEN, fp) != NULL){  
  63.                                 strcat(szBuf, szTmp);  
  64.                                 memset(szTmp, 0, BUFLEN);  
  65.                         }  
  66.                         pclose(fp);  
  67.                 }  
  68.                 pMsg = (MyMsg *)malloc(sizeof(MyMsg)+ strlen(szBuf)+1);  
  69.                 memset(pMsg, 0, sizeof(MyMsg));  
  70.                 strcpy(pMsg->szCmd, "RESP_DATA");  
  71.                 pMsg->iLen = strlen(szBuf)+1;  
  72.                 strcpy(pMsg->szData, szBuf);  
  73.         }else if (strcmp(pMsgIn->szCmd, "RESP_DATA") == 0){  
  74.         }  
  75.         return pMsg;  
  76. }  
  77. int recvProcess(int sefd){  
  78.         fd_set rdset;  
  79.         struct timeval timeout = {5, 0};  
  80.         char szBuf[BUFLEN] = { 0x00 };  
  81.         int ret, len;  
  82.         FD_ZERO(&rdset);  
  83.         FD_SET(sefd, &rdset);  
  84.         ret = select(sefd + 1, &rdset, NULL, NULL, &timeout);  
  85.   
  86.         if (ret < 0){  
  87.                 printf("select err:%s\n", strerror(errno));  
  88.                 exit(-1);  
  89.         }else if (0 == ret){  
  90.                 memset(szBuf, 0, BUFLEN);  
  91.                 strcpy(szBuf, "RE_LINK");  
  92.                 send(sefd, szBuf, strlen(szBuf) + sizeof(MyMsg), 0);  
  93.                 printf("select timeout send: RE_LINK\n");  
  94.                 recvProcess(sefd);  

你可能感兴趣的:(UNIX环境高级编程学习之第十六章网络IPC:套接字 - 非阻塞的Socket通信EPoll模型(多路复用), 实用Socket通信模板)