0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

為什么要有TIME_WAIT狀態(tài)

科技綠洲 ? 來源:Linux開發(fā)架構(gòu)之路 ? 作者:Linux開發(fā)架構(gòu)之路 ? 2023-11-13 11:26 ? 次閱讀

首先我們說下狀態(tài) TIME_WAIT 出現(xiàn)的原因

TCP的新建連接,斷開連接的流程和各個(gè)狀態(tài),如下圖所示

圖片

由上圖可知:TIME_WAIT 是主動(dòng)斷開連接的一方會(huì)出現(xiàn)的,客戶端,服務(wù)器都有可能出現(xiàn)

當(dāng)客戶端主動(dòng)斷開連接時(shí),發(fā)出最后一個(gè)ACK后就會(huì)處于 TIME_WAIT狀態(tài)

當(dāng)服務(wù)器主動(dòng)斷開連接時(shí),發(fā)出最后一個(gè)ACK后就會(huì)處于 TIME_WAIT狀態(tài)

結(jié)論:TIME_WAIT 是必然會(huì)出現(xiàn)的狀態(tài),是正常現(xiàn)象,且會(huì)定時(shí)回收

TIME_WAIT 狀態(tài)持續(xù)2MSL時(shí)間,MSL就是maximum segment lifetime(最大報(bào)文段的生命期),這是一個(gè)IP數(shù)據(jù)包能在互聯(lián)網(wǎng)上生存的最長時(shí)間,超過這個(gè)時(shí)間將在網(wǎng)絡(luò)中消失(被丟棄)。RFC 793中規(guī)定MSL為2分鐘,實(shí)際應(yīng)用中,可能為30S,1分鐘,2分鐘。

我的系統(tǒng)是ubuntu,輸入如下命令后可以看到,時(shí)間為60秒

圖片

請注意兩個(gè)狀態(tài),一個(gè)是TIME_WAIT,一個(gè)是CLOSE_WAIT,完全不同的兩個(gè)狀態(tài)

TIME_WAIT 出現(xiàn)在主動(dòng)斷開方,發(fā)出最后一個(gè)ACK后

CLOSE_WAIT 出現(xiàn)在被動(dòng)斷開方,收到主動(dòng)斷開方的FIN,發(fā)出自己的ACK后

問題:為什么要有TIME_WAIT狀態(tài)?

  1. 為了可靠地關(guān)閉TCP連接

舉例:我們把主動(dòng)斷開連接的一方稱為C端,被動(dòng)斷開連接的一方稱為S端,由于網(wǎng)絡(luò)不可靠,C端發(fā)送的最后一個(gè)ACK報(bào)文可能沒成功發(fā)送到S端,那么S端就會(huì)重新發(fā)上一個(gè)報(bào)文即FIN,如果C端處于TIME_WAIT狀態(tài)下,就可以重新發(fā)送報(bào)文ACK,然后重新計(jì)時(shí)2MSL時(shí)間才會(huì)進(jìn)入CLOSED狀態(tài),S端收到ACK后就可以正常關(guān)閉TCP連接了。反之,如果這時(shí)C端處于 CLOSED 狀態(tài) ,就會(huì)響應(yīng) RST報(bào)文而不是ACK報(bào)文,那S端會(huì)認(rèn)為這是一個(gè)錯(cuò)誤,只能異常關(guān)閉TCP連接

  1. 防止上一次連接中的包,迷路后重新出現(xiàn),影響新連接

由于網(wǎng)絡(luò)的不可靠,TCP分節(jié)可能因?yàn)槁酚善鳟惓6懊酝尽?,在迷途期間,TCP發(fā)送端會(huì)因確認(rèn)超時(shí)而重發(fā)這個(gè)分節(jié),這個(gè)分節(jié)最終被發(fā)送到對方時(shí),對方可能已經(jīng)是一個(gè)新的連接了,由此造成混亂。舉例:關(guān)閉一個(gè)TCP鏈接后,馬上又創(chuàng)建了一個(gè)相同的IP地址和端口之間的TCP鏈接,后一個(gè)鏈接被稱為前一個(gè)鏈接的化身(incarnation),那么此時(shí)有可能出現(xiàn)這種狀況,前一個(gè)鏈接的迷途重復(fù)分節(jié)在前一個(gè)鏈接終止后出現(xiàn)了,從而被誤解成從屬于新的連接的數(shù)據(jù)。為了不出現(xiàn)這種混亂,TCP不容許處于TIME_WAIT狀態(tài)的連接立即啟動(dòng)一個(gè)新連接,由于TIME_WAIT狀態(tài)持續(xù)2MSL,就能夠保證當(dāng)成功創(chuàng)建一個(gè)TCP鏈接的時(shí)候,來自前一個(gè)連接的迷途重復(fù)分節(jié)已經(jīng)在網(wǎng)絡(luò)中消逝

