亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

非阻塞socket調用connect, epoll和select檢查連

系統 2289 0

非阻塞socket調用connect, epoll和select檢查連接情況示例 - 語行 - 博客園

非阻塞socket調用connect, epoll和select檢查連接情況示例

我們知道,linux下socket編程有常見的幾個系統調用:

對于服務器來說, 有socket(), bind(),listen(), accept(),read(),write()

對于客戶端來說,有socket(),connect()

這里主要要講的是客戶端這邊的connect函數。

對于客戶端來說,需要打開一個套接字,然后與對端服務器連接,例如:

            
               1
            
            
              int
            
             main(
            
              int
            
             argc, 
            
              char
            
             **
            
              argv) 

            
            
               2
            
            
              {

            
            
               3
            
            
              struct
            
            
               sockaddr_in s_addr;

            
            
               4
            
                     memset(&s_addr, 
            
              0
            
            , 
            
              sizeof
            
            
              (s_addr));

            
            
               5
            
                     s_addr.sin_family =
            
               AF_INET;

            
            
               6
            
                     s_addr.sin_addr.s_addr = inet_addr(
            
              "
            
            
              remote host
            
            
              "
            
            
              );

            
            
               7
            
                     s_addr.sin_port =
            
               htons(remote port);

            
            
               8
            
                     socklen_t addr_len = 
            
              sizeof
            
            (
            
              struct
            
            
               sockaddr);

            
            
               9
            
            
              int
            
             c_fd = socket(AF_INET, SOCK_STREAM, 
            
              0
            
            
              );

            
            
              10
            
            
              int
            
             ret = connect(c_fd, (
            
              struct
            
             sockaddr*)&
            
              s_addr, addr_len);                                 

            
            
              11
            
            
                      ......

            
            
              12
            
             }
          

?

當connect上對端服務器之后,就可以使用該套接字發送數據了。

我們知道,如果socket為TCP套接字, 則connect函數會激發TCP的三次握手過程,而三次握手是需要一些時間的, 內核中對 connect的 超時限制是 75 秒,就是說如果超過75秒則connect會由于超時而返回失敗。但是如果對端服務器由于某些問題無法連接,那么每一個客戶端發起的connect都會要等待75才會返回,因為socket默認是阻塞的。對于一些線上服務來說,假設某些對端服務器出問題了,在這種情況下就有可能引發嚴重的后果。 或者在有些時候,我們不希望在調用connect的時候阻塞住,有一些額外的任務需要處理;

這種場景下,我們就可以將socket設置為非阻塞,如下代碼:

            
              int
            
             flags = fcntl(c_fd, F_GETFL, 
            
              0
            
            
              );

            
            
              if
            
            (flags < 
            
              0
            
            
              ) {
    
            
            
              return
            
            
              0
            
            
              ;      
}
fcntl(c_fd, F_SETFL, flags 
            
            | O_NONBLOCK);
          

當我們將socket設置為NONBLOCK后,在調用connect的時候,如果操作不能馬上完成,那connect便會立即返回,此時connect有可能返回-1, 此時需要根據相應的錯誤碼errno,來判斷連接是否在繼續進行。

當errno=EINPROGRESS時,這種情況是正常的,此時連接在繼續進行, 但是仍未完成;同時TCP的三路握手操作繼續進行; 后續只要用select/epoll去注冊對應的事件并設置超時時間來判斷連接否是連接成功就可以了。

            
              int
            
             ret = connect(c_fd, (
            
              struct
            
             sockaddr*)&
            
              s_addr, addr_len);

            
            
              while
            
            (ret < 
            
              0
            
            
              ) {
    
            
            
              if
            
            ( errno ==
            
               EINPROGRESS ) {
         
            
            
              break
            
            
              ;
    }  
            
            
              else
            
            
               {
         perror(
            
            
              "
            
            
              connect fail'\n
            
            
              "
            
            
              );
         
            
            
              return
            
            
              0
            
            
              ;
    }
}
            
          

