TCP異常關閉研究分析

版權聲明:本文由謝代斌原創文章,轉載請註明出處: 
文章原文連接:https://www.qcloud.com/community/article/108服務器

來源:騰雲閣 https://www.qcloud.com/community網絡

研究測試TCP斷開和異常的各類狀況,以便於分析網絡應用(好比tconnd)斷網的緣由和場景,幫組分析和定位鏈接異常掉線的問題,並提供給TCP相關的開發測試人員做爲參考。測試

各個遊戲接入都存在必定的掉線問題,並且有的遊戲項目的掉線比例還比較高,如今互娛自研遊戲的網絡接入基本上都用的是tconnd和ProtocalHandler組件(該組件請參考附件的《TSF4G_ProtocalHandler開發指導手冊》),所以參與其掉線緣由分析和研究。spa

在參與A項目的掉線問題研究分析過程當中,tconnd增長了玩家每一個鏈接的流水日誌和ProtocalHandler增長了每一個鏈接的Qos上報日誌,經過這些日誌記錄了每一次鏈接的斷開緣由和相關統計數據,其中包括了鏈接異常斷開時TCP的底層錯誤碼。操作系統

經過對tconnd的流水日誌和ProtocalHandler的Qos日誌進行統計分析,發現鏈接異常斷開時TCP的錯誤碼大部分是「104: Connection reset by peer」(Linux下)或「10054: An existing connection was forcibly closed by the remote host」(Windows下),單純從錯誤碼原本來講,你們都明白是「網絡被對端重置了」,但究竟什麼狀況下會致使這種狀況呢?所以就對TCP的各類關閉狀況作了進一步的測試研究。日誌

一. TCP異常關閉的研究測試

1. 服務器端只Recv消息而不Send消息

1.1 測試方法

服務器程序在接受客戶端的TCP鏈接後Sleep幾秒鐘,客戶端程序在TCP鏈接後當即發送不少消息給對端後作相應動做(退出或等待),服務器程序Sleep完後開始Recv消息。code

注意:服務器程序測試了Linux和Windows版本,但客戶端只測試了Windows版本,若是是Linux客戶端則有些Case的結果會不同。遊戲

1.2 測試Case

  • 客戶端程序正常運行的狀況下,拔掉網線,殺掉客戶端程序
    目的:模擬客戶端死機、系統忽然重啓、網線鬆動或網絡不通等狀況。
    結論:這種狀況下服務器程序沒有檢測到任何異常,並最後等待「超時」才斷開TCP鏈接。進程

  • 客戶端程序發送不少數據包後正常關閉Socket並exit進程(或不退出進程)
    目的:模擬客戶端發送完消息後正常退出的狀況。
    結論:這種狀況下服務器程序可以成功接收完全部消息,並最後收到「對端關閉」(Recv返回零)消息。事件

  • 客戶端程序發送不少數據包後不關閉Socket直接exit進程
    目的:模擬客戶端程序退出而忘記關閉Socket的狀況(好比經過Windows窗口的關閉圖標退出進程,而沒有捕獲相應關閉事件作正常退出處理等)。
    結論:這種狀況下服務器程序可以收到部分TCP消息,而後收到「104: Connection reset by peer」(Linux下)或「10054: An existing connection was forcibly closed by the remote host」(Windows下)錯誤。

  • 客戶端程序發送不少數據包的過程當中直接Kill進程
    目的:模擬客戶端程序崩潰或非正常方式結束進程(好比Linux下"kill -9"或Windows的任務管理器殺死進程)的狀況。
    結論:這種狀況下服務器程序很快收到「104: Connection reset by peer」(Linux下)或「10054: An existing connection was forcibly closed by the remote host」(Windows下)錯誤。

2.服務器端Recv消息並Send應答消息

2.1 測試方法

服務器程序在接受客戶端的TCP鏈接後Sleep幾秒鐘,客戶端程序在TCP鏈接後當即發送不少消息給對端後作相應動做(退出或等待),服務器程序Sleep完後開始Recv和Send消息。

注意:服務器程序測試了Linux和Windows版本,但客戶端只測試了Windows版本,若是是Linux客戶端則有些Case的結果可能會不同。

2.2 測試結果

  • 客戶端程序發送不少數據包後正常關閉Socket並exit進程(或不退出進程)
    目的:模擬客戶端正常關閉Socket後,服務器端在檢查到TCP對端關閉前向客戶端發送消息的狀況。
    結論:這種狀況下服務器程序接收和發送部分TCP消息後,在Send消息時產生「32: Broken pipe」(Linux下)或「10053: An established connection was aborted by the software in your host machine」(Windows下)錯誤。

  • 客戶端程序發送不少數據包後不關閉Socket直接exit或Kill進程
    目的:模擬客戶端程序退出而忘記關閉Socket、或客戶端程序崩潰或非正常方式結束進程的狀況。
    結論:這種狀況下服務器程序在Recv或Send消息時產生「104: Connection reset by peer」(Linux下)或「10054: An existing connection was forcibly closed by the remote host」(Windows下)錯誤。