注意close() 和 shutdown()的區(qū)別

close()其實(shí)只是將socket fd的引用計(jì)數(shù)減1,只有當(dāng)該socket fd的引用計(jì)數(shù)減至0時(shí),TCP傳輸層才會(huì)發(fā)起4次握手從而真正關(guān)閉連接。而shutdown則可以直接發(fā)起關(guān)閉連接所需的4次握手,而不用受到引用計(jì)數(shù)的限制

close()會(huì)終止TCP的雙工鏈路。由于TCP連接的全雙工特性,可能會(huì)存在這樣的應(yīng)用場景:local peer不會(huì)再向remote peer發(fā)送數(shù)據(jù),而remote peer可能還有數(shù)據(jù)需要發(fā)送過來,在這種情況下,如果local peer想要通知remote peer自己不會(huì)再發(fā)送數(shù)據(jù)但還會(huì)繼續(xù)收數(shù)據(jù)這個(gè)事實(shí),用close()是不行的,而shutdown()可以完成這個(gè)任務(wù)

服務(wù)器短時(shí)間內(nèi)大量的TIME_WAIT出現(xiàn),才是問題

會(huì)引發(fā)以下問題

  1. 由于處于TIME_WAIT狀態(tài),連接并未關(guān)閉,占據(jù)了大量的CPU,內(nèi)存,文件描述符等,造成新的連接無法建立,客戶端表現(xiàn)就是連接失敗
  2. 如果服務(wù)器上同時(shí)有nginx,且nginx由于反向代理,那么還會(huì)占用很多端口(S端處于TIME_WAIT,該連接的另一方即C端需獨(dú)占一個(gè)端口,C端是由nginx代理建立的),要知道端口是有限的,最多65535,一旦端口占用完,無論服務(wù)器配置如何高,新連接都無法建立了,客戶端表現(xiàn)仍然是連接失敗

短時(shí)間內(nèi)大量TIME_WAIT出現(xiàn)的根本原因:高并發(fā)且持續(xù)的短連接

  1. 業(yè)務(wù)上使用了持續(xù)且大量的短連接,純屬設(shè)計(jì)缺陷,例如爬蟲服務(wù)器就有可能出現(xiàn)這樣的問題
  2. http請求中connection的值被設(shè)置成close,因?yàn)榉?wù)器處理完http請求后會(huì)主動(dòng)斷開連接,然后這個(gè)連接就處于TIME_WAIT狀態(tài)了。持續(xù)時(shí)間長且量級較大的話,問題就顯現(xiàn)出來了。http洗衣1.0中,connection默認(rèn)為close,但在http1.1中connection默認(rèn)行為是keep-alive,就是因?yàn)檫@個(gè)原因
  3. 服務(wù)器被攻擊了,攻擊方采用了大量的短連接

重點(diǎn):解決辦法

  1. 代碼層修改,把短連接改為長連接,但代價(jià)較大
  2. 修改 ip_local_port_range,增大可用端口范圍,比如1024 ~ 65535
  3. 客戶端程序中設(shè)置socket的 SO_LINGER 選項(xiàng)
  4. 打開 tcp_tw_recycle 和tcp_timestamps 選項(xiàng),有一定風(fēng)險(xiǎn),且linux4.12之后被廢棄
  5. 打開 tcp_tw_reuse 和 tcp_timestamps 選項(xiàng)
  6. 設(shè)置 tcp_max_tw_buckets 為一個(gè)較小的值

下面我們開始對各個(gè)辦法進(jìn)行詳細(xì)講解

辦法1:代碼層修改,把短連接改為長連接

由于TIME_WAIT出現(xiàn)的根本原因是高并發(fā)且持續(xù)的短連接,所以如果能把短連接改成長連接,就能徹底解決問題。比如http請求中的connection設(shè)置為keep-alive。只是代碼層的修改往往會(huì)比較大,不再啰嗦

辦法2. 修改 ip_local_port_range,增大可用端口范圍

我這里linux系統(tǒng)是ubuntu,輸入如下命令可查看可用的端口范圍

圖片

默認(rèn)差不多有3萬個(gè),我們可以修改這個(gè)值,但是注意最小不能小于1024,最大不能大于65535,也就是說改完之后最多有6萬多個(gè)可用端口

