<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          從內(nèi)核角度看怎么設(shè)置connect超時(shí)

          共 3614字,需瀏覽 8分鐘

           ·

          2021-03-29 14:39

          我們?cè)诰帉?xiě)網(wǎng)絡(luò)程序時(shí),通常需要連接其他服務(wù)端(如微服務(wù)之間的通信),這時(shí)就需要通過(guò)調(diào)用 connect 函數(shù)來(lái)連接服務(wù)端。但我們發(fā)現(xiàn) connect 函數(shù)并沒(méi)有提供超時(shí)的設(shè)置,而在 Linux 系統(tǒng)中,connect 的默認(rèn)超時(shí)時(shí)間為75秒。所以,在連接不上服務(wù)端的情況下,我們需要等待75秒,這對(duì)我們不能接受的。

          通過(guò) SO_SNDTIMEO 設(shè)置 connect 超時(shí)時(shí)間

          雖然 connect 系統(tǒng)調(diào)用沒(méi)有提供超時(shí)的設(shè)置,但我們通過(guò)查閱 Linux 內(nèi)核代碼可以發(fā)現(xiàn),connect 系統(tǒng)調(diào)用的超時(shí)時(shí)間可以通過(guò) SO_SNDTIMEO 參數(shù)來(lái)設(shè)定的,而 SO_SNDTIMEO 參數(shù)可以通過(guò) setsockopt 系統(tǒng)調(diào)用來(lái)設(shè)置,如下代碼:

          struct timeval tv;
          tv.tv_sec = 1; /* 設(shè)置1秒超時(shí) */tv.tv_usec = 0;
          setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));

          一般來(lái)說(shuō),SO_SNDTIMEO 參數(shù)是用來(lái)設(shè)置 socket 的發(fā)送超時(shí)時(shí)間,為什么在 Linux 中還能設(shè)置 connect 的超時(shí)時(shí)間呢?我們來(lái)查看一下 connect 系統(tǒng)調(diào)用的實(shí)現(xiàn):

          // 調(diào)用鏈: connect() -> sys_connect() -> inet_stream_connect()
          int inet_stream_connect(struct socket *sock, struct sockaddr * uaddr, int addr_len, int flags){ struct sock *sk = sock->sk; int err; long timeo;
          lock_sock(sk); ... switch (sock->state) { ... case SS_UNCONNECTED: ... err = -EINPROGRESS; break; }
          timeo = sock_sndtimeo(sk, flags & O_NONBLOCK); // 獲取 connect 超時(shí)時(shí)間,如果是非阻塞會(huì)返回0
          if ((1<<sk->state)&(TCPF_SYN_SENT|TCPF_SYN_RECV)) { // 如果 socket 設(shè)置了非阻塞或者 connect 超時(shí)了 // 跳到 out 處執(zhí)行, 并且返回 EINPROGRESS 錯(cuò)誤 if (!timeo || !inet_wait_for_connect(sk, timeo)) goto out;
          err = sock_intr_errno(timeo); if (signal_pending(current)) goto out; }
          if (sk->state == TCP_CLOSE) goto sock_error;
          sock->state = SS_CONNECTED; err = 0;out: release_sock(sk); return err; ...}

          inet_stream_connect 函數(shù)中,首先調(diào)用了 sock_sndtimeo 獲取 socket 的 SO_SNDTIMEO 的值,我們來(lái)看看 sock_sndtimeo 函數(shù)的實(shí)現(xiàn):

          static inline long sock_sndtimeo(struct sock *sk, int noblock){    return noblock ? 0 : sk->sndtimeo; // 獲取socket的SO_SNDTIMEO的值,如果socket被設(shè)置了非阻塞,那么返回0}

          sock_sndtimeo 函數(shù)只是簡(jiǎn)單的從 socket 對(duì)象中獲取 sndtimeo 字段的值,如果 socket 被設(shè)置了非阻塞,那么就返回0。

          我們接著分析 inet_stream_connect 函數(shù),在獲取到 SO_SNDTIMEO 的值后,就調(diào)用 inet_wait_for_connect 函數(shù)等待 socket 連接返回。返回三種情況:

          • 連接成功了。

          • 連接超時(shí)了。

          • 連接被中斷了。

          如果連接成功,connect 會(huì)返回0;如果連接超時(shí),connect 會(huì)返回 EINPROGRESS 錯(cuò)誤;如果連接被中斷,connect 會(huì)返回 EINTR 錯(cuò)誤。

          通過(guò)非阻塞與多路復(fù)用IO設(shè)置 connect 超時(shí)時(shí)間

          從上面的分析可以看到,當(dāng)把 socket 設(shè)置為非阻塞時(shí),connect 系統(tǒng)調(diào)用會(huì)立刻返回 EINPROGRESS 錯(cuò)誤,這時(shí)我們可以把 socket 添加到多路復(fù)用 IO 中進(jìn)行監(jiān)聽(tīng),并且設(shè)置多路復(fù)用 IO 的超時(shí)時(shí)間即可達(dá)到設(shè)置 connect 超時(shí)時(shí)間的目的,如下代碼:

          int connect_timeout(int sockfd, struct sockaddr *serv_addr, int addrlen, int timeout){    int flags = fcntl(sockfd, F_GETFL, 0);
          fcntl(sockfd, F_SETFL, flags|O_NONBLOCK); // 設(shè)置為非阻塞 int n = connect(sockfd, serv_addr, sizeof(*serv_addr)); // 連接服務(wù)端 if (n < 0) { if (errno != EINPROGRESS && errno != EWOULDBLOCK) return -1; struct timeval tv; fd_set wset;
          tv.tv_sec = timeout/1000; tv.tv_usec = (timeout - tv.tv_sec*1000)*1000;
          FD_ZERO(&wset); FD_SET(sockfd, &wset); // 把socket添加到select中進(jìn)行監(jiān)聽(tīng)
          n = select(sockfd + 1, NULL, &wset, NULL, &tv); if (n < 0) { return -1; // 出錯(cuò) } else if (0 == n) { return 0; // 超時(shí) } }
          fcntl(fd,F_SETFL,flags & ~O_NONBLOCK); // 恢復(fù)為阻塞模式
          return 1;}

          connect_timeout 函數(shù)實(shí)現(xiàn)了有超時(shí)機(jī)制的 connect,其主要步驟有:

          • 通過(guò)調(diào)用 fcntl 函數(shù)把 socket 設(shè)置為非阻塞。

          • 調(diào)用 connect 函數(shù)進(jìn)行連接服務(wù)端。

          • 如果 connect 函數(shù)返回 EINPROGRESS 或者 EWOULDBLOCK 錯(cuò)誤,表示連接還沒(méi)有建立,所以此時(shí)把 socket 添加到 select 中進(jìn)行監(jiān)聽(tīng),并且設(shè)置 select 的超時(shí)時(shí)間。

          • 判斷 select 的返回值,如果返回值大于0,表示連接成功;如果返回值小于0,表示連接出錯(cuò);如果反正等于0,表示連接超時(shí)。

          • 最后把 socket 恢復(fù)到阻塞模式。

          這種設(shè)置 connect 的超時(shí)時(shí)間的方式比前面設(shè)置 SO_SNDTIMEO 值的方式更為通用,因?yàn)樵诜?Linux 系統(tǒng)中,設(shè)置 SO_SNDTIMEO 值的方式不一定有效。


          瀏覽 58
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一区二区这里只有精品 | 在线观看日本艹b视频 | 91污污污| 美女的尿口视频网站 | 日韩淫淫网 |