3. 效果和總結

3.1 總結

TCP發現網絡異常(特別是Linux下的104錯誤或Windows下10054錯誤)的狀況不少,好比網絡自己的問題、中間路由器問題、網卡驅動器問題等不可抗拒因素,但下面是應用程序自己可能會致使的問題,也是咱們須要進一步研究和解決的狀況,特別是程序崩潰致使問題:

  • 當TCP鏈接的進程在忘記關閉Socket而退出、程序崩潰、或非正常方式結束進程的狀況下
    (Windows客戶端),會致使TCP鏈接的對端進程產生「104: Connection reset by peer」(Linux下)或「10054: An existing connection was forcibly closed by the remote host」(Windows下)錯誤。

  • 當TCP鏈接的進程機器發生死機、系統忽然重啓、網線鬆動或網絡不通等狀況下
    -(Windows客戶端),鏈接的對端進程可能檢測不到任何異常,並最後等待「超時」才斷開TCP鏈接。

  • 當TCP鏈接的進程正常關閉Socket時,對端進程在檢查到TCP關閉事件以前仍然向TCP發送消息
    (Windows客戶端),則在Send消息時會產生「32: Broken pipe」(Linux下)或「10053: An established connection was aborted by the software in your host machine」(Windows下)錯誤。

3.2 效果

針對A項目的掉線問題,經過問卷調查和聯繫個別玩家等方法,發現掉線的狀況很大部分是客戶端程序直接退出了,所以推進項目組實現了客戶端的Qos上報功能,最後經過客戶端的Qos上報的統計數據得出客戶端程序的崩潰比例比較高,佔了總掉線的很大比率,固然其它狀況也存在,但比例相對比較小。

所以,A項目首先應該解決客戶端程序的崩潰問題,若是該問題解決了,也就解決大部分掉線問題。

二. TCP異常關閉的進一步研究測試

1. 背景

B項目遊戲在跨服跳轉時的掉線比例比較高,通過分析ProtocalHandler和tconnd的日誌,發現掉線出現的狀況是:tconnd發送了跨服跳轉消息後當即關閉了Socket,客戶端進程在接收到跨服跳轉消息以前發送消息後收到Windows 10054錯誤,而後作斷線重連失敗。

B項目實現跨服跳轉的流程是GameSvr給客戶端程序下發的跨服跳轉命令的同時攜帶了Stop請求,也就是說tconnd在向客戶端轉發跨服跳轉消息後當即就會關閉當前的Socket鏈接,並且B項目的客戶端程序會按期不斷地向服務器上報消息。這又怎麼會致使客戶端程序收到10054錯誤而呢?鑑於此,對TCP的鏈接作進一步的場景測試分析。

2. TCP異常進一步測試研究

2.1 測試方法

客戶端和服務器端程序創建TCP鏈接,服務器程序在TCP緩衝區中有消息或沒有消息的狀況下關閉Socket,客戶端在對端Socket已經關閉的狀況下繼續Send和Recv消息。

注意:服務器端只測試了Linux版本,但客戶端測試了Windows和Linux兩個版本。

2.2 測試結果

  • 服務器端已經close了Socket,客戶端再發送數據
    目的:測試在TCP對端進程已經關閉Socket時,本端進程還未檢測到鏈接關閉的狀況下繼續向對端發送消息。
    結論:第一包能夠發送成功,但第二包發送失敗,錯誤碼爲「10053: An established connection was aborted by the software in your host machine」(Windows下)或「32: Broken pipe,同時收到SIGPIPE信號」(Linux下)錯誤。

  • 服務器端發送數據到TCP後close了Socket,客戶端再發送一包數據,而後接收消息
    目的:測試在TCP對端進程發送數據後關閉Socket,本端進程還未檢測到鏈接關閉的狀況下發送一包消息,接着接收消息。
    結論:客戶端可以成功發送第一包數據(這會致使服務器端發送一個RST包 <已抓包驗證>),客戶端再去Recv時,對於Windows和Linux程序有以下不一樣的表現:
    Windows客戶端程序:Recv失敗,錯誤碼爲「10053: An established connection was aborted by the software in your host machine」。
    Linux客戶端程序:能正常接收完全部消息包,最後收到正常的對端關閉消息(這一點與Window下不同,RST包沒有被提早接收到)。

  • 服務器端在TCP的接收緩衝區中還有未接收數據的狀況下close了Socket,客戶端再收包
    目的:測試在TCP的接收緩衝區中還有未接收數據的狀況下關閉Socket時,對端進程是否正常。
    結論:這種狀況服務器端就會向對端發送RST包,而不是正常的FIN包(已經抓包證實),這就會致使客戶端提早(RST包比正常數據包先被收到)收到「10054: An existing connection was forcibly closed by the remote host」(Windows下)或「104: Connection reset by peer」(Linux下)錯誤。

