色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

什么是CLOSING狀態

科技綠洲 ? 來源:Linux開發架構之路 ? 作者:Linux開發架構之路 ? 2023-11-13 11:08 ? 次閱讀

很多資料講了關于TCP的CLOSING和CLOSE_WAIT狀態以及所謂的優雅關閉的細節,多數側重與Linux的內核實現(除了《UNIX網絡編程》)。本文不注重代碼細節,只關注邏輯。所使用的工具,tcpdump,packetdrill以及ss。

關于ss可以先多說幾句,它展示的信息跟netstat差不多,只不過更加詳細。netstat的信息是通過procfs獲取的,本質上來講就是遍歷/proc/net/netstat文件的內容,然后將其組織成可讀的形式展示出來,然而ss則可以針對特定的五元組信息提供更加詳細的內容,它不再通過procfs,而是用過Netlink來提取特定socket的信息,對于TCP而言,它可以提取到甚至tcp_info這種詳細的信息,它包括cwnd,ssthresh,rtt,rto等。

本文展示的邏輯使用了以下三樣工具:

1).packetdrill

使用packetdrill構造出一系列的包序列,使得TCP進入CLOSING狀態或者CLOSE_WAIT狀態。

2).tcpdump/tshark

抓取packetdrill注入的數據包以及協議棧反饋的包,以確認數據包序列確實如TCP標準所述的那樣。

3).ss/netstat

通過ss抓取packetdrill相關套接字的tcp_info,再次確認細節。

我想,我使用上述的三件套解析了CLOSING狀態之后,接下來的CLOSE_WAIT狀態就可以當作練習了。

我來一個一個說。

1.關于CLOSING狀態

首先我來描述一下而不是細說概念。

什么是CLOSING狀態呢?我們來看一下下面的局部狀態圖:

圖片

也就是說,當兩端都主動發送FIN的時候,并且在收到對方對自己發送的FIN之前收到了對方發送的FIN的時候,兩邊就都進入了CLOSING狀態,這個在狀態圖上顯示的很清楚。這個用俗話說就是”同時關閉“。時序圖我就不給出了,請自行搜索或者自己畫。

有很多人都說,這種狀態的TCP連接在系統中存在了好長時間并百思不得其解。這到底是為什么呢?通過狀態圖和時序圖,我們知道,在進入CLOSING狀態后,只要收到了對方對自己的FIN的ACK,就可以雙雙進入TIME_WAIT狀態,因此,如果RTT處在一個可接受的范圍內,發出的FIN會很快被ACK從而進入到TIME_WAIT狀態,CLOSING狀態應該持續的時間特別短。

以下是packetdrill腳本,很簡單的一個腳本:

0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
0.000 bind(3, ..., ...) = 0
0.000 listen(3, 1) = 0

0.100 < S 0:0(0) win 32792 < mss 1460,sackOK,nop,nop,nop,wscale 7 >
0.100 > S. 0:0(0) ack 1 win 5840 < mss 1460,nop,nop,sackOK,nop,wscale 7 >
0.200 < . 1:1(0) ack 1 win 257
0.200 accept(3, ..., ...) = 4

// 象征性寫入一些數據,裝的像一點一個正常的TCP連接:握手-傳輸-揮手
0.250 write(4, ..., 1000) = 1000

0.300 < . 1:1(0) ack 1001 win 257

// 主動斷開,發送FIN
0.400 close(4) = 0
// 在未對上述close的FIN進行ACK前,先FIN
0.500 < F. 1:1(0) ack 1001 win 260
// 至此,成功進入同時關閉的CLOSING狀態。

// 由于packetdrill不能用dup調用,也好用多線程,為了維持進程不退出,只能等待
10000.000 close(4) = 0

同時,我啟用tcpdump抓包,確認了TCP狀態圖的細節,即,還沒有收到對方對FIN的ACK時,收到了對方的FIN:

圖片

有個異常,沒有收到FIN的ACK(packetdrill沒有回復,這正常,因為腳本里本來就沒有這個語句),然而也沒有看到重傳,此時該連接應該是處于CLOSING狀態了,用ss來確認:

CLOSING 1 1 192.168.0.1:webcache 192.0.2.1:54442

cubic wscale:7,7 rto:2000 rtt:50/25 ssthresh:2 send 467.2Kbps rcv_space:5840

果然,進入了CLOSING狀態且沒有消失,時不我待,當過了2秒以后,ss的結果變成了:

CLOSING 1 1 192.168.0.1:webcache 192.0.2.1:54442

