日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
如何實現(xiàn)異步 Connect

本文轉(zhuǎn)載自微信公眾號「 Linux開發(fā)那些事兒」,作者LinuxThings。轉(zhuǎn)載本文請聯(lián)系 Linux開發(fā)那些事兒公眾號。

堅守“ 做人真誠 · 做事靠譜 · 口碑至上 · 高效敬業(yè) ”的價值觀,專業(yè)網(wǎng)站建設(shè)服務(wù)10余年為成都木制涼亭小微創(chuàng)業(yè)公司專業(yè)提供企業(yè)網(wǎng)站建設(shè)營銷網(wǎng)站建設(shè)商城網(wǎng)站建設(shè)手機網(wǎng)站建設(shè)小程序網(wǎng)站建設(shè)網(wǎng)站改版,從內(nèi)容策劃、視覺設(shè)計、底層架構(gòu)、網(wǎng)頁布局、功能開發(fā)迭代于一體的高端網(wǎng)站建設(shè)服務(wù)。

寫過網(wǎng)絡(luò)程序的同學(xué),應(yīng)該都知道 connect 函數(shù),在 socket 開始讀寫操作之前,先要進行連接,也即 TCP 的三次握手 , 這個過程就是在 connect 函數(shù)中完成的, connect 函數(shù)本身是阻塞的,通過設(shè)置 socket 的選項及調(diào)用 select/poll 函數(shù)可以實現(xiàn)異步 connect 的功能

socket 默認(rèn)是阻塞模式,處于阻塞模式時,調(diào)用 connect 函數(shù)之后, 會一直等待連接結(jié)果返回為止,要么成功,要么失敗,connect 函數(shù)返回 0 時成功,返回 -1 失敗

在局域網(wǎng)中,調(diào)用 connect 函數(shù),基本上會立即返回結(jié)果,當(dāng)服務(wù)器在國外時,connect 函數(shù)時會阻塞一段時間,大概幾秒鐘吧,具體的還要看當(dāng)時的網(wǎng)絡(luò)狀況

為什么要用異步 connect

Linux 下 connect 默認(rèn)的超時時間大概在一分鐘左右(不同的Linux版本略有差別),在實際的開發(fā)中,這個時間顯得有點兒長了

對于服務(wù)器來說,需要為很多的客戶端服務(wù),要盡量減少阻塞,所以,一般都是采用 異步 connect 的技術(shù)

對于每一個編寫網(wǎng)絡(luò)程序的同學(xué)來說,異步connect 應(yīng)該是必須掌握的基本功

異步connect 步驟

(1) 創(chuàng)建socket,調(diào)用 fcntl 函數(shù)將其設(shè)置為非阻塞

(2) 調(diào)用 connect 函數(shù),返回 0 表示連接成功,返回 -1,需要檢查錯誤碼

如果錯誤碼為 EINPROGRESS,表示正在建立連接中

如果錯誤碼是 EINTR 表示,表示發(fā)生了系統(tǒng)中斷,這時繼續(xù)執(zhí)行連接即可

如果是其他錯誤碼,調(diào)用 close(fd) 函數(shù)關(guān)閉 socket, 連接失敗

(3) 將 socket 加入 select/poll 的可寫文件描述符集合中,并設(shè)置超時時間

(4) 判斷 select/poll 函數(shù)的返回值

小于等于 0 表示失敗

其他,表示 socket 可寫,調(diào)用 getsockopt 函數(shù) 捕獲 socket 的錯誤信息

