JAVA IO

Linux網(wǎng)絡(luò)I/O模型

? ? ? ?Linux的內(nèi)核將所有外部設(shè)備都看作一個文件來操作瓷胧,對一個文件的讀寫操作會調(diào)用內(nèi)核提供的系統(tǒng)命令,返回一個file descriptor(fd,文件描述符)絮吵。而對一個socket的讀寫也會有相應(yīng)的描述符,稱為socketfd(socket描述符),描述符就是一個數(shù)字,它指向內(nèi)核中的一個結(jié)構(gòu)體(文件路徑儿惫,數(shù)據(jù)區(qū)等一些屬性)。
? ? ? ?根據(jù)UNIX網(wǎng)絡(luò)編程對I/O模型的分類伸但,UNIX提供了5種I/O模型:

  • 阻塞I/O模型:最常用的I/O模型肾请,缺省條件下,所有文件操作都是阻塞的更胖。以套接字接口為例:在進(jìn)程空間中調(diào)用recvfrom铛铁,其系統(tǒng)調(diào)用直到數(shù)據(jù)包到達(dá)且被復(fù)制到應(yīng)用進(jìn)程的緩沖區(qū)中或者發(fā)生錯誤時才返回,在此期間都是被阻塞的却妨。

  • 非阻塞I/O模型:recvfrom從應(yīng)用層到內(nèi)核的時候饵逐,如果該緩沖區(qū)沒有數(shù)據(jù),就直接返回一個EWOULDBLOCK錯誤彪标,一般都對非阻塞I/O模型進(jìn)行輪詢檢查這個狀態(tài)倍权,看內(nèi)核是否有數(shù)據(jù)到來。

  • I/O復(fù)用模型:Linux提供select/poll捞烟,進(jìn)程通過將一個或多個fd傳遞給select或poll系統(tǒng)調(diào)用薄声,阻塞在select操作上,select/poll可以通過順序掃描偵測多個fd是否處于就緒狀態(tài)题画,不過支持的fd數(shù)量有限默辨。Linux還提供了epoll,基于事件驅(qū)動方式代替順序掃描苍息,性能更高缩幸,當(dāng)有fd就緒時,立即回調(diào)函數(shù)rollback竞思。

  • 信號驅(qū)動I/O模型:首先開啟套接字信號驅(qū)動I/O功能桌粉,并通過系統(tǒng)調(diào)用sigaction執(zhí)行信號處理函數(shù)(此系統(tǒng)調(diào)用立即返回,非阻塞)衙四。當(dāng)數(shù)據(jù)準(zhǔn)備就緒時铃肯,為該進(jìn)程生成一個SIGIO信號,通過信號回調(diào)通知應(yīng)用程序調(diào)用recvfrom來讀取數(shù)據(jù)传蹈,并通知主循環(huán)函數(shù)處理數(shù)據(jù)押逼。

  • 異步I/O模型:告知內(nèi)核啟動某個操作步藕,并讓內(nèi)核在整個操作完成后(包括數(shù)據(jù)從內(nèi)核復(fù)制到用戶自己的緩沖區(qū))進(jìn)行通知。此模型與信號驅(qū)動模型的主要區(qū)別是:信號驅(qū)動I/O模型由內(nèi)核通知我們何時開始一個I/O操作挑格;異步I/O模型由內(nèi)核通知我們I/O操作何時已經(jīng)完成咙冗。

I/O多路復(fù)用技術(shù)

? ? ? ?I/O多路復(fù)用通過把多個I/O的阻塞復(fù)用到同一個select的阻塞上,從而使得系統(tǒng)在單線程的情況下可以同時處理多個客戶端請求漂彤。優(yōu)勢是:系統(tǒng)開銷小雾消,無需創(chuàng)建和維護(hù)額外線程,降低了系統(tǒng)維護(hù)工作量挫望,節(jié)省了系統(tǒng)資源立润。主要應(yīng)用場景如下:

  • 服務(wù)器需要同時處理多個處于監(jiān)聽狀態(tài)或者多個連接狀態(tài)的套接字;
  • 服務(wù)器需要同時處理多種網(wǎng)絡(luò)協(xié)議的套接字媳板;

