關(guān)于關(guān)閉 Socket 的一些坑

<div style="text-align: right">LexusLee</div>

背景

最近踩到一個(gè) "Socket 連接持續(xù)處于 Fin_Wait2 和 Close_Wait 狀態(tài)無(wú)法關(guān)閉" 的坑中吸占。起因是在維護(hù)大量連接時(shí)調(diào)用 socket.close() 時(shí)所刀,看到部分連接并沒(méi)有正常關(guān)閉撰洗,而是從 ESTABLISHED 的狀態(tài)變成 FIN_WAIT2 并且連接狀態(tài)沒(méi)有后續(xù)遷移,而對(duì)端的連接狀態(tài)則是從 ESTABLISHED 變成了 CLOSE_WAIT 措拇。

后來(lái)發(fā)現(xiàn)這和 TCP/IP 棧的4次揮手?jǐn)嚅_(kāi)連接有關(guān)我纪,列出一些踩坑時(shí)的收獲。

Socket 連接關(guān)閉的流程

先看一張 Socket 關(guān)閉連接的狀態(tài)遷移路徑圖:

Socket close state

在 Client 端調(diào)用 socket.close() 時(shí)丐吓,首先會(huì)往對(duì)端(即 Server 端)發(fā)送一個(gè) FIN 包浅悉,接著將自身的狀態(tài)置為 FIN_WAIT1 ,此時(shí)主動(dòng)關(guān)閉端(即 Client 端)處于持續(xù)等待接收對(duì)端的響應(yīng) FIN 包的 ACK 回應(yīng)狀態(tài)券犁,此時(shí)對(duì)端的狀態(tài)是處于 ESTABLISHED 术健,一旦收到了 Client 發(fā)來(lái)的 close 連接請(qǐng)求,就回應(yīng)一個(gè) FIN 包粘衬,表示收到該請(qǐng)求了荞估,并將自身狀態(tài)置為 CLOSE_WAIT,這時(shí)開(kāi)始等待 Server 端的應(yīng)用層向 Client 端發(fā)起 close 請(qǐng)求稚新。

這時(shí) Client 端一旦收到 Server 端對(duì)第一個(gè) FIN 包的回應(yīng) ACK 就會(huì)將進(jìn)入下一個(gè)狀態(tài) FIN_WAIT_2 來(lái)等待 Server 發(fā)起斷開(kāi)連接的 FIN 包勘伺。在FIN_WAIT_1 的 time_wait 中, Server 端會(huì)發(fā)起 close 請(qǐng)求褂删,向 Client 端發(fā)送 FIN 包飞醉,并將自身狀態(tài)從 CLOSE_WAIT 置為 LAST_ACK ,表示 Server 端的連接資源開(kāi)始釋放了屯阀。同時(shí) Client 端正處于 FIN_WAIT2 狀態(tài)缅帘,一旦接收到 Server 端的 FIN 包轴术,則說(shuō)明 Server 端連接已釋放,接著就可以釋放自身的連接了钦无,于是進(jìn)入 TIME_WAIT 狀態(tài)逗栽,開(kāi)始釋放資源,在經(jīng)過(guò)設(shè)置的 2個(gè) MSL 時(shí)間后失暂,狀態(tài)最終遷移到 CLOSE 說(shuō)明連接成功關(guān)閉彼宠,一次 TCP 4次揮手 關(guān)閉連接的過(guò)程結(jié)束。

