Linux 系統默認的創建 TCP 鏈接的超時時間爲 127 秒,對於許多客戶端來講,這個時間都太長了, 特別是當這個客戶端其實是一個服務的時候,更但願可以儘早失敗,以便可以選擇其它的可用服務從新嘗試。linux

socket 是 Linux 下實現的傳輸控制層協議,包括 TCP 和 UDP,一個 socket 端點由 IP 和端口對來惟一標識; 若是開啓了地址複用,那麼能夠進一步由協議,IP 和端口來惟一標識。編程

系統調用 connect(2) 則是用來嘗試創建 socket 鏈接(TCP)或者和遠程協商一致(UDP)的函數。 connect 對於 UDP 來講並非必須的,而對於 TCP 來講則是一個必須過程,註明的 TCP 3 次握手實際上也由 connect 來完成。網絡

注:這裏只分析 TCP 鏈接超時socket

網絡中的鏈接超時很是常見,無論是廣域網仍是局域網,爲了必定程度上容忍失敗,因此鏈接加入了重試機制, 而另外一方面,爲了避免給服務端帶來過大的壓力,重試也是有限制的。tcp

在 Linux 中,鏈接超時典型爲 2 分 7 秒,而對於一些 client 來講,這是一個很是長的時間; 因此在編程中,能夠使用非阻塞的方式來實現,例如:使用 poll(2), epoll(2), select(2) 等系統調用來實現多路複用等待。函數

下面來看看 2 分 7 秒是怎樣來的,以及怎樣配置 Linux kernel 來縮短這個超時。post

2 分 7 秒


2 分 7 秒即 127 秒,恰好是 2 的 7 次方減一,聰明的讀者可能已經看出來了,若是 TCP 握手的 SYN 包超時重試按照 2 的冪來 backoff, 那麼:spa

  1. 第 1 次發送 SYN 報文後等待 1s(2 的 0 次冪),若是超時,則重試
  2. 第 2 次發送後等待 2s(2 的 1 次冪),若是超時,則重試
  3. 第 3 次發送後等待 4s(2 的 2 次冪),若是超時,則重試
  4. 第 4 次發送後等待 8s(2 的 3 次冪),若是超時,則重試
  5. 第 5 次發送後等待 16s(2 的 4 次冪),若是超時,則重試
  6. 第 6 次發送後等待 32s(2 的 5 次冪),若是超時,則重試
  7. 第 7 次發送後等待 64s(2 的 6 次冪),若是超時,則超時失敗

上面的結果恰好是 127 秒。也就是說 Linux 內核在嘗試創建 TCP 鏈接時,最多會嘗試 7 次。code

那麼下面經過具體方法來驗證。blog

首先,配置 iptables 來丟棄指定端口的 SYN 報文

# iptables -A INPUT --protocol tcp --dport 5000 --syn -j DROP

而後,打開 tcpdump 觀察到達指定端口的報文

# tcpdump -i lo -Ss0 -n src 127.0.0.1 and dst 127.0.0.1 and port 5000

最後,使用 telnet 鏈接指定端口

$ date; telnet 127.0.0.1 5000; date

上面命令的輸出以下:

Tue Jan  3 16:39:05 CST 2017
Trying 127.0.0.1...
telnet: Unable to connect to remote host: Connection timed out
Tue Jan  3 16:41:12 CST 2017

而從 tcpdump 命令的輸出能夠看到:

16:39:05.690238 IP 127.0.0.1.58933 > 127.0.0.1.5000: Flags [S], seq 2286786481, win 43690, options [mss 65495,sackOK,TS val 179222486 ecr 0,nop,wscale 7], length 0
16:39:06.686988 IP 127.0.0.1.58933 > 127.0.0.1.5000: Flags [S], seq 2286786481, win 43690, options [mss 65495,sackOK,TS val 179222736 ecr 0,nop,wscale 7], length 0
16:39:08.690980 IP 127.0.0.1.58933 > 127.0.0.1.5000: Flags [S], seq 2286786481, win 43690, options [mss 65495,sackOK,TS val 179223237 ecr 0,nop,wscale 7], length 0
16:39:12.702973 IP 127.0.0.1.58933 > 127.0.0.1.5000: Flags [S], seq 2286786481, win 43690, options [mss 65495,sackOK,TS val 179224240 ecr 0,nop,wscale 7], length 0
16:39:20.718991 IP 127.0.0.1.58933 > 127.0.0.1.5000: Flags [S], seq 2286786481, win 43690, options [mss 65495,sackOK,TS val 179226244 ecr 0,nop,wscale 7], length 0
16:39:36.766986 IP 127.0.0.1.58933 > 127.0.0.1.5000: Flags [S], seq 2286786481, win 43690, options [mss 65495,sackOK,TS val 179230256 ecr 0,nop,wscale 7], length 0
16:40:08.830996 IP 127.0.0.1.58933 > 127.0.0.1.5000: Flags [S], seq 2286786481, win 43690, options [mss 65495,sackOK,TS val 179238272 ecr 0,nop,wscale 7], length 0

其中,Flags [S] 表示爲 SYN 報文,能夠看到總共發送了 7 次 SYN 報文,最後一次的時間爲 16:40:08,而 telnet 超時退出的時間爲 16:41:12,相差 64 秒。

怎樣修改 connect timeout


對於不少客戶端程序來講,127 秒都是一個很長的時間,特別是對於局域網來講,公司內部每每都具備網絡質量較好的局域網, 訪問內部的服務並不須要等待這麼長的超時,而能夠 fail earlier。

Linux 內核中,net.ipv4.tcp_syn_retries 表示創建 TCP 鏈接時 SYN 報文重試的次數,默認爲 6,能夠經過 sysctl 命令查看。

# sysctl -a | grep tcp_syn_retries
net.ipv4.tcp_syn_retries = 6

將其修改成 1,則能夠將 connect 超時時間改成 3 秒,例如:

# sysctl net.ipv4.tcp_syn_retries=1

再次使用 telnet 驗證超時時間,以下:

$ date; telnet 127.0.0.1 5000; date
Fri Feb 17 09:50:12 CST 2017
Trying 127.0.0.1...
telnet: Unable to connect to remote host: Connection timed out
Fri Feb 17 09:50:15 CST 2017

注意:sysctl 修改的內核參數在系統重啓後失效,若是須要持久化,能夠修改系統配置文件

例如:,對於 CentOS 7 來講,添加 net.ipv4.tcp_syn_retries = 1 到 /etc/sysctl.conf 中便可。

轉自:http://www.chengweiyang.cn/2017/02/18/linux-connect-timeout/