cubic wscale:7,7 rto:4000 rtt:50/25 ssthresh:2 send 467.2Kbps rcv_space:5840

明顯在退避!如果繼續觀察,你會發現rto退避到了64秒之多。在我的場景中,CLOSING狀態的套接字維持了兩分鐘之久。

然而,為什么呢?為什么CLOSING狀態會維持這么久?為什么它沒有繼續維持下去直到永久呢?

很明顯,一端的FIN發出去后,沒有收到ACK,因此會退避重發,知道4次退避,即22222*2秒之久?,F在的問題是,為什么重發FIN始終不成功呢?要是成功了的話,估計ACK瞬間也就回來了,那么CLOSING狀態也就可以進入TIME_WAIT了,但是沒有成功重傳FIN!

到此為止,我們知道,進入CLOSING狀態之后,兩邊都會等待接收自己FIN的ACK,一旦收到ACK,就會進入TIME_WAIT,如此反復,如果收不到ACK,則會不斷重傳FIN,直到忍無可忍,將socket銷毀。現在,我們集中于解釋為什么重傳沒有成功,但是請記住,并不是每次都這樣,只是在我這個packetdrill構造的場景中會有重傳不成功,不然如果大概率不成功的話。豈不是每個CLOSING狀態都要維持很長時間???。?/p>

在我的場景下,通過hook重傳函數以及抓包確認,發現所有的重傳雖然退避了,但是都沒有真正將數據包發送出去,究其原因,最終確認問題出在以下代碼上:

if (atomic_read(&sk- >sk_wmem_alloc) >
    min(sk- >sk_wmem_queued + (sk- >sk_wmem_queued > > 2), sk- >sk_sndbuf))
    return -EAGAIN;

在Linux協議棧的實現中,tcp_retransmit_skb由tcp_retransmit_timer調用,即便是這里出了些問題沒有重傳成功,也還是會退避的,退避超時到期后,繼續在這里出錯,直到”不可容忍“銷毀socket。

我們可以得知,不管如何CLOSING狀態的TCP連接即便沒有收到對自己FIN的ACK,也不會永久保持下去,保持多久取決于自己發送FIN時刻的RTT,然后RTT計算出的RTO按照最大的退避次數來退避,直到最終執行了固定次數的退避后,算出來的那個比較大的超時時間到期,然后TCP socket就銷毀了。

因此,CLOSING狀態并不可怕,起碼,不管怎樣,它有一個可控的銷毀時限。

...

現在我來解釋重傳不成功的細節。

我們知道,根據上述的代碼段,sk_wmem_alloc要足夠大,大到它比sk_wmem_queued+sk_wmem_queued/4更大的時候,才會返回錯誤造成重傳不成功,然而我們的packetdrill腳本中構造的TCP連接的生命周期中僅僅傳輸了1000個字節的數據,并且這1000個字節的數據得到了ACK,然后就結束了連接。一個socket保有一個sk_wmem_alloc字段,在skb交給這個socket的時候,該字段會增加skb長度的大小(skb本身大小包括skb數據大小),然而當skb不再由該socket持有的時候,也就是其被更底層的邏輯接管之后,socket的sk_wmem_alloc字段自然會減去skb長度的大小,這一切的過程由以下的函數決定,即skb_set_owner_w和skb_orphan。我們來看一下這兩個函數:

static inline void skb_set_owner_w(struct sk_buff *skb, struct sock *sk)
{
    skb_orphan(skb);
    skb- >sk = sk;
    // sock_wfree回調中會遞減sk_wmem_alloc相應的大小,其大小就是skb- >truesize
    skb- >destructor = sock_wfree;
    /*
     * We used to take a refcount on sk, but following operation
     * is enough to guarantee sk_free() wont free this sock until
     * all in-flight packets are completed
     */
    atomic_add(skb- >truesize, &sk- >sk_wmem_alloc);
}
static inline void skb_orphan(struct sk_buff *skb)
{
    // 調用回調函數,遞減sk_wmem_alloc
    if (skb- >destructor)
        skb- >destructor(skb);
    skb- >destructor = NULL;
    skb- >sk        = NULL;
}

也就是說,只要skb_orphan在skb通向網卡的路徑上被正確調用,就會保證sk_wmem_alloc的值隨著skb進入socket的管轄時而增加,而被實際發出后而減少。但是根據我的場景,事實好像不是這樣,sk_wmem_alloc的值只要發送一個skb就會增加,絲毫沒有減少的跡象...這是為什么呢?

