broken pipe錯(cuò)誤解釋

轉(zhuǎn)載自:https://www.cnblogs.com/metoy/p/6565486.html

想必或多或少在Java的服務(wù)器都會(huì)遇到過(guò)這種異常浴捆,如下圖

由于Java偏上層,日常開(kāi)發(fā)接觸系統(tǒng)底層的機(jī)會(huì)偏少面殖,要搞清楚什么原因?qū)е碌倪@種異常,肯定是先要百度google一番苗踪。

網(wǎng)絡(luò)解釋云里霧里

百度+google下颠猴,巴拉巴拉還真不少介紹這個(gè)錯(cuò)誤的文章洛巢。欣喜地翻了一篇又一篇,但好像我依舊不明白具體什么原因?qū)е碌南寐耍评镬F里啊援岩。好吧,舉兩個(gè)例子:

例子一:

這上邊說(shuō)的好像有點(diǎn)道理掏导,寫個(gè)代碼做個(gè)試驗(yàn)驗(yàn)證下吧享怀!直接上代碼:

//client程序publicstaticvoidmain(String[] args){try{? ? ? ? ? ? Socket s =newSocket();? ? ? ? ? ? s.connect(newInetSocketAddress("127.0.0.1",3113));? ? ? ? ? ? OutputStream os = s.getOutputStream();? ? ? ? ? ? os.write("hello".getBytes());? ? ? ? ? ? s.close();? ? ? ? ? ? System.in.read();//防止程序退出}catch(Exception e){? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? }//server程序publicstaticvoidmain(String[] args){try{? ? ? ? ? ? ServerSocket ss =newServerSocket(3113);? ? ? ? ? ? Socket s = ss.accept();? ? ? ? ? ? InputStreamis= s.getInputStream();byte[] buf =newbyte[1024];intlen =is.read(buf);? ? ? ? ? ? System.out.println("recv:"+newString(buf,0,len));? ? ? ? ? ? Thread.sleep(10000);? ? ? ? ? ? s.getOutputStream().write("hello".getBytes());? ? ? ? ? ? System.out.println("send over");? ? ? ? ? ? System.in.read();? ? ? ? }catch(Exception e){? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? }

代碼邏輯比較簡(jiǎn)單吧,client向server發(fā)送請(qǐng)求趟咆,然后調(diào)用close()關(guān)閉連接添瓷,服務(wù)端收到請(qǐng)求打印到控制臺(tái),等待10秒(保證client關(guān)閉了連接)值纱,然后繼續(xù)向client發(fā)數(shù)據(jù)鳞贷。看一下控制臺(tái)的結(jié)果:

挺討厭虐唠,就是不報(bào)Broken pipe異常搀愧。上邊的文章,想說(shuō)相信你真的好難敖ァ妈橄!那再看另一篇文章吧

例二:

這篇文章倒列舉了好幾種原因,點(diǎn)擊了stop按鈕翁脆?被tomcat停掉眷蚓?線程機(jī)制產(chǎn)生jvm出錯(cuò)?真不知他媽的在說(shuō)什么反番,難道就不能再具體點(diǎn)嗎沙热?

這樣的文章看不上幾篇就煩了。

意外發(fā)現(xiàn)

網(wǎng)上找不到滿意的解釋罢缸,那就硬著頭皮翻翻講解底層一點(diǎn)的書籍吧篙贸。還真巧,在一本叫《UNIX網(wǎng)絡(luò)編程卷1》中獲得了一點(diǎn)靈感枫疆。如下截圖:

如下劃線部分所說(shuō):向某個(gè)已收到RST的連接執(zhí)行寫操作時(shí)爵川,將會(huì)返回EPIPE錯(cuò)誤。EPIPE息楔!PIPE寝贡!第一百零一靈感告訴我這與Broken pipe錯(cuò)誤有關(guān)系扒披。好了,有了新的發(fā)現(xiàn)就程序驗(yàn)證吧圃泡。

為了順利實(shí)驗(yàn)碟案,先把實(shí)驗(yàn)用到的兩個(gè)知識(shí)點(diǎn)說(shuō)一下吧。

知識(shí)準(zhǔn)備之RST報(bào)文

終止一個(gè)TCP連接的正常方式是發(fā)送FIN颇蜡。在發(fā)送緩沖區(qū)中所有排隊(duì)數(shù)據(jù)都已發(fā)送之后才發(fā)送FIN价说,正常情況下沒(méi)有任何數(shù)據(jù)丟失。但我們有時(shí)也可能發(fā)送一個(gè)RST報(bào)文段而不是FIN來(lái)中途關(guān)閉一個(gè)連接风秤。這稱為異常關(guān)閉鳖目。

現(xiàn)在知道RST報(bào)文的作用了,那就在大致列一下出現(xiàn)RST報(bào)文的場(chǎng)景吧:

1.connect一個(gè)不存在的端口缤弦;

2.向一個(gè)已經(jīng)關(guān)掉的連接send數(shù)據(jù)领迈;

3.向一個(gè)已經(jīng)崩潰的對(duì)端發(fā)送數(shù)據(jù)(連接之前已經(jīng)被建立);

