中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁(yè) > news >正文

企業(yè)網(wǎng)站建設(shè)論文糕點(diǎn)烘焙專(zhuān)業(yè)培訓(xùn)學(xué)校

企業(yè)網(wǎng)站建設(shè)論文,糕點(diǎn)烘焙專(zhuān)業(yè)培訓(xùn)學(xué)校,建設(shè)電影網(wǎng)站需要什么,網(wǎng)站建設(shè)五年發(fā)展規(guī)劃一、NIO基礎(chǔ) Java New IO是從Java1.4版本開(kāi)始引入的一個(gè)新的IO api,可以替代以往的標(biāo)準(zhǔn)IO,NIO相比原來(lái)的IO有同樣的作用和目的,但是使用的方式完全不一樣,NIO是面向緩沖區(qū)的,基于通道的IO操作,這也讓它比傳…

一、NIO基礎(chǔ)

Java New IO是從Java1.4版本開(kāi)始引入的一個(gè)新的IO api,可以替代以往的標(biāo)準(zhǔn)IO,NIO相比原來(lái)的IO有同樣的作用和目的,但是使用的方式完全不一樣,NIO是面向緩沖區(qū)的,基于通道的IO操作,這也讓它比傳統(tǒng)IO有著更為高效的讀寫(xiě)。

1.1 IO和NIO的主要區(qū)別

IONIO
面向流面向緩沖
區(qū)阻塞IO非阻塞IO
無(wú)選擇器

1.1.1 傳統(tǒng)IO的流

以下用圖來(lái)簡(jiǎn)單理解一下,在傳統(tǒng)IO中當(dāng)App要對(duì)網(wǎng)絡(luò),磁盤(pán)中的文件進(jìn)行讀寫(xiě)的時(shí)候,它們必須建立一個(gè)連接,流到底是一個(gè)什么樣的概念呢,我們可以先把它想象成自來(lái)水,家里要用自來(lái)水,需要有水管,讓水從水管過(guò)來(lái)到家里,起到一個(gè)運(yùn)輸?shù)淖饔谩?/p>

所以當(dāng)我們文件中的數(shù)據(jù)需要輸入到App里面時(shí),它們就會(huì)建立一個(gè)輸入的管道。而當(dāng)我們的App有數(shù)據(jù)需要寫(xiě)入到文件系統(tǒng)的時(shí)候,就會(huì)建立一個(gè)輸出的管道,這兩條管道就是我們的輸入流和輸出流。那水從來(lái)沒(méi)有逆流而上的呀,所以它們都是單向管道。這么一講,是不是就很好懂了呢😁?

1.1.2 NIO

也是同樣的文件系統(tǒng)和App,不過(guò)此時(shí)把流換成了一個(gè)channel,現(xiàn)在我們可以先認(rèn)為它就是一條鐵道,那我們知道鐵道本身是不能傳遞貨物的呀,所以我們需要一個(gè)載具—火車(chē)(也就是緩沖區(qū)),App需要的數(shù)據(jù)就由這個(gè)名叫緩沖區(qū)的載具運(yùn)輸過(guò)來(lái)。那火車(chē)是可以開(kāi)過(guò)來(lái),也可以開(kāi)回去的,所以NIO是雙向傳輸?shù)摹?br />

1.2 Buffer

NIO的核心在于,通道(channel)和緩沖區(qū)(buffer)兩個(gè)。通道是打開(kāi)到IO設(shè)備的連接。使用時(shí)需要獲取用于連接IO設(shè)備的通道以及用于容納數(shù)據(jù)的緩沖區(qū),然后通過(guò)操作緩沖區(qū)對(duì)數(shù)據(jù)進(jìn)行處理。(其實(shí)就是上面那張圖的事兒,或者一句話(huà)就是一個(gè)負(fù)責(zé)傳輸,一個(gè)負(fù)責(zé)存儲(chǔ))。

緩沖區(qū)是Java.nio包定義好的,所有緩沖區(qū)都是Buffer抽象類(lèi)的子類(lèi)。Buffer根據(jù)數(shù)據(jù)類(lèi)型不同,常用子類(lèi)分別是基本數(shù)據(jù)類(lèi)型除了Boolean外的xxxBuffer(IntBuffer,DoubleBuffer···等)。不同的Buffer類(lèi)它們的管理方式都是相同的,獲取對(duì)象的方法都是

