新聞中心

EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > Linux協(xié)議棧accept和syn隊(duì)列問(wèn)題

Linux協(xié)議棧accept和syn隊(duì)列問(wèn)題

作者: 時(shí)間:2016-10-08 來(lái)源:網(wǎng)絡(luò) 收藏

環(huán)境:

本文引用地址:http://m.butianyuan.cn/article/201610/305659.htm

Client 通過(guò)tcp 連接server,server端只是listen,但是不調(diào)用accept。通過(guò)netstat –ant查看兩端的連接情況。

server端listen,不調(diào)用accept。

client一直去connect server。

問(wèn)題:

運(yùn)行一段時(shí)間后,為什么server端的ESTABLISHED連接的個(gè)數(shù)基本是固定的129個(gè),但是client端的ESTABLISHED連接的個(gè)數(shù)卻在不斷增加?

分析

Linux內(nèi)核協(xié)議棧為一個(gè)tcp連接管理使用兩個(gè)隊(duì)列,一個(gè)是半鏈接隊(duì)列(用來(lái)保存處于SYN_SENT和SYN_RECV狀態(tài)的請(qǐng)求),一個(gè)是accpetd隊(duì)列(用來(lái)保存處于established狀態(tài),但是應(yīng)用層沒(méi)有調(diào)用accept取走的請(qǐng)求)。

第一個(gè)隊(duì)列的長(zhǎng)度是/proc/sys/net/ipv4/tcp_max_syn_backlog,默認(rèn)是1024。如果開(kāi)啟了syncookies,那么基本上沒(méi)有限制。

第二個(gè)隊(duì)列的長(zhǎng)度是/proc/sys/net/core/somaxconn,默認(rèn)是128,表示最多有129個(gè)established鏈接等待accept。(為什么是129?詳見(jiàn)下面的附錄1)。

現(xiàn)在假設(shè)acceptd隊(duì)列已經(jīng)達(dá)到129的情況:

client發(fā)送syn到server。client(SYN_SENT),server(SYN_RECV)

server端處理流程:tcp_v4_do_rcv--->tcp_rcv_state_process--->tcp_v4_conn_request

if(sk_acceptq_is_full(sk) inet_csk_reqsk_queue_yong(sk)>1)

goto drop;

inet_csk_reqsk_queue_yong(sk)的含義是請(qǐng)求隊(duì)列中有多少個(gè)握手過(guò)程中沒(méi)有重傳過(guò)的段。

在第一次的時(shí)候,之前的握手過(guò)程都沒(méi)有重傳過(guò),所以這個(gè)syn包server端會(huì)直接drop掉,之后client會(huì)重傳syn,當(dāng)inet_csk_reqsk_queue_yong(sk) 1,那么這個(gè)syn被server端接受。server會(huì)回復(fù)synack給client。這樣一來(lái)兩邊的狀態(tài)就變?yōu)閏lient(ESTABLISHED), server(SYN_SENT)

Client收到synack后回復(fù)ack給server。

server端處理流程: tcp_check_req--->syn_recv_sock-->tcp_v4_syn_recv_sock

