首先,看看这个程序服务端设计的基本逻辑,其实非常简单,就在一个while(1)循环里面不停地轮询 accept 和 select函数。
有人可能问,accept不是会阻塞,直到有客户端连接进来的吗?
其实当你的socket套接字设置成非阻塞模式,那么accept也不会阻塞。
1.那怎么弄非阻塞呢?
这就涉及到 fcntl函数。fcntl能改变文件的属性。看看他的原型:int fcntl(int fd, int cmd);失败返回-1(当然这个函数还有很多用途,但这里只谈谈他如何实现非阻塞)
三行代码:
[cpp]  view plain copy
  1. long val = fcntl(sockfd,F_GETFL); //把sockfd套接字的属性拿出来给val
  2. val|=O_NONBLOCK;          //把非阻塞的属性O_NONBLOCK加进去,用或运算
  3. fcntl(sockfd,F_SETFL,val);    //再把做好的属性val,再加到sockfd中去
这时候 把sockfd 作为参数给 accept,accept就不会再阻塞。
2.select()函数:select函数能够同时监听多个文件描述符,若其中一个或多个文件描述符有反应(读或写),select就会返回。
原型: int select(nfds, readfds, writefds, errfds, timeout)  

fd_set *readfds, *writefds, *errfds;   //第2,3,4个参数都是文件描述符集,对Socket编程比较有用的是 readfds。  
struct timeval *timeout;               //控制select()如何返回,是非阻塞,还是阻塞一定时间返回,还是直接有文件描述符有响应才返回。

函数参数:
    1.nfs:最大的文件描述符+1,这个不能错,若不能确定,写一个算法找出来,下面提供的代码有
    2.可读文件描述符集。什么意思呢,就是这个集中的文件描述符随时可能给服务器写数据,那作为服务器,就要监测这些文件描述符,若不关心可读,填NULL。
    3.可写文件描述符集。这里不用到,就不说了。若不关心,可填NULL
    4.异常文件描述符集。若不关心,可填NULL。
    5.时间控制结构体。这个结构体里面有2个成员。一个代表秒,一个代表毫秒。两个都设成0表示select将会非阻塞返回。
