epoll:EPOLLLT模式下的正确读写方式

转自:epoll:EPOLLLT模式下的正确读写方式


epoll编程需要关注以下一些细节:

1.进程可打开的最大文件描述符,通过ulimit -a查看open files选项

2.read函数的返回值与errno,errno为EAGAIN和EINTR的含义,要如何处理?

3.write函数的返回值与errno,errno为EAGAIN和EINTR的含义,要如何处理?

4.epoll的events的含义,man 2 epoll_ctl有详细注解

EPOLLET

EPOLLIN

EPOLLOUT

EPOLLRDHUP

EPOLLPRI

EPOLLERR

EPOLLHUP

EPOLLONESHOT

5.EPOLLLT和EPOLLET的区别

6.并不是读取数据后马上就需要注册EPOLLOUT事件,按需注册


下面是简单的server和client代码,选用EPOLLLT模式,不涉及业务数据处理,关注点仅在数据收发与异常处理,

需要注意的是EPOLLET模式下的读写方式与EPOLLLT不同,代码复制至EPOLLET模式并不适用,

可参考EPOLLLT与EPOLLET的区别


epoll_event结构体的理解

[cpp]  view plain  copy
  1. // man 2 epoll_ctl  
  2.   
  3. typedef union epoll_data {  
  4.     void        *ptr;  
  5.     int          fd;  
  6.     __uint32_t   u32;  
  7.     __uint64_t   u64;  
  8. } epoll_data_t;  
  9.   
  10. struct epoll_event {  
  11.     __uint32_t   events;      /* Epoll events */  
  12.     epoll_data_t data;        /* User data variable */  
  13. };  


