当前位置:   article > 正文

C语言实现 基于poll的 多路复用 TCP echo 服务器模型

C语言实现 基于poll的 多路复用 TCP echo 服务器模型

设计灵感:

1 单线程io多路复用服务端

2 使用poll实现

3 将server_sockfd client_sockfd 设为非阻塞,实现最大io效率

4 使用套接字选项SO_REUSEADDR 用于测试环境调试

5 将server_sockfd 和每一个有效的client_sockfd 都设为poll的监控事件

6 有客户端关闭连接时,自动从数组中删除,并调整相应的count值

功能:

可实现多客户端同时连接,echo收发无感

注意事项:

1 运行环境 unix-like gnu_c 

2 开启服务端后 可无限开启客户端

3 赠送对比用双循环阻塞服务端

4 赠送测试用客户端

poll多路复用服务端:

  1. #define _GNU_SOURCE
  2. #include <stdio.h>
  3. #include <sys/socket.h>
  4. #include <sys/un.h>
  5. #include <fcntl.h>
  6. #include <arpa/inet.h>
  7. #include <netinet/in.h>
  8. #include <sys/poll.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include <unistd.h>
  12. #define SERVER_IP "192.168.142.132"
  13. #define SERVER_PORT 50013
  14. // 设置非阻塞用函数
  15. void set_non_blocking(int sockfd)
  16. {
  17. // 获取默认flags
  18. int flags = fcntl(sockfd, F_GETFL, 0);
  19. if (flags == -1)
  20. {
  21. perror("fcntl F_GETFL");
  22. }
  23. // 位运算 加上O_NONBLOCK
  24. flags |= O_NONBLOCK;
  25. // 设置 flags
  26. if (fcntl(sockfd, F_SETFL, flags) == -1)
  27. {
  28. perror("fcntl F_SETFL");
  29. }
  30. }
  31. int main()
  32. {
  33. int server_sockfd, client_sockfd;
  34. struct sockaddr_in server_sockaddr, client_sockaddr;
  35. memset(&server_sockaddr, 0, sizeof(server_sockaddr));
  36. memset(&client_sockaddr, 0, sizeof(client_sockaddr));
  37. socklen_t client_sockaddr_len = sizeof(client_sockaddr);
  38. socklen_t server_sockaddr_len = sizeof(server_sockaddr);
  39. ssize_t send_bytes, recv_bytes;
  40. char send_buf[1024] = "How can I he53453oday ?";
  41. char recv_buf[1024] = {0};
  42. // 循环用i,j,计数用count:pollfds[count]中每增加一个元素 count++
  43. int i = 0, j = 0, count = 0;
  44. // 系统允许的单进程 最大fd数量 1024576
  45. long max_fds = sysconf(_SC_OPEN_MAX);
  46. // 创建一个最大fd数量的数组,并将所有元素的所有字段设为0
  47. struct pollfd *pollfds = calloc(max_fds, sizeof(struct pollfd));
  48. if (pollfds == NULL)
  49. {
  50. perror("calloc");
  51. }
  52. // 将每个元素的fd设为-1,表示无效
  53. for (i = 0; i < max_fds; i++)
  54. {
  55. pollfds[i].fd = -1;
  56. }
  57. // 又名 listen socket 用于监听请求,ipv4 tcp
  58. server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
  59. if (server_sockfd == -1)
  60. {
  61. perror("socket");
  62. }
  63. // 为什么要将server_sockfd设为非阻塞?
  64. // server_sockfd相关的accept被poll监视,只有读就绪(有连接到来时)才回调用accept
  65. // 这意味着accept通常不会阻塞,但是在极端情况下,客户端连接有迅速关闭,则accept有可能阻塞
  66. set_non_blocking(server_sockfd);
  67. // SO_REUSEADDR 地址端口复用,任意时刻只有一个socket能监听,主要用于程序重启
  68. // SO_REUSEPORT 地址端口复用,同时监听,自动负载均衡
  69. int optval = 1;
  70. if (setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1)
  71. {
  72. perror("setsockopt");
  73. }
  74. // 设置协议
  75. server_sockaddr.sin_family = AF_INET;
  76. // 设置转换端口
  77. server_sockaddr.sin_port = htons(SERVER_PORT);
  78. // 设置转换ip地址
  79. if (inet_pton(AF_INET, SERVER_IP, &server_sockaddr.sin_addr.s_addr) == -1)
  80. {
  81. perror("inet_pton");
  82. }
  83. // 绑定
  84. if (bind(server_sockfd, (struct sockaddr *)&server_sockaddr, server_sockaddr_len) == -1)
  85. {
  86. perror("bind");
  87. }
  88. // 监听,调整了最大等待数量为1024
  89. if (listen(server_sockfd, 1024) == -1)
  90. {
  91. perror("listen");
  92. }
  93. // 将server_sockfd加入POLLIN监听事件
  94. pollfds[0].fd = server_sockfd;
  95. pollfds[0].events = POLLIN;
  96. // count现在是0,++后变成1,遍历i<count,也就是判断pollfds[0]
  97. // 但是pollfds[0]读就绪后,主要执行逻辑是accept,因此单独判断
  98. count++;
  99. printf("server start...\n");
  100. while (1)
  101. {
  102. // 如果没有事件真实发生,程序将阻塞在此,而不是一味地循环
  103. int r = poll(pollfds, count, -1);
  104. // 至少有一个revent被设置
  105. if (r > 0)
  106. {
  107. // 如果有连接请求
  108. if (pollfds[0].revents & POLLIN)
  109. {
  110. // 就连接
  111. client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_sockaddr, &client_sockaddr_len);
  112. if (client_sockfd == -1)
  113. {
  114. perror("accept");
  115. }
  116. // 将返回的client_sockfd设为非阻塞
  117. set_non_blocking(client_sockfd);
  118. // 如果代码首次到这,count就是1,pollfds[1]被填充,符合逻辑
  119. pollfds[count].fd = client_sockfd;
  120. // 同时监控读写
  121. pollfds[count].events = POLLIN | POLLOUT;
  122. // 向后挪一位
  123. count++;
  124. }
  125. // 首次加入监控事件的accept返回的client_sockfd不会在本次循环中被poll监控,需要等到下次循环
  126. for (i = 1; i < count; i++)
  127. {
  128. // 从索引1开始(索引0用于accept已单独处理),如果被监控的fd读就绪
  129. if (pollfds[i].revents & POLLIN)
  130. {
  131. // 读
  132. recv_bytes = recv(pollfds[i].fd, recv_buf, sizeof(recv_buf), 0);
  133. if (recv_bytes == -1)
  134. {
  135. perror("recv");
  136. }
  137. // 如果客户端关闭了连接
  138. if (recv_bytes == 0)
  139. {
  140. printf("close by peer\n");
  141. // 服务器也关闭连接
  142. close(pollfds[i].fd);
  143. // 1 将后面的fd往前挪
  144. for (j = i; j < count - 1; j++)
  145. {
  146. pollfds[j] = pollfds[j + 1];
  147. }
  148. // 2 最后一个fd是一个无效的副本 手动重置
  149. pollfds[count - 1].fd = -1;
  150. pollfds[count - 1].events = 0;
  151. pollfds[count - 1].revents = 0;
  152. // 3 count应该减1
  153. count--;
  154. }
  155. // 收到了数据
  156. if (recv_bytes > 0)
  157. {
  158. // 打印收到的数据
  159. printf("%s\n", recv_buf);
  160. // 同时,如果这个fd是写就绪的
  161. if (pollfds[i].revents & POLLOUT)
  162. {
  163. // 写
  164. send_bytes = send(pollfds[i].fd, send_buf, strlen(send_buf), 0);
  165. if (send_bytes == -1)
  166. {
  167. perror("send");
  168. }
  169. }
  170. }
  171. }
  172. }
  173. }
  174. if (r == -1)
  175. {
  176. perror("poll");
  177. }
  178. }
  179. // 关闭server_sockfd,代码不可达
  180. close(server_sockfd);
  181. // 释放动态分配的内存,代码不可达
  182. free(pollfds);
  183. return 0;
  184. }

