TCP协议+IO多路复用 --- select,poll,epoll分析

举报
王建峰 发表于 2021/11/19 03:22:13 2021/11/19
【摘要】   select、poll、epoll select,poll,epoll都是IO多路复用的机制。I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件...

 

select、poll、epoll

select,poll,epoll都是IO多路复用的机制。I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

 

1.select

select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。select的一 个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但 是这样也会造成效率的降低。

2.poll

不同与select使用三个位图来表示三个fdset的方式,poll使用一个 pollfd的指针实现。pollfd结构包含了要监视的event和发生的event,不再使用select“参数-值”传递的方式。同时,pollfd并没有最大数量限制(但是数量过大后性能也是会下降)。 和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符。

3.epoll

epoll是在2.6内核中提出的,是之前的select和poll的增强版本。相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。

参考来源:

Linux中select poll和epoll的区别
IO多路复用之select总结
IO多路复用之poll总结
IO多路复用之epoll总结


下面关于三种方法的C语言实例。

 

客户端代码采用多路复用的思路实现。


  
  1. //客户端代码,可分别实现三种多路复用的形式 select、poll、epoll ;
  2. //通过宏来实现
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. #include <errno.h>
  7. #include <stdlib.h>
  8. #include <strings.h>
  9. #include <sys/types.h> /* See NOTES */
  10. #include <sys/socket.h>
  11. #include <netinet/in.h>
  12. #include <netinet/ip.h> /* superset of previous */
  13. #include <poll.h>
  14. #include <sys/epoll.h>
  15. #define SERV_PORT 5001
  16. #define SERV_IP_ADDR "127.0.0.1"
  17. #define BACKLOG 5
  18. #define QUIT_STR "quit"
  19. #define SELECT 0
  20. #define POLL 0
  21. #define EPOLL 1
  22. void handle_select(int fd);
  23. void handle_poll(int fd);
  24. void handle_epoll(int fd);
  25. void add_event(int epollfd,int fd,int state);
  26. void handle_events(int epollfd,struct epoll_event *events,int num,int sockfd,char *buf);
  27. int main(int argc, const char *argv[])
  28. {
  29. int fd;
  30. struct sockaddr_in sin;
  31. bzero(&sin,sizeof(sin));
  32. sin.sin_family = AF_INET;
  33. inet_pton(AF_INET,SERV_IP_ADDR,&sin.sin_addr);
  34. sin.sin_port = htons(SERV_PORT);
  35. if( -1 == (fd = socket(AF_INET,SOCK_STREAM,0)) )
  36. {
  37. perror("socket failed");
  38. return -1;
  39. }
  40. if( -1 == connect(fd,(struct sockaddr*)&sin,sizeof(sin)) )
  41. {
  42. perror("connect failed");
  43. return -1;
  44. }
  45. #if SELECT
  46. handle_select(fd);
  47. #endif
  48. #if POLL
  49. handle_poll(fd);
  50. #endif
  51. #if EPOLL
  52. handle_epoll(fd);
  53. #endif
  54. close(fd);
  55. return 0;
  56. }
  57. void handle_select(int fd)
  58. {
  59. int real_read;
  60. char buf[64];
  61. fd_set fdset;
  62. int maxfd = -1;
  63. struct timeval tout;
  64. while(1)
  65. {
  66. FD_ZERO(&fdset);
  67. FD_SET(0,&fdset);
  68. FD_SET(fd,&fdset);
  69. maxfd = fd;
  70. tout.tv_sec = 5;
  71. tout.tv_usec = 0;
  72. select(maxfd+1,&fdset,NULL,NULL,&tout);
  73. if( FD_ISSET(0,&fdset) )
  74. {
  75. bzero(buf,sizeof(buf));
  76. do{
  77. real_read = read(0,buf,sizeof(buf));
  78. }while( real_read<0 && EINTR == errno );
  79. if(real_read < 0)
  80. {
  81. perror("read");
  82. continue;
  83. }
  84. else if( 0 == real_read )
  85. {
  86. continue;
  87. }
  88. write(fd,buf,strlen(buf)-1);
  89. if( 0 == (strncasecmp(buf,QUIT_STR,strlen(QUIT_STR))) )
  90. {
  91. printf("client quit\n");
  92. break;
  93. }
  94. }
  95. if( FD_ISSET(fd,&fdset) )
  96. {
  97. bzero(buf,sizeof(buf));
  98. do{
  99. real_read = read(fd,buf,sizeof(buf));
  100. }while( real_read<0 && EINTR == errno );
  101. if(real_read < 0)
  102. {
  103. perror("read");
  104. continue;
  105. }
  106. else if( 0 == real_read )
  107. {
  108. break;
  109. }
  110. printf("Received: %s\n",buf);
  111. if( 0 == (strncasecmp(buf,QUIT_STR,strlen(QUIT_STR))) )
  112. {
  113. printf("client quit\n");
  114. break;
  115. }
  116. }
  117. }
  118. }
  119. void handle_poll(int fd)
  120. {
  121. char buf[64];
  122. int maxfdp;
  123. struct pollfd pfds[2];
  124. int real_read;
  125. pfds[0].fd = fd;
  126. pfds[0].events = POLLIN;
  127. pfds[1].fd = 0;
  128. pfds[1].events = POLLIN;
  129. maxfdp = 2;
  130. while(1)
  131. {
  132. poll(pfds,maxfdp,5);
  133. if (pfds[0].revents & POLLIN)
  134. {
  135. bzero(buf,sizeof(buf));
  136. do{
  137. real_read = read(fd,buf,sizeof(buf));
  138. }while( real_read<0 && EINTR == errno );
  139. if(real_read < 0)
  140. {
  141. perror("read");
  142. continue;
  143. }
  144. else if( 0 == real_read )
  145. {
  146. break;
  147. }
  148. printf("Received: %s\n",buf);
  149. if( 0 == (strncasecmp(buf,QUIT_STR,strlen(QUIT_STR))) )
  150. {
  151. printf("client quit\n");
  152. break;
  153. }
  154. }
  155. if (pfds[1].revents & POLLIN)
  156. {
  157. bzero(buf,sizeof(buf));
  158. do{
  159. real_read = read(0,buf,sizeof(buf));
  160. }while( real_read<0 && EINTR == errno );
  161. if(real_read < 0)
  162. {
  163. perror("read");
  164. continue;
  165. }
  166. else if( 0 == real_read )
  167. {
  168. continue;
  169. }
  170. write(fd,buf,strlen(buf)-1);
  171. if( 0 == (strncasecmp(buf,QUIT_STR,strlen(QUIT_STR))) )
  172. {
  173. printf("client quit\n");
  174. break;
  175. }
  176. }
  177. }
  178. }
  179. void handle_epoll(int sockfd)
  180. {
  181. int epollfd;
  182. struct epoll_event events[100];
  183. char buf[64];
  184. int ret;
  185. epollfd = epoll_create(1000);
  186. add_event(epollfd,0,EPOLLIN);
  187. add_event(epollfd,sockfd,EPOLLIN);
  188. while(1)
  189. {
  190. ret = epoll_wait(epollfd,events,100,5);
  191. handle_events(epollfd,events,ret,sockfd,buf);
  192. }
  193. close(epollfd);
  194. }
  195. void add_event(int epollfd,int fd,int state)
  196. {
  197. struct epoll_event ev;
  198. ev.events = state;
  199. ev.data.fd = fd;
  200. epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev);
  201. }
  202. void
  203. handle_events(int epollfd,struct epoll_event *events,int num,int sockfd,char *buf)
  204. {
  205. int fd;
  206. int i;
  207. int real_read;
  208. for (i = 0;i < num;i++)
  209. {
  210. fd = events[i].data.fd;
  211. if (events[i].events & EPOLLIN)
  212. {
  213. if( fd == sockfd )
  214. {//判断就绪fd为连接套接字sockfd
  215. bzero(buf,64);
  216. do{
  217. real_read = read(sockfd,buf,64);
  218. }while( real_read<0 && EINTR == errno );
  219. if(real_read < 0)
  220. {
  221. perror("read");
  222. continue;
  223. }
  224. else if( 0 == real_read )
  225. {
  226. break;
  227. }
  228. printf("Received: %s\n",buf);
  229. if( 0 == (strncasecmp(buf,QUIT_STR,strlen(QUIT_STR))) )
  230. {
  231. printf("client quit\n");
  232. break;
  233. }
  234. }
  235. else if( 0 == fd )
  236. {//那么判断就绪fd 为标准输入
  237. bzero(buf,64);
  238. do{
  239. real_read = read(0,buf,64);
  240. }while( real_read<0 && EINTR == errno );
  241. if(real_read < 0)
  242. {
  243. perror("read");
  244. continue;
  245. }
  246. else if( 0 == real_read )
  247. {
  248. continue;
  249. }
  250. write(sockfd,buf,strlen(buf)-1);
  251. if( 0 == (strncasecmp(buf,QUIT_STR,strlen(QUIT_STR))) )
  252. {
  253. printf("client quit\n");
  254. break;
  255. }
  256. }
  257. }
  258. }
  259. }

 