if(sk_acceptq_is_full(sk)

goto exit_overflow;

如果server端設(shè)置了sysctl_tcp_abort_on_overflow,那么server會(huì)發(fā)送rst給client,并刪除掉這個(gè)鏈接;否則server端只是記錄一下LINUX_MIB_LISTENOVERFLOWS(詳見(jiàn)附錄2),然后返回。默認(rèn)情況下是不會(huì)設(shè)置的,server端只是標(biāo)記連接請(qǐng)求塊的acked標(biāo)志,之后連接建立定時(shí)器,會(huì)遍歷半連接表,重新發(fā)送synack,重復(fù)上面的過(guò)程(具體的函數(shù)是inet_csk_reqsk_queue_prune),如果重傳次數(shù)超過(guò)synack重傳的閥值(/proc/sys/net/ipv4/tcp_synack_retries),會(huì)把該連接從半連接鏈表中刪除。

一次異常問(wèn)題分析

Nginx通過(guò)FASTCGI協(xié)議連接cgi程序,出現(xiàn)cgi程序read讀取socket內(nèi)容的時(shí)候永遠(yuǎn)block。通過(guò)netstat查看,cgi程序所在的服務(wù)器上顯示連接存在,但是nginx所在的服務(wù)器上顯示不存在該連接。

下面是原始數(shù)據(jù)圖:

我們從上面的數(shù)據(jù)流來(lái)分析一下:

出現(xiàn)問(wèn)題的時(shí)候,cgi程序(tcp server端)處理非常慢,導(dǎo)致大量的連接請(qǐng)求放到accept隊(duì)列,把a(bǔ)ccept隊(duì)列阻塞。

148021 nginx(tcp client端) 連接cgi程序,發(fā)送syn

此時(shí)server端accpet隊(duì)列已滿,并且inet_csk_reqsk_queue_yong(sk) > 1,server端直接丟棄該數(shù)據(jù)包

148840 client端等待3秒后,重傳SYN

此時(shí)server端狀態(tài)與之前送變化,仍然丟棄該數(shù)據(jù)包

150163 client端又等待6秒后,重傳SYN

此時(shí)server端accept隊(duì)列仍然是滿的,但是存在了重傳握手的連接請(qǐng)求,server端接受連接請(qǐng)求,并發(fā)送synack給client端(150164)

150166 client端收到synack,標(biāo)記本地連接為ESTABLISHED狀態(tài),給server端應(yīng)答ack,connect系統(tǒng)調(diào)用完成。

Server收到ack后,嘗試將連接放到accept隊(duì)列,但是因?yàn)閍ccept隊(duì)列已滿,所以只是標(biāo)記連接為acked,并不會(huì)將連接移動(dòng)到accept隊(duì)列中,也不會(huì)為連接分配sendbuf和recvbuf等資源。

150167 client端的應(yīng)用程序,檢測(cè)到connect系統(tǒng)調(diào)用完成,開(kāi)始向該連接發(fā)送數(shù)據(jù)。

Server端收到數(shù)據(jù)包,由于acept隊(duì)列仍然是滿的,所以server端處理也只是標(biāo)記acked,然后返回。

150225 client端由于沒(méi)有收到剛才發(fā)送數(shù)據(jù)的ack,所以會(huì)重傳剛才的數(shù)據(jù)包

150296 同上

150496 同上

150920 同上

151112 server端連接建立定時(shí)器生效,遍歷半連接鏈表,發(fā)現(xiàn)剛才acked的連接,重新發(fā)送synack給client端。

151113 client端收到synack后,根據(jù)ack值,使用SACK算法,只重傳最后一個(gè)ack內(nèi)容。

Server端收到數(shù)據(jù)包,由于accept隊(duì)列仍然是滿的,所以server端處理也只是標(biāo)記acked,然后返回。

151896 client端等待3秒后,沒(méi)有收到對(duì)應(yīng)的ack,認(rèn)為之前的數(shù)據(jù)包也丟失,所以重傳之前的內(nèi)容數(shù)據(jù)包。

152579 server端連接建立定時(shí)器生效,遍歷半連接鏈表,發(fā)現(xiàn)剛才acked的連接,synack重傳次數(shù)在閥值以內(nèi),重新發(fā)送synack給client端。

152581 cient端收到synack后,根據(jù)ack值,使用SACK算法,只重傳最后一個(gè)ack內(nèi)容。

Server端收到數(shù)據(jù)包,由于accept隊(duì)列仍然是滿的,所以server端處理也只是標(biāo)記acked,然后返回

153455 client端等待3秒后,沒(méi)有收到對(duì)應(yīng)的ack,認(rèn)為之前的數(shù)據(jù)包也丟失,所以重傳之前的內(nèi)容數(shù)據(jù)包。

155399 server端連接建立定時(shí)器生效,遍歷半連接鏈表,發(fā)現(xiàn)剛才acked的連接,synack重傳次數(shù)在閥值以內(nèi),重新發(fā)送synack給client端。

155400 cient端收到synack后,根據(jù)ack值,使用SACK算法,只重傳最后一個(gè)ack內(nèi)容。

Server端收到數(shù)據(jù)包,由于accept隊(duì)列仍然是滿的,所以server端處理也只是標(biāo)記acked,然后返回。

156468 client端等待幾秒后,沒(méi)有收到對(duì)應(yīng)的ack,認(rèn)為之前的數(shù)據(jù)包也丟失,所以重傳之前的內(nèi)容數(shù)據(jù)包。

161309 server端連接建立定時(shí)器生效,遍歷半連接鏈表,發(fā)現(xiàn)剛才acked的連接,synack重傳次數(shù)在閥值以內(nèi),重新發(fā)送synack給client端。


上一頁(yè) 1 2 下一頁(yè)

關(guān)鍵詞:

評(píng)論


相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