? ? ? ?目前支持I/O多路復(fù)用的系統(tǒng)調(diào)用有select桑腮、pselect、poll蛉幸、epoll破讨,然而select有一些固有缺陷,為了克服select的缺點(diǎn)奕纫,epoll做了很多重大改進(jìn):

  • 支持一個進(jìn)程打開的socket描述符(FD)不受限制(僅受限于操作系統(tǒng)的最大文件句柄數(shù))提陶;

傳統(tǒng)的BIO

? ? ? ?網(wǎng)絡(luò)編程的基本模型是Client/Server模型,也就是兩個進(jìn)程之間進(jìn)行相互通信匹层,其中服務(wù)端提供位置信息(綁定的IP地址和監(jiān)聽端口)隙笆,客戶端通過連接操作向服務(wù)器端監(jiān)聽地址發(fā)起連接請求,通過三次握手建立連接又固,如果連接建立成功,雙方就可以通過網(wǎng)絡(luò)套接字(Socket)進(jìn)行通信煤率。
? ? ? ?采用BIO通信模型的服務(wù)端仰冠,通常由一個獨(dú)立的Acceptor線程負(fù)責(zé)監(jiān)聽客戶端的連接,接收到客戶端連接請求之后為每一個客戶端創(chuàng)建一個新的線程進(jìn)行鏈路處理蝶糯,處理完成之后洋只,通過輸出流返回應(yīng)答給客戶端,線程銷毀昼捍。此模型最大的問題就是缺乏彈性伸縮能力识虚,當(dāng)客戶端并發(fā)訪問量增加后,服務(wù)端的線程個數(shù)和客戶端并發(fā)訪問數(shù)呈1:1的正比關(guān)系妒茬,當(dāng)線程數(shù)膨脹担锤,系統(tǒng)性能將急劇下降。

偽異步I/O編程
? ? ? ?為解決同步阻塞I/O面臨的一個I/O鏈路需要一個線程處理的問題乍钻,通過一個線程池來處理多個客戶端的請求接入肛循,形成客戶端個數(shù)M : 線程池最大線程數(shù)N的比例關(guān)系铭腕。通過線程池可以靈活地調(diào)配線程資源,設(shè)置線程的最大值多糠,防止由于海量并發(fā)接入導(dǎo)致的線程耗盡累舷。

弊端分析
? ? ? ?當(dāng)對Socket的輸入流進(jìn)行讀取操作時,會一直阻塞直到發(fā)生下列三種事件夹孔,意味著當(dāng)對方發(fā)送請求或者應(yīng)答消息比較緩慢被盈,或者網(wǎng)絡(luò)傳輸較慢時,讀取輸入流一方的通信線程將被長時間阻塞:

  • 有數(shù)據(jù)可讀搭伤;
  • 可用數(shù)據(jù)已經(jīng)讀取完畢只怎;
  • 發(fā)生空指針或者I/O異常;

? ? ? ?當(dāng)寫輸出流時闷畸,將被阻塞直到所有要發(fā)送的字節(jié)全部寫入或者發(fā)生異常尝盼。但當(dāng)消息接收方處理緩慢時,其不能及時地從TCP緩沖區(qū)讀取數(shù)據(jù)佑菩,這將導(dǎo)致發(fā)送發(fā)的TCP發(fā)送窗口不斷減小盾沫,直到為0,雖然雙方處于Keep-Alive狀態(tài)殿漠,但發(fā)送方已經(jīng)不能再向TCP緩沖區(qū)寫入消息赴精,這時若采用的是同步阻塞I/O,write操作將被無限期的阻塞绞幌,直到TCP的發(fā)送窗口大于0或者發(fā)生I/O異常蕾哟。

NIO編程

1.緩沖區(qū)Buffer
? ? ? ?Buffer是一個對象,它包含一些要寫入或者要讀出的數(shù)據(jù)莲蜘。實(shí)質(zhì)是一個數(shù)組谭确,通常為字節(jié)數(shù)組,并且提供了對數(shù)據(jù)的結(jié)構(gòu)化訪問以及維護(hù)讀寫位置等信息票渠。

2.通道Channel
? ? ? ?通道與流不同之處在于通道是雙向的逐哈,流只是在一個方向上移動,而通道可以用于讀问顷、寫或者兩者同時進(jìn)行昂秃。

3.多路復(fù)用器Selector
? ? ? ?Selector會不斷地輪詢注冊在其上的Channel,如果某個Channel上面發(fā)生讀或者寫事件杜窄,此Channel就處于就緒狀態(tài)肠骆,然后通過SelectionKey獲取就緒Channel集合,進(jìn)行后續(xù)的I/O操作塞耕。JDK使用epoll()代替?zhèn)鹘y(tǒng)的select實(shí)現(xiàn)蚀腿,所以它并沒有最大連接句柄1024/2048的限制,這意味著只需要一個線程負(fù)責(zé)Selector的輪詢扫外,就可以接入成千上萬的客戶端唯咬。

4.NIO服務(wù)端序列圖

  • 步驟一:打開ServerSocketChannel纱注,監(jiān)聽客戶端連接,它是所有客戶端連接的父管道胆胰;
  • 步驟二:綁定監(jiān)聽端口狞贱,設(shè)置連接為非阻塞模式;
  • 步驟三:創(chuàng)建Reactor線程蜀涨,創(chuàng)建多路復(fù)用器并啟動線程瞎嬉;
  • 步驟四:將ServerSocketChannel注冊到Reactor線程的多路復(fù)用器Selector上,監(jiān)聽Accept事件厚柳;
  • 步驟五:多路復(fù)用器在線程run方法的無限循環(huán)體內(nèi)輪詢準(zhǔn)備就緒的Key氧枣;
  • 步驟六:多路復(fù)用器監(jiān)聽到新的客戶端接入請求,處理新的接入請求别垮,完成TCP三次握手便监,建立物理鏈路;
  • 步驟七:設(shè)置客戶端鏈路模式為非阻塞碳想;
  • 步驟八:將新接入的客戶端連接注冊到Rector線程的多路復(fù)用器上烧董,監(jiān)聽讀操作,讀取客戶端發(fā)送的網(wǎng)絡(luò)消息胧奔;
  • 步驟九:異步讀取客戶端請求消息到緩沖區(qū)逊移;
  • 步驟十:對ByteBuffer進(jìn)行編解碼,如果有半包消息龙填,指針reset胳泉,繼續(xù)讀取后續(xù)的報文,將解碼成功的消息封裝成Task岩遗,投遞到業(yè)務(wù)線程池中扇商,進(jìn)行業(yè)務(wù)邏輯編排;
  • 步驟十一:將POJO對象encode成ByteBuffer宿礁,調(diào)用SocketChannel的異步write接口案铺,將消息異步發(fā)送給客戶端。

注意:如果發(fā)送區(qū)TCP緩沖區(qū)滿窘拯,會導(dǎo)致寫半包红且,此時需要注冊監(jiān)聽寫操作位坝茎,循環(huán)寫涤姊,知道整包消息寫入TCP緩沖區(qū)。

5.NIO客戶端序列圖

  • 步驟一:打開SocketChannel嗤放,綁定客戶端本地地址(默認(rèn)會隨機(jī)分配一個可用的本地地址)思喊;
  • 步驟二:設(shè)置SocketChannel為非阻塞模式,同時設(shè)置客戶端連接的TCP參數(shù)次酌;
  • 步驟三:異步連接客戶端恨课;
  • 步驟四:判斷是否連接成功舆乔,若成功,則直接注冊讀狀態(tài)位到多路復(fù)用器中剂公,若未連接成功(異步連接)希俩,說明客戶端已經(jīng)發(fā)送了sync包,但服務(wù)端還未返回ack包纲辽,物理鏈路還未建立颜武,則注冊連接狀態(tài)到多路復(fù)用器中,監(jiān)聽服務(wù)端的TCP ACK應(yīng)答拖吼;
  • 步驟五:創(chuàng)建Reactor線程鳞上,創(chuàng)建多路復(fù)用器并啟動線程;
  • 步驟六:多路復(fù)用器在線程run方法的無限循環(huán)體內(nèi)輪詢準(zhǔn)備就緒的Key吊档;
  • 步驟七:接受connect事件進(jìn)行處理篙议;
  • 步驟八:判斷連接結(jié)果,若連接成功怠硼,注冊讀事件到多路復(fù)用器鬼贱;
  • 步驟九:異步讀客戶端請求消息到緩沖區(qū);
  • 步驟十:將POJO對象encode成ByteBuffer拒名,調(diào)用SocketChannel的異步write接口吩愧,將消息異步發(fā)送到客戶端。

AIO編程

? ? ? ?NIO 2.0引入了新的異步通道的概念增显,并提供了異步文件通道和異步套接字通道的實(shí)現(xiàn)雁佳。提供以下兩種方式獲取操作結(jié)果:

  • 通過java.util.concurrent.Future類來表示異步操作的結(jié)果;
  • 在執(zhí)行異步操作的時候傳入一個java.nio.channels同云;

? ? ? ?NIO 2.0的異步套接字通道是真正的異步非阻塞I/O糖权,對應(yīng)于UNIX網(wǎng)絡(luò)編程的事件驅(qū)動I/O(AIO)。不需要通過多路復(fù)用器(Selector)對注冊的通道進(jìn)行輪詢操作即可實(shí)現(xiàn)異步讀寫炸站,從而簡化了NIO的編程模型星澳。

? ? ? ?TCP以流的方式進(jìn)行數(shù)據(jù)傳輸箕速,上層的應(yīng)用程序?yàn)榱藢ο⑦M(jìn)行區(qū)分锁保,往往采用如下4種方式:

  • 消息長度固定,累計(jì)讀取到長度總和為定長LEN的報文后排龄,就認(rèn)為讀取到了一個完整的消息阀坏;將計(jì)數(shù)器置位如暖,重新開始讀取下一個數(shù)據(jù)包;
  • 將回車換行符作為消息結(jié)束符忌堂;
  • 將特殊的分隔符作為消息的結(jié)束標(biāo)志盒至,如回車換行符;
  • 通過在消息頭中定義長度字段來標(biāo)識消息的總長度;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末枷遂,一起剝皮案震驚了整個濱河市樱衷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌酒唉,老刑警劉巖矩桂,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異痪伦,居然都是意外死亡耍鬓,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進(jìn)店門流妻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來牲蜀,“玉大人,你說我怎么就攤上這事绅这』链铮” “怎么了?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵证薇,是天一觀的道長度苔。 經(jīng)常有香客問我,道長浑度,這世上最難降的妖魔是什么寇窑? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮箩张,結(jié)果婚禮上甩骏,老公的妹妹穿的比我還像新娘。我一直安慰自己先慷,他們只是感情好饮笛,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著论熙,像睡著了一般福青。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上脓诡,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天无午,我揣著相機(jī)與錄音,去河邊找鬼祝谚。 笑死宪迟,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的踊跟。 我是一名探鬼主播踩验,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼商玫!你這毒婦竟也來了箕憾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤拳昌,失蹤者是張志新(化名)和其女友劉穎袭异,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體炬藤,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡御铃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了沈矿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片上真。...
    茶點(diǎn)故事閱讀 38,643評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖羹膳,靈堂內(nèi)的尸體忽然破棺而出睡互,到底是詐尸還是另有隱情,我是刑警寧澤陵像,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布就珠,位于F島的核電站,受9級特大地震影響醒颖,放射性物質(zhì)發(fā)生泄漏妻怎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一泞歉、第九天 我趴在偏房一處隱蔽的房頂上張望逼侦。 院中可真熱鬧,春花似錦腰耙、人聲如沸偿洁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涕滋。三九已至,卻和暖如春挠阁,著一層夾襖步出監(jiān)牢的瞬間宾肺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工侵俗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留锨用,地道東北人。 一個月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓隘谣,卻偏偏與公主長得像增拥,于是被迫代替她去往敵國和親啄巧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評論 2 348

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