這個地方,我們很可能會判斷如果ret小于0,就直接判斷連接失敗而返回了,沒有根據errno去判斷EINPROGRESS這個錯誤碼。這里也是昨天在寫份程序的時候遇到的一個坑。

使用非阻塞 connect 需要注意的問題是:
1. 很可能 調用 connect 時會立即建立連接(比如,客戶端和服務端在同一臺機子上),必須處理這種情況。
2. Posix 定義了兩條與 select 和 非阻塞 connect 相關的規定:
1)連接成功建立時,socket 描述字變為可寫。(連接建立時,寫緩沖區空閑,所以可寫)
2)連接建立失敗時,socket 描述字既可讀又可寫。 (由于有未決的錯誤,從而可讀又可寫)

不過我同時用epoll也做了實驗(connect一個無效端口,errno=110, errmsg=connect refused),當連接失敗的時候,會觸發epoll的EPOLLERR與EPOLLIN,不會觸發EPOLLOUT。

當用select檢測連接時,socket既可讀又可寫,只能在可讀的集合通過getsockopt獲取錯誤碼。

當用epoll檢測連接時,socket既可讀又可寫,只能在EPOLLERR中通過getsockopt獲取錯誤碼。

完整代碼如下:

            
              /*
            
            
               
 * File:   main.cpp
 * Created on March 7, 2013, 5:54 PM
 
            
            
              */
            
            
              

#include 
            
            <cstdlib>
            
              
#include 
            
            <
            
              string
            
            >
            
              
#include 
            
            <iostream>
            
              

#include 
            
            <sys/epoll.h>
            
              
#include 
            
            <sys/socket.h>
            
              
#include 
            
            <sys/types.h>
            
              
#include 
            
            <sys/
            
              select
            
            .h>
            
              
#include 
            
            <error.h>
            
              
#include 
            
            <errno.h>
            
              
#include 
            
            <fcntl.h>
            
              
#include 
            
            <netinet/
            
              in
            
            .h>
            
              
