TCP在真正開始進行數據傳輸之前,Server 和 Client 之間必須建立一個連接。當數據傳輸完成后,雙方不再需要這個連接時,就可以釋放這個連接。
TCP連接的建立是通過三次握手,而連接的釋放是通過四次揮手。所以說,每個TCP連接的建立和釋放都是需要消耗資源和時間成本的。
二 TCP短連接
模擬一種TCP短連接的情況:
- client 向 server 發起連接請求
- server 收到連接請求,雙方建立TCP連接
- client 向 server 發送消息
- server 回應 client 消息
- 一次讀寫完成,此時雙方任何一方都可以發起關閉連接請求,即close操作。
在步驟5中,一般都是 client 先發起close操作。從上面的描述來看,短連接一般只會在 client 和 server 之間傳遞一次讀寫操作。
短連接的操作過程:建立連接 ——> 傳輸數據 ——> 關閉連接。
三 TCP長連接
模擬一種長連接的情況:
- client 向 server 發起連接請求
- server 收到連接請求,雙方建立TCP連接
- client 向 server 發送消息
- server 回應 client 消息
- 一次讀寫完成,TCP連接不關閉
- 后續讀寫操作...
- 長時間操作之后,client 發起關閉連接請求
TCP長連接是指在連接成功建立之后,即使通信雙方沒有數據傳輸也要保持連接,使其不斷開。
長連接的操作步驟:建立連接 ——> 傳輸數據 ——> ... (保持連接) ... ——> 傳輸數據 ——> 關閉連接
四 長連接和短連接的優缺點
4.1 短連接的優缺點
優點:管理起來比較簡單,存在的連接都是有用的連接,不需要額外的控制手段。
缺點:由于TCP的建立和關閉操作需要一定的系統開銷,如果客戶端連接請求頻繁,會降低服務器的處理速度、浪費系統資源和帶寬。
4.2 長連接的優缺點
優點:長連接可以省去較多的TCP連接的建立和關閉的操作,減少浪費,節約時間。
缺點:client 與 server 之間的連接如果一直不關閉的話,會存在一個問題,隨著客戶端的連接越來越多,服務器的負載壓力會增大,降低服務器的整體性能,更嚴重者,可能導致服務器崩潰;其次,如果大量處于連接狀態的TCP通信雙方長時間沒有進行數據傳輸,這也會浪費系統和網絡資源。
五 長連接/短連接的應用場景
- 長連接一般多用于需要頻繁進行讀寫操作,點對點通訊,而且連接數不太多的情況。
例如:數據庫的連接通常使用長連接,如果用短連接的話,頻繁的TCP socket創建和關閉,會造成socket錯誤,也是對資源的一種浪費。
- 短連接一般用于不需要頻繁進行讀寫操作,并且連接數很大的情況下。
例如:web網站的http服務一般都用短連接。因為長連接對于服務器來說是要耗費一定的系統資源的,像web網站服務,通常會有大量的客戶端連接請求,并發連接量大,使用短連接會更節省系統資源,能夠及時響應客戶請求。
總結:長連接和短連接的選擇要具體需求、實際情況而定。
六 長連接的保活機制
對于TCP長連接,當通信雙方在沒有數據傳輸的時候,如何保持TCP連接一直處于“保活(KeepAlive)”狀態,這是一個必須要解決的問題。
在Linux系統中,我們可以使用 netstat、lsof等命令可以查看TCP連接是否處于“ESTABLISHED”狀態。
6.1 TCP保活的必要性
(1)很多防火墻會主動關閉空閑的socket。
(2)可能出現的非正常斷連,服務器并不能檢測到,為了回收已斷連的socket資源,必須提供一種檢測機制。
導致TCP非正常斷連的可能原因:
(1)網絡故障
(2)客戶端/服務端一側突然斷電或者進程崩潰
6.2 保活機制的方式
6.2.1 應用層的心跳機制
在應用層中使用心跳(heartbeat)機制來主動檢測。具體做法:當TCP連接建立成功后,客戶端開啟一個定時任務,定時對已經建立連接的對端發送一個心跳請求消息,服務器收到該心跳消息后,返回一個心跳應答消息。如果在超時時間內沒有收到服務器的應答消息,則重發心跳請求消息,如果客戶端持續多次沒有響應,客戶端則可以認為該TCP連接不可用,主動斷開連接。當然,也可以是服務器端主動發送心跳請求消息給客戶端。
6.2.2 TCP協議自帶的保活機制
Linux內核自帶的保活機制keep-alive。使用的時候只需要打開keep-alive功能即可。
TCP的Keepalive機制的作用是在于探測連接的對端是否存活。
工作原理:TCP keep-alive是通過在空閑時發送TCP Keep-Alive數據包,然后對方回應TCP Keep-Alive ACK來實現的。
在socket網絡編程中,需要設置一個socket選項 SO_KEEPALIVE,才能開啟keepalive機制。代碼描述如下:
keepAlive = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(keepAlive));
在Linux的keepalive機制中,有3個重要的內核參數:tcp_keepalive_time、tcp_keepalive_probes 和 tcp_keepalive_intvl。
- tcp_keepalive_time:表示TCP連接閑置多長時間后開始發送探測報文。(單位:秒)
- tcp_keepalive_probes:表示一次探測過程中最多可以重發探測報文的次數。(沒有收到確認時重發次數)
- tcp_keepalive_intvl:表示前一個探測報文和后一個探測報文之間的時間間隔。(即超時重傳的時間間隔)(單位:秒)
這些內核參數可以在/proc/sys/net/ipv4/目錄下可以看到,也可以使用Linux命令查看其默認值:
# sysctl -a |grep keepalive
net.ipv4.tcp_keepalive_intvl = 75
net.ipv4.tcp_keepalive_probes = 9
net.ipv4.tcp_keepalive_time = 7200
可以看到,這3個內核參數的默認值分別為:
tcp_keepalive_time = 7200秒,即2小時。也就是說,從最后一次數據傳輸結束開始計時起到發送第一個保活探測報文的時間間隔為2小時。
tcp_keepalive_probes = 9。當沒有收到對方的確認時,繼續發送保活探測報文的默認次數為9次。
tcp_keepalive_intvl = 75秒。當沒有收到對方的確認時,繼續發送保活探測報文的間隔時間為75秒。
TCP連接探活的過程:
開啟 keepalive后,如果2小時內在此TCP連接的通信雙方沒有發生數據交換,TCPT就自動給對方發一個保活探測報文段(keepalive probe)。這是一個對方必須響應的TCP報文段。
它會導致以下三種情況:
- 對方成功接收,連接正常:以期望的ACK報文段響應。2小時后,TCP將發出另一個探測報文。
- 對方已崩潰且已重新啟動:已RST報文段響應。socket的待處理錯誤被置為ECONNRESET,socket本身則被關閉,斷開TCP連接。
- 對方無任何響應:發送保活探測報文的一方,相隔75秒后,再次重發保活探測報文,重發8次,一共嘗試9次。若仍無響應就放棄。socket的待處理錯誤被置為ETIMEOUT,socket本身則被關閉,斷開TCP連接。
設置TCP keepalive
上面提到的 TCP keepalive使用的是其默認值。如果我們不想使用這么長的等待時間,可以修改Linux內核關于網絡方面的配置參數。我們可以自定義那3個內核參數的值,有兩種修改方式:
(1)全局設置(操作系統層面)
(2)針對單個TCP連接設置(應用程序層面)
1、全局設置
在Linux系統中,我們可以通過修改 /etc/sysctl.conf 配置文件的全局配置:
net.ipv4.tcp_keepalive_time=300
net.ipv4.tcp_keepalive_intvl=30
net.ipv4.tcp_keepalive_probes=5
添加上面的配置后,輸入:sysctl -p 使其生效。
這種方法設置的全局內核參數,針對整個操作系統生效,對單個socket的設置不夠友好。
2、針對單個TCP連接的設置
我們可以在socket網絡編程中設置TCP的 TCP_KEEPCNT、TCP_KEEPIDLE、TCP_KEEPINTVL 這3個socket選項。
這三個選項的定義,可以通過man 命令查看。
man 7 tcp
TCP_KEEPCNT (since Linux 2.4)
The maximum number of keepalive probes TCP should send before dropping the connection. This option should not be
used in code intended to be portable.
關閉一個非活躍連接之前的最大重試次數。 該選項不具備可移植性。
TCP_KEEPIDLE (since Linux 2.4)
The time (in seconds) the connection needs to remain idle before TCP starts sending keepalive probes, if the
socket option SO_KEEPALIVE has been set on this socket. This option should not be used in code intended to be
portable.
設置連接上如果沒有數據發送的話,多久后發送keepalive探測報文,單位是秒。該選項不具備可移植性。
TCP_KEEPINTVL (since Linux 2.4)
The time (in seconds) between individual keepalive probes. This option should not be used in code intended to be
portable.
前后兩次探測報文之間的時間間隔,單位是秒。該選項不具備可移植性。
代碼層面的設置步驟如下:
int keepAlive = 1; // 非0值,開啟keepalive屬性
int keepIdle = 60; // 如該連接在60秒內沒有任何數據往來,則進行此TCP層的探測
int keepInterval = 5; // 探測發包間隔為5秒
int keepCount = 3; // 嘗試探測的最多次數
//開啟tcp-keepAlive探活機制
setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(keepAlive));
setsockopt(sockfd, SOL_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(keepIdle));
setsockopt(sockfd, SOL_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(keepInterval));
setsockopt(sockfd, SOL_TCP, TCP_KEEPCNT, &keepCount, sizeof(keepCount);
6.2.3 TCP Keepalive 常見異常
啟用TCP Keepalive 的應用程序,一般可以捕獲到下面幾種類型的錯誤:
- ETIMEOUT 超時錯誤
在發送一個探測報文段后經過(tcpkeepaliveTime + tcpkeepaliveIntvl * tcpkeepaliveProbes)時間后仍然沒有接收到ACK確認報文段的情況下觸發的異常,套接字被關閉:Connection timedout。
- EHOSTUNREACH 主機不可達錯誤
這個是網絡層的ICMP匯報給上層應用的異常錯誤:No route to host。
6.2.4 TCP Keepalive 和 應用層 heartbeat 優缺點
1、TCP協議的 Keepalive 機制
優點:TCP協議的Keepalive機制由系統內核實現,上層應用程序只需要處理數據的收發,連接異常通知即可,這就減少了應用層代碼的復雜度,內核層面的計時器相比應用層,更為高效。
缺點:第一,TCP keepalive機制,位于傳輸層,由操作系統負責,只能檢測到連接是否存活,但不能檢測檢測連接是否可用。例如,服務器因為某種原因導致負載超高,CPU使用率達到了100%,無法繼續響應任何業務請求,但是TCP探針卻仍能確定連接狀態,這就是典型的連接活著但是服務已死的狀態。對于客戶端而言,這時最好的選擇就是斷開連接重新連接到其他服務器上,而不是一直認為當前服務器仍處于可用狀態,一直向當前服務器發送那些必然會失敗的請求。
第二,TCP keepalive機制 對于連接異常斷開的情況不能及時有效地監測到。如果TCP連接的某一方突然異常斷開連接,這個時候發送方并不知道對端已經掉線。而此時,如果有數據發送失敗,tcp會自動進行超時重傳,而重傳報文段的優先級是要高于keepalive的探測報文段的,導致探測報文段總是不能發送出去,直到經過較長時間的重傳之后,我們才會知道。
2、應用層的HeartBeat 機制
優點:第一,具有更好的靈活可控性。可以控制心跳的監測時機、間隔和流程,甚至可以在心跳包上附帶額外信息,最重要的是不光可以檢測連接是否存在,還可以檢測到連接是否可用,而TCP的keepalive機制只能提供簡單的檢活功能。
第二,具有通用性。應用層的心跳不依賴傳輸層協議,如果有一天不用TCP要改用UDP了,傳輸層不提供心跳機制了,但是你應用層的心跳機制依然可以使用,只需做少許改動就可以繼續使用。
缺點:第一,需要開發人員自己實現,增加軟件開發的工作量,由于應用特定的網絡框架,還可能增加代碼結構的復雜度。
第二,應用層心跳的流量消耗會更大,畢竟這本質上還是一個普通的數據包。
-
數據傳輸
+關注
關注
9文章
1838瀏覽量
64476 -
服務器
+關注
關注
12文章
9021瀏覽量
85183 -
HTTP
+關注
關注
0文章
501瀏覽量
31063 -
TCP
+關注
關注
8文章
1350瀏覽量
78986
發布評論請先 登錄
相關推薦
評論