// 創(chuàng)建一個(gè)容量為capacity的xxx類(lèi)型的Buffer對(duì)象
static xxxBuffer allocate(int capacity)

而且緩沖區(qū)提供了兩個(gè)核心方法:get()和put(),put方法是將數(shù)據(jù)存入到緩沖區(qū),而get是獲取緩沖區(qū)的數(shù)據(jù)。

此時(shí)我們用代碼看一下

public class BufferTest {@Testpublic void testBuffer(){// 創(chuàng)建緩沖區(qū)對(duì)象ByteBuffer byteBuffer = ByteBuffer.allocate(1024);}
}

點(diǎn)進(jìn)去ByteBuffer,會(huì)看到這個(gè)東西是繼承了Buffer類(lèi)的

public abstract class ByteBuffer extends Buffer implements Comparable<ByteBuffer>

此時(shí)繼續(xù)點(diǎn)進(jìn)去Buffer類(lèi),第一眼看到的是有幾個(gè)自帶的屬性

1.2.1 buffer的基本屬性

① capacity容量
表示Buffer的最大數(shù)據(jù)容量,這個(gè)值不能為負(fù)。而且創(chuàng)建后是不能更改的。

② limit限制
第一個(gè)不能讀取或?qū)懭氲臄?shù)據(jù)的索引,位于此索引后的數(shù)據(jù)不可讀寫(xiě)。這個(gè)數(shù)值不能為負(fù)且不能超過(guò)capacity,如上圖中第三個(gè)緩沖區(qū),在下標(biāo)為5之后的數(shù)據(jù)塊均不能讀寫(xiě),那limit為5

③ position位置
下一個(gè)要讀取或?qū)懭氲臄?shù)據(jù)的索引,這個(gè)數(shù)值不能為負(fù)且不能超過(guò)capacity,如圖中第二個(gè)緩沖區(qū),前面5塊寫(xiě)完成,此時(shí)第6個(gè)數(shù)據(jù)塊的下標(biāo)為5,所以position為5

④ mark標(biāo)記/reset重置
mark是一個(gè)索引,通過(guò)Buffer的mark()方法指定Buffer中一個(gè)特定的position后,可以通過(guò)reset()方法重置到這個(gè)position,這個(gè)通過(guò)代碼來(lái)解釋會(huì)比較好說(shuō)明

1.2.2 code部分(非常簡(jiǎn)單)

1.首先我們創(chuàng)建一個(gè)緩沖區(qū)對(duì)象,然后把它的屬性打印出來(lái)

ByteBuffer byteBuffer = ByteBuffer.allocate(10);
System.out.println(byteBuffer.position());
System.out.println(byteBuffer.capacity());
System.out.println(byteBuffer.limit());運(yùn)行結(jié)果:0,10,10

2.執(zhí)行一個(gè)put()方法,來(lái)把一個(gè)字符丟進(jìn)去

String str = "abcde";
byteBuffer.put(str.getBytes());
System.out.println(byteBuffer.position());
System.out.println(byteBuffer.capacity());
System.out.println(byteBuffer.limit());運(yùn)行結(jié)果:5,10,10
"abcde"長(zhǎng)度為5,position已經(jīng)變化,其它不變

3.使用flip()切換為讀模式

byteBuffer.flip();
System.out.println(byteBuffer.position());
System.out.println(byteBuffer.capacity());
System.out.println(byteBuffer.limit());運(yùn)行結(jié)果:0,10,5

此時(shí)position變成為0了,因?yàn)橐婚_(kāi)始的5,是因?yàn)檫@時(shí)候要寫(xiě)的是下標(biāo)為5的數(shù)據(jù)塊,而轉(zhuǎn)換成讀模式后,第一個(gè)讀的明顯是下標(biāo)為0的數(shù)據(jù)塊呀。limit的數(shù)值也變成了5,因?yàn)楫?dāng)前能讀到的數(shù)據(jù)從下標(biāo)為5開(kāi)始就木有了,所以limit為5

4.簡(jiǎn)單獲取一下buffer中的數(shù)據(jù)

byte[] array = new byte[byteBuffer.limit()];
byteBuffer.get(array);
System.out.println(new String(array,0,array.length));運(yùn)行結(jié)果:abcde

5.mark() & reset()