有的時候,當你對某個邏輯理解足夠深入后,一定要相信自己的判斷,內核存在BUG!內核并不完美。我使用的是2.6.32老內核,這個內核我已經使用了6年多,這是我在這個內核上發現的第4個BUG了。

請注意,我的這個場景中,我使用了packetdrill來構造數據包,而packetdrill使用了tun網卡。為什么使用真實網卡甚至使用loopback網卡就不會有問題呢?這進一步引導我去調查tun的代碼,果不其然,在其hard_xmit回調中沒有調用skb_orphan!也就說說,但凡使用2.6.32內核版本tun驅動的,都會遇到這個問題呢。在tun的xmit中加入skb_orphan之后,問題消失,抓包你會發現大量的FIN重傳包,這些重傳隨著退避而間隔加大(注意,用ss命令比對一下rto字段的值和tcpdump抓取的實際值):

圖片

(為了驗證這個,我修改了packetdrill腳本,中間增加了很多的數據傳輸,以便盡快重現sk_wmem_alloc在使用tun時不遞減的問題)于是,我聯系了前公司的同事,讓他們修改OpenVPN使用的tun驅動代碼,因為當時確實出現過關于TCP使用OpenVPN隧道的重傳問題,然而,得到的答復卻是,xmit函數中已經有skb_orphan了...然后我看了下代碼,發現,公司的代碼已經不存在問題了,因為我在前年搞tun多隊列的時候,已經移植了3.9.6的tun驅動,這個問題已經被修復。

自己曾經做的事情,已然不再憶起...

2.關于CLOSE_WAIT狀態

和CLOSING狀態不同,CLOSE_WAIT狀態可能會持續更久更久的時間,導致無用的socket無法釋放,這個時間可能與應用進程的生命周期一樣久!

我們先看一下CLOSE_WAIT的局部狀態圖。

圖片

然后我來構造一個packetdrill腳本:

0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
0.000 bind(3, ..., ...) = 0
0.000 listen(3, 1) = 0

0.100 < S 0:0(0) win 32792 < mss 1460,sackOK,nop,nop,nop,wscale 7 >
0.100 > S. 0:0(0) ack 1 win 14600 < mss 1460,nop,nop,sackOK,nop,wscale 7 >
0.200 < . 1:1(0) ack 1 win 257
0.200 accept(3, ..., ...) = 4
// 什么也不發了,直接斷開
0.350 < F. 1:1(0) ack 1 win 260
// 協議棧會對這個FIN進行ACK,然則應用程序不關閉連接的話...
//0.450 close(4) = 0
// 該連接就會變成CLOSE_WAIT,并且只要其socket引用計數不為0,就一直僵死在那里
2000.000 close(4) = 0

同樣的,我來展示抓包結果:

圖片

最后,和描述CLOSING狀態不同的是,隔了N個小時之后,我來看ss -ip的結果:

CLOSE-WAIT 1 0 192.168.0.1:webcache 192.0.2.1:53753 users:(("ppp",2399,8))

cubic wscale:7,7 rto:300 rtt:100/50 ato:40 cwnd:10 send 1.2Mbps rcv_space:14600

這個CLOSE_WAIT還在!這是為什么呢?

很遺憾,上述的packetdrill腳本并不能直觀地展示這個現象,還得靠我說一說。

CLOSE_WAIT是一端在收到FIN之后,發送自己的FIN之前所處的狀態,那么很顯然,如果一個進程/線程始終不發送FIN,那么在該連接所隸屬的socket的生命周期內,這個socket就會一直存在,我們知道,在UNIX/Linux/WinSock中,socket作為一個描述符出現,只要進程/線程繼續持有它,它就會一直存在,因此大多數情況下進程/線程的生命周期內,此TCP套接字就會始終處在CLOSE_WAIT狀態。進程/線程長時間持有不需要的socket描述符,更多的并不是有意的,而是在進行諸如fork/clone之類的系統調用后,dup了父親的文件描述符,然后在孩子那里又沒有及時關閉,另外的原因就是編程者對socket描述符的close接口以及shutdown接口不是很理解了。

現在,我們用一個問題來繼續我們的討論。

什么時候進程在超長的生命周期內不會如愿關閉TCP從而發送FIN呢?

我的答案比較直接:不能指望close會發送FIN!

相信很多人在想斷開一個TCP連接的時候,都會調用close吧。并且這種做法幾乎都是正確的,以至于很多人都把這作為一種標準的做法。但是這是不對的!Why?!在《UNIX網絡編程》中,曾經提到了所謂的”優雅關閉TCP連接“,何謂優雅??!如果你充分理解close,shutdown,應該就會知道,CLOSE_WAIT出現,你應該可以給出一些解釋。

