当前位置:   article > 正文

Linux网络编程(3)_select服务端伪代码

select服务端伪代码

1. recv和send函数

2. tcp状态转换

3. 2msl等待时长

TIME_WAIT状态会持续大约一分钟,然后才关闭。

4. 半关闭

 

               

用dup2函数可以复制文件描述符,之后sfd和fd就都指向同一个socket了。如果用close函数关闭一个文件描述符的话,另一个文件描述符还可以对socket进行读写。所以还是用shutdown函数好,这个函数可以关闭对socket的指定操作权限,即所有指向该socket的文件描述符都会失去指定的操作权限。

5. netstat命令

执行结果:

先启动server端,然后用该命令查看。然后启动一个client端,用该命令查看。

注:如果断开一个client端的连接,在一分钟之内用该命令查看会发现该client端的状态为TIME_WAIT。

6. 端口复用设置

使用方法:在bind之前,设置端口复用。这样在服务器关闭后再立即打开时,就不会显示端口占用的错误了。

7. IO多路转接

  

8. 内核大致是如何实现IO转接的

9. select的参数和返回值

                                        

注:1. writefds和exceptfds一般传NULL,只设置readfds。并且本质上是用1024个位来记录。2. 函数返回值是有几个fd发生了变化。3. 如果timeout参数不传NULL,那么只有当设置的阻塞时间到了之后,函数才返回。

10. select工作过程

注:select函数返回后,reads可能会被改变:发生变化的fd其值为1,未发生变化的fd其值为0。我们只需要遍历一下reads就知道哪些fd发生了变化。

11. select伪代码

12. select代码实现

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <sys/types.h>
  5. #include <string.h>
  6. #include <sys/socket.h>
  7. #include <arpa/inet.h>
  8. #include <ctype.h>
  9. int main(int argc, const char* argv[])
  10. {
  11. if(argc < 2)
  12. {
  13. printf("eg: ./a.out port\n");
  14. exit(1);
  15. }
  16. struct sockaddr_in serv_addr;
  17. socklen_t serv_len = sizeof(serv_addr);
  18. int port = atoi(argv[1]);
  19. // 创建套接字
  20. int lfd = socket(AF_INET, SOCK_STREAM, 0);
  21. // 初始化服务器 sockaddr_in
  22. memset(&serv_addr, 0, serv_len);
  23. serv_addr.sin_family = AF_INET; // 地址族
  24. serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 监听本机所有的IP
  25. serv_addr.sin_port = htons(port); // 设置端口
  26. // 绑定IP和端口
  27. bind(lfd, (struct sockaddr*)&serv_addr, serv_len);
  28. // 设置同时监听的最大个数
  29. listen(lfd, 36);
  30. printf("Start accept ......\n");
  31. struct sockaddr_in client_addr;
  32. socklen_t cli_len = sizeof(client_addr);
  33. // 最大的文件描述符
  34. int maxfd = lfd;
  35. // 文件描述符读集合
  36. fd_set reads, temp;
  37. // init
  38. FD_ZERO(&reads);
  39. FD_SET(lfd, &reads);
  40. while(1)
  41. {
  42. // 委托内核做IO检测
  43. temp = reads;
  44. int ret = select(maxfd+1, &temp, NULL, NULL, NULL);
  45. if(ret == -1)
  46. {
  47. perror("select error");
  48. exit(1);
  49. }
  50. // 客户端发起了新的连接
  51. if(FD_ISSET(lfd, &temp))
  52. {
  53. // 接受连接请求 - accept不阻塞
  54. int cfd = accept(lfd, (struct sockaddr*)&client_addr, &cli_len);
  55. if(cfd == -1)
  56. {
  57. perror("accept error");
  58. exit(1);
  59. }
  60. char ip[64];
  61. printf("new client IP: %s, Port: %d\n",
  62. inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip, sizeof(ip)),
  63. ntohs(client_addr.sin_port));
  64. // 将cfd加入到待检测的读集合中 - 下一次就可以检测到了
  65. FD_SET(cfd, &reads);
  66. // 更新最大的文件描述符
  67. maxfd = maxfd < cfd ? cfd : maxfd;
  68. }
  69. // 已经连接的客户端有数据到达
  70. for(int i=lfd+1; i<=maxfd; ++i)
  71. {
  72. if(FD_ISSET(i, &temp))
  73. {
  74. char buf[1024] = {0};
  75. int len = recv(i, buf, sizeof(buf), 0);
  76. if(len == -1)
  77. {
  78. perror("recv error");
  79. exit(1);
  80. }
  81. else if(len == 0)
  82. {
  83. printf("客户端已经断开了连接\n");
  84. close(i);
  85. // 从读集合中删除
  86. FD_CLR(i, &reads);
  87. }
  88. else
  89. {
  90. printf("recv buf: %s\n", buf);
  91. send(i, buf, strlen(buf)+1, 0);
  92. }
  93. }
  94. }
  95. }
  96. close(lfd);
  97. return 0;
  98. }