只是一般線上遇到大量的TIME_WAIT,都是高并發(fā)且持續(xù)的短連接,單純擴(kuò)大端口范圍并不能從根本上解決問題,只是能多撐一會(huì)兒

辦法3. 客戶端程序中設(shè)置socket的 SO_LINGER 選項(xiàng)

SO_LINGER 選項(xiàng)可以用來控制調(diào)用close函數(shù)關(guān)閉連接后的行為,linger的定義如下

struct linger {
     int l_onoff; /* 0 = off, nozero = on */
     int l_linger; /* linger time */

有三種情況

  1. 設(shè)置 l_onoff 為0,l_linger的值會(huì)被忽略,也是內(nèi)核缺省的情況,和不設(shè)置沒區(qū)別。close調(diào)用會(huì)立即返回給調(diào)用者,TCP模塊負(fù)責(zé)嘗試發(fā)送殘留的緩沖區(qū)數(shù)據(jù),會(huì)經(jīng)過通常四分組終止序列(FIN/ACK/FIN/ACK),不能解決任何問題
  2. 設(shè)置 l_onoff 為1,l_linger為0,則連接立即終止,TCP將丟棄殘留在發(fā)送緩沖區(qū)中的任何數(shù)據(jù)并發(fā)送一個(gè)RST報(bào)文給對方,而不是通常的四分組終止序列。對方收到RST報(bào)文后直接進(jìn)入CLOSED狀態(tài),從根本上避免了TIME_WAIT狀態(tài)
  3. 設(shè)置 l_onoff 為1,l_linger > 0,有兩種情況

a. 如果socket為阻塞的,則close將阻塞等待l_linger 秒的時(shí)間。如果在l_linger秒時(shí)間內(nèi)TCP模塊成功發(fā)送完殘留在緩沖區(qū)的數(shù)據(jù),則close返回0,表示成功。如果l_linger時(shí)間內(nèi)TCP模塊沒有成功發(fā)送殘留的緩沖區(qū)數(shù)據(jù),則close返回-1,表示失敗,并將errno設(shè)置為EWOULDBLOCK

b. 如果socket為非阻塞的,那么close立即返回,此時(shí)需要根據(jù)close返回值以及errno來判斷TCP模塊是否成功發(fā)送殘留在緩沖區(qū)的數(shù)據(jù)

第3中情況,其實(shí)就是第1種和第2種的折中處理,且當(dāng)socket為非阻塞的場景下是沒有作用的

綜上所述:第2種情況,也就是l_onoff為1,l_linger不為0,可以用于解決服務(wù)器大量TIME_WAIT的問題

只是Linux上測試的時(shí)候,并未發(fā)現(xiàn)發(fā)送了RST報(bào)文,而是正常進(jìn)行了四步關(guān)閉流程,

初步推斷是“只有在丟棄數(shù)據(jù)的時(shí)候才發(fā)送RST”,如果沒有丟棄數(shù)據(jù),則走正常的關(guān)閉流程

查看Linux源碼,確實(shí)有這么一段注釋和源碼

/* As outlined in RFC 2525, section 2.17, we send a RST here because
* data was lost. To witness the awful effects of the old behavior of
* always doing a FIN, run an older 2.1.x kernel or 2.0.x, start a bulk
* GET in an FTP client, suspend the process, wait for the client to
* advertise a zero window, then kill -9 the FTP client, wheee...
* Note: timeout is always zero in such a case.
*/
if (data_was_unread) {
    /* Unread data was tossed, zap the connection. */
    NET_INC_STATS_USER(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE);
    tcp_set_state(sk, TCP_CLOSE);
    tcp_send_active_reset(sk, sk- >sk_allocation);
}

從原理上來說,這個(gè)選項(xiàng)有一定的危險(xiǎn)性,可能導(dǎo)致丟數(shù)據(jù),使用的時(shí)候要小心一些

從實(shí)測情況來看,打開這個(gè)選項(xiàng)后,服務(wù)器TIME_WAIT連接數(shù)為0,且不受網(wǎng)絡(luò)組網(wǎng)(例如是否虛擬機(jī)等)的影響

辦法4. 打開 tcp_tw_recycle 和tcp_timestamps 選項(xiàng),有一定風(fēng)險(xiǎn),且linux4.12之后被廢棄

官方文檔中解釋如下:

tcp_tw_recycle 選項(xiàng)作用為:Enable fast recycling TIME-WAIT sockets. Default value is 0.

tcp_timestamps 選項(xiàng)作用為:Enable timestamps as defined in RFC1323. Default value is 1

這兩個(gè)選項(xiàng)是linux內(nèi)核提供的控制選項(xiàng),和具體的應(yīng)用程序沒有關(guān)系,而且網(wǎng)上也能夠查詢到大量的相關(guān)資料,但信息都不夠完整,最主要的幾個(gè)問題如下;

1)快速回收到底有多快?

