三次握手
TCP連接建立的開始是三次握手,通過三次交互確認連接成功,在客戶端調用connect時,客戶端發送sync消息給服務端,服務端收到sync消息后,返回一個ack+sync,並等待ack,客戶端收到ack+sync后,返回一個ack,connect返回,服務端收到ack后,accept返回,如下圖所示:
connect超時設置
如果connect連接的服務端不存在,或是異常了,會出現什么情況,可能有以下幾種情況:
-
服務端返回連接錯誤
-
等待系統默認的75秒超時
上述兩種情況都是不可控的,因此可以通過設置超時時間的方式控制超時時間,有下述兩種方法。
非阻塞connect+select
將conenct設置成非阻塞后,connect在發送sync后直接返回-1,然后使用select等待ack+sync,select是可以設置超時時間,因此達到設置超時時間的目的。注意需要將socket恢復成阻塞模式,如下所示:
dwSign = 1; if((err = ioctl(socket, FIONBIO, &dwSign)) < 0) { printf("ioctl socket failed, errno = %d!\n", err); close(socket); return err; } SOCKADDR_IN tServerAddrInfo = {0}; tServerAddrInfo.sin_family = AF_INET; tServerAddrInfo.sin_addr.s_addr = ServerIP; tServerAddrInfo.sin_port = ServerPort; int bRet = 0x0; fd_set set = {0x0}; if(-1 == connect(socket, (SOCKADDR*)&tServerAddrInfo, sizeof(SOCKADDR))) { tm.tv_sec = 10; tm.tv_usec = 0; FD_ZERO(&set); FD_SET(socket, &set); // select返回值大於0,表示有數據可以操作;select返回值等於0,表示超時 if(select(socket + 1, NULL, &set, NULL, &tm) > 0) { // 獲取socket的error值,0表示成功 getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len); if(error == 0) { bRet = TRUE; } else { bRet = FALSE; } } else { bRet = FALSE; } } else { bRet = TRUE; } dwSign = 0; if(0 > (err = ioctl(socket, FIONBIO, &dwSign))) { printf("ioctl socket failed, errno = %d!\n", err); close(dwSocket); return err; }
設置socket的發送超時時間
調用connect函數實際上是發送sync報文,如果設置發送超時時間,那么也就可以實現connect的超時時間,如下所示:
struct timeval timeout = {10, 0); setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(struct timeval));
accpet超時設置
既然可以通過設置發送超時時間,控制connect的超時時間,那么同理,也可以通過設置接收超時時間來設置accept的超時時間,如下所示:
struct timeval timeout = {10, 0}; setsockopt(proxy_sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval));