摘要:通過(guò)協(xié)議向網(wǎng)絡(luò)讀寫(xiě)數(shù)據(jù)通過(guò)協(xié)議向網(wǎng)絡(luò)讀寫(xiě)數(shù)據(jù)以一個(gè)服務(wù)器的形式,監(jiān)聽(tīng)到來(lái)的連接,對(duì)每個(gè)連接建立一個(gè)。
Java NIO 教程 NIO是什么?
它是Java1.4之后出現(xiàn)的IO API,與傳統(tǒng)IO和網(wǎng)絡(luò)API不同,具有非阻塞的特點(diǎn)。
在BIO中我們使用字節(jié)流和字符流。NIO中我們使用channel和buffer。數(shù)據(jù)總是從一個(gè)channel中讀取到buffer中,或者從buffer中寫(xiě)入到channel中。
NIO的意思是一個(gè)線程可以讓一個(gè)channel將數(shù)據(jù)讀取到buffer中,與此同時(shí),這個(gè)線程還可以做其他的事情,線程可以等到數(shù)據(jù)全部進(jìn)入buffer之后再處理數(shù)據(jù),從buffer中寫(xiě)入線程也是一樣的。
selector:選擇器是一個(gè)NIO當(dāng)中的概念,指的是一個(gè)對(duì)象,能監(jiān)視多個(gè)channel發(fā)生的事件(如連接建立,數(shù)據(jù)到達(dá)等)。因此,一個(gè)單線程可以監(jiān)視多個(gè)channel的數(shù)據(jù)。
Java NIO 總覽Java NIO的三個(gè)核心基礎(chǔ)組件,
Channels
Buffers
Selectors
其余的諸如Pipe,F(xiàn)ileLcok都是在使用以上三個(gè)核心組件時(shí)幫助更好使用的工具類(lèi)。
Channels和Buffers的關(guān)系所有的IO操作在NIO中都是以Channel開(kāi)始的。一個(gè)Channel就像一個(gè)流。從Channel中,數(shù)據(jù)可以被讀取到buffer里,也可以從buffer里寫(xiě)到Channel中。
基本的Channel實(shí)現(xiàn)有以下這些:
FileChannel
DatagramChannel
SocketChannel
ServerSocketChannel
涵蓋了UDP,TCP以及文件的IO操作。
核心的buffer實(shí)現(xiàn)有這些
ByteBuffer
CharBuffer
DoubleBuffer
FloatBuffer
IntBuffer
LongBuffer
ShortBuffer
涵蓋了所有的基本數(shù)據(jù)類(lèi)型(4類(lèi)8種,除了Boolean)。也有其他的buffer如MappedByteBuffer,此處不講。
selectorsselector允許一個(gè)線程來(lái)監(jiān)視多個(gè)Channel,這在當(dāng)你的應(yīng)用建立了多個(gè)連接,但是每個(gè)連接吞吐量都較小的時(shí)候是可行的。例如:一個(gè)聊天服務(wù)器。圖為一個(gè)線程使用selector處理三個(gè)channel。
要使用一個(gè)Selector,你要先注冊(cè)這個(gè)selector的Channels。然后你調(diào)用selector的select()方法。這個(gè)方法會(huì)阻塞,直到它注冊(cè)的channels當(dāng)中有一個(gè)準(zhǔn)備好了的事件發(fā)生了。當(dāng)select()方法返回的時(shí)候,線程可以處理這些事件,如新的連接的到來(lái),數(shù)據(jù)收到了等。
NIO ChannelsNIO channel和流很近似但是也有一些不同。
你既可以讀取也可以寫(xiě)入到channel,流只能讀取或者寫(xiě)入,inputStream和outputStream。
channel可以異步地讀和寫(xiě)。
channel永遠(yuǎn)都是從一個(gè)buffer中讀或者寫(xiě)入到一個(gè)buffer中去。
channel的實(shí)現(xiàn)以下是NIO中最重要的幾個(gè)channel的實(shí)現(xiàn)。
FileChannel 向文件當(dāng)中讀寫(xiě)數(shù)據(jù)。
DatagramChannel 通過(guò)UDP協(xié)議向網(wǎng)絡(luò)讀寫(xiě)數(shù)據(jù)
SocketChannel 通過(guò)TCP協(xié)議向網(wǎng)絡(luò)讀寫(xiě)數(shù)據(jù)
ServerSocketChannel 以一個(gè)web服務(wù)器的形式,監(jiān)聽(tīng)到來(lái)的TCP連接,對(duì)每個(gè)連接建立一個(gè)SocketChannel。
一個(gè)簡(jiǎn)單的channel例子使用一個(gè)FileChannel將數(shù)據(jù)讀入一個(gè)buffer
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw"); FileChannel inChannel = aFile.getChannel(); ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buf); while (bytesRead != -1) { System.out.println("Read " + bytesRead); buf.flip(); while(buf.hasRemaining()){ System.out.print((char) buf.get()); } buf.clear(); bytesRead = inChannel.read(buf); } aFile.close();
buf.flip()的意思是讀寫(xiě)轉(zhuǎn)換,首先你讀入一個(gè)buffer,然后你flip,轉(zhuǎn)換讀寫(xiě),然后再?gòu)腷uffer中讀出,buffer的操作接下來(lái)會(huì)講。
NIO bufferNIO buffer在與NIO Channel交互時(shí)使用,數(shù)據(jù)從channel中讀取出來(lái)放入buffer,或者從buffer中讀取出來(lái)寫(xiě)入channel。
buffer就是一塊內(nèi)存,你可以寫(xiě)入數(shù)據(jù),并且在之后讀取它。這塊內(nèi)存被包裝成NIO buffer對(duì)象,它提供了一些方法來(lái)讓你更簡(jiǎn)單地操作內(nèi)存。
buffer的基本使用使用buffer讀寫(xiě)數(shù)據(jù)基本上分為以下4部操作:
將數(shù)據(jù)寫(xiě)入buffer
調(diào)用buffer.flip()
將數(shù)據(jù)從buffer中讀取出來(lái)
調(diào)用buffer.clear()或者buffer.compact()
在寫(xiě)buffer的時(shí)候,buffer會(huì)跟蹤寫(xiě)入了多少數(shù)據(jù),需要讀buffer的時(shí)候,需要調(diào)用flip()來(lái)將buffer從寫(xiě)模式切換成讀模式,讀模式中只能讀取寫(xiě)入的數(shù)據(jù),而非整個(gè)buffer。
當(dāng)數(shù)據(jù)都讀完了,你需要清空buffer以供下次使用,可以有2種方法來(lái)操作:
調(diào)用clear()
調(diào)用compact()
區(qū)別:clear方法清空整個(gè)buffer,compact方法只清除你已經(jīng)讀取的數(shù)據(jù),未讀取的數(shù)據(jù)會(huì)被移到buffer的開(kāi)頭,此時(shí)寫(xiě)入數(shù)據(jù)會(huì)從當(dāng)前數(shù)據(jù)的末尾開(kāi)始。
一個(gè)簡(jiǎn)單的buffer使用例子:
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw"); FileChannel inChannel = aFile.getChannel(); //創(chuàng)建一個(gè)容量為48的ByteBuffer ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buf); //從channel中讀(取數(shù)據(jù)然后寫(xiě))入buffer //下面是讀取buffer while (bytesRead != -1) { buf.flip(); //轉(zhuǎn)換buffer為讀模式 while(buf.hasRemaining()){ System.out.print((char) buf.get()); // 一次讀取一個(gè)byte } buf.clear(); //清空buffer準(zhǔn)備下一次寫(xiě)入 bytesRead = inChannel.read(buf); } aFile.close();buffer的Capacity,Position和Limit
buffer有3個(gè)屬性需要熟悉以理解buffer的工作原理:
容量(Capacity):緩沖區(qū)能夠容納的數(shù)據(jù)元素的最大數(shù)量。容量在緩沖區(qū)創(chuàng)建時(shí)被設(shè)定,并且永遠(yuǎn)不能被改變。
上界(Limit):寫(xiě)模式中等價(jià)于buffer的大小,即capacity;讀模式中為當(dāng)前緩沖區(qū)中一共有多少數(shù)據(jù),即可讀的最大位置。這意味著當(dāng)調(diào)用filp()方法切換成讀模式時(shí),limit的值變成position的值,而position重新指向0.
位置(Position):下一個(gè)要被讀或?qū)懙脑氐奈恢?。初始化?,buffer滿(mǎn)時(shí),position最大值為capacity-1。切換成讀模式的時(shí)候,position指向0。Position會(huì)自動(dòng)由相應(yīng)的 get( )和 put( )函數(shù)更新。
position和limit的值在讀/寫(xiě)模式中是不一樣的。
capacity的值永遠(yuǎn)表示buffer的大小。
下圖解釋了在讀/寫(xiě)模式中Capacity,Position和Limit的意思。
buffer的種類(lèi)Java NIO中有以下這些buffer種類(lèi):
ByteBuffer
MappedByteBuffer //比較特殊,會(huì)在以后講解
CharBuffer
DoubleBuffer
FloatBuffer
IntBuffer
LongBuffer
ShortBuffer
創(chuàng)建一個(gè)buffer獲得一個(gè)buffer 之前必須先分配一塊內(nèi)存,每個(gè)buffer類(lèi)都有一個(gè)靜態(tài)方法allocate() 來(lái)做這件事。
下例為創(chuàng)建一個(gè)容量為48byte的ByteBuffer:
ByteBuffer buf = ByteBuffer.allocate(48);
創(chuàng)建一個(gè)1024個(gè)字符的CharBuffer
CharBuffer buf = CharBuffer.allocate(1024);
寫(xiě)入buffer的方法有2種:
1.從一個(gè)channel中寫(xiě)入buffer。
2.調(diào)用buffer的put()方法來(lái)自行寫(xiě)入數(shù)據(jù)。
例:
int bytesRead = inChannel.read(buf); //從channel讀入buffer
buf.put(127); //自行寫(xiě)入buffer
put方法有很多的重載形式。以供你用各種不同的方法寫(xiě)入buffer中,比如從一個(gè)特定的position,或者寫(xiě)入一個(gè)array,詳見(jiàn)JavaDoc。
flip()flip方法將寫(xiě)模式切換成讀模式,調(diào)用flip()方法會(huì)將limit設(shè)置為position,將position設(shè)置回0。
換句話(huà)說(shuō),position標(biāo)志著寫(xiě)模式中寫(xiě)到哪里,切換成讀模式之后,limit標(biāo)志著之前寫(xiě)到哪里,也就是現(xiàn)在能讀到哪里。
從buffer中讀取數(shù)據(jù)有2種方法可以從buffer中讀取數(shù)據(jù)。
1.從buffer中讀取數(shù)據(jù)到channel中。
2.使用buffer的get()方法自行從buffer中讀出數(shù)據(jù)。
例子:
//從buffer中讀取數(shù)據(jù)到channel中 int bytesWritten = inChannel.write(buf); //使用buffer的get()方法自行從buffer中讀出數(shù)據(jù) byte aByte = buf.get();
get方法有很多的重載形式。以供你用各種不同的方法讀取buffer中的數(shù)據(jù)。例如從特定位置讀取數(shù)據(jù),或者讀一個(gè)數(shù)組出來(lái)。詳見(jiàn)JavaDoc。
rewind()rewind()方法將position設(shè)置為0,但是不會(huì)動(dòng)buffer里的數(shù)據(jù),這樣可以從頭開(kāi)始重新讀取數(shù)據(jù),limit的值不會(huì)變,這意味著limit依舊標(biāo)志著能讀多少數(shù)據(jù)。
clear()和compact()當(dāng)你讀完所有的數(shù)據(jù)想要重新寫(xiě)入數(shù)據(jù)時(shí),你可以調(diào)用clear或者compact方法。
當(dāng)你調(diào)用clear()方法的時(shí)候,position被設(shè)置為0,limit被設(shè)置為capacity,換句話(huà)說(shuō),buffer的數(shù)據(jù)雖然都還在,但是buffer被初始化了,處于可以被重寫(xiě)的狀態(tài)。
這也就意味著如果buffer中還有沒(méi)被讀取的數(shù)據(jù),在執(zhí)行clear之后,你無(wú)法知道數(shù)據(jù)讀到哪兒了,剩下的數(shù)據(jù)還有多少。
如果還有沒(méi)有讀完的數(shù)據(jù),但是你想先寫(xiě)數(shù)據(jù),可以用compact()方法,這樣未讀數(shù)據(jù)會(huì)放在buffer前端,可以在未讀數(shù)據(jù)之后跟著寫(xiě)新的數(shù)據(jù)。compact()會(huì)復(fù)制未讀數(shù)據(jù)到buffer前端,然后設(shè)置position為未讀數(shù)據(jù)單位后面緊跟的位置。limit還是設(shè)置為capacity,這和clear是一樣的。現(xiàn)在buffer處于可以寫(xiě)的狀態(tài),但是不會(huì)覆蓋之前未讀完的數(shù)據(jù)。
mark()和reset()你可以通過(guò)調(diào)用buffer.mark()來(lái)mark一個(gè)buffer中給定的位置。然后你就可以用buffer.reset()方法來(lái)講position設(shè)置回之前mark的位置。
例子:
buffer.mark(); //調(diào)用buffer.get()方法若干次,e.g. 比如在做parsing的時(shí)候 buffer.reset(); //set position back to mark.equals() 和 compareTo()
使用這2種方法能夠比較2個(gè)buffer。
equals()
equals()方法用于判斷2個(gè)buffer是否相等,2個(gè)buffer是equal的,當(dāng)它們:
是同一種數(shù)據(jù)類(lèi)型的buffer。
buffer中未讀取的bytes,chars等數(shù)據(jù)個(gè)數(shù)是一樣的,即(limit-position)相等,capacity不需要相等,剩余數(shù)據(jù)的索引也不需要相等。
未讀取的bytes,chars等內(nèi)容是一模一樣的,即各自[position,limit-1]索引的數(shù)據(jù)要完全相等。
如你所見(jiàn),equals()方法只比較buffer的部分內(nèi)容,而不是buffer中所有的數(shù)據(jù),事實(shí)上,它只比較buffer中剩余的元素是否一樣。
compareTo()
compareTo()方法比較兩個(gè)buffer的剩余元素(字節(jié),字符等),用于例如: 排序。
在下列情況下,緩沖區(qū)被認(rèn)為比另一個(gè)緩沖區(qū)“小”:
比較是針對(duì)每個(gè)緩沖區(qū)你剩余數(shù)據(jù)(從 position 到 limit)進(jìn)行的,與它們?cè)?equals() 中的方式相同,直到不相等的元素被發(fā)現(xiàn)或者到達(dá)緩沖區(qū)的上界。如果一個(gè)緩沖區(qū)在不相等元素發(fā)現(xiàn)前已經(jīng)被耗盡,較短的緩沖區(qū)被認(rèn)為是小于較長(zhǎng)的緩沖區(qū)。
if (buffer1.compareTo(buffer2) < 0) { // do sth, it means buffer2 < buffer1,not buffer1 < buffer2 doSth(); }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/76360.html
摘要:而我們現(xiàn)在都已經(jīng)發(fā)布了,的都不知道,這有點(diǎn)說(shuō)不過(guò)去了。而對(duì)一個(gè)的讀寫(xiě)也會(huì)有響應(yīng)的描述符,稱(chēng)為文件描述符,描述符就是一個(gè)數(shù)字,指向內(nèi)核中的一個(gè)結(jié)構(gòu)體文件路徑,數(shù)據(jù)區(qū)等一些屬性。 前言 只有光頭才能變強(qiáng) 回顧前面: 給女朋友講解什么是代理模式 包裝模式就是這么簡(jiǎn)單啦 本來(lái)我預(yù)想是先來(lái)回顧一下傳統(tǒng)的IO模式的,將傳統(tǒng)的IO模式的相關(guān)類(lèi)理清楚(因?yàn)镮O的類(lèi)很多)。 但是,發(fā)現(xiàn)在整理的過(guò)程已...
摘要:操作系統(tǒng)是能夠獲取到事件操作完成的事件,基于回調(diào)函數(shù)機(jī)制和操作系統(tǒng)的操作控制實(shí)現(xiàn)事件檢測(cè)機(jī)制。 前面的文章NIO基礎(chǔ)知識(shí)介紹了Java NIO的一些基本的類(lèi)及功能說(shuō)明,Java NIO是用來(lái)替換java 傳統(tǒng)IO的,NIO的一些新的特性在網(wǎng)絡(luò)交互方面會(huì)更加的明顯。 Java 傳統(tǒng)IO的弊端 ????基于JVM來(lái)實(shí)現(xiàn)每個(gè)通道的輪詢(xún)檢查通道狀態(tài)的方法是可行的,但仍然是有問(wèn)題的,檢查每個(gè)通道...
摘要:后改良為用線程池的方式代替新增線程,被稱(chēng)為偽異步。最大的問(wèn)題是阻塞,同步。每次請(qǐng)求都由程序執(zhí)行并返回,這是同步的缺陷。這些都會(huì)被注冊(cè)在多路復(fù)用器上。多路復(fù)用器提供選擇已經(jīng)就緒狀態(tài)任務(wù)的能力。并沒(méi)有采用的多路復(fù)用器,而是使用異步通道的概念。 Netty是一個(gè)提供異步事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)用框架,用以快速開(kāi)發(fā)高性能、高可靠的網(wǎng)絡(luò)服務(wù)器和客戶(hù)端程序。Netty簡(jiǎn)化了網(wǎng)絡(luò)程序的開(kāi)發(fā),是很多框架和公司...
摘要:后改良為用線程池的方式代替新增線程,被稱(chēng)為偽異步。最大的問(wèn)題是阻塞,同步。每次請(qǐng)求都由程序執(zhí)行并返回,這是同步的缺陷。這些都會(huì)被注冊(cè)在多路復(fù)用器上。多路復(fù)用器提供選擇已經(jīng)就緒狀態(tài)任務(wù)的能力。并沒(méi)有采用的多路復(fù)用器,而是使用異步通道的概念。 Netty是一個(gè)提供異步事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)用框架,用以快速開(kāi)發(fā)高性能、高可靠的網(wǎng)絡(luò)服務(wù)器和客戶(hù)端程序。Netty簡(jiǎn)化了網(wǎng)絡(luò)程序的開(kāi)發(fā),是很多框架和公司...
閱讀 2755·2023-04-25 22:15
閱讀 1818·2021-11-19 09:40
閱讀 2164·2021-09-30 09:48
閱讀 3238·2021-09-03 10:36
閱讀 2040·2021-08-30 09:48
閱讀 1876·2021-08-24 10:00
閱讀 2742·2019-08-30 15:54
閱讀 716·2019-08-30 15:54