3 效果和總結

3.1 總結

TCP應用程序某些看是正常的行爲下也可能會致使對端接收到異常,好比當TCP接收緩衝區中還有未收數據的狀況下關閉Socket,則會致使對端接收到異常關閉而不是正常關閉;反過來講,當TCP檢測到異常關閉時並不必定表示業務上出問題了,由於極可能就是業務正常結束了。下面是本次測試的主要結論:

  • 當TCP鏈接的對端進程已經關閉了Socket的狀況下,本端進程再發送數據時,第一包能夠發送成功(但會致使對端發送一個RST包過來):以後若是再繼續發送數據會失敗,錯誤碼爲「10053: An established connection was aborted by the software in your host machine」(Windows下)或「32: Broken pipe,同時收到SIGPIPE信號」(Linux下)錯誤;以後若是接收數據,則Windows下會報10053的錯誤,而Linux下則收到正常關閉消息。

  • TCP鏈接的本端接收緩衝區中還有未接收數據的狀況下close了Socket,則本端TCP會向對端發送RST包,而不是正常的FIN包,這就會致使對端進程提早(RST包比正常數據包先被收到)收到「10054: An existing connection was forcibly closed by the remote host」(Windows下)或「104: Connection reset by peer」(Linux下)錯誤。

3.2 效果

B項目跨服跳轉的掉線問題有至關一部分的種狀況是tconnd向客戶端轉發跨服跳轉消息後當即關閉Socket鏈接,而此時恰好客戶端向tconnd發送了數據包:

  • 第一種狀況:tconnd在關閉Socket的時刻其TCP的接收緩衝區中有未收的消息,這就使得tconnd進程的TCP向客戶端發送的是RST包而不是正常結束的FIN包,因此客戶端程序就會提早收到RST包(RST包會比正常數據提早收到),而不會先收完跨服跳轉消息後再接收到正常結束消息,這就致使客戶端收到網絡異常斷線而作重連,但以前的鏈接是tconnd主動關閉的,因此不可能重連成功,從而致使掉線。

  • 第二種狀況:tconnd已經關閉了Socket後,客戶端在接收到跳轉消息和檢測到TCP關閉以前向tconnd發送了消息,這就會致使客戶端程序收到異常斷線而作重連並失敗。

最後,與B項目項目組一塊兒討論,改進了大部分跨服跳轉的業務流程後,掉線比例j減小了不少,固然仍是存在必定比例的掉線,但這應該就是其它一些緣由了(網絡異常問題緣由不少,國內當前的網絡環境下,掉線問題是不可能徹底避免的)。

三.結束語

一般狀況下,向TCP的Socket發送完數據後關閉Socket,你們認爲這樣很正常的方式確定沒有問題,對端應該正確收完數據後收到TCP的關閉消息,但實際上在某些狀況下並不是如此:當TCP本端的接收緩衝區中有未收的數據時關閉Socket,這會致使對端收到RST的異常關閉消息;當對端在本端已經關閉Socket的狀況下再次發送消息時,也會致使對端收到異常關閉消息;還有爲了不TIME_WAIT而設置了SO_LINGER選項的話,也會致使鏈接提早夭折使對端收到RST異常關閉消息。

有些時候業務流程對是否引發掉線也很重要(特別是鏈接關閉流程),好比前面的B項目的跨服跳轉掉線問題很大部分就是由於GameSvr請求關閉鏈接致使的。建議各個遊戲項目的關閉流程(包括跨服跳轉的原鏈接的關閉)最好都由客戶端發起關閉,這樣就必定程度上避免上述問題的發生(由於客服端發起關閉的時候,通常業務流程都走完了,服務器端也不會再向客戶端發送消息了)。

程序收到網絡異常的狀況不少(最多的就是Linux下的104錯誤和Windos下的10054/10053錯誤):有網絡自己的問題、也有應用使用不當的問題;有運營商之間的跨網絡問題、網絡中間路由器問題、玩家機器硬件(好比網卡及其驅動)問題和操做系統、殺毒軟件、防火牆等軟件問題,還有玩家的上網設備和路由器等中間設備問題等,但客戶端程序崩潰問題有可能會佔掉線的很高比例,這也是值得咱們注意和改進的地方。還有種狀況值得咱們注意,有些TP-LINK的路由器,當UDP包大小超過其MTU值時會致使用戶機器的網絡斷開,從而引發掉線(這個問題在某些項目的個別玩家中已經出現過)。

網絡異常關閉引發掉線是當前遊戲中廣泛存在的問題,區別只在於掉線的比例多少,特別是國內各運營商之間跨網絡訪問更是不太順暢,要將其徹底消除是不可能的,但咱們的目標是將其控制在較小的可接受範圍內。

相關文章
相關標籤/搜索