仪陇家园分类信息网、仪陇生活网、仪陇家园网

搜索

北京加密机现场select问题

[复制链接]
seo 发表于 2022-5-31 13:31:11 | 显示全部楼层 |阅读模式
北京加密机现场select问题发布时间:2022/5/31 13:12:34
            
                                                       
                                                       
            
        
        
               
                    问题描述

北京项目通过调用我们提供的库libsigxt.a与加密机通信,c/s架构,客户端启用多个线程,每个线程流程有以下三步,连接加密机,签名,关闭链接。在正常运行一段时间后会出现不能连接加密机服务问题。

连接服务器代码如下:

setnonblocking(sk_fd);

ret = connect(sk_fd, (struct sockaddr *)&sa_serv, server_len);
     P_DEBUG("main connect ret:%d\n",ret);
     if(ret
         if(errno != EINPROGRESS){
             P_DEBUG("mainip connect error ret.\n");
         }else{
             tv.tv_sec = timeout;
             tv.tv_usec = 0;
             fd_set wset;
             FD_ZERO(&wset);
             FD_SET(sk_fd,&wset);
             ret = select(sk_fd+1,NULL,&wset,NULL,&tv);
             printf("main select ret is %d\n",ret);
             if(ret
                 P_DEBUG("mainip select error,can not connect to server.\n");
             }else if(ret == 0){
                 P_DEBUG("mainip connect timeout.\n");
             }else{
                 if(FD_ISSET(sk_fd,&wset)){
                     P_DEBUG("mainip connect successful.\n");
                     setblocking(sk_fd);
                 }else{
                     P_DEBUG("not fd_isset(). not connected.\n");
                     goto quit;
                 }   
             }
         }
     }

先设置非阻塞模式,然后通过connect连接,当返回0时,表示连接成功;当返回-1,并且errno 为 EINPROGRESS表示正在连接过程中,通过select监听wset,当select返回小于0时,表示连接失败;当select返回0时,表示超时;当返回大于0时,表示连接成功,重新再设置成阻塞模式。

根据现场日志打印,当select返回非1的正数,则监听不到可写事件,连接失败,发送数据也失败。



原因查找定位

通过网上搜索发现,1024限定的不只是监听的个数,还是文件描述符的最大值。FD_SETSIZE限制了文件描述符的个数。
但是根据fd_set存储文件描述符的原理,FD_SETSIZE限制的应该是文件描述符的最大值,当然限制了最大值也就限制了个数。

下面一段摘自man select中的原话
"Executing FD_CLR() or FD_SET() with a value of fd that is negative or is equal to  or larger than FD_SETSIZE will result in undefined behavior."

再查下现场打开的描述符个数,的确大于1024,造成select返回的结果不可预期。



解决方案

既然select使用除了问题,那就换成epoll来监听,修改代码如下:

setnonblocking(sk_fd);

        ret = connect(sk_fd, (struct sockaddr *)&sa_serv, server_len);
         if(ret == 0){
             goto connect_success;
         }
         
         if(ret
             P_DEBUG("back connect failed.\n");
             goto connect_failure;   
         }

        if((epfd = epoll_create(1))
             P_DEBUG("main epoll create error.\n");
             goto connect_failure;
         }
         memset(&ev, 0, sizeof ev);
         ev.events = EPOLLOUT;
         ev.data.fd = sk_fd;

        if(epoll_ctl(epfd, EPOLL_CTL_ADD, sk_fd, &ev)
             P_DEBUG("main epoll_ctl error.\n");
             goto connect_failure;
         }
         
         n = epoll_wait(epfd,events,1,itimeout);
         P_DEBUG("back epoll_wait n is %d\n",n);
         if(n
             P_DEBUG("back connect failure.\n");
             goto connect_failure;
         }
      

connect_success:
     
     P_DEBUG("connect success.\n");
     setblocking(sk_fd);

   ......

connect_failure:
     if(sk_fd>0)
         close(sk_fd);
     sk_fd = -1;
     if(epfd>0)
         close(epfd);
     return -1;

}

经测试发现,没有再出现连接失败问题。

结论:建议linux下弃用select。

      
   
               
        
        
   
            
        
        
回复

使用道具 举报

全部回复0 显示全部楼层

发表回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

楼主

审核员

热门推荐

联系客服 关注微信 下载APP 返回顶部 返回列表