byte[] array = new byte[byteBuffer.limit()];
byteBuffer.get(array,0,2);
System.out.println(new String(array,0,2));
System.out.println(byteBuffer.position());byteBuffer.mark();
byteBuffer.get(array,2,2);
System.out.println(new String(array,2,2));
System.out.println(byteBuffer.position());byteBuffer.reset();
System.out.println(byteBuffer.position());運(yùn)行結(jié)果:ab,2,cd,4,2

其實(shí)很簡(jiǎn)單,就是第一次讀取的時(shí)候,只是讀取了前面兩個(gè)字符,然后此時(shí)position的結(jié)果為2,然后再讀取后兩個(gè),position為4,可是因?yàn)槲以谧x取前面2個(gè)的時(shí)候進(jìn)行了一個(gè)mark操作,它就自動(dòng)回到我mark之前的那個(gè)讀取位置而已,就是這么簡(jiǎn)單

6.其他的一些方法
rewind()方法,可重復(fù)讀,clear()清空緩沖區(qū),不過(guò)這個(gè)方法的清空緩沖區(qū),是一種被遺忘的狀態(tài),就是說(shuō),數(shù)據(jù)仍然還存于緩沖區(qū)中,可是自動(dòng)忽略掉了。此時(shí)再次讀取數(shù)據(jù),是還是可以get()到的。hasRemaining()方法就是表示剩余可操作的數(shù)據(jù)量還有多少,比如剛剛的mark的那個(gè)例子中,我reset回去之后,剩余的可操作數(shù)據(jù)就是3,因?yàn)槲抑蛔x了ab,還有cde這三個(gè)。

1.2.3 直接緩沖區(qū)和非直接緩沖區(qū)

非直接緩沖區(qū):通過(guò)allocate()方法來(lái)分配緩沖區(qū)。將緩沖區(qū)建立在JVM的內(nèi)存中。
直接緩沖區(qū):通過(guò)allocateDirect()方法分配緩沖區(qū),將緩沖區(qū)建立在物理內(nèi)存中。效率更高。

① 非直接緩沖區(qū)

應(yīng)用程序想要在磁盤(pán)中讀取數(shù)據(jù)時(shí),首先它發(fā)起請(qǐng)求,讓物理磁盤(pán)先把它的數(shù)據(jù)讀到內(nèi)核地址空間當(dāng)中,之后這個(gè)內(nèi)核空間再將這個(gè)數(shù)據(jù)copy一份到用戶(hù)地址空間去。然后數(shù)據(jù)才能通過(guò)read()方法將數(shù)據(jù)返回個(gè)應(yīng)用程序。而應(yīng)用程序需要寫(xiě)數(shù)據(jù)進(jìn)去,也是同理,先寫(xiě)到用戶(hù)地址空間,然后copy到內(nèi)核地址空間,再寫(xiě)入磁盤(pán)。此時(shí)不難發(fā)現(xiàn),這個(gè)copy的操作顯得十分的多余,所以非直接緩沖區(qū)的效率相對(duì)來(lái)說(shuō)會(huì)低一些。

② 直接緩沖區(qū)

直接緩沖區(qū)就真的顧名思義非常直接了,寫(xiě)入的時(shí)候,寫(xiě)到物理內(nèi)存映射文件中,再由它寫(xiě)入物理磁盤(pán),讀取也是磁盤(pán)把數(shù)據(jù)讀到這個(gè)文件然后再由它讀取到應(yīng)用程序中即可。沒(méi)有了copy的中間過(guò)程。

1.3 channel

1.3.1 扯一下概念背景

由java.nio.channels包定義,表示IO源與目標(biāo)打開(kāi)的鏈接,它本身不存在直接訪(fǎng)問(wèn)數(shù)據(jù)的能力,只能和Buffer進(jìn)行交互

傳統(tǒng)的IO由cpu來(lái)全權(quán)負(fù)責(zé),此時(shí)這個(gè)設(shè)計(jì)在有大量文件讀取操作時(shí),CPU的利用率會(huì)被拉的非常低,因?yàn)镮O操作把CPU的資源都搶占了。

在這種背景下進(jìn)行了一些優(yōu)化,把對(duì)cpu的連接取消,轉(zhuǎn)為DMA(直接內(nèi)存存取)的方式。當(dāng)然DMA這個(gè)操作本身也是需要CPU進(jìn)行調(diào)度的。不過(guò)這個(gè)損耗自然就會(huì)比大量的IO要小的多。