close調用

close的參數只是一個文件描述符號,它不理解這個文件真正的細節,它只是一個文件系統內范疇的一個調用,它只是關閉文件描述符,保證此進程不會在讀取它而已。如果你關閉了文件描述符4,即close(4),你知道4代表的文件會作何反應嗎??文件系統并不知道4號描述符代表的文件到底是什么,更不知道有多少進程共享這個底層的”實體“,所以一個進程層面上邏輯根本沒有權力去徹底關閉一個socket。如果你想了解close的細節,更應該去看看UNIX文件抽象或者文件系統的細節,而不是socket。請參見位于fs/open.c中的:

SYSCALL_DEFINE1(close, unsigned int, fd)
{
    ...
    fdt = files_fdtable(files);
    ...
    filp = fdt- >fd[fd];
    ...
    retval = filp_close(filp, files);
    ...
    return retval;
    ...
}
EXPORT_SYMBOL(sys_close);

在filp_close中會有fput調用:

void fput(struct file *file)
{
    if (atomic_long_dec_and_test(&file- >f_count))
        __fput(file);
}

看到那個引用計數了嗎?只有當這個文件的引用計數變成0的時候,才會調用底層的關閉邏輯,對于socket而言,如果仍然還有一個進程或者線程持有這個socket對應的文件系統的描述符,那么即便你調用了close,也不會進入了socket的close邏輯,它在文件系統層面就返回了!

shutdown調用

這個才是真正關閉一個TCP連接的調用!shutdown并沒有文件系統的語義,它專門針對內核層的TCP socket。因此,調用shutdown的邏輯,才是真正關閉了與之共享信道的tcp socket。

所謂的優雅關閉,就是在調用close之前, 首先自己調用shutdown(RD or WD)。這樣的時序才是關閉TCP的必由之路!

如果你想優雅關閉一個TCP連接,請先用shutdown,然后后面跟一個close。不過有點詭異的是,Linux的shutdown(SHUT_RD)貌似沒有任何效果,不過這無所謂了,本來對于讀不讀的,就不屬于TCP的范疇,只有SHUT_WR才會實際發送一個FIN給對方。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 數據
    +關注

    關注

    8

    文章

    7134

    瀏覽量

    89515
  • 代碼
    +關注

    關注

    30

    文章

    4823

    瀏覽量

    68985
  • 腳本
    +關注

    關注

    1

    文章

    391

    瀏覽量

    14932