#include 
            
            <arpa/inet.h>



            
              using
            
            
              namespace
            
            
               std;


            
            
              struct
            
            
               so {
    
            
            
              int
            
            
               fd;
    
            
            
              string
            
            
               val;
};


            
            
              int
            
             select_version(
            
              int
            
             *
            
              fd) {
    
            
            
              int
            
             c_fd = *
            
              fd;
    fd_set rset, wset;
    
            
            
              struct
            
            
               timeval tval;
    FD_ZERO(
            
            &
            
              rset);
    FD_SET(c_fd, 
            
            &
            
              rset);
    wset 
            
            =
            
               rset;
    tval.tv_sec 
            
            = 
            
              0
            
            
              ;
    tval.tv_usec 
            
            = 
            
              300
            
             * 
            
              1000
            
            ; 
            
              //
            
            
              300毫秒
            
            
              int
            
            
               ready_n;
    
            
            
              if
            
             ((ready_n = 
            
              select
            
            (c_fd + 
            
              1
            
            , &rset, &wset, NULL, &tval)) == 
            
              0
            
            
              ) {
        close(c_fd); 
            
            
              /*
            
            
               timeout 
            
            
              */
            
            
              
        errno 
            
            =
            
               ETIMEDOUT;
        perror(
            
            
              "
            
            
              select timeout.\n
            
            
              "
            
            
              );
        
            
            
              return
            
             (-
            
              1
            
            
              );
    }
    
            
            
              if
            
             (FD_ISSET(c_fd, &
            
              rset)) {
        
            
            
              int
            
            
               error;
        socklen_t len 
            
            = 
            
              sizeof
            
            
               (error);
        
            
            
              if
            
             (getsockopt(c_fd, SOL_SOCKET, SO_ERROR, &error, &len) < 
            
              0
            
            
              ) {
            cout 
            
            << 
            
              "
            
            
              getsockopt error.
            
            
              "
            
             <<
            
               endl;
            
            
            
              return
            
             -
            
              1
            
            
              ;
        }
        cout 
            
            << 
            
              "
            
            
              in fire.
            
            
              "
            
             << error <<
            
               endl;
    }
    
            
            
              if
            
             (FD_ISSET(c_fd, &
            
              wset)) {
        
            
            
              int
            
            
               error;
        socklen_t len 
            
            = 
            
              sizeof
            
            
               (error);
        
            
            
              if
            
             (getsockopt(c_fd, SOL_SOCKET, SO_ERROR, &error, &len) < 
            
              0
            
            
              ) {
            cout 
            
            << 
            
              "
            
            
              getsockopt error.
            
            
              "
            
             <<
            
               endl;
            
            
            
              return
            
             -
            
              1
            
            
              ;
        }
        cout 
            
            << 
            
              "
            
            
              out fire.
            
            
              "
            
             << error <<
            
               endl;
    }
    
            
            
              return
            
            
              0
            
            
              ;
}


            
            
              int
            
             epoll_version(
            
              int
            
             *
            
              fd) {
    
            
            
              int
            
             c_fd = *
            
              fd;
    
            
            
              int
            
             ep = epoll_create(
            
              1024
            
            
              );
    
            
            
              struct
            
             epoll_event 
            
              event
            
            
              ;
    
            
            
              event
            
            .events = (uint32_t) (EPOLLIN | EPOLLOUT |
            
               EPOLLET);
    
            
            
              struct
            
            
               so _data;
    _data.fd 
            
            =
            
               c_fd;
    _data.val 
            
            = 
            
              "
            
            
              test
            
            
              "
            
            
              ;
    
            
            
              event
            
            .data.ptr = (
            
              void
            
            *) &
            
              _data;
    epoll_ctl(ep, EPOLL_CTL_ADD, c_fd, 
            
            &
            
              event
            
            
              );
    
            
            
              struct
            
             epoll_event eventArr[
            
              1000
            
            
              ];
    
            
            
              int
            
            
               status, err;
    socklen_t len;
    err 
            
            = 
            
              0
            
            
              ;
    len 
            
            = 
            
              sizeof
            
            
               (err);
    
            
            
              int
            
             n = epoll_wait(ep, eventArr, 
            
              20
            
            , 
            
              300
            
            
              );
    
            
            
              for
            
             (
            
              int
            
             i = 
            
              0
            
            ; i < n; i++
            
              ) {
        epoll_event ev 
            
            =
            
               eventArr[i];
        
            
            
              int
            
             events =
            
               ev.events;
        
            
            
              if
            
             (events &
            
               EPOLLERR) {
            
            
            
              struct
            
             so* so_data = (
            
              struct
            
             so*
            
              ) ev.data.ptr;
            cout 
            
            << so_data->val << 
            
              "
            
            
              ,err event fire.
            
            
              "
            
             <<
            
               endl;
            status 
            
            = getsockopt(c_fd, SOL_SOCKET, SO_ERROR, &err, &
            
              len);
            cout 
            
            << status << 
            
              "
            
            
              ,
            
            
              "
            
             << err <<
            
               endl;
        }
        
            
            
              if
            
             (events &
            
               EPOLLIN) {
            
            
            
              struct
            
             so* so_data = (
            
              struct
            
             so*
            
              ) ev.data.ptr;
            cout 
            
            << so_data->val << 
            
              "
            
            
              ,in event fire.
            
            
              "
            
             <<
            
               endl;
            status 
            
            = getsockopt(c_fd, SOL_SOCKET, SO_ERROR, &err, &
            
              len);
            cout 
            
            << status << 
            
              "
            
            
              ,
            
            
              "
            
             << err <<
            
               endl;
        }
        
            
            
              if
            
             (events &
            
               EPOLLOUT) {
            
            
            
              struct
            
             so* so_data1 = (
            
              struct
            
             so*
            
              ) ev.data.ptr;
            cout 
            
            << so_data1->val << 
            
              "
            
            
              ,out event fire.
            
            
              "
            
             <<
            
               endl;
        }
    }

}


            
            
              int
            
             main(
            
              int
            
             argc, 
            
              char
            
            **
            
               argv) {
    
            
            
              string
            
             ip = 
            
              "
            
            
              127.0.0.1
            
            
              "
            
            
              ;
    
            
            
              int
            
             port = 
            
              25698
            
            
              ;
    
            
            
              int
            
            
               c_fd, flags, ret;
    
            
            
              struct
            
            
               sockaddr_in s_addr;
    memset(
            
            &s_addr, 
            
              0
            
            , 
            
              sizeof
            
            
               (s_addr));
    s_addr.sin_family 
            
            =
            
               AF_INET;
    s_addr.sin_port 
            
            =
            
               htons(port);
    s_addr.sin_addr.s_addr 
            
            =
            
               inet_addr(ip.c_str());

    
            
            
              if
            
             ((c_fd = socket(AF_INET, SOCK_STREAM, 
            
              0
            
            )) < 
            
              0
            
            
              ) {
        perror(
            
            
              "
            
            
              create socket fail.\n
            
            
              "
            
            
              );
        exit(
            
            
              0
            
            
              );
    }
    flags 
            
            = fcntl(c_fd, F_GETFL, 
            
              0
            
            
              );
    
            
            
              if
            
             (flags < 
            
              0
            
            
              ) {
        perror(
            
            
              "
            
            
              get socket flags fail.\n
            
            
              "
            
            
              );
        
            
            
              return
            
             -
            
              1
            
            
              ;
    }

    
            
            
              if
            
             (fcntl(c_fd, F_SETFL, flags | O_NONBLOCK) < 
            
              0
            
            
              ) {
        perror(
            
            
              "
            
            
              set socket O_NONBLOCK fail.\n
            
            
              "
            
            
              );
        
            
            
              return
            
             -
            
              1
            
            
              ;
    }
    ret 
            
            = connect(c_fd, (
            
              struct
            
             sockaddr*) &s_addr, 
            
              sizeof
            
             (
            
              struct
            
            
               sockaddr));
    
            
            
              while
            
             (ret < 
            
              0
            
            
              ) {
        
            
            
              if
            
             (errno ==
            
               EINPROGRESS) {
            
            
            
              break
            
            
              ;
        } 
            
            
              else
            
            
               {
            perror(
            
            
              "
            
            
              connect remote server fail.\n
            
            
              "
            
            
              );
            printf(
            
            
              "
            
            
              %d\n
            
            
              "
            
            
              , errno);
            exit(
            
            
              0
            
            
              );
        }
    }
    
            
            
              //
            
            
              select_version(&c_fd);
            
            
    epoll_version(&
            
              c_fd);
    
            
            
              return
            
            
              0
            
            
              ;
}
            
          

