北京加密机现场select问题
問題描述
北京項目通過調用我們提供的庫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 < 0){
?? ??? ?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<0){
?? ??? ??? ??? ?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<0 && errno != EINPROGRESS){
?? ??? ??? ?P_DEBUG("back connect failed.\n");
?? ??? ??? ?goto connect_failure;?? ?
?? ??? ?}
?? ??? ?if((epfd = epoll_create(1))<0){
?? ??? ??? ?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)<0){
?? ??? ??? ?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<=0 || events[0].events & EPOLLERR){
?? ??? ??? ?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。
? ??? ?
???
總結
以上是生活随笔為你收集整理的北京加密机现场select问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LOL里KDA为9算不算高了额?
- 下一篇: 详细解释signal和sigaction