-----  server.cpp -----

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <sys/types.h>  
  4. #include <sys/socket.h>  
  5. #include <netinet/in.h>  
  6. #include <arpa/inet.h>  
  7. #include <unistd.h>  
  8. #include <sys/epoll.h>  
  9. #include <fcntl.h>  
  10. #include <errno.h>  
  11. #include <string.h>  
  12.   
  13. #define MAX_EVENTS 1000  
  14. #define MAX_LEN 1024  
  15.   
  16. /* 
  17. 参考 man epoll_ctl 
  18. EPOLLIN 可读 
  19. EPOLLOUT 可写 
  20. EPOLLPRI 紧急数据 
  21. EPOLLRDHUP 出错  
  22. EPOLLERR 出错 
  23. EPOLLHUP 出错 
  24. */  
  25.   
  26. //设置非阻塞  
  27. static void setnonblocking(int sockfd) {  
  28.     int flag = fcntl(sockfd, F_GETFL, 0);  
  29.     if (flag < 0) {  
  30.         perror("fcntl F_GETFL fail");  
  31.         return;  
  32.     }  
  33.     if (fcntl(sockfd, F_SETFL, flag | O_NONBLOCK) < 0) {  
  34.         perror("fcntl F_SETFL fail");  
  35.     }  
  36. }  
  37.   
  38. //添加epoll事件  
  39. static int epoll_add(int efd, int sockfd) {  
  40.     struct epoll_event ev;  
  41.     ev.events = EPOLLIN;  
  42.     ev.data.fd = sockfd;  
  43.     if (-1 == epoll_ctl(efd, EPOLL_CTL_ADD, sockfd, &ev)) {  
  44.         perror("epoll_ctl EPOLL_CTL_ADD fail");  
  45.         return 1;  
  46.     }  
  47.     return 0;  
  48. }  
  49.   
  50. //修改epoll事件,默认LT  
  51. static void epoll_write(int efd, int sockfd, bool enable) {  
  52.     struct epoll_event ev;  
  53.     ev.events = EPOLLIN | (enable ? EPOLLOUT : 0);  
  54.     ev.data.fd = sockfd;  
  55.     epoll_ctl(efd, EPOLL_CTL_MOD, sockfd, &ev);  
  56. }  
  57.   
  58. //删除epoll事件  
  59. static void epoll_del(int efd, int sockfd) {  
  60.     perror("close by");  
  61.     epoll_ctl(efd, EPOLL_CTL_DEL, sockfd , NULL);  
  62.     close(sockfd);  
  63. }  
  64.   
  65. //读,定长  
  66. int readn(int fd, void *vptr, size_t n) {  
  67.     // see man 2 read  
  68.     size_t nleft;  
  69.     int nread;  
  70.     char *ptr;  
  71.   
  72.     ptr = (char*)vptr;  
  73.     nleft = n;  
  74.     while (nleft > 0) {  
  75.         nread = (int)read(fd, ptr, nleft);  
  76.         if (nread < 0) {  
  77.             if (errno == EINTR)  
  78.                 nread = 0; /* call read() again */  
  79.             else  
  80.                 return -1; /* maybe errno == EAGAIN */  
  81.         } else if (0 == nread) {  
  82.             break/* EOF */  
  83.         }  
  84.         nleft -= nread;  
  85.         ptr += nread;  
  86.     }  
  87.     return(n - nleft); /* return >= 0 */  
  88. }  
  89.   
  90.   
  91. //写,定长  
  92. int writen(int fd, const void *vptr, size_t n) {  
  93.     // see man 2 write  
  94.     size_t nleft;  
  95.     int nwritten;  
  96.     const char *ptr;  
  97.   
  98.     ptr = (char*)vptr;  
  99.     nleft = n;  
  100.     while (nleft > 0) {  
  101.         nwritten = write(fd, ptr, nleft);  
  102.         if (nwritten <= 0) {  
  103.             if (nwritten < 0 && errno == EINTR)  
  104.                 nwritten = 0; /* call write() again */  
  105.             else  
  106.                 return(-1); /* error */  
  107.         }  
  108.         nleft -= nwritten;  
  109.         ptr += nwritten;  
  110.     }  
  111.     return(n);  
  112. }  
  113.   
  114.   
  115. int main()  
  116. {  
  117.     // socket  
  118.     int listenfd;  
  119.     struct sockaddr_in servaddr;  
  120.     short port = 9527;  
  121.     
  122.     servaddr.sin_family = AF_INET;  
  123.     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  
  124.     servaddr.sin_port = htons(port);  
  125.   
  126.     listenfd = socket(AF_INET, SOCK_STREAM, 0);  
  127.     setnonblocking(listenfd); // accept调用时非阻塞  
  128.     int res = bind(listenfd, (sockaddr *)&servaddr, sizeof(servaddr));  
  129.     if (0 == res)  
  130.         printf("server bind success, 0.0.0.0:%d\n", port);  
  131.     else {  
  132.         perror("bind fail");  
  133.         exit(EXIT_FAILURE);  
  134.     }  
  135.     res = listen(listenfd, 100);  
  136.     if (0 == res)  
  137.         printf("server listen success\n");  
  138.     else {  
  139.         perror("listen fail");  
  140.         exit(EXIT_FAILURE);  
  141.     }  
  142.   
  143.   
  144.     // epoll create,see man epoll_create  
  145.     struct epoll_event ev, events[MAX_EVENTS];  
  146.     int epollfd = epoll_create(10);  
  147.     if (-1 == epollfd) {  
  148.         perror("epoll_create fail");  
  149.         exit(EXIT_FAILURE);  
  150.     }  
  151.   
  152.     // epoll add  
  153.     if (epoll_add(epollfd, listenfd)) {  
  154.         exit(EXIT_FAILURE);  
  155.     }  
  156.   
  157.     for (;;) {  
  158.         int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);  
  159.         if (-1 == nfds) {  
  160.             perror("epoll_wait fail");  
  161.             exit(EXIT_FAILURE);  
  162.         }  
  163.         for (int n = 0; n < nfds; ++n) {  
  164.             if (events[n].data.fd == listenfd) {  
  165.                 struct sockaddr_in cliaddr;  
  166.                 socklen_t len = sizeof(cliaddr);  
  167.                 int connfd = accept(listenfd, (sockaddr *)&cliaddr, &len);  
  168.                 if (-1 == connfd) {  
  169.                     perror("accept fail");  
  170.                     continue;  
  171.                 }  
  172.                 setnonblocking(connfd);  
  173.                 if (epoll_add(epollfd, connfd)) {  
  174.                     close(connfd);  
  175.                     continue;  
  176.                 }  
  177.   
  178.                 // 这里进行一些处理,比如解析cliaddr  
  179.                 char buff[INET_ADDRSTRLEN + 1] = {0};  
  180.                 inet_ntop(AF_INET, &cliaddr.sin_addr, buff, INET_ADDRSTRLEN);  
  181.                 uint16_t port = ntohs(cliaddr.sin_port);  
  182.                 printf("connection from %s, port %d\n", buff, port);  
  183.   
  184.             } else if (events[n].events & EPOLLIN) {  
  185.                 char buffer[MAX_LEN + 1] = {0}; /* keep the end '\0' */  
  186.                 size_t count = MAX_LEN;  
  187.                 int connfd = events[n].data.fd;  
  188.                 int res = readn(connfd, buffer, count);  
  189.   
  190.                 // 处理网络异常情况  
  191.                 if (res < 0 && errno != EAGAIN) {  
  192.                     epoll_del(epollfd, connfd);  
  193.                     continue;  
  194.                 } else if (0 == res) {  
  195.                     epoll_del(epollfd, connfd);  
  196.                     continue;  
  197.                 }  
  198.   
  199.                 if (strlen(buffer) != 0) {  
  200.                     printf("fd:%d, read len:%ld\n", connfd, strlen(buffer));  
  201.                 }  
  202.   
  203.                 // 假设要发送数据,注册EPOLLOUT  
  204.                 if (strlen(buffer) != 0)  
  205.                     epoll_write(epollfd, connfd, true);  
  206.   
  207.             } else if (events[n].events & EPOLLOUT) {  
  208.                 const char* vptr = "hi client!"/* 伪造的发送数据 */  
  209.                 int connfd = events[n].data.fd;  
  210.                 size_t count = strlen(vptr);  
  211.                 int len = writen(connfd, vptr, count);  
  212.   
  213.                 // 处理网络异常情况  
  214.                 if (len < 0 && errno != EAGAIN) {  
  215.                     epoll_del(epollfd, connfd);  
  216.                     continue;  
  217.                 }   
  218.   
  219.                 if (len > 0) {  
  220.                     printf("fd:%d, write len:%d\n", connfd, len);  
  221.                 }  
  222.   
  223.                 if (len < count) {  
  224.                     epoll_write(epollfd, connfd, true); /* 还有可写数据,注册EPOLLOUT */  
  225.                 } else {  
  226.                     epoll_write(epollfd, connfd, false); /* 已经没有可写数据,注册EPOLLIN */  
  227.                 }  
  228.   
  229.             } else if (events[n].events & EPOLLPRI) {  
  230.                 // pass  
  231.             } else if (events[n].events & EPOLLRDHUP) {  
  232.                 epoll_del(epollfd, events[n].data.fd);  
  233.             } else if (events[n].events & EPOLLERR) {  
  234.                 epoll_del(epollfd, events[n].data.fd);  
  235.             } else if (events[n].events & EPOLLHUP) {  
  236.                 epoll_del(epollfd, events[n].data.fd);  
  237.             } else {  
  238.                 // pass  
  239.             }  
  240.         }  
  241.     }  
  242.   
  243. }  



