使用select函数能够以非堵塞的方式和多个socket通信。程序仅仅是演示select函数的使用,功能很easy,即使某个连接关闭以后也不会改动当前连接数。连接数达到最大值后会终止程序。

1. 程序使用了一个数组fd_A,通信開始后把须要通信的多个socket描写叙述符都放入此数组。

2. 首先生成一个叫sock_fd的socket描写叙述符,用于监听port。

3. 将sock_fd和数组fd_A中不为0的描写叙述符放入select将检查的集合fdsr。

4. 处理fdsr中能够接收数据的连接。假设是sock_fd,表明有新连接增加。将新增加连接的socket描写叙述符放置到fd_A。

这部分代码实现逻辑不错,只是有点bug,对套接字缓存未做处理完整。

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <errno.h>
  5. #include <string.h>
  6. #include <sys/types.h>
  7. #include <sys/socket.h>
  8. #include <netinet/in.h>
  9. #include <arpa/inet.h>
  10.  
  11. #define MYPORT 1234 // the port users will be connecting to
  12. #define BACKLOG 5 // how many pending connections queue will hold
  13. #define BUF_SIZE 200
  14. int fd_A[BACKLOG]; // accepted connection fd
  15. int conn_amount; // current connection amount
  16.  
  17. void showclient()
  18. {
  19. int i;
  20. printf("client amount: %d\n", conn_amount);
  21.  
  22. for (i = 0; i < BACKLOG; i++) {
  23.  
  24. printf("[%d]:%d ", i, fd_A[i]);
  25.  
  26. }
  27. printf("\n\n");
  28. }
  29.  
  30. int main(void)
  31. {
  32. int sock_fd, new_fd; // listen on sock_fd, new connection on new_fd
  33. struct sockaddr_in server_addr; // server address information
  34. struct sockaddr_in client_addr; // connector's address information
  35. socklen_t sin_size;
  36. int yes = 1;
  37. char buf[BUF_SIZE];
  38. int ret;
  39. int i;
  40.  
  41. if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
  42. perror("socket");
  43. exit(1);
  44. }
  45.  
  46. if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
  47. perror("setsockopt");
  48. exit(1);
  49. }
  50.  
  51. server_addr.sin_family = AF_INET; // host byte order
  52. server_addr.sin_port = htons(MYPORT); // short, network byte order
  53. server_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP
  54. memset(server_addr.sin_zero, '\0', sizeof(server_addr.sin_zero));
  55.  
  56. if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
  57. perror("bind");
  58. exit(1);
  59. }
  60.  
  61. if (listen(sock_fd, BACKLOG) == -1) {
  62. perror("listen");
  63. exit(1);
  64. }
  65. printf("listen port %d\n", MYPORT);
  66. fd_set fdsr;
  67. int maxsock;
  68. struct timeval tv;
  69. conn_amount = 0;
  70.  
  71. sin_size = sizeof(client_addr);
  72. maxsock = sock_fd;
  73. while (1) {
  74. // initialize file descriptor set
  75. FD_ZERO(&fdsr);
  76. FD_SET(sock_fd, &fdsr);
  77. // timeout setting
  78. tv.tv_sec = 30;
  79. tv.tv_usec = 0;
  80. // add active connection to fd set
  81.  
  82. for (i = 0; i < BACKLOG; i++) {
  83. if (fd_A[i] != 0) {
  84. FD_SET(fd_A[i], &fdsr);
  85. }
  86. }
  87.  
  88. ret = select(maxsock + 1, &fdsr, NULL, NULL, &tv);
  89. if (ret < 0) {
  90. perror("select");
  91. break;
  92. } else if (ret == 0) {
  93. printf("timeout\n");
  94. continue;
  95. }
  96.  
  97. // check every fd in the set
  98. for (i = 0; i < conn_amount; i++) {
  99. if (FD_ISSET(fd_A[i], &fdsr)) {
  100. ret = recv(fd_A[i], buf, sizeof(buf), 0);
  101. if (ret <= 0) { // client close
  102. printf("client[%d] close\n", i);
  103. close(fd_A[i]);
  104. FD_CLR(fd_A[i], &fdsr);
  105. fd_A[i] = 0;
  106. } else { // receive data
  107. if (ret < BUF_SIZE)
  108. memset(&buf[ret], '\0', 1);
  109. printf("client[%d] send:%s\n", i, buf);
  110. }
  111. }
  112. }
  113.  
  114. // check whether a new connection comes
  115. if (FD_ISSET(sock_fd, &fdsr)) {
  116. new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &sin_size);
  117. if (new_fd <= 0) {
  118. perror("accept");
  119. continue;
  120. }
  121.  
  122. // add to fd queue
  123. if (conn_amount < BACKLOG) {
  124. fd_A[conn_amount++] = new_fd;
  125. printf("new connection client[%d] %s:%d\n", conn_amount,
  126. inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
  127. if (new_fd > maxsock)
  128. maxsock = new_fd;
  129. }
  130. else {
  131. printf("max connections arrive, exit\n");
  132. send(new_fd, "bye", 4, 0);
  133. close(new_fd);
  134. break;
  135. }
  136. }
  137. showclient();
  138. }
  139.  
  140. // close other connections
  141. for (i = 0; i < BACKLOG; i++) {
  142. if (fd_A[i] != 0) {
  143. close(fd_A[i]);
  144. }
  145. }
  146.  
  147. exit(0);
  148.  
  149. }