2)有的資料說只要打開tcp_tw_recycle即可,有的又說要tcp_timestamps同時(shí)打開,到底是哪個(gè)正確?

3)為什么從虛擬機(jī)NAT出去發(fā)起客戶端連接時(shí)選項(xiàng)無效,非虛擬機(jī)連接就有效?為了搞清楚上面的疑問,只能看代碼,看出一些相關(guān)的代碼供大家參考:

=====linux-2.6.37 net/ipv4/tcp_minisocks.c 269======

void tcp_time_wait(struct sock *sk, int state, int timeo)
{
    struct inet_timewait_sock *tw = NULL;
    const struct inet_connection_sock *icsk = inet_csk(sk);
    const struct tcp_sock *tp = tcp_sk(sk);
    int recycle_ok = 0;


    // 判斷是否快速回收,這里可以看出tcp_tw_recycle和tcp_timestamps兩個(gè)選項(xiàng)都打開的時(shí)候才進(jìn)行快速回收,
    //且還有進(jìn)一步的判斷條件,后面會(huì)分析,這個(gè)進(jìn)一步的判斷條件和第三個(gè)問題有關(guān)
    if (tcp_death_row.sysctl_tw_recycle && tp- >rx_opt.ts_recent_stamp)
        recycle_ok = icsk- >icsk_af_ops- >remember_stamp(sk);


    if (tcp_death_row.tw_count < tcp_death_row.sysctl_max_tw_buckets)
        tw = inet_twsk_alloc(sk, state);


    if (tw != NULL) {
        struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw);
        //計(jì)算快速回收的時(shí)間,等于 RTO * 3.5,回答第一個(gè)問題的關(guān)鍵是RTO(RetransmissionTimeout)大概是多少
        const int rto = (icsk- >icsk_rto < < 2) - (icsk- >icsk_rto > > 1);

        //。。。。。。此處省略很多代碼。。。。。。

    if (recycle_ok) 
    {
        //設(shè)置快速回收的時(shí)間
        tw- >tw_timeout = rto;
    } 
    else 
    {
        tw- >tw_timeout = TCP_TIMEWAIT_LEN;
        if (state == TCP_TIME_WAIT)
            timeo = TCP_TIMEWAIT_LEN;
    }

    //。。。。。。此處省略很多代碼。。。。。。
}

這里講下RTO(Retransmission Time Out):重傳超時(shí)時(shí)間,即從數(shù)據(jù)發(fā)送時(shí)刻算起,超過這個(gè)時(shí)間便執(zhí)行重傳

RFC中有關(guān)于RTO計(jì)算的詳細(xì)規(guī)定,一共有三個(gè):RFC-793、RFC-2988、RFC-6298,Linux的實(shí)現(xiàn)是參考RFC-2988。

對于這些算法的規(guī)定和Linux的實(shí)現(xiàn),有興趣的同學(xué)可以自己深入研究,實(shí)際應(yīng)用中我們只要記住Linux如下兩個(gè)邊界值:

=====linux-2.6.37 net/ipv4/tcp.c 126================

#define TCP_RTO_MAX ((unsigned)(120*HZ))

#define TCP_RTO_MIN ((unsigned)(HZ/5))

==========================================

這里的HZ是1s,因此可以得出RTO最大是120s,最小是200ms,對于局域網(wǎng)的機(jī)器來說,正常情況下RTO基本上就是200ms,因此3.5 RTO就是700ms

也就是說,快速回收是TIME_WAIT的狀態(tài)持續(xù)700ms,而不是正常的2MSL

實(shí)測結(jié)果也驗(yàn)證了這個(gè)推論,不停的查看TIME_WAIT狀態(tài)的連接,偶爾能看到1個(gè)

最后一個(gè)問題是為什么從虛擬機(jī)發(fā)起的連接即使設(shè)置了tcp_tw_recycle和tcp_timestamps,也不會(huì)快速回收,繼續(xù)看代碼:

tcp_time_wait函數(shù)中的代碼行:recycle_ok = icsk->icsk_af_ops->remember_stamp(sk);對應(yīng)的實(shí)現(xiàn)如下:

=====linux-2.6.37 net/ipv4/tcp_ipv4.c 1772=====

