http://blog.csdn.net/lianghe_work/article/details/46519633

与多线程、多进程相比,I/O复用最大的优势是系统开销小,系统不需要建立新的进程或者线程,也不必维护这些线程和进程。

代码示例:

[csharp] view plain copy
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <errno.h>
  5. #include <string.h>
  6. #include <sys/socket.h>
  7. #include <sys/types.h>
  8. #include <netinet/in.h>
  9. #include <arpa/inet.h>
  10. #include <sys/select.h>
  11. #define SERV_PORT 8080
  12. #define LIST 20                //服务器最大接受连接
  13. #define MAX_FD 10              //FD_SET支持描述符数量
  14. int main(int argc, char *argv[])
  15. {
  16. int sockfd;
  17. int err;
  18. int i;
  19. int connfd;
  20. int fd_all[MAX_FD]; //保存所有描述符,用于select调用后,判断哪个可读
  21. //下面两个备份原因是select调用后,会发生变化,再次调用select前,需要重新赋值
  22. fd_set fd_read;    //FD_SET数据备份
  23. fd_set fd_select;  //用于select
  24. struct timeval timeout;         //超时时间备份
  25. struct timeval timeout_select;  //用于select
  26. struct sockaddr_in serv_addr;   //服务器地址
  27. struct sockaddr_in cli_addr;    //客户端地址
  28. socklen_t serv_len;
  29. socklen_t cli_len;
  30. //超时时间设置
  31. timeout.tv_sec = 10;
  32. timeout.tv_usec = 0;
  33. //创建TCP套接字
  34. sockfd = socket(AF_INET, SOCK_STREAM, 0);
  35. if(sockfd < 0)
  36. {
  37. perror("fail to socket");
  38. exit(1);
  39. }
  40. // 配置本地地址
  41. memset(&serv_addr, 0, sizeof(serv_addr));
  42. serv_addr.sin_family = AF_INET;         // ipv4
  43. serv_addr.sin_port = htons(SERV_PORT);  // 端口, 8080
  44. serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // ip
  45. serv_len = sizeof(serv_addr);
  46. // 绑定
  47. err = bind(sockfd, (struct sockaddr *)&serv_addr, serv_len);
  48. if(err < 0)
  49. {
  50. perror("fail to bind");
  51. exit(1);
  52. }
  53. // 监听
  54. err = listen(sockfd, LIST);
  55. if(err < 0)
  56. {
  57. perror("fail to listen");
  58. exit(1);
  59. }
  60. //初始化fd_all数组
  61. memset(fd_all, -1, sizeof(fd_all));
  62. fd_all[0] = sockfd;   //第一个为监听套接字
  63. FD_ZERO(&fd_read);  // 清空
  64. FD_SET(sockfd, &fd_read);  //将监听套接字加入fd_read
  65. int maxfd = fd_all[0];  //监听的最大套接字
  66. while(1){
  67. // 每次都需要重新赋值,fd_select,timeout_select每次都会变
  68. fd_select = fd_read;
  69. timeout_select = timeout;
  70. // 检测监听套接字是否可读,没有可读,此函数会阻塞
  71. // 只要有客户连接,或断开连接,select()都会往下执行
  72. err = select(maxfd+1, &fd_select, NULL, NULL, NULL);
  73. //err = select(maxfd+1, &fd_select, NULL, NULL, (struct timeval *)&timeout_select);
  74. if(err < 0)
  75. {
  76. perror("fail to select");
  77. exit(1);
  78. }
  79. if(err == 0){
  80. printf("timeout\n");
  81. }
  82. // 检测监听套接字是否可读
  83. if( FD_ISSET(sockfd, &fd_select) ){//可读,证明有新客户端连接服务器
  84. cli_len = sizeof(cli_addr);
  85. // 取出已经完成的连接
  86. connfd = accept(sockfd, (struct sockaddr *)&cli_addr, &cli_len);
  87. if(connfd < 0)
  88. {
  89. perror("fail to accept");
  90. exit(1);
  91. }
  92. // 打印客户端的 ip 和端口
  93. char cli_ip[INET_ADDRSTRLEN] = {0};
  94. inet_ntop(AF_INET, &cli_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);
  95. printf("----------------------------------------------\n");
  96. printf("client ip=%s,port=%d\n", cli_ip,ntohs(cli_addr.sin_port));
  97. // 将新连接套接字加入 fd_all 及 fd_read
  98. for(i=0; i < MAX_FD; i++){
  99. if(fd_all[i] != -1){
  100. continue;
  101. }else{
  102. fd_all[i] = connfd;
  103. printf("client fd_all[%d] join\n", i);
  104. break;
  105. }
  106. }
  107. FD_SET(connfd, &fd_read);
  108. if(maxfd < connfd)
  109. {
  110. maxfd = connfd;  //更新maxfd
  111. }
  112. }
  113. //从1开始查看连接套接字是否可读,因为上面已经处理过0(sockfd)
  114. for(i=1; i < maxfd; i++){
  115. if(FD_ISSET(fd_all[i], &fd_select)){
  116. printf("fd_all[%d] is ok\n", i);
  117. char buf[1024]={0};  //读写缓冲区
  118. int num = read(fd_all[i], buf, 1024);
  119. if(num > 0){
  120. //收到 客户端数据并打印
  121. printf("receive buf from client fd_all[%d] is: %s\n", i, buf);
  122. //回复客户端
  123. num = write(fd_all[i], buf, num);
  124. if(num < 0){
  125. perror("fail to write ");
  126. exit(1);
  127. }else{
  128. //printf("send reply\n");
  129. }
  130. }
  131. else if(0 == num){ // 客户端断开时
  132. //客户端退出,关闭套接字,并从监听集合清除
  133. printf("client:fd_all[%d] exit\n", i);
  134. FD_CLR(fd_all[i], &fd_read);
  135. close(fd_all[i]);
  136. fd_all[i] = -1;
  137. continue;
  138. }
  139. }else {
  140. //printf("no data\n");
  141. }
  142. }
  143. }
  144. return 0;
  145. }