?

ps:文中如有不妥的地方,望各位博友指正。

?

非阻塞socket調用connect, epoll和select檢查連接情況示例


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦?。?!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 四虎精品在线视频 | 亚洲五月综合缴情婷婷 | 一区免费在线观看 | 激情综合网五月 | 午夜论坛 | 97在线播放 | 中文字幕亚洲欧美一区 | 国内久久精品 | 国产激情视频趣趣在线观看的 | 男人深夜网站 | 香蕉视频在线视频 | 欧美在线观看一区 | 国产亚洲自拍一区 | 久久在线免费 | 桃花综合| 成人三级做爰在线观看男女 | 十八毛片 | 日韩欧美在线免费观看 | 中文字幕一视频97色伦 | 69久久| 久久精品国产视频在热 | 青青操夜夜操 | 丹武至尊在线观看动漫 | 91精品久久久久久久久网影视 | 久久综合久美利坚合众国 | 成人一级网站 | 国产亚洲综合成人91精品 | 精品国产日韩久久亚洲 | 欧美jizzhd精品欧美另类 | 国产精品久久久久久搜索 | 精品日韩在线视频 | 国产精品亚洲二区在线 | 国产精品毛片va一区二区三区 | 亚洲精品综合网 | 2021久久精品永久免费 | 毛片精品 | 看免费的毛片 | 亚洲系列中文字幕一区二区 | 尤物精品视频在线观看 | 久久国产欧美另类久久久 | 色综合在|