int tcp_v4_remember_stamp(struct sock *sk)
{
    //。。。。。。此處省略很多代碼。。。。。。

    //當(dāng)獲取對端信息時(shí),進(jìn)行快速回收,否則不進(jìn)行快速回收
    if (peer) 
    {
        if ((s32)(peer- >tcp_ts - tp- >rx_opt.ts_recent) <= 0 ||
           ((u32)get_seconds() - peer- >tcp_ts_stamp > TCP_PAWS_MSL &&
            peer- >tcp_ts_stamp <= (u32)tp- >rx_opt.ts_recent_stamp)) 
        {
            peer- >tcp_ts_stamp = (u32)tp- >rx_opt.ts_recent_stamp;
            peer- >tcp_ts = tp- >rx_opt.ts_recent;
        }

        if (release_it)
            inet_putpeer(peer);
        return 1;
    }

    return 0;
}

上面這段代碼應(yīng)該就是測試的時(shí)候虛擬機(jī)環(huán)境不會(huì)釋放的原因,當(dāng)使用虛擬機(jī)NAT出去的時(shí)候,服務(wù)器無法獲取隱藏在NAT后的機(jī)器信息。

生產(chǎn)環(huán)境也出現(xiàn)了設(shè)置了選項(xiàng),但TIME_WAIT連接數(shù)達(dá)到4W多的現(xiàn)象,可能和虛擬機(jī)有關(guān),也可能和組網(wǎng)有關(guān)。

總結(jié)一下:

1)快速回收到底有多快?

答:局域網(wǎng)環(huán)境下,700ms就回收

2)有的資料說只要打開tcp_tw_recycle即可,有的又說要tcp_timestamps同時(shí)打開,到底是哪個(gè)正確?

答:需要同時(shí)打開,但默認(rèn)情況下tcp_timestamps就是打開的,所以會(huì)有人說只要打開tcp_tw_recycle即可

3)為什么從虛擬機(jī)發(fā)起客戶端連接時(shí)選項(xiàng)無效,非虛擬機(jī)連接就有效?

答:和網(wǎng)絡(luò)組網(wǎng)有關(guān)系,無法獲取對端信息時(shí)就不進(jìn)行快速回收。

注意1

NAT環(huán)境下,打開 tcp_tw_recycle選項(xiàng)可能會(huì)引發(fā)其他問題,tcp_tw_recycle是依賴tcp_timestamps參數(shù)的。例如辦公室的外網(wǎng)地址只有一個(gè),所有人訪問后臺都會(huì)通過路由器做SNAT將內(nèi)網(wǎng)地址映射為公網(wǎng)IP,由于服務(wù)端和客戶端都啟用了tcp_timestamps,因此TCP頭部中增加時(shí)間戳信息,而在服務(wù)器看來,同一客戶端的時(shí)間戳必然是線性增長的,但是,由于我的客戶端網(wǎng)絡(luò)環(huán)境是NAT,因此每臺主機(jī)的時(shí)間戳都是有差異的,在啟用tcp_tw_recycle后,一旦有客戶端斷開連接,服務(wù)器可能就會(huì)丟棄那些時(shí)間戳較小的客戶端的SYN包,這也就導(dǎo)致了網(wǎng)站訪問極不穩(wěn)定。

簡單來說就是,Linux會(huì)丟棄所有來自遠(yuǎn)端的timestramp時(shí)間戳小于上次記錄的時(shí)間戳(由同一個(gè)遠(yuǎn)端發(fā)送的)的任何數(shù)據(jù)包。也就是說要使用該選項(xiàng),則必須保證數(shù)據(jù)包的時(shí)間戳是單調(diào)遞增的。同時(shí)從4.10內(nèi)核開始,官方修改了時(shí)間戳的生成機(jī)制,所以導(dǎo)致 tcp_tw_recycle 和新時(shí)間戳機(jī)制工作在一起不那么友好,同時(shí) tcp_tw_recycle 幫助也不那么的大。

此處的時(shí)間戳并不是我們通常意義上面的絕對時(shí)間,而是一個(gè)相對時(shí)間。很多情況下,我們是沒法保證時(shí)間戳單調(diào)遞增的,比如業(yè)務(wù)服務(wù)器之前部署了NAT,LVS等情況。相信很多小伙伴上班的公司大概率實(shí)用實(shí)用各種公有云,而各種公有云的 LVS 網(wǎng)關(guān)都是 FullNAT 。所以可能導(dǎo)致在高并發(fā)的情況下,莫名其妙的 TCP 建聯(lián)不是那么順暢或者丟連接

主機(jī)A SIP:P1 (時(shí)間戳T0) —> Server

主機(jī)A斷開后

主機(jī)B SIP:P1 (時(shí)間戳T1) T1 < T0 —> Server 丟棄