-----  client.cpp -----

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. #include <sys/types.h>  
  3. #include <sys/socket.h>  
  4. #include <netinet/in.h>  
  5. #include <arpa/inet.h>  
  6. #include <stdlib.h>  
  7. #include <unistd.h>  
  8. #include <sys/epoll.h>  
  9. #include <fcntl.h>  
  10. #include <errno.h>  
  11. #include <string.h>  
  12.   
  13. // 设置非阻塞  
  14. static void setnonblocking(int sockfd) {  
  15.     int flag = fcntl(sockfd, F_GETFL, 0);  
  16.     if (flag < 0) {  
  17.         perror("fcntl F_GETFL fail");  
  18.         return;  
  19.     }  
  20.     if (fcntl(sockfd, F_SETFL, flag | O_NONBLOCK) < 0) {  
  21.         perror("fcntl F_SETFL fail");  
  22.     }  
  23. }  
  24.   
  25. //读,定长  
  26. int readn(int fd, void *vptr, size_t n) {  
  27.     /* see man 2 read */  
  28.     size_t nleft;  
  29.     int nread;  
  30.     char *ptr;  
  31.   
  32.     ptr = (char*)vptr;  
  33.     nleft = n;  
  34.     while (nleft > 0) {  
  35.         nread = (int)read(fd, ptr, nleft);  
  36.         if (nread < 0) {  
  37.             if (errno == EINTR)  
  38.                 nread = 0; /* call read() again */  
  39.             else  
  40.                 return -1; /* maybe errno == EAGAIN */  
  41.         } else if (0 == nread) {  
  42.             break/* EOF */  
  43.         }  
  44.         nleft -= nread;  
  45.         ptr += nread;  
  46.     }  
  47.     return(n - nleft); /* return >= 0 */  
  48. }  
  49.   
  50.   
  51. //写,定长  
  52. int writen(int fd, const void *vptr, size_t n) {  
  53.     /* see man 2 write */  
  54.     size_t nleft;  
  55.     int nwritten;  
  56.     const char *ptr;  
  57.   
  58.     ptr = (char*)vptr;  
  59.     nleft = n;  
  60.     while (nleft > 0) {  
  61.         nwritten = write(fd, ptr, nleft);  
  62.         if (nwritten <= 0) {  
  63.             if (nwritten < 0 && errno == EINTR)  
  64.                 nwritten = 0; /* call write() again */  
  65.             else  
  66.                 return(-1); /* error */  
  67.         }  
  68.         nleft -= nwritten;  
  69.         ptr += nwritten;  
  70.     }  
  71.     return(n);  
  72. }  
  73.   
  74. int main()    
  75. {    
  76.     // socket  
  77.     struct sockaddr_in servaddr;    
  78.     short port = 9527;    
  79.     int sockfd = socket(AF_INET, SOCK_STREAM, 0);    
  80.     servaddr.sin_family = AF_INET;    
  81.     servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");    
  82.     servaddr.sin_port = htons(port);  
  83.     setnonblocking(sockfd);  
  84.     if (connect(sockfd, (sockaddr *) &servaddr, sizeof(sockaddr_in)) < 0) {  
  85.         if (errno != EINPROGRESS) {  
  86.             perror("connect fail");  
  87.             close(sockfd);  
  88.             exit(EXIT_FAILURE);  
  89.         }  
  90.     }  
  91.   
  92.     const char* sendbuf = "hello server!";  
  93.     for (;;) {  
  94.   
  95.         // write  
  96.         int len = writen(sockfd, sendbuf, strlen(sendbuf));  
  97.         if (len < 0 && errno != EAGAIN) {  
  98.             break;  
  99.         }  
  100.         if (len > 0) {  
  101.             printf("fd:%d, write len:%d\n", sockfd, len);  
  102.         }  
  103.   
  104.         // read  
  105.         char recvbuf[1024+1] = {0};  
  106.         int res = readn(sockfd, recvbuf, 1024);  
  107.         if (res < 0 && errno != EAGAIN) {  
  108.             break;  
  109.         } else if (0 == res) {  
  110.             break;  
  111.         }  
  112.   
  113.         if (strlen(recvbuf) > 0) {  
  114.             printf("fd:%d, read len:%ld\n", sockfd, strlen(recvbuf));  
  115.         }  
  116.   
  117.         sleep(1);  
  118.     }  
  119.     perror("close by");  
  120.     close(sockfd);  
  121. }    


----- 简单的编译脚本 -----

[cpp]  view plain  copy
  1. #!/bin/bash  
  2. g++ client.cpp -o ctest  
  3. g++ server.cpp -o stest  



----- 简单的批量启动client脚本 -----

[plain]  view plain  copy
  1. #!/bin/bash  
  2. num=10000  
  3. progress="ctest"  
  4. for (( i=0; i<$num; i++)); do    
  5.     ./$progress > /dev/null 2>&1 &    
  6. done  


注:

readn和writen函数参考:《unix网络编程》卷1第3章



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值