此時(shí),就出現(xiàn)了通道這個(gè)概念,它是一個(gè)完全獨(dú)立的處理器。專(zhuān)門(mén)用來(lái)負(fù)責(zé)文件的IO操作。

1.3.2 常用通道

Java為Channel接口提供的主要實(shí)現(xiàn)類(lèi):

FileChannel:用于讀取,寫(xiě)入,映射和操作文件的通道
DatagramChannel:通過(guò)UDP讀寫(xiě)網(wǎng)絡(luò)中的數(shù)據(jù)通道
SocketChannel:通過(guò)TCP讀寫(xiě)網(wǎng)絡(luò)中的數(shù)據(jù)通道
ServerSocketChannel:可以監(jiān)聽(tīng)新進(jìn)來(lái)的TCP連接,對(duì)每一個(gè)新進(jìn)來(lái)的連接都會(huì)創(chuàng)建一個(gè)SocketChannel              

獲取channel的一種方式是對(duì)支持通道的對(duì)象調(diào)用getChannel()方法,支持類(lèi)如下

FileInputStream
FileOutputStream
RandomAccessFile
DatagramSocket
Socket
ServerSocket

獲取的其他方式是使用Files類(lèi)的靜態(tài)方法newByteChannel()獲取字節(jié)通道。再或者是通過(guò)通道的靜態(tài)方法open()打開(kāi)并返回指定通道。

1.3.3 常用方法和簡(jiǎn)單使用

① 使用非直接緩沖區(qū)完成文件復(fù)制

// 創(chuàng)建輸入輸出流對(duì)象
FileInputStream fileInputStream = new FileInputStream("testPic.jpg");
FileOutputStream fileOutputStream = new FileOutputStream("testPic2.jpg");// 通過(guò)流對(duì)象獲取通道channel
FileChannel inChannel = fileInputStream.getChannel();
FileChannel outChannel = fileOutputStream.getChannel();// 創(chuàng)建指定大小的緩沖區(qū)
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);// 將通道中的數(shù)據(jù)寫(xiě)入到緩沖區(qū)中
while (inChannel.read(byteBuffer) != -1){// 切換成讀取模式byteBuffer.flip();// 將緩沖區(qū)中的數(shù)據(jù)寫(xiě)到輸出通道outChannel.write(byteBuffer);// 清空緩沖區(qū)byteBuffer.clear();}
//回收資源(這里為了省時(shí)間直接拋出去了,反正這段不太重要)
outChannel.close();
inChannel.close();
fileInputStream.close();
fileOutputStream.close();運(yùn)行結(jié)果:就自然是復(fù)制了一個(gè)testPic2出來(lái)啦

因?yàn)榇a本身不難,注釋已經(jīng)寫(xiě)得比較詳細(xì),就不展開(kāi)了

② 使用直接緩沖區(qū)來(lái)完成文件的復(fù)制

注意這里的StandardOpenOption是一個(gè)枚舉,表示模式,很顯然這里是要選擇READ讀取模式。