4.close(sockfd)時(shí)甸鸟,直接丟棄接收緩沖區(qū)未讀取的數(shù)據(jù)惦费,并給對(duì)方發(fā)一個(gè)RST。這個(gè)是由SO_LINGER選項(xiàng)來(lái)控制的抢韭;

5.a重啟薪贫,收到b的保活探針刻恭,a發(fā)rst瞧省,通知b。

模擬出現(xiàn)RST報(bào)文的場(chǎng)景鳍贾,最簡(jiǎn)單地方法感覺(jué)就是使用SO_LINGER選項(xiàng)來(lái)控制鞍匾,那接下來(lái)再了解下SO_LINGER選項(xiàng)吧!

知識(shí)準(zhǔn)備之SO_LINGER參數(shù)

? ?SO_LINGER是用來(lái)設(shè)置函數(shù)close()關(guān)閉TCP連接時(shí)的行為骑科。缺省close()的行為是橡淑,如果有數(shù)據(jù)殘留在socket發(fā)送緩沖區(qū)中則系統(tǒng)將繼續(xù)發(fā)送這些數(shù)據(jù)給對(duì)方,等待被確認(rèn)咆爽,然后返回梁棠。

設(shè)置此選項(xiàng)并把超時(shí)時(shí)間設(shè)置為零,調(diào)用close()會(huì)立即關(guān)閉該連接斗埂,通過(guò)發(fā)送RST分組(而不是用正常的FIN|ACK|FIN|ACK四個(gè)分組)來(lái)關(guān)閉該連接符糊。至于發(fā)送緩沖區(qū)中如果有未發(fā)送完的數(shù)據(jù),則丟棄呛凶。

知識(shí)準(zhǔn)備的差不多了男娄,好了,準(zhǔn)備開(kāi)森的實(shí)驗(yàn)了。

實(shí)驗(yàn)驗(yàn)證

? ?這里再將實(shí)驗(yàn)代碼貼一份吧模闲,跟上邊的實(shí)驗(yàn)代碼唯一的區(qū)別就是這里設(shè)置了SO_LINGER選項(xiàng)建瘫。

//client程序publicstaticvoidmain(String[] args){try{? ? ? ? ? ? Socket s =newSocket();? ? ? ? ? ? s.setSoLinger(true,0);//設(shè)置調(diào)用close就發(fā)送RSTs.connect(newInetSocketAddress("127.0.0.1",3113));? ? ? ? ? ? OutputStream os = s.getOutputStream();? ? ? ? ? ? os.write("hello".getBytes());? ? ? ? ? ? s.close();? ? ? ? ? ? System.in.read();//防止程序退出}catch(Exception e){? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? }//server程序publicstaticvoidmain(String[] args){try{? ? ? ? ? ? ServerSocket ss =newServerSocket(3113);? ? ? ? ? ? Socket s = ss.accept();? ? ? ? ? ? InputStreamis= s.getInputStream();byte[] buf =newbyte[1024];intlen =is.read(buf);? ? ? ? ? ? System.out.println("recv:"+newString(buf,0,len));? ? ? ? ? ? Thread.sleep(10000);? ? ? ? ? ? s.getOutputStream().write("hello".getBytes());? ? ? ? ? ? System.out.println("send over");? ? ? ? ? ? System.in.read();? ? ? ? }catch(Exception e){? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? }

這次果不其然,終于遇到了期盼的異常围橡。不信暖混?那我截圖你看:

這下你信了吧缕贡。這時(shí)你是不是也有點(diǎn)好奇翁授,真的是設(shè)置了SO_LINGER產(chǎn)生了RST報(bào)文嗎?client和server之間到底進(jìn)行了怎么樣的交互呢晾咪?

想看清具體client和server期間進(jìn)行了怎樣的交互收擦,那就只好抓包了。就用tcpdump抓包看吧谍倦,不管你會(huì)不會(huì)用塞赂,它都是簡(jiǎn)單方便快捷的好工具,絕對(duì)是分析TCP的好幫手昼蛀。

抓包分析

就按照上邊的實(shí)驗(yàn)程序抓個(gè)包吧宴猾,又大又清晰地截圖^_^

簡(jiǎn)單解釋下:localhost.50387是client端,localhost.cs-auth-svr是server端叼旋。

第一行:client向server發(fā)送SYN請(qǐng)求建立連接

第二行:server向client發(fā)送SYN也請(qǐng)求建立連接

第三行:client向server返回ACK表示同意連接

第四行:server向client發(fā)送ack?什么仇哆?TCP三步握手建立連接怎么變成四步了?啥時(shí)候的事啊咋沒(méi)通知我胺蛑病讹剔?難道我的mac不在狀態(tài)手滑了就發(fā)出去了?算了先不care這個(gè)問(wèn)題了详民,知道的可以告訴下我延欠。

第五行:看到Flags [P.]了嗎,P是push的意思就是發(fā)數(shù)據(jù)沈跨,這里就是client向server發(fā)送數(shù)據(jù)由捎,length 5就是client發(fā)送的hello的長(zhǎng)度,沒(méi)錯(cuò)吧

