zoukankan      html  css  js  c++  java
  • 浅析tcp中read阻塞

           最近学习route组件,了解了些关于tcp通信中I/O复用的知识。比如:select,poll,epoll。目前系统主要是用select。本来以为select是个好东西,解决了单进程单线程的server可以连接多个客户端的问题。后来,同事跟我说read函数是阻塞的,那么连接建立后,server会阻塞在read处,其他连接就没法正常工作了。然后这个问题就一直困扰着。想起了之前在知乎上有个问题是:怎么设计tcp连接?有个点赞很多的是建多线程,一个线程一个连接。但是,也有不少人批判这个设计,说这样太费资源,指出I/O多路复用中使用类似于select在一个线程中可以实现连接多个客户端。当时也是没想明白,而且公司route组件(老版本)的设计是一个线程一个连接,apache也是一个进程一个连接,坏处就是连接数量很少,毕竟进程切换是耗cpu的。后来就查阅资料,然后通过代码测试,read的阻塞可以不会干扰其他连接的,一个server连N客户端跟连一个客户端一个麻溜溜的。测试代码如下: 

     1     for (;;) {
     2         memset(szBuf, 0, sizeof(szBuf));
     3         FD_ZERO(&fset);
     4         FD_SET(fd, &fset);
     5         tv.tv_sec = 5;
     6         tv.tv_usec = 0;
     7 
     8         for (int i = 0; i < BACKLOG; i++) {
     9             if (fd_A[i] != 0)
    10                 FD_SET(fd_A[i], &fset);
    11         }
    12 
    13         ret = select(maxfd+1, &fset, NULL, NULL, &tv);
    14 
    15         if (ret < 0) {
    16             printf("select调用发生错误
    ");
    17             break;
    18         }
    19         else if (ret == 0) {
    20             printf("select timeout
    ");
    21             continue;
    22         }
    23         else {
    24             printf("select normal
    ");
    25         }
    26 
    27         for (int i = 0; i < BACKLOG; i++) {
    28             if (fd_A[i] && FD_ISSET(fd_A[i], &fset)) {
    29                 printf("recv before
    ");
    30                 if ((ret = recv(fd_A[i], szBuf, sizeof(szBuf), 0)) == 0) {
    31                     close(fd_A[i]);
    32                     FD_CLR(fd_A[i], &fset);
    33                     fd_A[i] = 0;
    34                     conn_amount--;
    35                 }
    36                 else {
    37                     printf("fd_A[%d]:%s", i, szBuf);
    38                 }
    39             }
    40         }
    41 
    42         if (FD_ISSET(fd, &fset)) {
    43             newfd = accept(fd, (struct sockaddr *)&cli_addr, &cli_len);
    44             if (newfd <= 0) {
    45                 printf("accept出错
    ");
    46                 continue;
    47             }
    48             else
    49                 printf("accept normal
    ");
    50 。。。

     

               当客户端connect连接上的时候,会输出"select normal"  "accept normal",没有走到read/recv这块;如果5秒内客户端没其他操作,server就会在select处超时(select的超时时间设置的是5秒)。接着客户端调用send,然后代码会走到select->read这块,并没有进去accept。因为在调用accept,recv/read之前我用了FD_ISSET来判断。

              fd_set是一组文件描述符(fd)的集合,它用一位来表示一个fd。至于fd有多大,操作系统定义了常量FD_SETSIZE。在很久 以前是32,现在一般是1024。select函数用于检查fd_set集合中是否有可读的,同时也会更新fd_set集合。FD_ISSET用于测试指定的文件描述符是否在该集合中。假设现在客户端1是成功连接的,如果客户端2发起连接,那么select后客户端1对应fd使用FD_ISSET后返回值是false的,那么就不去调用recv/read函数。如果客户端1发送数据过来,select检测到后,使用FD_ISSET判断连接1返回true,可以用recv/read不会阻塞;使用FD_ISSET判断连接2的返回是false的,不去调用recv/read函数。

    同时,客户端在send的时候一次发2k数据,在server接收一次1k的,第一次没取完,select会再次检测到该fd可读,再收一次,正好2k,select才不会检测到该fd可读。这个例子是一个简单的非阻塞(NIO)的例子,难点就是对于半包问题要处理好。很多时候我们接收到的数据要完整了才行进行decode。

  • 相关阅读:
    web网站开发反面教材
    phpstudy 做的后台长时间运行的脚本,设置了脚本运行时间还是40秒就返回500,用的apache2.4.39
    PHP_EOL
    web文件下载,a标签文件下载,php文件下载
    邮件发送
    网站调用qq第三方登录
    微信Pcweb登录简介
    JqueryOn绑定事件方法介绍
    php+ajax文件上传
    php操作数组函数
  • 原文地址:https://www.cnblogs.com/ikel/p/7494692.html
Copyright © 2011-2022 走看看