FileChannel inChannel = FileChannel.open(Paths.get("testPic.jpg",StandardOpenOption.READ));
FileChannel outChannel = FileChannel.open(Paths.get("testPic2.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
// 進(jìn)行內(nèi)存映射
MappedByteBuffer inMappedBuffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
MappedByteBuffer outMapBuffer = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());// 對(duì)緩沖區(qū)進(jìn)行數(shù)據(jù)的讀寫(xiě)操作
byte[] array = new byte[inMappedBuffer.limit()];
inMappedBuffer.get(array);
outMapBuffer.put(array);// 回收資源
inChannel.close();
outChannel.close();

如果需要看一下它們兩個(gè)的時(shí)間差,自己用最常規(guī)的系統(tǒng)時(shí)間來(lái)瞧瞧就好,在這里就不再加上了。

二、NIO非阻塞式網(wǎng)絡(luò)通信

傳統(tǒng)的IO流都是阻塞式的,當(dāng)一個(gè)線(xiàn)程調(diào)用read或者write時(shí),該線(xiàn)程被阻塞,直到數(shù)據(jù)被讀取或者寫(xiě)入,該線(xiàn)程在此期間都是不能執(zhí)行其他任務(wù)的,因此,在完成網(wǎng)絡(luò)通信進(jìn)行IO操作時(shí),線(xiàn)程被阻塞,所以服務(wù)器端必須為每個(gè)客戶(hù)端提供一個(gè)獨(dú)立線(xiàn)程進(jìn)行處理,當(dāng)服務(wù)器端需要處理大量客戶(hù)端時(shí),性能將會(huì)急劇下降。

NIO是非阻塞的,當(dāng)線(xiàn)程從某通道進(jìn)行讀寫(xiě)數(shù)據(jù)時(shí),若沒(méi)有數(shù)據(jù)可用,該線(xiàn)程可以進(jìn)行其他任務(wù)。線(xiàn)程通常將非阻塞IO的空閑時(shí)間用于在其他通道上執(zhí)行IO操作,所以單獨(dú)的線(xiàn)程可以管理多個(gè)輸入和輸出通道。因此NIO可以讓服務(wù)器端使用一個(gè)或有限幾個(gè)線(xiàn)程來(lái)同時(shí)處理連接到服務(wù)器端的所有客戶(hù)端。

2.1 Selector

這個(gè)選擇器其實(shí)就是在客戶(hù)端和服務(wù)端之間引入一個(gè)通道的注冊(cè)器,比如現(xiàn)在我的客戶(hù)端要像服務(wù)端傳輸數(shù)據(jù)了,客戶(hù)端會(huì)給選擇器去發(fā)送一個(gè)channel的注冊(cè)請(qǐng)求,注冊(cè)完成后,Selector就會(huì)去監(jiān)控這個(gè)channel的IO狀態(tài)(讀寫(xiě),連接)。只有當(dāng)通道中的數(shù)據(jù)完全準(zhǔn)備就緒,Selector才會(huì)將數(shù)據(jù)分配到服務(wù)端的某個(gè)線(xiàn)程去處理。

這種非阻塞性的流程就可以更好地去使用CPU的資源。提高CPU的工作效率。這個(gè)可以用收快遞來(lái)說(shuō)明。如果你一開(kāi)始就告訴我半小時(shí)后過(guò)來(lái)取快遞,而我在這時(shí)候已經(jīng)到目的地了,我有可能就原地不動(dòng)站著等半個(gè)小時(shí)。這個(gè)期間啥地都去不了,可是你是到了之后,才打電話(huà)告訴我過(guò)來(lái)取,那我就有了更多的自由時(shí)間。

2.2 code(阻塞性IO的網(wǎng)絡(luò)通信)

現(xiàn)在我們來(lái)演示一下阻塞性IO的網(wǎng)絡(luò)通信

2.2.1 client(阻塞性IO)

這個(gè)代碼大家可以嘗試這刪除sChannel.shutdownOutput(),此時(shí)會(huì)發(fā)現(xiàn)在啟動(dòng)好server,運(yùn)行client程序的時(shí)候,程序也會(huì)阻塞,這是因?yàn)檫@時(shí)服務(wù)端并無(wú)法確定你是否已經(jīng)發(fā)送完成數(shù)據(jù)了,所以client端也產(chǎn)生了阻塞,雙方就一直僵持。

還有一種方法是解阻塞,之后進(jìn)行闡述。

// 1.獲取通道
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("你的IP地址",9898));
// 2.創(chuàng)建文件通道
FileChannel inChannel = FileChannel.open(Paths.get("C:/Users/Administrator/Desktop/testPic.jpg"),StandardOpenOption.READ);
// 3.分配指定大小的緩沖區(qū)
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);// 4.發(fā)送數(shù)據(jù),需要讀取文件
while (inChannel.read(byteBuffer) != -1){byteBuffer.flip();// 將buffer的數(shù)據(jù)寫(xiě)入到通道中sChannel.write(byteBuffer);byteBuffer.clear();
}// 主動(dòng)告訴服務(wù)端,數(shù)據(jù)已經(jīng)發(fā)送完畢
sChannel.shutdownOutput();while (sChannel.read(byteBuffer) != -1){byteBuffer.flip();System.out.println("接收服務(wù)端數(shù)據(jù)成功···");byteBuffer.clear();}// 5.關(guān)閉通道
inChannel.close();
sChannel.close();

2.2.2 server(阻塞性IO)