注意2

在linux內(nèi)核版本從4.12之后,tcp_tw_recycle已經(jīng)被廢棄了

我的linux機(jī)器是ubuntu,查看內(nèi)核版本如下

圖片

linux 內(nèi)核版本已經(jīng)4.15了,如果開發(fā)tcp_tw_recycle,執(zhí)行命令刷新的話,就會(huì)有如下報(bào)錯(cuò)

圖片

提示得很明確,這個(gè)文件不存在

sysctl: cannot stat /proc/sys/net/ipv4/tcp_tw_recycle: No such file or directory

綜上

可以看出這種方法不是很保險(xiǎn),在實(shí)際應(yīng)用中可能受到虛擬機(jī)、網(wǎng)絡(luò)組網(wǎng)、防火墻之類的影響從而導(dǎo)致不能進(jìn)行快速回收。

辦法5. 打開tcp_tw_reuse和tcp_timestamps選項(xiàng)

官方文檔中解釋如下:

tcp_tw_recycle選項(xiàng):Allow to reuse TIME-WAIT sockets for new connections when it is

safe from protocol viewpoint. Default value is 0

這里的關(guān)鍵在于“協(xié)議什么情況下認(rèn)為是安全的”,由于環(huán)境限制,沒有辦法進(jìn)行驗(yàn)證,通過看源碼簡單分析了一下

=====linux-2.6.37 net/ipv4/tcp_ipv4.c 114=====

int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
{
    const struct tcp_timewait_sock *tcptw = tcp_twsk(sktw);
    struct tcp_sock *tp = tcp_sk(sk);


    /* With PAWS, it is safe from the viewpoint
      of data integrity. Even without PAWS it is safe provided sequence
      spaces do not overlap i.e. at data rates <= 80Mbit/sec.
      Actually, the idea is close to VJ's one, only timestamp cache is
      held not per host, but per port pair and TW bucket is used as state
      holder.
      If TW bucket has been already destroyed we fall back to VJ's scheme
      and use initial timestamp retrieved from peer table.
    */

    //從代碼來看,tcp_tw_reuse選項(xiàng)和tcp_timestamps選項(xiàng)也必須同時(shí)打開;否則tcp_tw_reuse就不起作用
    //另外,所謂的“協(xié)議安全”,從代碼來看應(yīng)該是收到最后一個(gè)包后超過1s
    if (tcptw- >tw_ts_recent_stamp &&
       (twp == NULL || (sysctl_tcp_tw_reuse &&
        get_seconds() - tcptw- >tw_ts_recent_stamp > 1))) 
    {
        tp- >write_seq = tcptw- >tw_snd_nxt + 65535 + 2;
        if (tp- >write_seq == 0)
            tp- >write_seq = 1;
        tp- >rx_opt.ts_recent  = tcptw- >tw_ts_recent;
        tp- >rx_opt.ts_recent_stamp = tcptw- >tw_ts_recent_stamp;
        sock_hold(sktw);
        return 1;
    }

    return 0;
}

總結(jié)一下:

  1. tcp_tw_reuse選項(xiàng)和tcp_timestamps選項(xiàng)也必須同時(shí)打開;
  2. 重用TIME_WAIT的條件是收到最后一個(gè)包后超過1s。

官方手冊有一段警告:

It should not be changed without advice/request of technical experts.

對于大部分局域網(wǎng)或者公司內(nèi)網(wǎng)應(yīng)用來說,滿足條件都是沒有問題的,因此官方手冊里面的警告其實(shí)也沒那么可怕

辦法6:設(shè)置tcp_max_tw_buckets為一個(gè)較小的值,要比可用端口范圍小,比如可用端口范圍為6萬,這個(gè)值可以設(shè)置為5.5萬

tcp_max_tw_buckets - INTEGER

官方文檔解釋如下

Maximal number of timewait sockets held by system simultaneously. If this number is exceeded time-wait socket is immediately destroyed and warning is printed.

翻譯一下:內(nèi)核持有的狀態(tài)為TIME_WAIT的最大連接數(shù)。如果超過這個(gè)數(shù)字,新的TIME_WAIT的連接會(huì)被立即銷毀,并打印警告

官方文檔沒有說明默認(rèn)值,通過幾個(gè)系統(tǒng)的簡單驗(yàn)證,初步確定默認(rèn)值是180000

源碼如下