我这里做改动(大概逻辑)

  1. for(i=0;i<BACKLOG;i++)
  2. {
  3. tcpSockIndex[i] = -1;
  4. }
  5.  
  6. while(1)
  7. {
  8. FD_ZERO(&readfds);
  9. FD_SET(tcpSock, &readfds);
  10.  
  11. maxfdp = maxfdp>tcpSock?maxfdp:tcpSock;
  12.  
  13. for(i=0;i<BACKLOG;i++)
  14. {
  15. // AB_LOG("FD_SET tcpSockIndex[%d] = %d.\n", i, tcpSockIndex[i]);
  16. if(-1 != tcpSockIndex[i])
  17. {
  18. FD_SET(tcpSockIndex[i], &readfds);
  19. maxfdp = maxfdp>tcpSockIndex[i]?
  20.  
  21. maxfdp:tcpSockIndex[i];
  22. }
  23. }
  24.  
  25. timeout.tv_sec = SELECT_TIME_OUT_TM;
  26. timeout.tv_usec = 0;
  27.  
  28. ret = select(maxfdp+1, &readfds, NULL, NULL, &timeout);
  29. if(ret < 0)
  30. {
  31. AB_PERROR("select error!\n");
  32. return ;
  33. }
  34. else if(0 == ret)
  35. {
  36. AB_PERROR("select time out!\n");
  37. }
  38.  
  39. //处理client发送的报文
  40. for(i=0; i<BACKLOG; i++)
  41. {
  42. if ( -1 != tcpSockIndex[i] &&
  43. FD_ISSET(tcpSockIndex[i], &readfds))
  44. {
  45. AB_LOG("--- tcp client ---.\n");
  46. pthread_t pthd2;
  47. TCP_SOCK_T * pTcpSock = NULL;
  48. pTcpSock = (TCP_SOCK_T *)malloc(sizeof(TCP_SOCK_T));
  49. pTcpSock->sock = tcpSockIndex[i];
  50. pthread_create(&pthd2, NULL, bc_sock_handle_client_data, (void *)pTcpSock);
  51.  
  52. #if 0
  53. bc_sock_handle_client_data(tcpSockIndex[i]);
  54. ret = read(tcpSockIndex[i], NULL, 0);
  55. AB_LOG("close tcpSockIndex[%d] = %d, ret = %d.\n", i, tcpSockIndex[i], ret );
  56.  
  57. //关闭client连接的套接字
  58. if(-1 == tcpSockIndex[i]) close(tcpSockIndex[i]);
  59. #endif
  60.  
  61. //清空client字符集
  62. FD_CLR(tcpSockIndex[i], &readfds);
  63. tcpSockIndex[i] = -1;
  64. tcpClientConnNum --;
  65. }
  66. }
  67.  
  68. //获取client连接过来的套接字
  69. if(FD_ISSET(tcpSock, &readfds))
  70. {
  71. AB_LOG("--- tcp server ---.\n");
  72. if((tcpSockClient = accept(tcpSock,
  73. (struct sockaddr*)&chiAddr, &cliLen)) <= 0 )
  74. {
  75. AB_PERROR("BCHV accept socket error: %s(errno: %d).\n",strerror(errno), errno);
  76. continue;
  77. }
  78.  
  79. //在套接字数组中找出一个可用加入的位置。
  80.  
  81. for(i=0,tcpSockFlag=0; i<BACKLOG; i++)
  82. {
  83. AB_LOG("tcpSockIndex[%d] = %d.\n", i, tcpSockIndex[i]);
  84. if(-1 == tcpSockIndex[i])
  85. {
  86. tcpSockIndex[i] = tcpSockClient;
  87. tcpClientConnNum ++;
  88. AB_LOG("new connection client[%d] %08X:%d.\n", tcpClientConnNum,
  89. chiAddr.sin_addr.s_addr, ntohs(chiAddr.sin_port));
  90. //错误打印
  91. //AB_LOG("new connection client[%d] %s.\n", tcpClientConnNum,
  92. // inet_ntoa(chiAddr.sin_addr));
  93. #if 0
  94. if (sock_c > maxfdp)
  95. maxfdp = sock_c;
  96. #endif
  97. tcpSockFlag = 1;
  98. break;
  99. }
  100. }
  101. //超过最大连接请求。能够发送client断开连接
  102. if(0 == tcpSockFlag)
  103. {
  104. AB_LOG("max connections arrive, exit\n");
  105. send(tcpSockClient, "bye", 4, 0);
  106. close(tcpSockClient);
  107. }
  108. }
  109. }
  110.  
  111. //关闭全部client套接字
  112. for(i=0; i<BACKLOG; i++)
  113. {
  114. if(-1 != tcpSockIndex[i])
  115. {
  116. close(tcpSockIndex[i]);
  117. }
  118. }