// 1.獲取通道
ServerSocketChannel ssChannel = ServerSocketChannel.open();
// 創(chuàng)建一個(gè)輸出通道,將讀取到的數(shù)據(jù)寫(xiě)入到輸出通道中,保存為testPic2
FileChannel outChannel = FileChannel.open(Paths.get("testPic2.jpg"),StandardOpenOption.WRITE,StandardOpenOption.CREATE);
// 2.綁定端口
ssChannel.bind(new InetSocketAddress(9898));
// 3.等待客戶(hù)端連接,連接成功時(shí)會(huì)得到一個(gè)通道
SocketChannel sChannel = ssChannel.accept();
// 4.創(chuàng)建緩沖區(qū)
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 5.接收客戶(hù)端的數(shù)據(jù)存儲(chǔ)到本地
while (sChannel.read(byteBuffer) != -1){byteBuffer.flip();outChannel.write(byteBuffer);byteBuffer.clear();
}// 發(fā)送反饋給客戶(hù)端// 向緩沖區(qū)中寫(xiě)入應(yīng)答信息byteBuffer.put("服務(wù)端接收數(shù)據(jù)成功".getBytes());byteBuffer.flip();sChannel.write(byteBuffer);// 關(guān)閉通道
sChannel.close();
outChannel.close();
byteBuffer.clear();

外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳

然后再當(dāng)我們的客戶(hù)端運(yùn)行起來(lái),就會(huì)進(jìn)行copy操作

2.3 Selector完成非阻塞IO

使用NIO完成網(wǎng)絡(luò)通信需要三個(gè)核心對(duì)象:
channel:java.nio.channels.Channel接口,SocketChannel,ServerSocketChannel,DatagramChannel
管道相關(guān):Pipe.SinkChannel,Pine.SourceChannel
buffer:負(fù)責(zé)存儲(chǔ)數(shù)據(jù)
Selector:其中Selector是SelectableChannel的多路復(fù)用器,主要是用于監(jiān)控SelectableChannel的IO狀態(tài)

2.3.1 client(非阻塞)

// 1.獲取通道,默認(rèn)是阻塞的
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("192.168.80.1",9898));// 1.1 將阻塞的套接字變成非阻塞
sChannel.configureBlocking(false);// 2.創(chuàng)建指定大小的緩沖區(qū)
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 3.發(fā)送數(shù)據(jù)給服務(wù)端,直接將數(shù)據(jù)存儲(chǔ)到緩沖區(qū)
byteBuffer.put(new Date().toString().getBytes());
// 4.將緩沖區(qū)的數(shù)據(jù)寫(xiě)入到sChannel
byteBuffer.flip();
sChannel.write(byteBuffer);
byteBuffer.clear();// 關(guān)閉
sChannel.close();

2.3.2 server(非阻塞)

代碼的注釋中已經(jīng)解釋了整個(gè)過(guò)程的做法,這里就不一一展開(kāi)了。