通常會(huì)出現(xiàn)狀態(tài)滯留的情況有下面幾種:

  • Client 處于 FIN_WAIT1 , Server 處于 ESTABLISHED => 這種情況通常是連接異常弟塞,socket.close() 發(fā)送的 FIN 包對(duì)端無(wú)法收到兵志。由于 TCP FIN_WAIT 自身有 Timeout, 在 Timeout 后如果還沒(méi)有收到響應(yīng),則會(huì)停止等待宣肚。這種情況在 DDoS 攻擊中比較常見(jiàn),Server 端在某一時(shí)刻需要處理大量 FIN_WAIT1 時(shí)就會(huì)卡死悠栓。解決方法是修改 /etc/sysctl.confnet.ipv4.tcp_fin_timeout 來(lái)提高 Timeout 值霉涨,保證大量連接能正常在超時(shí)時(shí)間內(nèi)收到響應(yīng),當(dāng)然這對(duì)服務(wù)器負(fù)載有要求惭适。而如果是異常 ip 在某時(shí)間段內(nèi)發(fā)送大量流量的 DDoS 攻擊笙瑟,則可以在 iptable 上手動(dòng)封 ip 或者開(kāi)啟防火墻。
  • Client 處于 FIN_WAIT2, Server 處于 CLOSE_WAIT => 這種情況通常是 Server 端還在使用連接進(jìn)行讀寫(xiě)或資源還未釋放完癞志,所以還沒(méi)主動(dòng)往對(duì)端發(fā)送 FIN 包進(jìn)入 LAST_ACK 狀態(tài)往枷,連接一直處于掛起的狀態(tài)。這種情況需要去檢查是否有資源未釋放或者代碼阻塞的問(wèn)題凄杯。通常來(lái)說(shuō) CLOSE_WAIT 的持續(xù)時(shí)間應(yīng)該較短错洁,如果出現(xiàn)長(zhǎng)時(shí)間的掛起,那么應(yīng)該是代碼出了問(wèn)題戒突。
  • Client 出于 TIME_WAIT, Server 處于 LAST_ACK => 首先 TIME_WAIT 需要等待 2個(gè) MSL (Max Segment Lifetime) 時(shí)間屯碴,這個(gè)時(shí)間是確保 TCP 段能夠被接收到的最大壽命。默認(rèn)是 60 s 膊存。解決方案是: 1. 調(diào)整內(nèi)核參數(shù) /etc/sysctl.conf 中的 net.ipv4.tcp_tw_recycle = 1 確保 TIME_WAIT 狀態(tài)的連接能夠快速回收导而,或者縮短 MSL 時(shí)間。 2. 檢查是否有些連接可以使用 keepalive 狀態(tài)來(lái)減少連接數(shù)隔崎。

此外今艺,如果在單臺(tái)服務(wù)器上并且不做負(fù)載均衡而處理大量連接的話,可以在 /proc/sys/net/ipv4/ip_local_port_range 中減少端口的極限值爵卒,限制每個(gè)時(shí)間段的最大端口使用數(shù)虚缎,從而保證服務(wù)器的穩(wěn)定性,一旦出現(xiàn)大量的 TIME_WAIT 阻塞后續(xù)連接技潘,是比較致命的遥巴。

Socket.terminate() 和 Socket.close()

此外還遇到了另一個(gè)小問(wèn)題千康,在關(guān)閉連接時(shí),一開(kāi)始用的是 socket.terminate() 铲掐,然而 netstat 時(shí)卻發(fā)現(xiàn)大量連接沒(méi)有釋放拾弃,后來(lái)發(fā)現(xiàn) Python Socket 的 terminate() 只是發(fā)送 socket.SHUT_WRsocket.SHUT_RD 來(lái)關(guān)閉通道的讀寫(xiě)權(quán)限而并沒(méi)有釋放連接句柄。導(dǎo)致了連接已經(jīng)無(wú)法使用摆霉,但仍然處于 ESTABLISHED 狀態(tài)豪椿。

解決方法就是使用 socket.close() 來(lái)替換 socket.terminate()

后來(lái)又看到如果是 DDoS 攻擊的話,可能會(huì)阻塞住 socket.close() 携栋,導(dǎo)致后續(xù)連接未關(guān)閉搭盾,大量流量進(jìn)入服務(wù)器。

所以比較好的方式是在 socket.close() 之前先調(diào)用 socket.terminate() 關(guān)閉通道的讀寫(xiě)權(quán)限婉支,再調(diào)用 socket.close()

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鸯隅,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子向挖,更是在濱河造成了極大的恐慌蝌以,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件何之,死亡現(xiàn)場(chǎng)離奇詭異跟畅,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)溶推,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門徊件,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蒜危,你說(shuō)我怎么就攤上這事虱痕。” “怎么了舰褪?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵皆疹,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我占拍,道長(zhǎng)略就,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任晃酒,我火速辦了婚禮表牢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘贝次。我一直安慰自己崔兴,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著敲茄,像睡著了一般位谋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上堰燎,一...
    開(kāi)封第一講書(shū)人閱讀 51,578評(píng)論 1 305
  • 那天掏父,我揣著相機(jī)與錄音,去河邊找鬼秆剪。 笑死赊淑,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的仅讽。 我是一名探鬼主播陶缺,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼洁灵!你這毒婦竟也來(lái)了饱岸?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤徽千,失蹤者是張志新(化名)和其女友劉穎伶贰,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體罐栈,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年泥畅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了荠诬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡位仁,死狀恐怖柑贞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情聂抢,我是刑警寧澤钧嘶,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站琳疏,受9級(jí)特大地震影響有决,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜空盼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一书幕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧揽趾,春花似錦台汇、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)痒芝。三九已至,卻和暖如春牵素,著一層夾襖步出監(jiān)牢的瞬間严衬,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工两波, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瞳步,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓腰奋,卻偏偏與公主長(zhǎng)得像单起,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子劣坊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容