搜索
bottom↓
回复: 4

网络服务器 select同时处理读写的疑问

[复制链接]

出0入0汤圆

发表于 2016-1-9 15:03:32 | 显示全部楼层 |阅读模式
我现在实现个网络server有这样的需求,  一般情况下是 从客户端读数据的,  但是有时候需要往客户端主动去写数据,我想到了用select.

select的原型是 ( nfds, readfds,   writefds, errorfds, time)是这样的,  看这个是能够同时处理 read和write的同时访问,
但我无论是 书上 和网络上,查了一下,select的一般例子都是 用于多客户端读的情况, 基本代码如下:


  1.     server_sockfd = socket(AF_INET, SOCK_STREAM, 0);

  2.     server_address.sin_family = AF_INET;
  3.     server_address.sin_addr.s_addr = htonl(INADDR_ANY);
  4.     server_address.sin_port = htons(9734);
  5.     server_len = sizeof(server_address);

  6.     bind(server_sockfd, (struct sockaddr *)&server_address, server_len);

  7. /*  Create a connection queue and initialize readfds to handle input from server_sockfd.  */

  8.     listen(server_sockfd, 5);

  9.     FD_ZERO(&readfds);
  10.     FD_SET(server_sockfd, &readfds);

  11. /*  Now wait for clients and requests.
  12.     Since we have passed a null pointer as the timeout parameter, no timeout will occur.
  13.     The program will exit and report an error if select returns a value of less than 1.  */

  14.     while(1) {
  15.         char ch;
  16.         int fd;
  17.         int nread;

  18.         testfds = readfds;

  19.         printf("server waiting\n");
  20.         result = select(FD_SETSIZE, &testfds, (fd_set *)0,
  21.             (fd_set *)0, (struct timeval *) 0);

  22.         if(result < 1) {
  23.             perror("server5");
  24.             exit(1);
  25.         }

  26. /*  Once we know we've got activity,
  27.     we find which descriptor it's on by checking each in turn using FD_ISSET.  */

  28.         for(fd = 0; fd < FD_SETSIZE; fd++) {
  29.             if(FD_ISSET(fd,&testfds)) {

  30. /*  If the activity is on server_sockfd, it must be a request for a new connection
  31.     and we add the associated client_sockfd to the descriptor set.  */

  32.                 if(fd == server_sockfd) {
  33.                     client_len = sizeof(client_address);
  34.                     client_sockfd = accept(server_sockfd,
  35.                         (struct sockaddr *)&client_address, &client_len);
  36.                     FD_SET(client_sockfd, &readfds);
  37.                     printf("adding client on fd %d\n", client_sockfd);
  38.                 }

  39. /*  If it isn't the server, it must be client activity.
  40.     If close is received, the client has gone away and we remove it from the descriptor set.
  41.     Otherwise, we 'serve' the client as in the previous examples.  */

  42.                 else {
  43.                     ioctl(fd, FIONREAD, &nread);

  44.                     if(nread == 0) {
  45.                         close(fd);
  46.                         FD_CLR(fd, &readfds);
  47.                         printf("removing client on fd %d\n", fd);
  48.                     }

  49.                     else {
  50.                         read(fd, &ch, 1);
  51.                         sleep(5);
  52.                         printf("serving client on fd %d\n", fd);
  53.                         ch++;
  54.                         write(fd, &ch, 1);
  55.                     }
  56.                 }
  57.             }
  58.         }
  59.     }
复制代码



那想请教如何用select实现 读写都可以呢?

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出10入61汤圆

发表于 2016-1-9 17:32:52 | 显示全部楼层
因为读不知道什么时候有数据来,所以才用SELECT, 要写直接写就行了

出0入0汤圆

 楼主| 发表于 2016-1-9 18:29:28 来自手机 | 显示全部楼层
tcm123 发表于 2016-1-9 17:32
因为读不知道什么时候有数据来,所以才用SELECT, 要写直接写就行了

在同一个线程里吗? 譬如我的例子,写在哪里

出0入0汤圆

 楼主| 发表于 2016-1-9 21:28:57 | 显示全部楼层
本帖最后由 imjacob 于 2016-1-9 22:10 编辑

又仔细查了些资料,加上自己的思考,是否可以这样,基本原理是 每次读写 读写之前用select判断是否可读或者可写,代码大致如下

  1.     //这里区分是需要读还是写
  2. BOOL NET::SelectFun(SOCKET sock,BOOL ret) //ret为true表示要读取数据,ret为false表示要发送数据  
  3. {
  4.     if (ret) //ret只是用来区分读写  
  5.     {  
  6.        num=select(0,&fd,NULL,NULL,&tval);  
  7.     }  
  8.     else  
  9.     {  
  10.         num=select(0,NULL,&fd,NULL,&tval);  
  11.     }
  12. }

  13. //接收的线程里
  14. UINT NET::ThreadReceive()
  15. {
  16.       while (1)  
  17.     {  
  18.         if(NET->SelectFun(NET->m_sock,TRUE)) // 判断,如果select判断为真则读取数据  
  19.         {  
  20.   
  21.             res=recv ()
  22.   
  23.         }
  24. }


  25. //发送的线程里
  26. UINT NET::ThreadSend()
  27. {
  28.       while (1)  
  29.     {  
  30.         if(NET->SelectFun(pDlg->m_sock,TRUE)) // 判断,如果select判断为真则读取数据  
  31.         {  
  32.   
  33.             res=send()         
  34.   
  35.         }
  36. }
  37.   
复制代码


虽然感觉可以,但是需要用两个线程,能不能用一个呢?两个的情况下,这种所谓的IO多路复用方式没体现出优势啊,因为我原始的用socket分别在两个socket读和写也是可以的啊。恳请各位指教。

当然在另外一个帖子里,有朋友说那些libuv之类的库,我看了下确实能够比较好的解决这个问题,但担心效率速度。
因为是在单片机里,那些库要占用额外的资源的,所以不太敢用。

出0入0汤圆

 楼主| 发表于 2016-1-10 15:52:53 | 显示全部楼层
更新一下,想到的另外一个方式是:
一个线程专门写, 不能阻塞
另一个线程用select来查询是否可读。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-29 17:58

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表