void tcp_time_wait(struct sock *sk, int state, int timeo)
{
    struct inet_timewait_sock *tw = NULL;
    const struct inet_connection_sock *icsk = inet_csk(sk);
    const struct tcp_sock *tp = tcp_sk(sk);
    int recycle_ok = 0;

    if (tcp_death_row.sysctl_tw_recycle && tp- >rx_opt.ts_recent_stamp)
        recycle_ok = icsk- >icsk_af_ops- >remember_stamp(sk);

    // 這里判斷TIME_WAIT狀態(tài)的連接數(shù)是否超過上限
    if (tcp_death_row.tw_count < tcp_death_row.sysctl_max_tw_buckets)
        tw = inet_twsk_alloc(sk, state);

    if (tw != NULL) 
    {
        //分配成功,進(jìn)行TIME_WAIT狀態(tài)處理,此處略去很多代碼
    }   
    else 
    {
        //分配失敗,不進(jìn)行處理,只記錄日志: TCP: time wait bucket table overflow
        /* Sorry, if we're out of memory, just CLOSE this
        * socket up.  We've got bigger problems than
        * non-graceful socket closings.
        */
        NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPTIMEWAITOVERFLOW);
    }

    tcp_update_metrics(sk);
    tcp_done(sk);
}

官方手冊中有一段警告:

This limit exists only to prevent simple DoS attacks, you must not lower the limit artificially,

but rather increase it (probably, after increasing installed memory), if network conditions require more than default value.

基本意思是這個(gè)用于防止Dos攻擊,我們不應(yīng)該人工減少,如果網(wǎng)絡(luò)條件需要的話,反而應(yīng)該增加。

但其實(shí)對于我們的局域網(wǎng)或者公司內(nèi)網(wǎng)應(yīng)用來說,這個(gè)風(fēng)險(xiǎn)并不大。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • IP
    IP
    +關(guān)注

    關(guān)注

    5

    文章

    1709

    瀏覽量

    149584
  • TCP
    TCP
    +關(guān)注

    關(guān)注

    8

    文章

    1356

    瀏覽量

    79098
  • TIME
    +關(guān)注

    關(guān)注

    0

    文章

    13

    瀏覽量

    14326
  • 流程
    +關(guān)注

    關(guān)注

    0

    文章

    4

    瀏覽量

    3609
收藏 人收藏

    評論

    相關(guān)推薦

    Java中sleep和wait的區(qū)別

    。  wait()是線程交互時(shí),如果線程對一個(gè)同步對象x 發(fā)出一個(gè)wait()調(diào)用,該線程會(huì)暫停執(zhí)行,被調(diào)對象進(jìn)入等待狀態(tài),直到被喚醒或等待時(shí)間到?! ‘?dāng)調(diào)用wait()后,線程會(huì)釋放
    發(fā)表于 11-25 11:50

    CC3200 socket狀態(tài)如何查詢?

    已,正在等待本地套接字的關(guān)閉確認(rèn) FIN_WAIT_2 套接字已關(guān)閉,正在等待遠(yuǎn)程套接字關(guān)閉TIME_WAIT 這個(gè)套接字已經(jīng)關(guān)閉,正在等待遠(yuǎn)程套接字的關(guān)閉傳送
    發(fā)表于 05-06 11:00

    WEB CLOSE_WAIT socket不釋放如何解決呢

    :web連接過后發(fā)現(xiàn)有大量的TIME_WAIT狀態(tài)的socket,怎么限制WEB的并發(fā)數(shù)量,把rtConfig.h中的這個(gè)宏改小到多少合適呢?如果太小了會(huì)不會(huì)導(dǎo)致web登錄不了的情況?#define WEBNET_CONN_MAX 8
    發(fā)表于 09-27 10:06

    什么是TIME

    什么是TIME  英文原義:Time Server Protocol 中文釋義:標(biāo)準(zhǔn)時(shí)間服務(wù)器協(xié)議 注  解:這
    發(fā)表于 02-23 11:25 ?945次閱讀

    建立時(shí)間和保持時(shí)間(setup time 和 hold time

    建立時(shí)間和保持時(shí)間貫穿了整個(gè)時(shí)序分析過程。只要涉及到同步時(shí)序電路,那么必然有上升沿、下降沿采樣,那么無法避免setup-time 和 hold-time這兩個(gè)概念。 1. 什么是setup-time
    發(fā)表于 02-08 14:48 ?6063次閱讀

    分析在java中sleep和wait的不同

    的監(jiān)控狀態(tài)依然保持者,當(dāng)指定的時(shí)間到了又會(huì)自動(dòng)恢復(fù)運(yùn)行狀態(tài)。 在調(diào)用sleep()方法的過程中,線程不會(huì)釋放對象鎖。 而當(dāng)調(diào)用wait()方法的時(shí)候,線程會(huì)放棄對象鎖,進(jìn)入等待此對象的等待鎖定池,只有針對此對象調(diào)用notify(
    發(fā)表于 09-27 14:41 ?0次下載

    服務(wù)器產(chǎn)生大量的TIME_WAIT究竟是因?yàn)槭裁?/a>

    寫在開頭,大概 4 年前,聽到運(yùn)維同學(xué)提到 TIME_WAIT 狀態(tài)的 TCP 連接過多的問題,但是當(dāng)時(shí)沒有去細(xì)琢磨;最近又聽人說起,是一個(gè)新手進(jìn)行壓測過程中,遇到的問題,因此,花點(diǎn)時(shí)間,細(xì)深究一下
    的頭像 發(fā)表于 10-13 16:47 ?1.4w次閱讀
    服務(wù)器產(chǎn)生大量的<b class='flag-5'>TIME_WAIT</b>究竟是因?yàn)槭裁? />    </a>