对比用双循环阻塞服务端:

  1. #define _GNU_SOURCE
  2. #include <stdio.h>
  3. #include <sys/socket.h>
  4. #include <sys/un.h>
  5. #include <arpa/inet.h>
  6. #include <netinet/in.h>
  7. #include <string.h>
  8. #include <unistd.h>
  9. #define SERVER_IP "192.168.142.132"
  10. #define SERVER_PORT 50013
  11. int main()
  12. {
  13. int server_sockfd, client_sockfd;
  14. struct sockaddr_in server_sockaddr, client_sockaddr;
  15. memset(&server_sockaddr, 0, sizeof(server_sockaddr));
  16. memset(&client_sockaddr, 0, sizeof(client_sockaddr));
  17. socklen_t client_sockaddr_len = sizeof(client_sockaddr);
  18. ssize_t send_bytes, recv_bytes;
  19. char send_buf[1024] = "How can I help you today ?";
  20. char recv_buf[1024] = {0};
  21. server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
  22. if (server_sockfd == -1)
  23. {
  24. perror("socket");
  25. }
  26. int optval = 1;
  27. setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
  28. server_sockaddr.sin_family = AF_INET;
  29. inet_pton(AF_INET, SERVER_IP, &server_sockaddr.sin_addr.s_addr);
  30. server_sockaddr.sin_port = htons(SERVER_PORT);
  31. if (bind(server_sockfd, (struct sockaddr *)&server_sockaddr, sizeof(server_sockaddr)) == -1)
  32. {
  33. perror("bind");
  34. }
  35. if (listen(server_sockfd, 16) == -1)
  36. {
  37. perror("listen");
  38. }
  39. printf("server start...\n");
  40. while (1)
  41. {
  42. client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_sockaddr, &client_sockaddr_len);
  43. if (client_sockfd == -1)
  44. {
  45. perror("accept");
  46. }
  47. while (1)
  48. {
  49. recv_bytes = recv(client_sockfd, recv_buf, sizeof(recv_buf), 0);
  50. if (recv_bytes == -1)
  51. {
  52. perror("recv");
  53. }
  54. else if (recv_bytes == 0)
  55. {
  56. printf("closed by peer\n");
  57. break;
  58. }
  59. else
  60. {
  61. printf("%s\n", recv_buf);
  62. }
  63. send_bytes = send(client_sockfd, send_buf, strlen(send_buf), 0);
  64. if (send_bytes == -1)
  65. {
  66. perror("send");
  67. }
  68. }
  69. }
  70. close(server_sockfd);
  71. return 0;
  72. }

