Windows上如何玩非阻塞的connect?---讓程式設計師自定義connect函式的超時時間
阿新 • • 發佈:2019-01-02
我們知道, 對於阻塞的socket而言, connect函式也是阻塞的, 我在Windows上測試過, 對於阻塞的socket而言, connect的阻塞時間約為25s(linux上是75s吧, 各個平臺都不一樣). 也就是說, 很多時候, 客戶端需要等25s才繼續往下執行。 我們想象一下, 使用者肯定會不滿意啊, 得罪了使用者, 那就糟糕了。 那能不能搞個自己設定超時時間的connect函式呢? 完全可以! 在本文中, 我們來學習一下非阻塞connect函式的實現---讓程式設計師自定義connect函式的超時時間。
說明: 兩年後, 當我再次審視這些程式的時候, 我發現, select後, 強烈建議做FD_ISSET檢查。
直接上客戶端的程式碼:
#include <stdio.h> #include <winsock2.h> #pragma comment(lib, "ws2_32.lib") int main() { // 網路初始化 WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD(2, 2); WSAStartup( wVersionRequested, &wsaData ); // 建立客戶端socket(預設為是阻塞socket) SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0); // 設定為非阻塞的socket int iMode = 1; ioctlsocket(sockClient, FIONBIO, (u_long FAR*)&iMode); // 定義服務端 SOCKADDR_IN addrSrv; addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); addrSrv.sin_family = AF_INET; addrSrv.sin_port = htons(8888); // 超時時間 struct timeval tm; tm.tv_sec = 5; tm.tv_usec = 0; int ret = -1; // 嘗試去連線服務端 if (-1 != connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR))) { ret = 1; // 連線成功 } else { fd_set set; FD_ZERO(&set); FD_SET(sockClient, &set); if (select(-1, NULL, &set, NULL, &tm) <= 0) { ret = -1; // 有錯誤(select錯誤或者超時) } else { int error = -1; int optLen = sizeof(int); getsockopt(sockClient, SOL_SOCKET, SO_ERROR, (char*)&error, &optLen); // 之所以下面的程式不寫成三目運算子的形式, 是為了更直觀, 便於註釋 if (0 != error) { ret = -1; // 有錯誤 } else { ret = 1; // 無錯誤 } } } // 設回為阻塞socket iMode = 0; ioctlsocket(sockClient, FIONBIO, (u_long FAR*)&iMode); //設定為阻塞模式 // connect狀態 printf("ret is %d\n", ret); // 傳送資料到服務端測試以下 if(1 == ret) { send(sockClient, "hello world", strlen("hello world") + 1, 0); } // 釋放網路連線 closesocket(sockClient); WSACleanup(); return 0; }
我們先不管服務端, 直接執行上面的程式, 過5s後, 程式結果為:ret is -1
好, 我們關掉當面的客戶端。 並啟用下面的服務端:
#include <stdio.h> #include <winsock2.h> // winsock介面 #pragma comment(lib, "ws2_32.lib") // winsock實現 int main() { WORD wVersionRequested; // 雙位元組,winsock庫的版本 WSADATA wsaData; // winsock庫版本的相關資訊 wVersionRequested = MAKEWORD(1, 1); // 0x0101 即:257 // 載入winsock庫並確定winsock版本,系統會把資料填入wsaData中 WSAStartup( wVersionRequested, &wsaData ); // AF_INET 表示採用TCP/IP協議族 // SOCK_STREAM 表示採用TCP協議 // 0是通常的預設情況 unsigned int sockSrv = socket(AF_INET, SOCK_STREAM, 0); SOCKADDR_IN addrSrv; addrSrv.sin_family = AF_INET; // TCP/IP協議族 addrSrv.sin_addr.S_un.S_addr = inet_addr("0.0.0.0"); // socket對應的IP地址 addrSrv.sin_port = htons(8888); // socket對應的埠 // 將socket繫結到某個IP和埠(IP標識主機,埠標識通訊程序) bind(sockSrv,(SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); // 將socket設定為監聽模式,5表示等待連線佇列的最大長度 listen(sockSrv, 5); SOCKADDR_IN addrClient; int len = sizeof(SOCKADDR); unsigned int sockConn = accept(sockSrv,(SOCKADDR*)&addrClient, &len); printf("To receive...\n"); char recvBuf[100] = {0}; recv(sockConn, recvBuf, 100, 0); // 接收客戶端資料,最後一個引數一般設定為0 printf("recv is %s\n", recvBuf); while(1); closesocket(sockConn); closesocket(sockSrv); WSACleanup(); return 0; }
然後呢, 我們再啟動客戶端, 發現客戶端立即出現:ret is 1, 服務端對應的結果為:
To receive...
recv is hello world
由此可見, 上面的客戶端程式實現了非阻塞的connect, 也就是用, 程式設計師可以自定義超時時間。 ok, 先這樣。