connect函数在阻塞和非阻塞模式下的行为
connect函數在阻塞和非阻塞模式下的行為
當socket使用阻塞模式時,connect函數會阻塞到有明確結果才會返回,如果網絡環境較差,可能要等一會,影響體驗,
為了解決這個問題,我們使用異步connect技術
創建socket,將socket設置為非阻塞模式
調用connect函數,此時無論connect函數是否連接成功,都會立即返回,如果返回-1,不一定表示連接出錯,如果此時錯誤碼為EINPROGRESS表示正在嘗試連接
調用select函數,在指定時間內判斷該socket是否可寫,可寫說明連接成功,反之,連接失敗
上述流程代碼
#include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <iostream> #include <string.h> #include <stdio.h> #include <fcntl.h> #include <errno.h>#define SERVER_ADDRESS "127.0.0.1" #define SERVER_PORT 3000 #define SEND_DATA "helloworld"int main(int argc, char* argv[]) {//1.創建一個socketint clientfd = socket(AF_INET, SOCK_STREAM, 0);if (clientfd == -1){std::cout << "create client socket error." << std::endl;return -1;}//將clientfd設置成非阻塞模式int oldSocketFlag = fcntl(clientfd, F_GETFL, 0);int newSocketFlag = oldSocketFlag | O_NONBLOCK;if (fcntl(clientfd, F_SETFL, newSocketFlag) == -1){close(clientfd);std::cout << "set socket to nonblock error." << std::endl;return -1;}//2.連接服務器struct sockaddr_in serveraddr;serveraddr.sin_family = AF_INET;serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);serveraddr.sin_port = htons(SERVER_PORT);for (;;){int ret = connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));if (ret == 0){std::cout << "connect to server successfully." << std::endl;close(clientfd);return 0;} else if (ret == -1) {if (errno == EINTR){//connect 動作被信號中斷,重試connectstd::cout << "connecting interruptted by signal, try again." << std::endl;continue;} else if (errno == EINPROGRESS){//連接正在嘗試中break;} else{//真的出錯了,close(clientfd);return -1;}}}fd_set writeset;FD_ZERO(&writeset);FD_SET(clientfd, &writeset);struct timeval tv;tv.tv_sec = 3; tv.tv_usec = 0;//3.調用select函數判斷socket是否可寫if (select(clientfd + 1, NULL, &writeset, NULL, &tv) == 1){std::cout << "[select] connect to server successfully." << std::endl;} else {std::cout << "[select] connect to server error." << std::endl;}close(clientfd);return 0; }首先先用nc命令啟動一個服務端程序并執行
nc -v -l -n 0.0.0.0 3000然后運行程序,我用的clion
把服務端關掉,在重新啟動客戶端,一看結果,還是
為什么連接不上也會輸出同樣的結果?原因如下:
-
在Windows上,一個socket沒有建立連接之前,我們用select檢測是否可寫,是可以得到正確結果的,即不可寫;連接成功后在檢測,就會變為可寫
-
在Linux上一個socket沒有建立連接之前,用select函數檢測是否可寫,我們也會得到可寫的結果,**所以,在Linux上,我們不僅要用select檢測socket是否可寫還要用getsocketopt檢測socket此時是否出錯
#include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <iostream> #include <string.h> #include <stdio.h> #include <fcntl.h> #include <errno.h>#define SERVER_ADDRESS "127.0.0.1" #define SERVER_PORT 3000 #define SEND_DATA "helloworld"int main(int argc, char* argv[]) {//1.創建一個socketint clientfd = socket(AF_INET, SOCK_STREAM, 0);if (clientfd == -1){std::cout << "create client socket error." << std::endl;return -1;}//將clientfd設置成非阻塞模式int oldSocketFlag = fcntl(clientfd, F_GETFL, 0);int newSocketFlag = oldSocketFlag | O_NONBLOCK;if (fcntl(clientfd, F_SETFL, newSocketFlag) == -1){close(clientfd);std::cout << "set socket to nonblock error." << std::endl;return -1;}//2.連接服務器struct sockaddr_in serveraddr;serveraddr.sin_family = AF_INET;serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);serveraddr.sin_port = htons(SERVER_PORT);for (;;){int ret = connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));if (ret == 0){std::cout << "connect to server successfully." << std::endl;close(clientfd);return 0;}else if (ret == -1){if (errno == EINTR){//connect 動作被信號中斷,重試connectstd::cout << "connecting interruptted by signal, try again." << std::endl;continue;}else if (errno == EINPROGRESS){//連接正在嘗試中break;}else{//真的出錯了,close(clientfd);return -1;}}}fd_set writeset;FD_ZERO(&writeset);FD_SET(clientfd, &writeset);struct timeval tv;tv.tv_sec = 3;tv.tv_usec = 0;//3.調用select函數判斷socket是否可寫if (select(clientfd + 1, NULL, &writeset, NULL, &tv) != 1){std::cout << "[select] connect to server error." << std::endl;close(clientfd);return -1;}int err;socklen_t len = static_cast<socklen_t>(sizeof err);//4.調用getsockopt檢測此時socket是否出錯if (::getsockopt(clientfd, SOL_SOCKET, SO_ERROR, &err, &len) < 0){close(clientfd);return -1;}if (err == 0)std::cout << "connect to server successfully." << std::endl;elsestd::cout << "connect to server error." << std::endl;close(clientfd);return 0; }
TCP網絡編程的基本流程
Linux與C++11多線程編程(學習筆記)
Linux select函數用法和原理
socket的阻塞模式和非阻塞模式(send和recv函數在阻塞和非阻塞模式下的表現)
connect函數在阻塞和非阻塞模式下的行為
獲取socket對應的接收緩沖區中的可讀數據量
總結
以上是生活随笔為你收集整理的connect函数在阻塞和非阻塞模式下的行为的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python调用数据库数据类型_Pyth
- 下一篇: 手把手写Demo系列之车道线检测