测试用客户端:

  1. #define _GNU_SOURCE
  2. #include <stdio.h>
  3. #include <sys/socket.h>
  4. #include <sys/un.h>
  5. #include <arpa/inet.h>
  6. #include <netinet/in.h>
  7. #include <string.h>
  8. #include <unistd.h>
  9. #include <stdlib.h>
  10. #include <time.h>
  11. #include <fcntl.h>
  12. #define SERVER_IP "192.168.142.132"
  13. #define SERVER_PORT 50013
  14. int set_non_blocking(int sockfd)
  15. {
  16. int flags = fcntl(sockfd, F_GETFL, 0);
  17. if (flags == -1)
  18. {
  19. perror("fcntl(F_GETFL)");
  20. }
  21. flags |= O_NONBLOCK;
  22. if (fcntl(sockfd, F_SETFL, flags) == -1)
  23. {
  24. perror("fcntl(F_SETFL)");
  25. }
  26. return 0;
  27. }
  28. int main()
  29. {
  30. int client_sockfd;
  31. struct sockaddr_in server_sockaddr, client_sockaddr;
  32. memset(&server_sockaddr, 0, sizeof(server_sockaddr));
  33. memset(&client_sockaddr, 0, sizeof(client_sockaddr));
  34. socklen_t client_sockaddr_len = sizeof(client_sockaddr);
  35. ssize_t send_bytes, recv_bytes;
  36. char send_buf[1024] = {0};
  37. char recv_buf[1024] = {0};
  38. client_sockfd = socket(AF_INET, SOCK_STREAM, 0);
  39. if (client_sockfd == -1)
  40. {
  41. perror("socket");
  42. }
  43. //set_non_blocking(client_sockfd);
  44. inet_pton(AF_INET, SERVER_IP, &server_sockaddr.sin_addr.s_addr);
  45. server_sockaddr.sin_port = htons(SERVER_PORT);
  46. server_sockaddr.sin_family = AF_INET;
  47. if (connect(client_sockfd, (struct sockaddr *)&server_sockaddr, sizeof(server_sockaddr)) == -1)
  48. {
  49. perror("connect");
  50. }
  51. getsockname(client_sockfd, (struct sockaddr *)&client_sockaddr, &client_sockaddr_len);
  52. snprintf(send_buf, sizeof(send_buf), "%u:he###llo s???ver !!!",
  53. ntohs(client_sockaddr.sin_port));
  54. while (1)
  55. {
  56. send_bytes = send(client_sockfd, send_buf, strlen(send_buf), 0);
  57. if (send_bytes == -1)
  58. {
  59. perror("send");
  60. }
  61. recv_bytes = recv(client_sockfd, recv_buf, sizeof(recv_buf), 0);
  62. if (recv_bytes == -1)
  63. {
  64. perror("recv");
  65. }
  66. printf("%s\n", recv_buf);
  67. sleep(2);
  68. }
  69. close(client_sockfd);
  70. return 0;
  71. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/362008?site
推荐阅读
相关标签
  

闽ICP备14008679号