</div>                            <div   id=

    請問一下RPB是干啥用的

    注意:同一網(wǎng)段內(nèi)的所有節(jié)點(diǎn),對應(yīng)的CANNM_MSG_CYCLE_TIME、CANNM_REPEAT_MESSAGE_TIME、CANNM_WAIT_BUS_SLEEP_TIME、NM-TIM
    的頭像 發(fā)表于 08-30 10:19 ?5905次閱讀
    請問一下RPB是干啥用的

    學(xué)習(xí)Linux命令的正確姿勢

    短時(shí)間后,所有的 TIME_WAIT 全都消失,被回收,端口包括服務(wù),均正常。即,在高并發(fā)的場景下,TIME_WAIT 連接存在,屬于正?,F(xiàn)象。
    的頭像 發(fā)表于 08-31 10:27 ?499次閱讀

    WAiT:野生動(dòng)物追蹤器

    電子發(fā)燒友網(wǎng)站提供《WAiT:野生動(dòng)物追蹤器.zip》資料免費(fèi)下載
    發(fā)表于 10-31 09:38 ?3次下載
    <b class='flag-5'>WAiT</b>:野生動(dòng)物追蹤器

    圖解Java多線程中的wait()和notify()方法

    wait()和notify()是Object類的方法,用于線程的等待與喚醒,必須搭配synchronized 鎖來使用。
    的頭像 發(fā)表于 03-22 09:29 ?3064次閱讀

    KUKA機(jī)器人WAIT FOR運(yùn)用條件

    WAIT FOR … : 等至條件已滿足 WAITFOR 停止程序,直到已滿足特定的條件。然后程序繼續(xù)運(yùn)行。WAIT FOR 將觸發(fā)預(yù)進(jìn)停止。 編譯器識別不到由于錯(cuò)誤的表達(dá)而使表達(dá)式無法采用數(shù)值
    的頭像 發(fā)表于 06-13 11:31 ?3345次閱讀

    TCP和UDP分別是什么 TCP和UDP協(xié)議各有什么特點(diǎn)

    在 TCP 四次揮手的最后一步,客戶端進(jìn)入 TIME_WAIT 狀態(tài),需要等待一段時(shí)間再進(jìn)入 CLOSED 狀態(tài)。等待時(shí)間通常是兩個(gè)最大報(bào)文段生命周期,即 2MSL,這是為了確保服務(wù)器端能夠收到客戶端發(fā)送的最后一個(gè) ACK 報(bào)文
    的頭像 發(fā)表于 08-09 12:34 ?4431次閱讀

    TIME_WAIT狀態(tài)的優(yōu)化思路

    為什么需要TIME_WAIT狀態(tài)?為什么TIME_WAIT的時(shí)長是2*MSL? 原因1:防止連接關(guān)閉時(shí)四次揮手中的最后一次ACK丟失: TCP需要保證每一包數(shù)據(jù)都可靠的到達(dá)對端,包括正常連接
    的頭像 發(fā)表于 11-10 10:44 ?576次閱讀

    TIME_WAIT是什么

    ,遇到了這道題目: 生產(chǎn)環(huán)境 Nginx 后端服務(wù)大量 TIME-WAIT 的解決步驟 這里小編給大家做一下系統(tǒng)化、體系化的梳理,使得大家可以充分展示一下大家雄厚的 “技術(shù)肌肉”,讓面試官愛到 “不能自已、口水直流”。 基礎(chǔ)知識 TIME_WAIT是什么? 在構(gòu)建TCP客
    的頭像 發(fā)表于 11-10 14:48 ?666次閱讀
    <b class='flag-5'>TIME_WAIT</b>是什么