文章摘自 http://www.cnblogs.com/faraway/archive/2009/03/06/1404449.html

select监听多个client -- linux函数的更多相关文章

  1. select监听服务端

    # can_read, can_write, _ = select.select(inputs, outputs, None, None)## 第一个参数是我们需要监听可读的套接字, 第二个参数是我们 ...

  2. Hi3559AV100板载开发系列-pthread_create()下V4L2接口MJPEG像素格式的VIDIOC_DQBUF error问题解决-采用阻塞方式下select监听

     最近一直加班加点进行基于Hi3559AV100平台的BOXER-8410AI板载开发,在开发的过程中,遇到了相当多的问题,其一是板载的开发资料没有且功能不完整,厂家不提供太多售后技术支持,厂家对部分 ...

  3. 多个进程对同一个监听套接字调用函数gen_tcp:accept/1

    源于<<erlang程序设计>>的第14章的14.1.4大约第197页. 未发现多个进程对同一个监听套接字调用函数gen_tcp:accept/1比单进程的效率更高或者更快.

  4. select监听udp消息

    服务端 #!/usr/bin/python2.6 # -*- coding:utf-8 -*- import json import socket import select def socketse ...

  5. layui的select监听

    首先,select一定要放在<form class="layui-form" ></form>里面 然后,加监听<select id="id ...

  6. Js事件监听封装(支持匿名函数)

    先看demo:http://liutian1937.github.io/demo/EventListen.html/*绑定事件与取消绑定*/ var handleHash = {}; var bind ...

  7. 实时监听 mysql 操作,Linux 版

    效果 场景:某数据库新增了某条记录,服务器可以监听到变化的数据与操作,如 增加一条记录: id = 1009,name=''test,number = 11 服务器监听结果: 实现过程 测试过程:数据 ...

  8. PostgreSQL服务端监听设置及client连接方法

    背景介绍: PostgreSQL服务端执行在RedHat Linux上,IP为:192.168.230.128 client安装在Windows XP上, IP为:192.168.230.1 配置方法 ...

  9. linux epoll机制对TCP 客户端和服务端的监听C代码通用框架实现

    1 TCP简介 tcp是一种基于流的应用层协议,其“可靠的数据传输”实现的原理就是,“拥塞控制”的滑动窗口机制,该机制包含的算法主要有“慢启动”,“拥塞避免”,“快速重传”. 2 TCP socket ...

随机推荐

  1. LeetCode OJ--Unique Paths II **

    https://oj.leetcode.com/problems/unique-paths-ii/ 图的深搜,有障碍物,有的路径不通. 刚开始想的时候用组合数算,但是公式没有推导出来. 于是用了深搜, ...

  2. Drupal service module 介绍

    https://www.ostraining.com/blog/drupal/services/ https://www.drupal.org/node/1246470 https://www.dru ...

  3. Lucene.net站内搜索-最简单搜索引擎代码

    Lucene.Net核心类简介 先运行写好的索引的代码,再向下讲解各个类的作用,不用背代码. (*)Directory表示索引文件(Lucene.net用来保存用户扔过来的数据的地方)保存的地方,是抽 ...

  4. MX

    A mail exchanger record (MX record) is a type of resource record in the Domain Name System that spec ...

  5. VMware虚拟机直连物理网络的两种方式

    VMware虚拟机直连物理网络的两种方式   使用VMware构建虚拟机,通常虚拟机都使用NAT模式.这时,虚拟机有独立的网段.使用NAT模式,虚拟机之间数据都通过虚拟网络传输,不会影响实体机所在的实 ...

  6. 东方14模拟赛之noip2015/day1/3/神奇的幻方

    总时间限制:  10000ms 单个测试点时间限制:  1000ms 内存限制:  128000kB 描述 幻方是一种很神奇的N*N 矩阵:它由数字 1,2,3, … …,N*N 构成,且每行.每列及 ...

  7. 小W计树

    排列组合思想. 先跑一遍最短路, 再从1节点开始搜索, 假如搜到一个点的路径长度等于最短路, 则记录到达该点的路径数 + 1. 最后遍历一遍, ans *= rec[i] 输出答案即可. 关键在于想到 ...

  8. 【spring mvc】后台的API,测试中,总提示接口实体的某一个字段不能为null,但是明明给值了还提示不能为空

    实体是这三个字段 接口的实现类Controller 前台测试给值 依旧报错 解决方法: 需要添加@RequestBody注解

  9. 6.【nuxt起步】-完成一个静态的页面

    1.接下来新建/component/maincontent.vue 把这些html代码copy到maincontent.vue 发现格式比较难看,就格式化一下 2.插件安装 beautify,安装后重 ...

  10. 文件系统之-JAVA Sftp远程操作:

    转载:http://blog.csdn.net/lee272616/article/details/52789018 java远程操作文件服务器(linux),使用sftp协议版本会持续更新,当前版本 ...