// 1.獲取通道
ServerSocketChannel ssChannel = ServerSocketChannel.open();
// 2.將阻塞的套接字設(shè)置為非阻塞的
ssChannel.configureBlocking(false);
// 3.綁定端口號(hào)
ssChannel.bind(new InetSocketAddress(9898));// 4.創(chuàng)建選擇器對(duì)象
Selector selector = Selector.open();// 5.將通道注冊(cè)到選擇器上(這里的第二個(gè)參數(shù)為selectionKey),下面有解釋
// 此時(shí)選擇器就開(kāi)始監(jiān)聽(tīng)這個(gè)通道的接收時(shí)間,此時(shí)接收工作準(zhǔn)備就緒,才開(kāi)始下一步的操作
ssChannel.register(selector,SelectionKey.OP_ACCEPT);// 6.通過(guò)輪詢(xún)的方式獲取選擇器上準(zhǔn)備就緒的事件
// 如果大于0,至少有一個(gè)SelectionKey準(zhǔn)備就緒
while (selector.select() > 0){// 7.獲取當(dāng)前選擇器中所有注冊(cè)的selectionKey(已經(jīng)準(zhǔn)備就緒的監(jiān)聽(tīng)事件)Iterator<SelectionKey> selectionKeyIterator = selector.selectedKeys().iterator();// 迭代獲取已經(jīng)準(zhǔn)備就緒的選擇鍵while (selectionKeyIterator.hasNext()){// 8.獲取已經(jīng)準(zhǔn)備就緒的事件SelectionKey selectionKey = selectionKeyIterator.next();if (selectionKey.isAcceptable()){// 9.調(diào)用accept方法SocketChannel sChannel = ssChannel.accept();// 將sChannel設(shè)置為非阻塞// 再次強(qiáng)調(diào),整個(gè)過(guò)程不能有任何一條阻塞通道sChannel.configureBlocking(false);// 進(jìn)行數(shù)據(jù)接收工作,而且把sChannel也注冊(cè)上選擇器讓選擇器來(lái)監(jiān)聽(tīng)sChannel.register(selector,SelectionKey.OP_READ);}else if (selectionKey.isReadable()){// 如果讀狀態(tài)已經(jīng)準(zhǔn)備就緒,就開(kāi)始讀取數(shù)據(jù)// 10.獲取當(dāng)前選擇器上讀狀態(tài)準(zhǔn)備就緒的通道SocketChannel sChannel = (SocketChannel) selectionKey.channel();// 11.讀取客戶(hù)端發(fā)送的數(shù)據(jù),需要先創(chuàng)建緩沖區(qū)ByteBuffer byteBuffer = ByteBuffer.allocate(1024);// 12.讀取緩沖區(qū)的數(shù)據(jù)while (sChannel.read(byteBuffer) > 0){byteBuffer.flip();// 這里sChannel.read(byteBuffer)就是這個(gè)字節(jié)數(shù)組的長(zhǎng)度System.out.println(new String(byteBuffer.array(),0,sChannel.read(byteBuffer)));// 清空緩沖區(qū)byteBuffer.clear();}}// 當(dāng)selectionKey使用完畢需要移除,否則會(huì)一直優(yōu)先selectionKeyIterator.remove();}}

當(dāng)調(diào)用register方法將通道注冊(cè)到選擇器時(shí),選擇器對(duì)通道的監(jiān)聽(tīng)事件需要通過(guò)第二個(gè)參數(shù)ops決定

讀:SelectionKey.OP_READ(1)
寫(xiě):SelectionKey.OP_WRITE(4)
連接:SelectionKey.OP_CONNECT(8)
接收:SelectionKey.OP_ACCEPT(16)

若注冊(cè)時(shí)不僅僅只有一個(gè)監(jiān)聽(tīng)事件,則需要用位或操作符連接

int selectionKeySet = SelectionKey.OP_READ|SelectionKey.OP_WRITE

而關(guān)于這個(gè)selectionKey,它表示著SelectableChannel和Selectr之間的注冊(cè)關(guān)系。它也有一系列對(duì)應(yīng)的方法

2.3.3 客戶(hù)端的改造

引入Scanner接收輸入信息,不過(guò)請(qǐng)注意,在測(cè)試代碼中輸入IDEA需要進(jìn)行一些設(shè)置,具體做法是在Help-Edit Custom VM Option中加入一行
-Deditable.java.test.console=true

這樣就可以輸入了。

// 1.獲取通道,默認(rèn)是阻塞的
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("192.168.80.1",9898));// 1.1 將阻塞的套接字變成非阻塞
sChannel.configureBlocking(false);// 2.創(chuàng)建指定大小的緩沖區(qū)
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()){String str = scanner.next();// 3.發(fā)送數(shù)據(jù)給服務(wù)端,直接將數(shù)據(jù)存儲(chǔ)到緩沖區(qū)byteBuffer.put((new Date().toString()+str).getBytes());// 4.將緩沖區(qū)的數(shù)據(jù)寫(xiě)入到sChannelbyteBuffer.flip();sChannel.write(byteBuffer);byteBuffer.clear();
}
// 關(guān)閉
sChannel.close();

這樣就完成了一個(gè)問(wèn)答模式的網(wǎng)絡(luò)通信。

2.4 Pipe管道

Java NIO中的管道是兩個(gè)線(xiàn)程之間的單向數(shù)據(jù)連接,Pipe有一個(gè)source管道和一個(gè)sink管道,數(shù)據(jù)會(huì)被寫(xiě)到sink,從source中獲取