对此,还有一些列的宏提供给select用:
1.FD_SET();  用来把文件描述符加到文件描述符集中
2.FD_ZERO(); 清空文件描述符集中的所有描述符
3.FD_ISSET();判断某个文件描述符有没有响应。
注意:select每返回一次后,都要重新清空文件描述符集,和重新把文件描述符加到文件描述符集中。
下面给出服务端的代码:
[cpp]  view plain copy
  1. //TCP服务端
  2. #include"myhead.h"
  3. struct client_list
  4. {
  5. int sock;
  6. struct client_list *next;
  7. };
  8. struct client_list *head = NULL;
  9. struct client_list *init_list(struct client_list*head)
  10. {
  11. head = malloc(sizeof(struct client_list));
  12. head->sock = -1;
  13. head->next = NULL;
  14. return head;
  15. }
  16. //新的客户端加到客户端队列中
  17. int add_sock(struct client_list*head,int new_sock)
  18. {
  19. struct client_list *p = head;
  20. struct client_list *new_node = malloc(sizeof(struct client_list));
  21. new_node->sock = new_sock;
  22. new_node->next = NULL;
  23. while(p->next!=NULL)
  24. {
  25. p = p->next;
  26. }
  27. p->next = new_node;
  28. return 0;
  29. }
  30. //找出最大的文件描述符
  31. int find_max(struct client_list*head)
  32. {
  33. struct client_list *p = head->next;
  34. if(p==NULL)
  35. return 0;
  36. int max_sd = p->sock;
  37. for(p;p!=NULL;p=p->next)
  38. {
  39. if(max_sd < p->sock)
  40. max_sd = p->sock;
  41. }
  42. return max_sd;
  43. }
  44. //信息转发给其他客户端
  45. int write_to_client(struct client_list*head,char *wbuf,int size)
  46. {
  47. struct client_list *p=head;
  48. for(p=head->next;p!=NULL;p=p->next)
  49. {
  50. write(p->sock,wbuf,size);
  51. }
  52. return 0;
  53. }
  54. //当有新的客户端作为新的文件描述符加进来时,显示客户端列表中的所有客户端文件描述符
  55. void show_client_list(struct client_list*head)
  56. {
  57. struct client_list *p = head;
  58. if(p->next == NULL)
  59. {
  60. printf("IS A EMPTY LIST!\n");
  61. return ;
  62. }
  63. else
  64. {
  65. puts("client_list is :");
  66. for(p =head->next; p!=NULL;p = p->next)
  67. {
  68. printf("%d ",p->sock);
  69. }
  70. printf("\n");
  71. }
  72. }
  73. <pre class="cpp" name="code">//取消退出客户端的结点。
  74. int del_node(struct client_list*head,int sock)
  75. {
  76. struct client_list *p = head->next;
  77. struct client_list *q = head;
  78. while(p!=NULL)
  79. {
  80. if(p->sock == sock)
  81. {
  82. q->next = p->next;
  83. free(p);
  84. p = NULL;
  85. }
  86. else
  87. {
  88. p = p->next;
  89. q = q->next;
  90. }
  91. }
  92. return 0;
  93. }
  94. int main(int argc, char const *argv[])
  95. {
  96. char rbuf[50]={0};
  97. char wbuf[50]={0};
  98. int sockfd,size,on=1;
  99. int new_sock;
  100. int max_sd;
  101. struct client_list *pos;
  102. struct timeval timeout = {0,0};  //设置select为非阻塞返回
  103. fd_set fdset;
  104. long val;
  105. head = init_list(head);   //初始化客户端链表。
  106. pos = head;
  107. struct sockaddr_in saddr;
  108. struct sockaddr_in caddr;
  109. size = sizeof(struct sockaddr_in);
  110. bzero(&saddr,size);
  111. saddr.sin_family = AF_INET;
  112. saddr.sin_port = htons(8888);
  113. saddr.sin_addr.s_addr = htonl(INADDR_ANY);
  114. sockfd = socket(AF_INET,SOCK_STREAM,0);
  115. setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));//设置socket套接字为复用,不设也可以
  116. //把sockfd设置为非阻塞
  117. val = fcntl(sockfd,F_GETFL);
  118. val|=O_NONBLOCK;
  119. fcntl(sockfd,F_SETFL,val);
  120. bind(sockfd,(struct sockaddr*)&saddr,size);
  121. listen(sockfd,10);
  122. while(1)
  123. {
  124. new_sock = accept(sockfd,(struct sockaddr*)&caddr,&size);//循环接受新连接的客户端
  125. if (new_sock!= -1)
  126. {
  127. puts("new node come!\n");
  128. printf("new_sock = %d\n",new_sock);
  129. add_sock(head,new_sock);
  130. show_client_list(head);
  131. }
  132. max_sd = find_max(head);     //从客户端队列中,找出最大的文件描述符
  133. FD_ZERO(&fdset);             //清空文件描述符集
  134. pos = head;
  135. //把每个套接字加入到集合中
  136. if(pos->next != NULL)   //若套接字列表不是空
  137. {
  138. for(pos=head->next;pos!=NULL;pos=pos->next)
  139. {
  140. FD_SET(pos->sock,&fdset);
  141. }
  142. }
  143. select(max_sd+1,&fdset,NULL,NULL,&timeout); //等待描述符
  144. for(pos=head->next;pos!=NULL;pos = pos->next) //检查哪个套接字有响应
  145. {
  146. if(FD_ISSET(pos->sock,&fdset))           //判断pos->sock这个文件描述符指向的客户端有没有数据写过来
  147. {
  148. bzero(rbuf,50);
  149. read(pos->sock,rbuf,50);
  150. printf("%s\n",rbuf);
  151. if(strcmp(rbuf,"quit")==0)  //若客户端发来的信息为quit,则取消这个客户端的结点。
  152. {
  153. del_node(head,pos->sock);
  154. }
  155. write_to_client(head,rbuf,50);     //把写过来的信息转发给队列中的其他客户端。
  156. }
  157. }
  158. }
  159. return 0;
  160. }</pre>
  161. <pre></pre>
  162. <br>
客户端代码:
[cpp]  view plain copy
  1. //客户端
  2. #include"myhead.h"
  3. int main(int argc, char const *argv[])
  4. {
  5. int sockfd;
  6. char rbuf[50]={0};
  7. char wbuf[50]={0};
  8. char ipbuf[50]={0};
  9. int port;
  10. int max_sd;
  11. int size,on=1;
  12. int ret;
  13. fd_set fdset;
  14. struct sockaddr_in saddr;
  15. size = sizeof(struct sockaddr_in);
  16. saddr.sin_family = AF_INET;
  17. saddr.sin_port = htons(8888);
  18. saddr.sin_addr.s_addr = inet_addr("192.168.152.128");
  19. sockfd = socket(AF_INET,SOCK_STREAM,0);
  20. setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
  21. ret = connect(sockfd,(struct sockaddr*)&saddr,size);
  22. if(ret ==0)
  23. {
  24. printf("connect sucess\n");
  25. inet_ntop(AF_INET,(void*)&saddr.sin_addr.s_addr,ipbuf,50);
  26. port = ntohs(saddr.sin_port);
  27. printf("ip:%s,port:%d\n",ipbuf,port);
  28. }
  29. else if(ret == -1)
  30. {
  31. printf("failed to connect\n");
  32. return -1;
  33. }
  34. while(1)
  35. {
  36. FD_ZERO(&fdset);
  37. FD_SET(sockfd,&fdset); //把监测服务端的描述符集放到集合中
  38. FD_SET(STDIN_FILENO,&fdset); //STDIN_FILENO这个文件描述符用于监测标准输入(键盘)
  39. max_sd = sockfd>STDIN_FILENO?sockfd:STDIN_FILENO;
  40. select(max_sd+1,&fdset,NULL,NULL,NULL); //这里的select是设置为一直阻塞到有文件描述符发生响应
  41. if(FD_ISSET(sockfd,&fdset)) //判断是否服务端有数据发过来
  42. {
  43. bzero(rbuf,50);
  44. read(sockfd,rbuf,50);
  45. printf("%s\n",rbuf);
  46. }
  47. if(FD_ISSET(STDIN_FILENO,&fdset))  //判断键盘是否有数据传过来
  48. {
  49. bzero(wbuf,50);
  50. scanf("%s",wbuf);
  51. write(sockfd,wbuf,50);         //把键盘传过来的数据发给服务端
  52. if(strcmp(wbuf,"quit")==0)    //若键盘过来的数据为quit,则关闭这个客户端
  53. {
  54. printf("quit!\n");
  55. return 0;
  56. }
  57. }
  58. }
  59. return 0;
  60. }