服务端使用多线程的思想


  
  1. //循环里处理主监听套接字listenfd,accept监听请求连接。然后将每一个accept连接成功 返回的连接套接
  2. //字connf分配给一个线程处理。。。
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. #include <errno.h>
  7. #include <stdlib.h>
  8. #include <strings.h>
  9. #include <sys/types.h> /* See NOTES */
  10. #include <sys/socket.h>
  11. #include <netinet/in.h>
  12. #include <netinet/ip.h> /* superset of previous */
  13. #include <poll.h>
  14. #include <sys/epoll.h>
  15. #define SERV_PORT 5001
  16. #define SERV_IP_ADDR "127.0.0.1"
  17. #define BACKLOG 5
  18. #define QUIT_STR "quit"
  19. void *cli_data_handle(void* arg);
  20. int main(int argc, const char *argv[])
  21. {
  22. int listenfd,connfd;
  23. struct sockaddr_in servaddr,cliaddr;
  24. socklen_t peerlen;
  25. char buf[64];
  26. pthread_t thread;
  27. bzero(&servaddr,sizeof(servaddr));
  28. servaddr.sin_family = AF_INET;
  29. inet_pton(AF_INET,SERV_IP_ADDR,(void *)&servaddr.sin_addr);
  30. servaddr.sin_port = htons(SERV_PORT);
  31. if( -1 == (listenfd = socket(AF_INET,SOCK_STREAM,0)) )
  32. {
  33. perror("socket failed");
  34. return -1;
  35. }
  36. printf("socket create success....\n");
  37. int b_reuse = 1;
  38. setsockopt (listenfd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof (int));
  39. if(-1 == bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) )
  40. {
  41. perror("bind failed");
  42. return -1;
  43. }
  44. printf("bind success....\n");
  45. if( -1 == listen(listenfd,10) )
  46. {
  47. perror("listen failed");
  48. return -1;
  49. }
  50. printf("listen success....\n");
  51. while(1)
  52. {
  53. bzero(&cliaddr,sizeof(cliaddr));
  54. peerlen = sizeof(cliaddr);
  55. if( (connfd = accept(listenfd,(struct sockaddr *)&cliaddr,&peerlen)) < 0)
  56. {
  57. perror("accept");
  58. return -1;
  59. }
  60. if( 0 != pthread_create(&thread,NULL,cli_data_handle,(void *)&connfd) )
  61. {
  62. perror("pthread_create");
  63. return -1;
  64. }
  65. }
  66. close(listenfd);
  67. return 0;
  68. }
  69. void *cli_data_handle(void* arg)
  70. {
  71. char buf[64];
  72. int connfd = *(int *)arg;
  73. int real_read =0;
  74. while(1)
  75. {
  76. bzero(buf,sizeof(buf));
  77. do{
  78. real_read = read(connfd,buf,sizeof(buf));
  79. }while( real_read<0 && EINTR == errno );
  80. if(real_read < 0)
  81. {
  82. perror("read");
  83. continue;
  84. }
  85. else if( 0 == real_read )
  86. {
  87. break;
  88. }
  89. printf("Received:%s\n",buf);
  90. write(connfd,buf,strlen(buf));
  91. if( 0 == (strncasecmp(buf,QUIT_STR,strlen(QUIT_STR))) )
  92. {
  93. printf("client quit\n");
  94. break;
  95. }
  96. }
  97. close(connfd);
  98. }

 

结果验证

文章来源: blog.csdn.net,作者:hinzer,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/feit2417/article/details/82528105

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。