日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

SOCKET【3】-select+getsockopt客户端检测connect是否成功

發布時間:2024/3/12 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SOCKET【3】-select+getsockopt客户端检测connect是否成功 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 前言
  • 一、使用`select+getsockopt`如何知道是否真的成功的連接到遠程服務器?
  • 1.1 connect 返回的幾種情況:
  • 1.2 針對1.1中的第二種情況的處理
  • 三、 `getsockopt`獲取`SO_ERROR`等于0一定是沒有問題嗎?
  • 四、 select 服務器如何編程能快速知道對端是否已經斷開?
  • 總結


前言

  • 使用select+getsockopt如何知道是否真的成功的連接到遠程服務器?
  • 客戶端一旦鏈接成功服務器 fd 的狀態是什么?是確定不變的?還是多種多樣的?
  • getsockopt獲取SO_ERROR等于0一定是沒有問題嗎?
  • select 服務器如何編程能快速知道對端是否已經斷開?

  • 一、使用select+getsockopt如何知道是否真的成功的連接到遠程服務器?

    最近在看前輩的網絡客戶端編程,遇到以下代碼:

    fd = socket(AF_INET, SOCK_STREAM, 0);ret = connect(fd, ( struct sockaddr* )&addr, sizeof(addr));if (ret == -1 && errno != EINPROGRESS) // connect失敗{ret = CONNECT_ERROR;}else{while(1){FD_ZERO(&read_fds);FD_ZERO(&write_fds);FD_SET(fd, &read_fds);FD_SET(fd, &write_fds);switch (flag){case CHECKSOCKET_CONN:ret = select(fd + 1, &read_fds, &write_fds, NULL, &tv);if (ret <= 0){ // errorret = CONNECT_ERROR;}break;case CHECKSOCKET_READ:ret = select(fd + 1, &read_fds, NULL, NULL, &tv);if (ret < 0){ // errorret = RECV_ERROR;}break;case CHECKSOCKET_WRITE:ret = select(fd + 1, NULL, &write_fds, NULL, &tv);if (ret < 0){ // errorret = SEND_ERROR;}break;default: // never go hereret = RET_UNKNOWN;break;}if (ret == 0){ // timeoutret = TIMEOUT_ERROR;}if (ret < 0) // select error or timeout ,exit{break;}ret = RET_OK; // set ret OKswitch (flag){case CHECKSOCKET_CONN:if (!FD_ISSET(fd, &read_fds) && !FD_ISSET(fd, &write_fds)){ret = CONNECT_ERROR;}else if (FD_ISSET(fd, &read_fds) && FD_ISSET(fd, &write_fds)){ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &sock_error, &tmp);if (ret == 0 && sock_error == 0){ // connect succ}else if (ret == 0 && sock_error != 0){ret = RET_SOCKET_CONNECT_ERROR;}else{ret = RET_SOCKET_CONNECT_ERROR;}}else if (!FD_ISSET(fd, &read_fds) && FD_ISSET(fd, &write_fds)){ // connect succ}else if (FD_ISSET(fd, &read_fds) && !FD_ISSET(fd, &write_fds)){ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &sock_error, &tmp);if (ret == 0){}ret = RET_SOCKET_CONNECT_ERROR;}else // never go here{ret = RET_UNKNOWN;}break;case CHECKSOCKET_READ:if (!FD_ISSET(fd, &read_fds)){ret = RET_SOCKET_RECV_ERROR;}break;case CHECKSOCKET_WRITE:if (!FD_ISSET(fd, &write_fds)){ret = SEND_ERROR;}break;default: // never go hereret = RET_UNKNOWN;break;} } }

    在網絡編程中,經常使用的是異步的socket也就是非阻塞socket,但是在在異步的情況下如何判斷connect是否連接成功成為了重中之重。

    1.1 connect 返回的幾種情況:

  • connect 返回0,則直接連接成功了。
  • connect 返回錯誤,errno 等于EINPROGRESS,表明socket正在連接,這種情況需要后續使用epoll wait 后者select 函數確認。
  • connect 返回錯誤,errno不等于EINPROGRESS,那就是連接失敗了
  • 1.2 針對1.1中的第二種情況的處理

  • select 模型:將連接的文件描述符加入到讀寫錯誤的三個事件中,調用select函數,
    如果socket連接失敗,則socket同時變的可讀可寫。
    如果socket連接成功,則第一socket變得可寫,第二就是socket同時變的可讀(連接成功,對端馬上有數據發過來)可寫。
    那么如何判斷connect是否真正的成功了呢?
    第15行,程序在connect之后,緊接著調用select,如果返回錯誤,或者因為超時返回0,這兩種情況需要后續通過查看socket的可寫可讀的情況進行分析。
    所以在可讀可寫的情況下,調用getsockopt函數選定SO_ERROR,通過該選項我們可以判斷出socket是否連接成功,注意不是獲取該函數的返回值而是函數的參數返回值,函數調用中的第四個參數會返回socket連接錯誤的錯誤碼,如果成功錯誤碼為0,否則不為0。
  • 三、 getsockopt獲取SO_ERROR等于0一定是沒有問題嗎?

    TCP socket連接失敗后getsockopt獲取SO_ERROR等于0誤以為連接成功;
    在Linux下使用非阻塞TCP連接的方式連接一個錯誤的地址,第一次可以connect返回失敗,getsockopt能夠得到正確的錯誤碼。但使用此socket進行第二次連接的時候,connect也返回失敗,getsockopt返回獲取錯誤(SO_ERROR)為0,從而認為連接成功。
    解決方法:tcp 在調用connect失敗后需要重新創建socket。

    這是因為TCP連接的三次握手產生的問題,具體參考

    網絡編程Socket之TCP之connect詳解

    四、 select 服務器如何編程能快速知道對端是否已經斷開?

    使用select重寫客戶端程序,目的是服務器進程一終止,客戶就能馬上得到通知。新版程序不是阻塞在套接字事件或者IO調用上,而是阻塞于select調用上,或是等待套接字或是等待標準IO輸入。
    客戶的套接字上的處理條件:

  • 如果對端TCP發送數據,那么套接字變為可讀,并且read返回一個大于0的值
  • 如果對端TCP發送一個FIN(對端進程終止),那么套接字變為可讀,并且read返回0(EOF)
  • 如果對端TCP發送一個RST(對端主機崩潰并重啟),那么套接字變為可讀,并且返回 -1
  • #include "unp.h" void str_cli(FILE *fp, int sockfd) {int maxfdp1;fd_set rset;char sendline[MAXLINE],recvline[MAXLINE];FD_ZERO(&rset);for(;;){FD_SET(fileno(fp),&rset);FD_SET(sockfd, &rset);maxfdp1 = max(fileno(fp), sockfd) + 1;select(maxfdp1,&rset,NULL,NULL,NULL);if (FD_ISSET(sockfd, &rset)){ /*socket is readable*/if(readline(sockfd, recvline,MAXLINE) == 0)err_quit("str_cli:server terminated prematurely");Fputs(recvline, stdout);}if (FD_ISSET(fileno(fp), &rset)) { /* input is readable */if (Fgets(sendline, MAXLINE, fp) == NULL)return; /* all done */Writen(sockfd, sendline, strlen(sendline));}} }

    總結

    總結

    以上是生活随笔為你收集整理的SOCKET【3】-select+getsockopt客户端检测connect是否成功的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。