如何解决Connect超时导致的阻塞问题
版權聲明:本文為博主原創文章,遵循 CC 4.0 by-sa 版權協議,轉載請附上原文出處鏈接和本聲明。
本文鏈接:https://blog.csdn.net/clirus/article/details/50577352
????????這幾天發現一個現象,客戶端正常連接服務器connect顯然不會出現問題。
????????在異常情況下,如果是服務器出現異常,connect能夠立即返回失敗;但是當客戶端出現異常的情況下,分為兩種情況:
????????一種是不插網線,客戶端沒有獲得ip地址,在這種情況下,connect也可以立即返回錯誤;
????????二是但是當客戶端插上網線,但是連接網絡失敗,也就是說能夠獲取到ip地址,但是和服務器是ping不通的。這種情況下connect就可能會發生阻塞,因為按照《UNIX 網絡編程》中講解,connect的在進行三次握手,如果失敗情況,需要等待75s的超市時間的。
????????我們主要討論第二種情況如何解決,可以讓connect快速返回結果,不至于阻塞等待超長的時間。
????????如下是我的代碼
/******************************
* Time out for connect()
******************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
?
?
#define TIME_OUT_TIME 20 //connect超時時間20秒
?
bool setBlockOpt(int m_fd,bool blocked)
{
?? ??? ??? ?
#ifndef WIN32
?? ?int ?flags;
?? ?flags = fcntl(m_fd, F_GETFL, 0);
?? ?if(flags < 0)
?? ?{
?? ??? ?return false;
?? ?}
?? ?if(blocked)
?? ?{?? ?
?? ??? ?printf("Set BLOCK !!!\n");
?? ??? ?flags &= ~O_NONBLOCK;
?? ?}
?? ?else
?? ?{
?? ??? ?printf("Set NONBLOCK !!!\n");
?? ??? ?flags |= O_NONBLOCK;
?? ?}
?? ?if(fcntl(m_fd, F_SETFL, flags) < 0)
?? ?{
?? ??? ?return false;
?? ?}
?? ?
#else
?? ?u_long ulValue;
?? ?if(blocked)
?? ?{
?? ??? ?ulValue = 1;
?? ?}
?? ?else
?? ?{
?? ??? ?ulValue = 0;
?? ?}?? ?
?? ?int n = ioctlsocket(m_fd, FIONBIO, &ulValue);
?? ?if (n != 0)
?? ?{
?? ??? ?return false;
?? ?}
#endif
?? ?return true;
}
?
int connectWithTimeout(int m_fd,int timeout)
{
?? ?int selectFlag = -1;
?? ?int error=-1, len;
?? ?len = sizeof(int);
?? ?bool ret = false;
?? ?int connectFlag = -1;
?? ?
?? ?const char* m_ip = "115.239.210.27";
?? ?int m_port = 80;
?? ?
?? ?if("" == m_ip || 0 > m_port)
?? ?{
?? ??? ?return -1;
?? ?}
?? ?
?? ?if(m_fd < 0 && "" != m_ip && m_port >=0)
?? ?{
?? ??? ?m_fd = ?socket(AF_INET, SOCK_STREAM, 0);
?? ??? ?if(m_fd < 0)
?? ??? ?{
?? ??? ??? ?return -1;
?? ??? ?}
?? ?}
?? ?
?? ?if(m_fd < 0)
?? ?{
?? ??? ?return -1;
?? ?}
?
?? ?struct sockaddr_in servAddr;
?? ?memset(&servAddr, 0, sizeof(servAddr));
?? ?servAddr.sin_family = AF_INET;
?? ?servAddr.sin_port = htons((unsigned short)m_port);
?? ?servAddr.sin_addr.s_addr = inet_addr(m_ip);
?
?? ?setBlockOpt(m_fd,false);?? ?//設置為非阻塞模式
?? ?if( (connectFlag= connect(m_fd, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0))
?? ?{
?? ??? ?if(errno != EINPROGRESS)
?? ??? ?{
?? ??? ??? ?goto done;
?? ??? ?}
?? ?}
?? ?else
?? ?{
?? ??? ?ret = true;
?? ??? ?goto done;
?? ?}
?? ?timeval tm;
?? ?tm.tv_sec = timeout/1000;
?? ?tm.tv_usec = timeout%1000;
?? ?fd_set rest, west;
?? ?FD_ZERO(&rest);
?? ?FD_ZERO(&west);
?? ?FD_SET(m_fd, &rest);
?? ?FD_SET(m_fd, &west);
?
?? ?if( (selectFlag = select(m_fd+1, &rest, &west, NULL, &tm)) > 0)
?? ?{
?? ??? ?//如果套接口及可寫也可讀,需要進一步判斷
?? ??? ?if(FD_ISSET(m_fd, &rest) && FD_ISSET(m_fd, &west))?
?? ??? ?{
?? ??? ??? ?if(getsockopt(m_fd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len) < 0)
?? ??? ??? ?{
?? ??? ??? ??? ?printf("getsockopt error!!!\n");
?? ??? ??? ?}
?? ??? ??? ?else
?? ??? ??? ?{
?? ??? ??? ??? ?if(error == 0)
?? ??? ??? ??? ?{
?? ??? ??? ??? ??? ?ret = true;
?? ??? ??? ??? ?}
?? ??? ??? ??? ?else?
?? ??? ??? ??? ?{
?? ??? ??? ??? ??? ?printf("connect getsockopt error!!! %d\n",error);
?? ??? ??? ??? ?}
?? ??? ??? ?}
?? ??? ?}
?? ??? ?//如果套接口可寫不可讀,則鏈接完成
?? ??? ?else if(FD_ISSET(m_fd, &west) && !FD_ISSET(m_fd, &rest))?
?? ??? ?{?
?? ??? ??? ?ret = true;
?? ??? ?}
?? ?}
?? ?else if(selectFlag == 0)
?? ?{
?? ??? ?printf("connect select timeout!!!\n");
?? ?}
?? ?else?
?? ?{
?? ??? ?printf("connect select error!!!\n");
?? ?}
?? ?
done:
?? ?setBlockOpt(m_fd,true);// 設置為阻塞模式
?? ?if(!ret)
?? ?{
?? ??? ?return -1;
?? ?}
?? ?return 0;
}
?
?
int main(int argc,char* argv[])
{
?? ?if(argc <= 1)
?? ?{
?? ??? ?printf("input error!!!\n");
?? ??? ?exit(1);
?? ?}
?? ?int sockfd = socket(AF_INET, SOCK_STREAM, 0);
?? ?if(sockfd < 0)?
?? ?{
?? ??? ?exit(1);
?? ?}
?? ?if(connectWithTimeout(sockfd,atoi(argv[1])) == 0)
?? ?{
?? ??? ?printf("connect sucess!!!\n");
?? ?}
?? ?else
?? ?{
?? ??? ?printf("connect filed!!!\n");
?? ?}
?? ?close(sockfd);
?? ?
?? ?
?? ?return 0;
}
??????
???????原理很簡單,就是先把套接字設置為非阻塞,因為在非阻塞情況下,connect的結果是立即返回的,然后我們再使用select或者poll等機制來檢測套接字一定的時間,如果在超時時間內不可寫,則認為connect失敗,然后需要把套接字重新設置為阻塞,當然如果你不需要在阻塞模式下工作,可以不用設置。
??????如上,我們就可以對connect的超時進行可控。
---------------------?
版權聲明:本文為CSDN博主「clirus」的原創文章,遵循CC 4.0 by-sa版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/clirus/article/details/50577352
總結
以上是生活随笔為你收集整理的如何解决Connect超时导致的阻塞问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 开发IOT WiFi设备时,需要测试的几
- 下一篇: 非阻塞connect的实现