select函数用法相关推荐

  1. linux c语言 select函数用法

    原文地址:点击打开链接 linux c语言 select函数用法 Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如 co ...

  2. linux select函数用法

    select系统调用是用来让我们的程序监视多个文件句柄的状态变化的.程序会停在select这里等待,直到被监视的文件句柄有一个或多个发生了状态改变.关于文件句柄,其实就是一个整数,我们最熟悉的句柄是0 ...

  3. select函数用法详解

    1. select函数 select的作用: 轮询的方式,从多个文件描述符中获取状态变化后的情况. 头文件 #include <sys/time.h> //for struct timev ...

  4. select函数用法详解,及实例分析

    select函数是系统调用函数,用于多路监控.当没有一个文件满足要求时,select将阻塞调用进程.在有些情况下,采用select函数可以大大简化程序结构.比如一个系统有10个输入设备,如果想实时读取 ...

  5. linux之select函数用法详解

    select系统调用是用来让我们的程序监视多个文件句柄(file descriptor)的状态变化的.程序会停在select这里等待,直到被监视的文件句柄有某一个或多个发生了状态改变.   何为文件句 ...

  6. select()函数用法

    select的函数格式(我所说的是Unix系统下的伯克利socket编程,和windows下的有区别,一会儿说明): int select(int maxfdp,fd_set *readfds,fd_ ...

  7. select 函数用法

    Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect.accept.recv或recvfrom这样的阻塞程序( ...

  8. C++网络编程Select函数用法

    Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如 connect.accept.recv或recvfrom这样的阻塞程序 ...

  9. select函数(一)

    int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); ...

最新文章

  1. 小猿圈讲解Java可以做什么?
  2. c++中结构体套结构体用 = {0}初始化编译报错解决办法(用memset或者={})(error: invalid conversion)
  3. 全国计算机二级vb得分技巧,全国计算机等级考试二级VB笔试各题型答题技巧(2)...
  4. 复习Python DB-API
  5. JDK8 Stream 操作
  6. 安全计算:AVG免费版提供免费病毒防护
  7. Ubuntu联网安装GCC
  8. halcon自动对焦
  9. 为什么要用大写L替换小写L(l),idea提醒literal ‘xxx‘ ends with lowercase ‘l‘
  10. [欣赏]——天长地久
  11. 中值滤波medianBlur函数的使用示例程序
  12. 飞腾cpu服务器浪潮信息,推动国产化进程 浪潮发布飞腾服务器
  13. evga x58服务器芯片组,何谓板皇?四大顶级X58主板巅峰对决
  14. 饥荒联机版-Centos7-阿里云服务器-搭建
  15. 解决 plt.savefig() 生成空白图片的问题
  16. 360 android系统 流量,360手机卫士Android版增流量监控
  17. stm32按键控制mg90s数字舵机、pwm调节舵机转动角度
  18. 阳性感染者被要求带病上班?广东省人社厅:阳性感染者应居家隔离
  19. u8用友服务器系统在哪,用友u8数据库服务器在哪
  20. 快速入门Unity机器学习:一:

热门文章

  1. Linux - 第11节 - 网络入门
  2. 人脸识别签到考勤系统毕业设计,人脸识别考勤系统设计与实现,人脸识别系统论文毕设作品参考
  3. 广电网络宽带电视网关简介与优化设置
  4. 计算机最强网卡价格,高下一目了然 随手怒拆价值十万元整机
  5. 副业新风口:淘宝无货源,1天1小时!10个月全款买房!
  6. Python绘制美国队长盾牌
  7. CSS 单词换行 word-break属性
  8. 几款炫酷的CSS代码样式
  9. 一文搞懂Spark的Task调度器(TaskScheduler)
  10. 遗传算法的基本原理和方法(转)