// 1.獲取管道
Pipe pipe = Pipe.open();// 2.創(chuàng)建緩沖區(qū)對(duì)象
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 3.獲取sink通道
Pipe.SinkChannel sinkChannel = pipe.sink();
byteBuffer.put("通過(guò)單向管道傳輸數(shù)據(jù)".getBytes());// 4.將數(shù)據(jù)寫(xiě)入sinkChannel
byteBuffer.flip();
sinkChannel.write(byteBuffer);
// 5.讀取緩沖區(qū)中的數(shù)據(jù)
Pipe.SourceChannel sourceChannel = pipe.source();
// 6.讀取sourceChannel中的數(shù)據(jù)放入到緩沖區(qū)
byteBuffer.flip();
sourceChannel.read(byteBuffer);
System.out.println(new String(byteBuffer.array(),0,sourceChannel.read(byteBuffer)));sourceChannel.close();
sinkChannel.close();運(yùn)行結(jié)果就是打印了我們的那串字符"通過(guò)單向管道傳輸數(shù)據(jù)"
http://www.risenshineclean.com/news/22826.html

相關(guān)文章:

  • 網(wǎng)站域名免費(fèi)申請(qǐng)網(wǎng)站排名優(yōu)化培訓(xùn)哪家好
  • 做 愛(ài) 網(wǎng)站小視頻下載查詢(xún)網(wǎng)官網(wǎng)
  • 中石化第五建設(shè)有限公司官方網(wǎng)站濰坊關(guān)鍵詞優(yōu)化平臺(tái)
  • wordpress商家展示主題企業(yè)網(wǎng)站的優(yōu)化建議
  • 社區(qū)工作者優(yōu)化設(shè)計(jì)方法
  • 保定市住房和城鄉(xiāng)建設(shè)局網(wǎng)站競(jìng)價(jià)廣告點(diǎn)擊軟件
  • 武漢黃浦醫(yī)院網(wǎng)站建設(shè)關(guān)鍵詞優(yōu)化排名平臺(tái)
  • 獨(dú)立做網(wǎng)站需要學(xué)什么條件關(guān)鍵詞優(yōu)化怎么寫(xiě)
  • 做代購(gòu)注冊(cè)什么網(wǎng)站搜索引擎優(yōu)化解釋
  • 臺(tái)州網(wǎng)站開(kāi)發(fā)太原網(wǎng)站優(yōu)化公司
  • 學(xué)會(huì)了vue 能搭建一個(gè)網(wǎng)站平臺(tái)比較靠譜的電商培訓(xùn)機(jī)構(gòu)
  • jsp開(kāi)發(fā)網(wǎng)站百度指數(shù)人群畫(huà)像哪里查詢(xún)
  • 網(wǎng)站備案的是域名還是空間電子商務(wù)網(wǎng)站建設(shè)與維護(hù)
  • 搭建網(wǎng)站難嗎電商培訓(xùn)學(xué)校
  • 玉溪做網(wǎng)站的公司seo的優(yōu)化步驟
  • 奪寶網(wǎng)站怎樣做優(yōu)化泰安做百度推廣的公司
  • 網(wǎng)站開(kāi)發(fā)有什么點(diǎn)子軟文生成器
  • 公司主頁(yè)網(wǎng)站怎么做免費(fèi)推廣軟件 推廣幫手
  • 服務(wù)器方面如何規(guī)劃建設(shè)網(wǎng)站外貿(mào)網(wǎng)站有哪些平臺(tái)
  • 政府門(mén)戶(hù)網(wǎng)站建設(shè)的基本意義有哪些網(wǎng)絡(luò)營(yíng)銷(xiāo)的概念和特點(diǎn)是什么
  • 網(wǎng)站建設(shè)項(xiàng)目合同如何做好網(wǎng)絡(luò)推廣
  • 網(wǎng)站建設(shè)網(wǎng)站維護(hù)的具體內(nèi)容是什么seo推廣員是做什么的
  • 實(shí)際網(wǎng)站開(kāi)發(fā)怎樣分工2023百度秒收錄技術(shù)
  • 北京環(huán)球影城每日客流量統(tǒng)計(jì)排名優(yōu)化公司口碑哪家好
  • 駐馬店廣告制作公司抖音seo教程
  • 自助建站系統(tǒng)個(gè)人網(wǎng)站怎樣開(kāi)自己的網(wǎng)站
  • 產(chǎn)品網(wǎng)站用什么軟件做百度一下網(wǎng)頁(yè)首頁(yè)
  • 注冊(cè)城鄉(xiāng)規(guī)劃師報(bào)考條件提高seo排名
  • css做電商網(wǎng)站首頁(yè)株洲seo優(yōu)化首選
  • crm 都免費(fèi)了城關(guān)網(wǎng)站seo