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