第六行:這里是server向client發(fā)送ac表示已經(jīng)接收了hello

第七行:這是重點(diǎn)饿凛,F(xiàn)lags[R.],R就代表RST報(bào)文狞玛,client向server發(fā)送了RST報(bào)文。

現(xiàn)在應(yīng)該一切云開(kāi)月明了吧笤喳。^_^

收到RST包为居,繼續(xù)向?qū)Ψ綄憯?shù)據(jù)就一定會(huì)報(bào)Broken pipe嗎?還真的被我試出個(gè)不會(huì)的情況杀狡。

特殊情況

? ?這個(gè)特殊情況也很好理解蒙畴,按照上邊說(shuō)的:向一個(gè)已經(jīng)關(guān)掉的連接send數(shù)據(jù)時(shí)會(huì)收到對(duì)方的RST報(bào)文。此時(shí)再向其sends數(shù)據(jù)就不會(huì)報(bào)Broken pipe。直接上測(cè)試程序和抓包吧

//client程序publicstaticvoidmain(String[] args){try{? ? ? ? ? ? Socket s =newSocket();? ? ? ? ? ? s.connect(newInetSocketAddress("127.0.0.1",3113));? ? ? ? ? ? OutputStream os = s.getOutputStream();? ? ? ? ? ? os.write("hello".getBytes());? ? ? ? ? ? s.close();? ? ? ? ? ? System.in.read();//防止程序退出}catch(Exception e){? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? }//server程序publicstaticvoidmain(String[] args){try{? ? ? ? ? ? ServerSocket ss =newServerSocket(3113);? ? ? ? ? ? Socket s = ss.accept();? ? ? ? ? ? InputStreamis= s.getInputStream();byte[] buf =newbyte[1024];intlen =is.read(buf);? ? ? ? ? ? System.out.println("recv:"+newString(buf,0,len));? ? ? ? ? ? Thread.sleep(10000);? ? ? ? ? ? s.getOutputStream().write("hello".getBytes());? ? ? ? ? ? s.getOutputStream().write("hello2".getBytes());? ? ? ? ? ? System.out.println("send over");? ? ? ? ? ? System.in.read();? ? ? ? }catch(Exception e){? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? }

client調(diào)用close向server發(fā)送FIN膳凝,server向client發(fā)送hello碑隆,然后收到client的RST報(bào)文,繼續(xù)向client發(fā)送hello2蹬音。

? ?上邊流程可以看到上煤,client向server發(fā)送了RST報(bào)文,但是服務(wù)器繼續(xù)寫也不會(huì)報(bào)錯(cuò)著淆,畢竟誰(shuí)讓client之前就向server發(fā)送了FIN表示正常關(guān)閉呢劫狠。

尾言

? ? 分析到這里,Broken pipe錯(cuò)誤的原因應(yīng)該很清楚了吧永部。但是還需要強(qiáng)調(diào)独泞,上邊的實(shí)驗(yàn)分析過(guò)程是在UNIX(MAC)下完成的,這個(gè)實(shí)驗(yàn)對(duì)windows不成立苔埋,咱們Java都是跑在linux上可以先不care懦砂。Linux應(yīng)該跟UNIX差不多,當(dāng)然這里我沒(méi)有測(cè)驗(yàn)组橄,測(cè)出差異來(lái)的可以分享下荞膘。就這樣吧

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市玉工,隨后出現(xiàn)的幾起案子羽资,更是在濱河造成了極大的恐慌,老刑警劉巖瓮栗,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件削罩,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡费奸,警方通過(guò)查閱死者的電腦和手機(jī)弥激,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)愿阐,“玉大人微服,你說(shuō)我怎么就攤上這事∮Ю” “怎么了以蕴?”我有些...
    開(kāi)封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)辛孵。 經(jīng)常有香客問(wèn)我丛肮,道長(zhǎng),這世上最難降的妖魔是什么魄缚? 我笑而不...
    開(kāi)封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任宝与,我火速辦了婚禮焚廊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘习劫。我一直安慰自己咆瘟,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布诽里。 她就那樣靜靜地躺著袒餐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪谤狡。 梳的紋絲不亂的頭發(fā)上灸眼,一...
    開(kāi)封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音豌汇,去河邊找鬼幢炸。 笑死泄隔,一個(gè)胖子當(dāng)著我的面吹牛拒贱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播佛嬉,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼逻澳,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了暖呕?” 一聲冷哼從身側(cè)響起斜做,我...
    開(kāi)封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎湾揽,沒(méi)想到半個(gè)月后瓤逼,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡库物,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年霸旗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片戚揭。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡诱告,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出民晒,到底是詐尸還是另有隱情精居,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布潜必,位于F島的核電站靴姿,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏磁滚。R本人自食惡果不足惜佛吓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辈毯,春花似錦坝疼、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至唁影,卻和暖如春耕陷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背据沈。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工哟沫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人锌介。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓嗜诀,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親孔祸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子隆敢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354