收藏 人收藏

    評論

    相關推薦

    CC3200 socket狀態如何查詢?

    如對一個TCP server來說,我怎么知道這個Socket處于什么狀態呢?Tcp Socket大概有如下狀態,但是我在TI的SDK中沒找到相關的定義。附:NETSTAT TCP套接字解釋
    發表于 05-06 11:00

    是否有一些命令知道LAN820板的狀態

    ,ping不工作,但2 LED仍然閃爍。PIC代碼仍然完美地工作,我可以通過串口驗證它。我想知道是否有一些命令知道LAN820板的狀態?我試圖檢測何時拔出RJ45,但它不起作用。我很喜歡這個,謝謝你的幫助
    發表于 10-26 16:14

    關閉34908A繼電器并保持關閉狀態

    )?2。如何關閉一個多路復用器的繼電器,比如說(@ 101)并在掃描和測量(@ 201:240)的直流電壓時保持關閉狀態?我使用以下命令測試了一張卡的關閉一個繼電器(并保持關閉以便進行測量):ROUT
    發表于 07-30 06:39

    UC3901/UC2901/UC1901 pdf datas

    The UC1901 family is designed to solve many of the problems associatedwith closing a feedback
    發表于 09-14 01:35 ?16次下載

    水電機組的狀態監測及狀態檢修

    該文介紹水電機組在線監測技術以及狀態檢修系統的組成結構,提出水電廠開展狀態檢修的I作思路。
    發表于 04-07 15:15 ?16次下載

    狀態機舉例

    狀態機舉例 你可以指定狀態寄存器和狀態機的狀態。以下是一個有四種狀態的普通狀態機。 // Th
    發表于 03-28 15:18 ?998次閱讀

    MC33972抑制喚醒多路開關檢測接口

    The 33972 Multiple Switch Detection Interface with suppressed wake-up is designed to detect the closing and opening of up to 22 switch contacts.
    發表于 09-19 12:53 ?21次下載
    MC33972抑制喚醒多路開關檢測接口

    基于設備狀態的網絡狀態評估方案

    當前通信網絡的異構性較強、兼容性較差,網絡狀態的評估受到極大限制,技術與市場等因素導致網絡狀態評估標準難以統一。本體具有良好的開放性與可擴展性,能很好地承載知識的形式化,有助于推動標準的統一。采用
    發表于 01-18 17:05 ?0次下載

    Richard Gerber致閉幕詞

    Closing Remarks with Richard Gerber
    的頭像 發表于 10-26 07:12 ?2040次閱讀

    狀態模式(狀態機)

    以前寫狀態機,比較常用的方式是用 if-else 或 switch-case,高級的一點是函數指針列表。最近,看了一文章《c語言設計模式–狀態模式(狀態機)》(來源:embed linux
    發表于 12-16 16:53 ?9次下載
    <b class='flag-5'>狀態</b>模式(<b class='flag-5'>狀態</b>機)

    linux 中 ACPI 電源管理 G 狀態、S 狀態、D 狀態、C 狀態、P 狀態

    ACPI 高級電源管理ACPI 中定義了 G、D、S、C、P 這 5 個大的電力狀態。G 狀態 Global system stateG 狀態表示的是用戶看到的整個系統的電力狀態。G0
    發表于 01-05 14:12 ?4次下載
    linux 中 ACPI 電源管理 G <b class='flag-5'>狀態</b>、S <b class='flag-5'>狀態</b>、D <b class='flag-5'>狀態</b>、C <b class='flag-5'>狀態</b>、P <b class='flag-5'>狀態</b>

    HTTP的狀態消息

     HTTP狀態消息是指HTTP服務器在響應客戶端請求時返回的狀態信息。狀態消息由數字狀態碼和可選的文本描述組成,主要有以下幾種類型
    發表于 05-06 16:01 ?529次閱讀

    就緒狀態和等待狀態的區別

    就緒狀態和等待狀態是計算機領域中一對常用的術語,用于描述進程或線程在執行時的不同狀況。下面我將詳細解釋就緒狀態和等待狀態的區別。 就緒狀態
    的頭像 發表于 11-17 11:29 ?3044次閱讀

    阻塞狀態和等待狀態的區別

    阻塞狀態和等待狀態是計算機領域中常用的術語,用來描述進程或線程的狀態。盡管這兩個狀態在表面上有些相似,但它們有著本質上的區別。本文將詳盡、詳實、細致地討論阻塞
    的頭像 發表于 11-17 11:33 ?4145次閱讀

    時序邏輯電路中如何判斷有效狀態和無效狀態

    在時序邏輯電路中,有效狀態和無效狀態的判斷是電路分析和設計的重要環節。有效狀態是指電路在實際工作過程中被利用到的狀態,它們構成了電路的有效循環;而無效
    的頭像 發表于 08-12 15:51 ?3148次閱讀
    主站蜘蛛池模板: 苍老师刺激的120分钟 | 亚洲成人网导航 | 97无码欧美熟妇人妻蜜 | 在线 国产 欧美 专区 | 黑丝制服影院 | 手机在线成人精品视频网 | 亚洲呦女专区 | 99久久婷婷国产综合精品青草 | 69式国产真人免费视频 | jk制服啪啪网站 | yellow视频免费观看 | 千禧金瓶梅 快播 | 日本bbwhd| 羞羞答答的免费视频在线观看 | 亚洲三级在线中文字幕 | 久久精品小视频 | 人妻体体内射精一区二区 | 亚洲 欧美 国产 综合五月天 | 青柠视频在线观看高清HD | 国产精品v片在线观看不卡 国产精品v欧美精品v日韩 | 俄罗斯女人与马Z00Z视频 | 亚洲精品无码葡京AV天堂 | 黑兽在线观看高清在线播放樱花 | 国产成人精品系列在线观看 | 亚洲爆乳无码精品AAA片蜜桃 | 久亚洲AV无码专区A片 | 国产XXXXXX农村野外 | 久久综合给合久久狠狠狠… | 翘臀少妇被扒开屁股日出水爆乳 | 色综合久久88色综合天天提莫 | 亚洲熟妇无码乱子AV电影 | 99久久婷婷国产综合精品青草 | 国产偷国产偷亚洲高清SWAG | 韩国伦理片2018在线播放免费观看 | 久久精品热在线观看85 | 邪恶肉肉全彩色无遮琉璃神社 | 亚洲黄色大片 | 18禁在线无遮挡羞羞漫画 | 一道精品视频一区二区三区 | 亚洲精品偷拍影视在线观看 | 亚洲福利精品电影在线观看 |