具體的代碼如下:

 
 
 
 
  1. /* 
  2.     異步 connect 測試代碼, test_connect.cpp 
  3. */ 
  4. #include  
  5. #include  
  6. #include  
  7. #include  
  8. #include  
  9. #include  
  10. #include  
  11. #include  
  12. #include  
  13. #include  
  14. #include  
  15. #include  
  16. #include  
  17. #include  
  18. #include  
  19. #include  
  20. #include  
  21. #include  
  22. using namespace std; 
  23.  
  24. int32_t main(int32_t argc, char *argv[]) 
  25.     if(argc < 3) 
  26.     { 
  27.         std::cout << "argc < 3..." << std::endl; 
  28.         return -1; 
  29.     } 
  30.     std::string strip = argv[1]; 
  31.     uint32_t port = atoi(argv[2]); 
  32.     //創(chuàng)建 socket 
  33.     int32_t fd = socket(AF_INET, SOCK_STREAM, 0); 
  34.     if(-1 == fd) 
  35.     { 
  36.         std::cout << "create socket error:" << errno << std::endl; 
  37.         return -1; 
  38.     } 
  39.     //將 socket 設(shè)置成非阻塞 
  40.     int32_t flag = fcntl(fd, F_GETFL, 0); 
  41.     flag |= O_NONBLOCK; 
  42.     if(-1 == fcntl(fd, F_SETFL, flag)) 
  43.     { 
  44.         std::cout << " set socket nonblock error:" << errno << std::endl; 
  45.         close(fd); 
  46.         return -1; 
  47.     } 
  48.     //服務(wù)器地址 
  49.     struct sockaddr_in addr; 
  50.     addr.sin_family = AF_INET; 
  51.     addr.sin_port = htons(port); 
  52.     addr.sin_addr.s_addr = inet_addr(strip.c_str()); 
  53.     // 
  54.     for(; ;) 
  55.     { 
  56.         //連接服務(wù)器 
  57.         int32_t ret = connect(fd, (struct sockaddr*)&addr, sizeof(addr) ); 
  58.         if(-1 == ret) 
  59.         { 
  60.             int32_t err = errno; 
  61.             if(EINTR == err) 
  62.             { 
  63.                 //connect被中斷,繼續(xù)重試 
  64.                 //如果不處理 EINTR 錯誤的話,connect邏輯可以不用放到 for 循環(huán)中 
  65.                 continue; 
  66.             } 
  67.             if(EINPROGRESS != err) 
  68.             { 
  69.                 std::cout << "connect err:" << errno << ", str:" << strerror(errno) <<  std::endl; 
  70.                 goto exit; 
  71.             } 
  72.             //正在連接中 
  73.             std::cout << "connecting..." << std::endl; 
  74.             //處理結(jié)果 
  75.             int32_t result = -1; 
  76.     #if 1 
  77.             //將 socket 加入到 poll 的可寫集合中 
  78.             struct pollfd wfd[1]; 
  79.             wfd[0].fd = fd; 
  80.             wfd[0].events = POLLOUT; 
  81.             //檢測 socket 是否可寫 
  82.             result = poll(wfd, 1, 3000); 
  83.     #elif 0 
  84.             //設(shè)置超時時間 
  85.             struct timeval tval; 
  86.             tval.tv_sec = 3; 
  87.             tval.tv_usec = 0; 
  88.             //將 socket 加入到 select 的可寫集合中 
  89.             fd_set wfds; 
  90.             FD_ZERO(&wfds); 
  91.             FD_SET(fd,&wfds); 
  92.             //檢測 socket 是否可寫 
  93.             result = select(fd + 1, nullptr, &wfds, nullptr,&tval); 
  94.     #endif 
  95.             std::cout << "async connect result:" << result << std::endl; 
  96.             // 失敗 
  97.             if(result <= 0 ) 
  98.             {  
  99.                 std::cout << "async connect err:" << errno << ", str:" << strerror(errno) << std::endl; 
  100.                 goto exit; 
  101.             } 
  102.             //檢查socket 錯誤信息 
  103.             int32_t temperr = 0; 
  104.             socklen_t temperrlen = sizeof(temperr); 
  105.             if(-1 == getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&temperr, &temperrlen) ) 
  106.             { 
  107.                 std::cout << "async connect...getsockopt err:" << errno << ", str:" << strerror(errno) <<  std::endl; 
  108.                 goto exit; 
  109.             } 
  110.             if(0 != temperr) 
  111.             { 
  112.                 std::cout << "async connect...getsockopt temperr:" << temperr << ", str:" << strerror(temperr) << std::endl; 
  113.                 goto exit; 
  114.             } 
  115.             //成功 
  116.             std::cout << "async connect success..." << std::endl; 
  117.             goto exit; 
  118.         } 
  119.         else 
  120.         { 
  121.              //連接成功 
  122.             std::cout << "connect success..." << std::endl; 
  123.             goto exit;           
  124.         } 
  125.     } // end of  for(; ;) 
  126. exit: 
  127.     std::cout << "quit...." << std::endl; 
  128.     close(fd); 
  129.     return 0; 
  • 代碼說明

如果不處理 EINTR 錯誤的話,connect 函數(shù)及后面的邏輯可以不用放到 for 循環(huán)中

檢查 socket 是否可寫,調(diào)用 select 或者 poll 函數(shù)都可以,上述代碼中使用的是 poll 函數(shù),將代碼中的 #if 1 改成 #if 0 以及 #elif 0 改成 #elif 1 , 就是使用 select 函數(shù)檢測 socket 是否可寫了

測試

在另一臺機器上執(zhí)行 nc -l -v -k 192.168.70.20 5000 命令,啟動一個服務(wù)器程序

在當(dāng)前機器上執(zhí)行 g++ -g -Wall -std=c++11 -o test_connect test_connect.cpp 進行編譯

執(zhí)行 ./test_connect 192.168.70.20 5000, 結(jié)果如下圖

此時,服務(wù)器程序顯示如下:

通過 test_connect 程序端的截圖可以看出,調(diào)用 connect 函數(shù)之后,返回了 EINPROGRESS 錯誤碼,然后調(diào)用 select/poll 函數(shù)返回 1, 表示 socket 可寫,緊接著調(diào)用 getsockopt 函數(shù)檢查 socket 錯誤信息,通過打印的信息知道,socket 無錯誤信息,即 連接成功

我們在服務(wù)器機器上按 CTRL + C 停止服務(wù)器程序,然后關(guān)閉 test_connect 程序,再次執(zhí)行 ./test_connect 192.168.70.20 5000 ,結(jié)果如下圖:

從上圖可以看出,即使服務(wù)器程序已經(jīng)退出了,調(diào)用 select/poll 之后還是返回 socket 可寫,當(dāng)繼續(xù)調(diào)用 getsockopt 函數(shù)檢查 socket 錯誤碼,此時錯誤碼是 111, 表示連接被拒絕,也即連接失敗

這里要注意一個很重要的點, 在 Linux 上,即使 socket 沒有連接成功,調(diào)用 select/poll 時,仍然返回 socket 是可寫的,所以 除了調(diào)用 select/poll 檢查 socket 可寫之外,還需要調(diào)用 getsockopt 函數(shù)檢查 socket 的錯誤碼,錯誤碼為 0 表示連接成功,其他表示連接失敗


分享題目:如何實現(xiàn)異步 Connect
文章源于:http://www.5511xx.com/article/dhjgdis.html