执行结果:

 

13. select函数的优缺点

14. poll函数介绍

15. poll实现IO转接代码

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <sys/types.h>
  5. #include <string.h>
  6. #include <sys/socket.h>
  7. #include <arpa/inet.h>
  8. #include <ctype.h>
  9. #include <poll.h>
  10. #define SERV_PORT 8989
  11. int main(int argc, const char* argv[])
  12. {
  13. int lfd, cfd;
  14. struct sockaddr_in serv_addr, clien_addr;
  15. int serv_len, clien_len;
  16. // 创建套接字
  17. lfd = socket(AF_INET, SOCK_STREAM, 0);
  18. // 初始化服务器 sockaddr_in
  19. memset(&serv_addr, 0, sizeof(serv_addr));
  20. serv_addr.sin_family = AF_INET; // 地址族
  21. serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 监听本机所有的IP
  22. serv_addr.sin_port = htons(SERV_PORT); // 设置端口
  23. serv_len = sizeof(serv_addr);
  24. // 绑定IP和端口
  25. bind(lfd, (struct sockaddr*)&serv_addr, serv_len);
  26. // 设置同时监听的最大个数
  27. listen(lfd, 36);
  28. printf("Start accept ......\n");
  29. // poll结构体
  30. struct pollfd allfd[1024];
  31. int max_index = 0;
  32. // init
  33. for(int i=0; i<1024; ++i)
  34. {
  35. allfd[i].fd = -1;
  36. allfd[i].events = POLLIN;
  37. }
  38. allfd[0].fd = lfd;
  39. while(1)
  40. {
  41. int i = 0;
  42. int ret = poll(allfd, max_index+1, -1);
  43. if(ret == -1)
  44. {
  45. perror("poll error");
  46. exit(1);
  47. }
  48. // 判断是否有连接请求
  49. if(allfd[0].revents & POLLIN)
  50. {
  51. clien_len = sizeof(clien_addr);
  52. // 接受连接请求
  53. int cfd = accept(lfd, (struct sockaddr*)&clien_addr, &clien_len);
  54. printf("============\n");
  55. // cfd添加到poll数组
  56. for(i=0; i<1024; ++i)
  57. {
  58. if(allfd[i].fd == -1)
  59. {
  60. allfd[i].fd = cfd;
  61. break;
  62. }
  63. }
  64. // 更新最后一个元素的下标
  65. max_index = max_index < i ? i : max_index;
  66. if(--ret <= 0)
  67. continue;
  68. }
  69. // 遍历数组
  70. for(i=1; i<=max_index; ++i)
  71. {
  72. int fd = allfd[i].fd;
  73. if(fd == -1)
  74. {
  75. continue;
  76. }
  77. if(allfd[i].revents & POLLIN)
  78. {
  79. // 接受数据
  80. char buf[1024] = {0};
  81. int len = recv(fd, buf, sizeof(buf), 0);
  82. if(len == -1)
  83. {
  84. perror("recv error");
  85. exit(1);
  86. }
  87. else if(len == 0)
  88. {
  89. allfd[i].fd = -1;
  90. close(fd);
  91. printf("客户端已经主动断开连接。。。\n");
  92. }
  93. else
  94. {
  95. printf("recv buf = %s\n", buf);
  96. for(int k=0; k<len; ++k)
  97. {
  98. buf[k] = toupper(buf[k]);
  99. }
  100. printf("buf toupper: %s\n", buf);
  101. send(fd, buf, strlen(buf)+1, 0);
  102. }
  103. if(--ret <= 0)
  104. break;
  105. }
  106. }
  107. }
  108. close(lfd);
  109. return 0;
  110. }

 

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

闽ICP备14008679号