新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > Linux協議棧accept和syn隊列問題

        Linux協議棧accept和syn隊列問題

        作者: 時間:2016-10-08 來源:網絡 收藏

        環境:

        本文引用地址:http://www.104case.com/article/201610/305659.htm

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

        server端listen,不調用accept。

        client一直去connect server。

        問題:

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

        分析

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

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

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

        現在假設acceptd隊列已經達到129的情況:

        client發送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,當inet_csk_reqsk_queue_yong(sk) 1,那么這個syn被server端接受。server會回復synack給client。這樣一來兩邊的狀態就變為client(ESTABLISHED), server(SYN_SENT)

        Client收到synack后回復ack給server。

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

        if(sk_acceptq_is_full(sk)

        goto exit_overflow;

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

        一次異常問題分析

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

        下面是原始數據圖:

        我們從上面的數據流來分析一下:

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

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

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

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

        此時server端狀態與之前送變化,仍然丟棄該數據包

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

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

        150166 client端收到synack,標記本地連接為ESTABLISHED狀態,給server端應答ack,connect系統調用完成。

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

        150167 client端的應用程序,檢測到connect系統調用完成,開始向該連接發送數據。

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

        150225 client端由于沒有收到剛才發送數據的ack,所以會重傳剛才的數據包

        150296 同上

        150496 同上

        150920 同上

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

        151113 client端收到synack后,根據ack值,使用SACK算法,只重傳最后一個ack內容。

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

        151896 client端等待3秒后,沒有收到對應的ack,認為之前的數據包也丟失,所以重傳之前的內容數據包。

        152579 server端連接建立定時器生效,遍歷半連接鏈表,發現剛才acked的連接,synack重傳次數在閥值以內,重新發送synack給client端。

        152581 cient端收到synack后,根據ack值,使用SACK算法,只重傳最后一個ack內容。

        Server端收到數據包,由于accept隊列仍然是滿的,所以server端處理也只是標記acked,然后返回

        153455 client端等待3秒后,沒有收到對應的ack,認為之前的數據包也丟失,所以重傳之前的內容數據包。

        155399 server端連接建立定時器生效,遍歷半連接鏈表,發現剛才acked的連接,synack重傳次數在閥值以內,重新發送synack給client端。

        155400 cient端收到synack后,根據ack值,使用SACK算法,只重傳最后一個ack內容。

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

        156468 client端等待幾秒后,沒有收到對應的ack,認為之前的數據包也丟失,所以重傳之前的內容數據包。

        161309 server端連接建立定時器生效,遍歷半連接鏈表,發現剛才acked的連接,synack重傳次數在閥值以內,重新發送synack給client端。


        上一頁 1 2 下一頁

        關鍵詞:

        評論


        相關推薦

        技術專區

        關閉
        主站蜘蛛池模板: 富锦市| 紫云| 平谷区| 越西县| 珠海市| 濉溪县| 介休市| 花莲市| 襄垣县| 客服| 将乐县| 界首市| 招远市| 南川市| 佛教| 淳化县| 霍山县| 自治县| 莱州市| 宁都县| 南平市| 黄浦区| 新丰县| 潜山县| 股票| 德阳市| 扬中市| 南康市| 龙江县| 密山市| 谢通门县| 库车县| 克什克腾旗| 渝中区| 三河市| 南乐县| 察哈| 平顺县| 民乐县| 游戏| 方正县|