當(dāng)前位置:首頁 > 嵌入式 > 嵌入式軟件
[導(dǎo)讀] 環(huán)境:Client 通過tcp 連接server,server端只是listen,但是不調(diào)用accept。通過netstat –ant查看兩端的連接情況。server端listen,不調(diào)用accept。client一直去con

 環(huán)境:

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

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

client一直去connect server。

問題:

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

分析

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

第一個隊列的長度是/proc/sys/net/ipv4/tcp_max_syn_backlog,默認是1024。如果開啟了syncookies,那么基本上沒有限制。

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

現(xiàn)在假設(shè)acceptd隊列已經(jīng)達到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)的含義是請求隊列中有多少個握手過程中沒有重傳過的段。

在第一次的時候,之前的握手過程都沒有重傳過,所以這個syn包server端會直接drop掉,之后client會重傳syn,當(dāng)inet_csk_reqsk_queue_yong(sk) < 1,那么這個syn被server端接受。server會回復(fù)synack給client。這樣一來兩邊的狀態(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會發(fā)送rst給client,并刪除掉這個鏈接;否則server端只是記錄一下LINUX_MIB_LISTENOVERFLOWS(詳見附錄2),然后返回。默認情況下是不會設(shè)置的,server端只是標(biāo)記連接請求塊的acked標(biāo)志,之后連接建立定時器,會遍歷半連接表,重新發(fā)送synack,重復(fù)上面的過程(具體的函數(shù)是inet_csk_reqsk_queue_prune),如果重傳次數(shù)超過synack重傳的閥值(/proc/sys/net/ipv4/tcp_synack_retries),會把該連接從半連接鏈表中刪除。

一次異常問題分析

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

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

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

出現(xiàn)問題的時候,cgi程序(tcp server端)處理非常慢,導(dǎo)致大量的連接請求放到accept隊列,把accept隊列阻塞。

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

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

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

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

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

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

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

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

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

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

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

150296 同上

150496 同上

150920 同上

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

164828 client端等待一段時間后,認為連接不可用,于是發(fā)送FIN、ACK給server端。Client端的狀態(tài)變?yōu)镕IN_WAIT1,等待一段時間后,client端將看不到該鏈接。

164829 server端收到ACK后,此時cgi程序處理完一個請求,從accept隊列中取走一個連接,此時accept隊列中有了空閑,server端將請求的連接放到accept隊列中。

這樣cgi所在的服務(wù)器上顯示該鏈接是established的,但是nginx(client端)所在的服務(wù)器上已經(jīng)沒有該鏈接了。

之后,當(dāng)cgi程序從accept隊列中取到該連接后,調(diào)用read去讀取sock中的內(nèi)容,但是由于client端早就退出了,所以read就會block那里了。

問題解決

或許你會認為在164829中,server端不應(yīng)該建立連接,這是內(nèi)核的bug。但是內(nèi)核是按照RFC來實現(xiàn)的,在3次握手的過程中,是不會判斷FIN標(biāo)志位的,只會處理SYN、ACK、RST這三種標(biāo)志位。

從應(yīng)用層的角度來考慮解決問題的方法,那就是使用非阻塞的方式read,或者使用select超時方式read;亦或者nginx中關(guān)閉連接的時候使用RST方式,而不是FIN方式。

附錄1

when I use linux TCP socket, and find there is a bug in function sk_acceptq_is_full():

When a new SYN comes, TCP module first checks its validation. If valid,send SYN,ACK to the client and add the sock

to the syn hash table.

Next time if received the valid ACK for SYN,ACK from the client. server will accept this connection and increase the

sk->sk_ack_backlog -- which is done in function tcp_check_req().

We check wether acceptq is full in function tcp_v4_syn_recv_sock().

Consider an example:

After listen(sockfd, 1) system call, sk->sk_max_ack_backlog is set to

As we know, sk->sk_ack_backlog is initialized to 0. Assuming accept() system call is not invoked now

1. 1st connection comes. invoke sk_acceptq_is_full(). sk->sk_ack_backlog=0 sk->sk_max_ack_backlog=1, function return 0 accept this connection. Increase the sk->sk_ack_backlog

2. 2nd connection comes. invoke sk_acceptq_is_full(). sk->sk_ack_backlog=1 sk->sk_max_ack_backlog=1, function return 0 accept this connection. Increase the sk->sk_ack_backlog

3. 3rd connection comes. invoke sk_acceptq_is_full(). sk->sk_ack_backlog=2 sk->sk_max_ack_backlog=1, function return 1. Refuse this connection.I think it has bugs. after listen system call. sk->sk_max_ack_backlog=1

but now it can accept 2 connections.

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險,如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