從tcp原理角度理解Broken pipe和Connection reset by peer的區別

從tcp原理角度理解Broken pipe和Connection reset by peer的區別

  之前咱們常常會碰到Broken pipe或者Connection reset by peer之類的異常,可是tcp實現裏什麼狀況下會拋出這些異常呢,之前我給對方的回答都是模棱兩可的,本身說實話都沒把握,由於本身也沒有驗證過,對它們的認識都是從網上看來的,正確與否也不知道,昨天獨明忽然又問到這個問題,前段時間正好對tcp這塊研究了一段時間,有了點理論知識以後再從實踐角度對此問題進行一下分析,下面對我此次的調研過程進行下描述與你們分享,但願你們之後對此類問題都能很自信地應答。java

三次握手和四次揮手過程

  在講具體的緣由以前,咱們有必要補充下tcp這塊的一些基礎知識,咱們都知道tcp通訊有三次握手和四次揮手,網上介紹的文章也一大堆,圖我也懶得畫了,直接網上找一個圖給你們linux

  三次握手是最前面的三條線表示的過程,四次揮手是最後面的四條線表示的過程,裏面涉及到幾個關鍵詞,SYN,ACK,FIN,MSS,其中SYN是主要用在三次握手過程當中的,FIN用在四次揮手過程當中,ACK在三次握手和四次揮手過程當中的做用就是對收到的SYN和FIN作一個確認,SYN,FIN等存在於TCP頭裏(tcp報文圖也給你們弄了個圖,不用再去找啦),0/1表示有無此標記,在tcp實現裏後面還會跟一個依次遞增的數字,好比上面的J,K等,確認就是遞增這些數字(真正的數據報文的ack除外),MSS是表示每個tcp報文裏數據字段的最大長度,不包括tcp頭的大小噢 相信你們看到這兩個圖會對這些概念有了一個清晰的認識了服務器

tcpdump抓包工具

  介紹了基礎原理以後,再介紹下抓包工具,tcpdump,這工具對你瞭解tcp的整個過程會很是有幫助,在你沒法調試tcp實現的狀況下這個工具天然也是必不可少的,具體用法網上有不少介紹,直接從man page上也能夠看到詳細的介紹,我也很少說啦,下面的截圖就是tcpdump根據tcp通訊過程獲取到的less

  這要稍微提下tcpdump的結果和上面的幾個過程的對應關係 前面三條其實就是咱們上面所說的三次握手,四次握手過程上面沒有徹底表現出來,只完成了一半的揮手過程(5,8兩條表示的) 裏面有幾個標識S,F,ack,P,其實還有個R,若是有這些標識那麼在tcp頭裏的SYN,FIN,ACK,PSH,RET分別爲1,其中PSH表示要求tcp當即將數據傳遞給上層,不要作別的什麼處理,RET這個表示重置鏈接,也是和咱們今天討論的問題有很大關係的FLAG,下面會詳細介紹jvm

reset報文發送場景

  RST的標誌位,這個標識爲在以下幾種狀況下會被設置,如下是我瞭解的狀況,可能還有更多的場景,沒有驗證socket

  • 當嘗試和未開放的服務器端口創建tcp鏈接時,服務器tcp將會直接向客戶端發送reset報文
  • 雙方以前已經正常創建了通訊通道,也可能進行過了交互,當某一方在交互的過程當中發生了異常,如崩潰等,異常的一方會向對端發送reset報文,通知對方將鏈接關閉
  • 當收到TCP報文,可是發現該報文不是已創建的TCP鏈接列表可處理的,則其直接向對端發送reset報文
  • ack報文丟失,而且超出必定的重傳次數或時間後,會主動向對端發送reset報文釋放該TCP鏈接

Broken pipe以及Connection reset by peer

  作了這麼些鋪墊以後下面進入正題,那麼Broken pipe或者Connection reset by peer分別表明什麼意思呢,下面從glibc的源碼裏有對此的介紹tcp

#. TRANS Broken pipe; there is no process reading from the other end of a pipe. #. TRANS Every library function that returns this error code also generates a #. TRANS @code{SIGPIPE} signal; this signal terminates the program if not handled #. TRANS or blocked. Thus, your program will never actually see @code{EPIPE} #. TRANS unless it has handled or blocked @code{SIGPIPE}. #: sysdeps/generic/siglist.h:39 sysdeps/gnu/errlist.c:359 #: sysdeps/unix/siglist.c:39 msgid "Broken pipe" msgstr "斷開的管道" #. TRANS A network connection was closed for reasons outside the control of the #. TRANS local host, such as by the remote machine rebooting or an unrecoverable #. TRANS protocol violation. #: sysdeps/gnu/errlist.c:614 msgid "Connection reset by peer" msgstr ""

  其實咱們java異常裏看到的Broken pipe或者Connection reset by peer信息不是jdk或者jvm裏定義的,我看到這些關鍵字每每會首先搜索下jdk或者hotspot源碼找到位置進行上下文分析,可是此次沒找到,後面纔想到應該是linux或者glibc裏定義的,果真在glibc離看到了如上的描述和定義ide

  對於Broken pipe在管道的另一端沒有進程再讀的時候就會拋出此異常,Connection reset by peer的描述其實不是很正確,從個人實踐來看只描述了一方面,其實在某一端正常close以後,也是可能會有此異常的。工具

異常模擬

  從個人測試場景是這樣的, 共同的前提是客戶端向服務端發了數據以後立馬調用close關閉socket並進程退出,而服務端在收到客戶端的數據以後sleep一會,保證對方的socket已經關閉,接着分別進行兩種場景測試測試

  場景:

  1. 服務端往socket裏寫一次數據,返回繼續作select

  2. 服務端連續寫兩次數據,必須保證兩次的buffer都是有數據的,也就是保證ByteBuffer的pos和limit要不是一個值

  結果:

  1. 會拋出Connection reset by peer 

  2. 會拋出Broken pipe

  分析:

  1. 當咱們往一個對端已經close的通道寫數據的時候,對方的tcp會收到這個報文,而且反饋一個reset報文,tcpdump的結果以下所示,當收到reset報文的時候,繼續作select讀數據的時候就會拋出Connect reset by peer的異常,從堆棧能夠看得出 

  2. 當第一次往一個對端已經close的通道寫數據的時候會和上面的狀況同樣,會收到reset報文,當再次往這個socket寫數據的時候,就會拋出Broken pipe了 ,根據tcp的約定,當收到reset包的時候,上層必需要作出處理,調用將socket文件描述符進行關閉,其實也意味着pipe會關閉,所以會拋出這個顧名思義的異常

相關文章
相關標籤/搜索