运行结果:

Linux网络编程——tcp并发服务器(I/O复用之select相关推荐

  1. Linux网络编程——tcp并发服务器(poll实现)

    https://blog.csdn.net/lianghe_work/article/details/46535859 想详细彻底地了解poll或看懂下面的代码请参考<Linux网络编程--I/ ...

  2. Linux网络编程——tcp并发服务器(多进程)

    https://blog.csdn.net/lianghe_work/article/details/46503895 一.tcp并发服务器概述 一个好的服务器,一般都是并发服务器(同一时刻可以响应多 ...

  3. Linux网络编程——tcp并发服务器(多线程)

    https://blog.csdn.net/lianghe_work/article/details/46504243 tcp多线程并发服务器 多线程服务器是对多进程服务器的改进,由于多进程服务器在创 ...

  4. Linux网络编程——tcp并发服务器(epoll实现)

    https://blog.csdn.net/lianghe_work/article/details/46551871 通过epoll实现tcp并发回执服务器(客户端给服务器发啥,服务器就给客户端回啥 ...

  5. Linux网络编程——tcp并发服务器(I/O复用之select)

    https://blog.csdn.net/lianghe_work/article/details/46519633 与多线程.多进程相比,I/O复用最大的优势是系统开销小,系统不需要建立新的进程或 ...

  6. 【Linux网络编程】并发服务器的三种实现模型

    服务器设计技术有很多,按使用的协议来分有 TCP 服务器和 UDP 服务器,按处理方式来分有循环服务器和并发服务器. 循环服务器与并发服务器模型 在网络程序里面,一般来说都是许多客户对应一个服务器(多 ...

  7. linux网络编程之并发服务器的三种实现模型 (超级经典)

    转载 : http://blog.csdn.net/tennysonsky/article/details/45671215 服务器设计技术有很多,按使用的协议来分有 TCP 服务器和 UDP 服务器 ...

  8. 【Linux网络编程】并发服务器之select模型

    00. 目录 文章目录 00. 目录 01. 概述 02. I/O复用技术概述 03. select模型服务器实现思路 04. select模型服务器实现 05. 附录 01. 概述 服务器设计技术有 ...

  9. 【Linux网络编程】并发服务器之多线程模型

    00. 目录 文章目录 00. 目录 01. 概述 02. 多线程服务器 03. 多线程服务器实现思路 04. 多线程服务器实现 05. 附录 01. 概述 服务器设计技术有很多,按使用的协议来分有 ...

最新文章

  1. php libdir,PHP扩展小结
  2. python实现均值滤波_python如何实现均值滤波?
  3. python的none是什么-python中stream=None什么意思?
  4. HTML5 input 类型
  5. 安卓投屏大师_苹果,安卓手机如何免费投屏?只要悄悄按下这里,便能轻松实现...
  6. java自适应table_Java 设置Word中的表格自适应的3种方式
  7. HashMap、TreeMap、Hashtable、HashSet和ConcurrentHashMap区别
  8. git maven 一键部署_jenkins+git+maven搭建自动化部署项目环境
  9. 浅谈多个社交账号的登录注册设计
  10. 使用免费ssl证书在iis6 https的配置方法!
  11. 【网络原理】数据链路层
  12. 淘宝网打开很慢、特别慢的完美解决方法
  13. 如何将多张图片合成一个pdf?
  14. 高维曲面: 方向导数, 梯度, 切平面, 法向量
  15. [坐标纠偏] WGS84转GCJ02、BD09,GCJ02转BD09总结
  16. 数据统计分析(SPSS)【2】
  17. 每晚泡脚15分钟,5年下来有哪些变化
  18. Python内置函数、匿名函数
  19. 蒙特卡洛方法 (Monte Carlo Method)(5)
  20. 精通人脸识别10:RGB颜色空间

热门文章

  1. bzoj 4332:JSOI2012 分零食
  2. 【poj2464】树状数组
  3. 如何配置能让fiddler抓去https的请求?
  4. 精通init ramfs构建
  5. extjs中xtype类型
  6. CodePage简介(转)
  7. ruby sinatra mysql_一分钟开始持续集成之旅系列之:Ruby + Sinatra 应用
  8. 记录程序写入日志_终于有人把MySQL 三大日志讲清楚了
  9. html 访问节点,HTML DOM 访问节点
  10. c语言 两条线段位置,C++/STL实现判断平面内两条线段的位置关系代码示例