色婷婷综合在线,在线日韩欧美一区二区三区,欧美日韩中文字幕在线,一区国产视频,极品嫩模,h美女漫画网站,亚洲wuma

您的位置:首頁(yè)>熱點(diǎn)推薦 >

Java 網(wǎng)絡(luò)編程 —— 非阻塞式編程

2023-05-14 16:25:03    來(lái)源:博客園
線程阻塞概述

在生活中,最常見(jiàn)的阻塞現(xiàn)象是公路上汽車(chē)的堵塞。汽車(chē)在公路上快速行駛,如果前方交通受阻,就只好停下來(lái)等待,等到公路順暢,才能恢復(fù)行駛。


【資料圖】

線程在運(yùn)行中也會(huì)因?yàn)槟承┰蚨枞?。所有處于阻塞狀態(tài)的線程的共同特征:放棄 CPU,暫停運(yùn)行,只有等到導(dǎo)致阻塞的原因消除,才能恢復(fù)運(yùn)行,或者被其他線程中斷該線程會(huì)退出阻塞狀態(tài),并且拋出 InterruptedException

導(dǎo)致線程阻塞的原因主要有以下方面:

線程執(zhí)行了 Threadsleep(int n)方法,線程放棄 CPU,睡眠 n ms,然后恢復(fù)運(yùn)行線程要執(zhí)行一段同步代碼,由于無(wú)法獲得相關(guān)的同步鎖,只好進(jìn)入阻塞狀態(tài),等到獲取同步鎖再恢復(fù)運(yùn)行線程執(zhí)行了一個(gè)對(duì)象的 wait()方法,進(jìn)入阻塞狀態(tài),只有等到其他線程執(zhí)行了該對(duì)象的 notify()notifyAll()方法,才可能將其喚醒線程執(zhí)行 IO 操作或進(jìn)行遠(yuǎn)程通信時(shí),會(huì)因?yàn)榈却嚓P(guān)的資源而進(jìn)入阻塞狀態(tài)

進(jìn)行遠(yuǎn)程通信時(shí),在客戶程序中,線程在以下情況下可能進(jìn)入阻塞狀態(tài):

請(qǐng)求與服務(wù)器建立連接時(shí),即當(dāng)線程執(zhí)行 Socket 的帶參數(shù)的構(gòu)造方法,或執(zhí)行 Socke 的 connect()方法時(shí),會(huì)進(jìn)入阻塞狀態(tài),直到連接成功,此線程才從 Socket 的構(gòu)造方法或 connect()方法返回

線程從 Socket 的輸入流讀入數(shù)據(jù)時(shí),如果沒(méi)有足夠的數(shù)據(jù),就會(huì)進(jìn)入阻塞狀態(tài),直到讀到了足夠的數(shù)據(jù),或者到達(dá)輸入流的末尾,或者出現(xiàn)了異常,才從輸入流的 read()方法返回或異常中斷

輸入流中有多少數(shù)據(jù)才算足夠呢?這要看線程執(zhí)行的 read()方法的類(lèi):

int read():只要輸入流中有 1 字節(jié),就算足夠int read(byte[] buf):只要輸入流中的字節(jié)數(shù)目與參數(shù) buff 數(shù)組的長(zhǎng)度相同,就算足夠String readLine():只要輸入流中有 1 行字符,就算足夠

線程向 Socket 的輸出流寫(xiě)一批數(shù)據(jù)時(shí),可能會(huì)進(jìn)入阻塞狀態(tài),等到輸出了所有的數(shù)據(jù),或者出現(xiàn)異常,才從輸出流的 write()方法返回或異常中斷

如果調(diào)用 Socket 的 setSoLinger()方法設(shè)置了關(guān)閉 Socket 的延遲時(shí)間,那么當(dāng)線程執(zhí)行 Socket 的 close()方法時(shí),會(huì)進(jìn)入阻塞狀態(tài),直到底層 Socket 發(fā)送完所有剩余數(shù)據(jù)或者超過(guò)了 setSoLinger()方法設(shè)置的延遲時(shí)間,才從 close()方法返回

在服務(wù)器程序中,線程在以下情況下可能會(huì)進(jìn)入阻塞狀態(tài):

線程執(zhí)行 ServerSocket 的 accept()方法,等待客戶的連接,直到接收到了客戶連接才從 accept()方法返回線程從 Socket 的輸入流讀入數(shù)據(jù)時(shí),如果輸入流沒(méi)有足夠的數(shù)據(jù)就會(huì)進(jìn)入阻塞狀態(tài)線程向 Socket 的輸出流寫(xiě)一批數(shù)據(jù)時(shí),可能會(huì)進(jìn)入阻塞狀態(tài),等到輸出了所有的數(shù)據(jù),或者出現(xiàn)異常,才從輸出流的 write()方法返回或異常中斷

由此可見(jiàn),無(wú)論是在服務(wù)器程序還是客戶程序中,當(dāng)通過(guò) Socket 的輸入流和輸出流讀寫(xiě)數(shù)據(jù)時(shí),都可能進(jìn)入阻塞狀態(tài)。這種可能出現(xiàn)阻塞的輸入和輸出操作被稱(chēng)為阻塞 IO。與此對(duì)照,如果執(zhí)行輸入和輸出操作時(shí),不會(huì)發(fā)生阻塞,則稱(chēng)為非阻塞 IO

非阻塞通信的基本思想

假如同時(shí)要做兩件事:燒開(kāi)水和煮粥

燒開(kāi)水的步驟如下:

鍋?zhàn)永锓潘?,打開(kāi)煤氣爐等待水燒開(kāi) // 阻塞關(guān)閉煤氣爐,把開(kāi)水灌到水壺里

煮粥的步驟如下:

鍋?zhàn)永锓潘兔祝蜷_(kāi)煤氣爐等待粥煮開(kāi) // 阻塞調(diào)整煤氣爐,改為小火等待粥煮熟 // 阻塞關(guān)閉煤氣爐

為了同時(shí)完成兩件事,一種方案是同時(shí)請(qǐng)兩個(gè)人分別做其中的一件事,這相當(dāng)于采用多線程來(lái)同時(shí)完成多個(gè)任務(wù)。還有一種方案是讓一個(gè)人同時(shí)完成兩件事,這個(gè)人應(yīng)該善于利用一件事的空閑時(shí)間去做另一件事,這個(gè)人一刻也不應(yīng)該閑著:

鍋?zhàn)永锓潘?,打開(kāi)煤氣爐 // 開(kāi)始燒開(kāi)水鍋?zhàn)永锓潘兔?,打開(kāi)煤氣爐 // 開(kāi)始煮粥while(一直等待,直到有水燒開(kāi)、粥煮開(kāi)或粥煮熟事件發(fā)生) { // 阻塞if(水燒開(kāi))關(guān)閉煤氣爐,把開(kāi)水灌到水壺里;if((粥煮開(kāi))調(diào)整煤氣爐,改為小火;if(粥熟)關(guān)閉煤氣爐;}

這個(gè)人不斷監(jiān)控?zé)椭笾嗟臓顟B(tài),如果發(fā)生了條件中任一事件就去處理,處理完一件事后繼續(xù)監(jiān)控,直到所有的任務(wù)都完成

以上工作方式也可以被運(yùn)用到服務(wù)器程序中,服務(wù)器程序只需要一個(gè)線程就能同時(shí)接收客戶的連接、接收各個(gè)客戶發(fā)送的數(shù)據(jù),以及向各個(gè)客戶發(fā)送響應(yīng)數(shù)據(jù)。服務(wù)器程序的處理流程如下:

while(一直等待,直到有接收連接就緒事件、讀緒事件或?qū)懢途w事件發(fā)生) { //阻塞if(有客戶連接)接收客戶的連接; // 非阻塞if(某個(gè)socket的輸入流中有可讀數(shù)據(jù))從輸入流中讀數(shù)據(jù); // 非阻塞if(某個(gè)socket的輸出流可以寫(xiě)數(shù)據(jù))向輸出流寫(xiě)數(shù)據(jù); // 非阻塞}

以上處理流程采用了輪詢的工作方式,當(dāng)某一種操作就緒,就執(zhí)行該操作,否則就查看是否還有其他就緒的操作可以執(zhí)行。線程不會(huì)因?yàn)槟骋粋€(gè)操作還沒(méi)有就緒,就進(jìn)入阻塞狀態(tài),一直傻傻地在那里等待這個(gè)操作就緒

為了使輪詢的工作方式順利進(jìn)行,接收客戶的連接、從輸入流讀數(shù)據(jù),以及向輸出流寫(xiě)數(shù)據(jù)的操作都應(yīng)該以非阻寒的方式運(yùn)行。所謂非阻塞,指當(dāng)線程執(zhí)行這些方法時(shí),如果操作還沒(méi)有就緒,就立即返回,而不會(huì)一直等到操作就緒

非阻塞通信 API

java.nio.channels包提供了支持非阻塞通信的類(lèi),如下所述:

ServerSocketChannelServerSocket的替代類(lèi),支持阻塞通信與非阻塞通信SocketChannelSocket的替代類(lèi),支持阻塞通信與非阻塞通信Selector:為 ServerSocketChannel監(jiān)控接收連接就緒事件,為 SocketChannel監(jiān)控連接就緒、讀就緒和寫(xiě)就緒事件SelectionKey:代表 ServerSocketChannel以及 SocketChannelSelector注冊(cè)事件的句柄。當(dāng)一個(gè) SelectionKey對(duì)象位于 Selector對(duì)象的 selected-keys集合中,就表示與這個(gè) SelectionKey對(duì)象相關(guān)的事件發(fā)生了

ServerSocketChannelSocketChannel都是 SelectableChannel的子類(lèi),如圖所示,SelectableChannel類(lèi)及其子類(lèi)都能委托 Selector來(lái)監(jiān)控它們可能發(fā)生的一些事件,這種委托過(guò)程也被稱(chēng)為注冊(cè)事件過(guò)程

ServerSocketChannelSelector注冊(cè)接收連接就緒事件的代碼如下:

SelectionKey key = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

SelectionKey類(lèi)的一些靜態(tài)常量表示事件類(lèi)型,ServerSockerChamnel只可能發(fā)生一種事件:

SelectionKey.OP_ACCEPT:接收連接緒事件,表示至少有了一個(gè)客戶連接,服務(wù)器可以接收這個(gè)連接、

SocketChannel可能發(fā)生以下三種事件:

SelectionKey.OP_CONNECT:連接就緒事件,表示客戶與服務(wù)器的連接已經(jīng)建立成功SelectionKey.OP_READ:讀就緒事件,表示輸入流中已經(jīng)有了可讀數(shù)據(jù),可以執(zhí)行讀操作了SelectionKey.OP_WRITE: 寫(xiě)就緒事件,表示已經(jīng)可以向輸出流寫(xiě)數(shù)據(jù)了

SocketChannel提供了接收和發(fā)送數(shù)據(jù)的方法:

read(ByteBuffer buffer):接收數(shù)據(jù),把它們存放到參數(shù)指定的 ByteBufferwrite(ByteBuffer buffer):把參數(shù)指定的 ByteBuffer 中的數(shù)據(jù)發(fā)送出去

ByteBuffer表示字節(jié)緩沖區(qū),SocketChannelread()write()方法都會(huì)操縱 ByteBuffer。ByteBuffer類(lèi)繼承于 Buffer類(lèi)。ByteBuffer中存放的是字節(jié),為了把它們轉(zhuǎn)換為字符串還需要用到 Charset類(lèi),Charset類(lèi)代表字符編碼,它提供了把字節(jié)流轉(zhuǎn)換為字符串(解碼過(guò)程)和把字符串轉(zhuǎn)換為字節(jié)流(編碼過(guò)程)的實(shí)用方法

下面分別介紹 Buffer、Charset、SelectableChannel、ServerSocketChannel、SocketChannel、SelectorSelectionKey的用法

緩沖區(qū) Buffer

數(shù)據(jù)輸入和輸出往往是比較耗時(shí)的操作,緩沖區(qū)從兩個(gè)方面提高 I/O 操作的效率:

減少實(shí)際的物理讀寫(xiě)次數(shù)緩沖區(qū)在創(chuàng)建時(shí)被分配內(nèi)存,這塊內(nèi)存區(qū)域一直被重用,這可以減少動(dòng)態(tài)分配和回收內(nèi)存區(qū)域的次數(shù)

java.nio包公開(kāi)了 Buffer類(lèi)的 API,使得 Java 程序可以直接控制和運(yùn)用緩沖區(qū),所有的緩沖區(qū)都有以下屬性:

容量(capacity):表示緩沖區(qū)可以保存多少數(shù)據(jù)極限(limit):表示緩沖區(qū)的當(dāng)前終點(diǎn),不能對(duì)緩沖區(qū)中超過(guò)極限的區(qū)域進(jìn)行讀寫(xiě)操作位置(position):表示緩沖區(qū)中下一個(gè)讀寫(xiě)單元的位置

以上三個(gè)屬性的關(guān)系為:容量 > 極限 >= 位置 >= 0

緩沖區(qū)提供了用于改變以上三個(gè)屬性的方法:

// 把極限設(shè)為容量,把位置設(shè)為0clear();// 把極限設(shè)為位置,把位置設(shè)為 0flip();// 不改變極限,把位置設(shè)為0rewind();

Buffer類(lèi)的 remaining()方法返回緩沖區(qū)的剩余容量,取值等于 極限 - 位置

Buffer類(lèi)的 compact()方法刪除緩沖區(qū)內(nèi)從 0 到當(dāng)前位置 position 的內(nèi)容,然后把從當(dāng)前位置 position 到極限limit 的內(nèi)容拷貝到 0 到 limit - position 的區(qū)域內(nèi)

java.nio.Buffer類(lèi)是一個(gè)抽象類(lèi),不能被實(shí)例化。它共有 8 個(gè)具體的緩沖區(qū)類(lèi),其中最基本的緩沖區(qū)是 ByteBuffer,它存放的數(shù)據(jù)單元是字節(jié),ByteBufer類(lèi)并沒(méi)有提供公開(kāi)的構(gòu)造方法,但是提供了兩個(gè)獲得 ByteBuffer實(shí)例的靜態(tài)工廠方法:

// 返回一個(gè)ByteBuffer對(duì)象,參數(shù)capacity指定緩沖區(qū)的容量allocate(int capacity);// 返回一個(gè)ByteBuffer對(duì)象,參數(shù)capacity指定緩沖區(qū)的容量// 該方法返回的緩沖區(qū)被稱(chēng)為直接緩沖區(qū),能進(jìn)一步提高 I/O 操作的速度// 分配直接緩沖區(qū)的系統(tǒng)開(kāi)銷(xiāo)很大,因此只有在緩沖區(qū)較大并且長(zhǎng)期存在,或經(jīng)常重用時(shí),才使用該緩沖區(qū)directAllocate(int capacity);

除 boolean 類(lèi)型以外,每種基本類(lèi)型都有對(duì)應(yīng)的緩沖區(qū)類(lèi),包括 CharBufferDoubleBuffer,FloatBufferIntBuffer,LongBufferShortBuffer。在 CharBuffer中存放的數(shù)據(jù)單元為字符,以此類(lèi)推。還有一種緩沖區(qū)是 MappedByteBuffer,它是 ByteBuffer的子類(lèi),能夠把緩沖區(qū)和文件的某個(gè)區(qū)域直接映射

所有具體緩沖區(qū)類(lèi)都提供了讀寫(xiě)緩沖區(qū)的方法:

// 相對(duì)讀,從緩沖區(qū)的當(dāng)前位置讀取一個(gè)單元的數(shù)據(jù),讀完后把位置加1get();// 絕對(duì)讀,從參數(shù) index 指定的位置讀取一個(gè)單元的數(shù)據(jù)get(int index);// 相對(duì)寫(xiě),向緩沖區(qū)的當(dāng)前位置寫(xiě)一個(gè)單元的數(shù)據(jù),寫(xiě)完后把位置加1put(單元數(shù)據(jù)類(lèi)型 data);// 絕對(duì)寫(xiě),向參數(shù)index指定的位置寫(xiě)入一個(gè)單元的數(shù)據(jù)put(int index, 單元數(shù)據(jù)類(lèi)型 data);

ByteBuffer類(lèi)不僅可以讀取和寫(xiě)入一個(gè)單元的字節(jié),還可以讀取和寫(xiě)入 int、char、float 和 double 等基本類(lèi)型的數(shù)據(jù),例如:

getInt()getInt(int index)

以上不帶 index 參數(shù)的方法會(huì)在當(dāng)前位置讀取或?qū)懭霐?shù)據(jù),稱(chēng)為相對(duì)讀寫(xiě)。帶 index 參數(shù)的方法會(huì)在 index 參數(shù)指定的位置讀取或?qū)懭霐?shù)據(jù),稱(chēng)為絕對(duì)讀寫(xiě)

字符編碼 Charset

java.nio.Charset類(lèi)的每個(gè)實(shí)例代表特定的字符編碼類(lèi)型,把字節(jié)序列轉(zhuǎn)換為字符串的過(guò)程稱(chēng)為解碼,把字符串轉(zhuǎn)換為字節(jié)序列的過(guò)程稱(chēng)為編碼

Charset類(lèi)提供了編碼與解碼的方法:

// 對(duì)參數(shù)str指定的字符串進(jìn)行編碼,把得到的字節(jié)序列存放在一個(gè)ByteBuffer對(duì)象并將其返回ByteBuffer encode(String str);// 對(duì)參數(shù)cb指定的字符緩沖區(qū)中的字符進(jìn)行編碼,把得到的字節(jié)序列存放在一個(gè)ByteBuffer對(duì)象并將其返回ByteBuffer encode(CharBuffer cb);// 對(duì)參數(shù)bb指定的ByteBuffer的字節(jié)序列進(jìn)行解碼,把得到的字符序列存放在一個(gè)CharBuffer對(duì)象并將其返回CharBuffer decode(ByteBuffer bb);

Charset類(lèi)的靜態(tài) forName(String encode)方法返回一個(gè) Charset對(duì)象,參數(shù) encode指定編碼類(lèi)型。例如以下代碼創(chuàng)建了一個(gè)代表 GBK 編碼的 Charset對(duì)象

Charset charset = Charset.forName("GBK");

Charset類(lèi)還有一個(gè)靜態(tài)方法 defaultCharset(),它返回代表本地平臺(tái)的默認(rèn)字符編碼的 Charset對(duì)象

通道 Channel

通道(Channel)用來(lái)連接緩沖區(qū)與數(shù)據(jù)源或數(shù)據(jù)匯(即數(shù)據(jù)目的地),數(shù)據(jù)源的數(shù)據(jù)經(jīng)過(guò)通道到達(dá)緩沖區(qū),緩沖區(qū)的數(shù)據(jù)經(jīng)過(guò)通道到達(dá)數(shù)據(jù)匯

Channel 的主要層次結(jié)構(gòu)如下:

java.nio.channels.Channel接口只聲明了兩個(gè)方法:

// 關(guān)閉通道close();// 判斷通道是否打開(kāi)isOpen();

Channel接口的兩個(gè)最重要的子接口是 ReadableByteChannelWritableByteChannel。ReadableByteChannel接口聲明了 read(ByteBuffer dst)方法,該方法把數(shù)據(jù)源的數(shù)據(jù)讀入?yún)?shù)指定的 ByteBuffer緩沖區(qū)中。WritableByteChannel接口聲明了 write(ByteBuffer src)方法,該方法把參數(shù)指定的 ByteBuffer緩沖區(qū)中的數(shù)據(jù)寫(xiě)到數(shù)據(jù)匯中

ByteChannel接口是一個(gè)便利接口,它擴(kuò)展了 ReadableByteChannelWritableByteChannel接口,因而同時(shí)支持讀寫(xiě)操作

ScatteringByteChannel接口擴(kuò)展了 ReadableByteChannel接口,允許分散地讀取數(shù)據(jù)。分散讀取數(shù)據(jù)指單個(gè)讀取操作能填充多個(gè)緩沖區(qū),ScatteringByteChannel接口聲明了 read(ByteBuffer[] dsts)方法,該方法把從數(shù)據(jù)源讀取的數(shù)據(jù)依次填充到參數(shù)指定的各個(gè) ByteBuffer

GatheringByteChannel擴(kuò)展了 WritableByteChannel接口,允許集中地寫(xiě)入數(shù)據(jù)。集中寫(xiě)入數(shù)據(jù)指單個(gè)寫(xiě)操作能把多個(gè)緩沖區(qū)的數(shù)據(jù)寫(xiě)到數(shù)據(jù), GatheringByteChannel接口聲明了 write(ByteBuffer[] srcs)方法,該方法依次把參數(shù)指定的每個(gè) ByteBuffer中的數(shù)寫(xiě)到數(shù)據(jù)匯

FileChannel類(lèi)是 Channel接口的實(shí)現(xiàn)類(lèi),代表一個(gè)與文件相連的通道。該類(lèi)實(shí)現(xiàn)了 ByteChannel、ScatteringByteChannelGatheringByteChannel接口,支持讀操作、寫(xiě)操作、分散讀操作和集中寫(xiě)操作。FileChannel類(lèi)沒(méi)有提供公開(kāi)的構(gòu)造方法,因此不能用 new語(yǔ)句來(lái)構(gòu)造它的實(shí)例。不過(guò),在FileInputStream、FileOutputStreamRandomAccessFile類(lèi)中提供了 getChannel()方法,該方法返回相應(yīng)的 FileChannel對(duì)象

SelectableChannel也是一種通道,它不僅支持阻塞的 I/O操作,還支持非阻塞的 I/OSelectableChannel有兩個(gè)子類(lèi),ServerSocketChannelSocketChannel。SocketChannel還實(shí)現(xiàn)了 ByteChannel接口,具有 read(ByteBuffer dst)write(ByteBuffer src)方法

1. SelectableChannel 類(lèi)

SelectableChannel是一種支持阻塞 IO 和非阻塞 IO 的通道。在非阻塞模式下,讀寫(xiě)數(shù)據(jù)不會(huì)阻塞,并且 SelectableChannel可以向 Selector注冊(cè)讀就緒和寫(xiě)就緒等事件。Selector負(fù)責(zé)監(jiān)控這些事件,等到事件發(fā)生時(shí),比如發(fā)生了讀就緒事件,SelectableChannel就可以執(zhí)行讀操作了

SelectableChannel的主要方法如下:

// 當(dāng)參數(shù)block為true,表示把SelectableChannel設(shè)為阻塞模式// 當(dāng)參數(shù)block為false時(shí),表示把SelectableChannel設(shè)為非阻塞模式// SelectableChannel默認(rèn)采用阻塞模式// 該方法返回SelectableChannel對(duì)象本身的引用,相當(dāng)于return thispublic SelectableChannel configureBlocking(boolean block) throws IOException// 以下兩個(gè)方法都向Selector注冊(cè)事件public SelectionKey register(Selector sel,int ops) throws ClosedChannelExceptionpublic SelectionKey register(Selector sel,int ops,Object attachment) throws ClosedChannelException

以下是 socketChannelSelector注冊(cè)讀就緒和寫(xiě)就緒事件

SelectionKey key = socketChannel.register(selector.SelectionKey.OP_READ | SelectionKey.OP_WRITE);

register()方法返回一個(gè) SelectionKey對(duì)象,SeletionKey被用來(lái)跟蹤被注冊(cè)的事件。第二個(gè) register()方法還有一個(gè) Object類(lèi)型的參數(shù) attachment,用于為 SelectionKey關(guān)聯(lián)附件,當(dāng)被注冊(cè)事件發(fā)生后,需要處理該事件時(shí),可以從 SelectionKey中獲得這個(gè)附件,該附件可用來(lái)包含與處理這個(gè)事件相關(guān)的信息

2. ServerSocketChannel 類(lèi)

ServerSocketChannel繼承自 SelectableChannel,是 ServerSocket的替代類(lèi),通過(guò)它的靜態(tài)方法 open()來(lái)創(chuàng)建。每個(gè) ServerSockeChannel對(duì)象都與一個(gè) ServerSocket對(duì)象關(guān),通過(guò) socket()方法返回與它關(guān)聯(lián)的 ServerSocket對(duì)象??赏ㄟ^(guò)以下方式把服務(wù)器進(jìn)程綁定到一個(gè)本地端口:

serverSocketChannel.socket().bind(port);

ServerSocketChannel的主要方法如下:

// 返回一個(gè)ServerSocketChannel對(duì)象,該對(duì)象沒(méi)有與任何本地端口綁定,并且處于阻塞模式public static ServerSocketChannel open() throws IOException// 用于接收客戶的連接,如果處于非阻塞狀態(tài),當(dāng)沒(méi)有客戶連接時(shí)就立即返回nullpublic SocketChannel accept() throws IOException// 返回ServerSocketChannel所能產(chǎn)生的事件,這個(gè)方法總是返回SelectionKey.OP_ACCEPTpublic final int validOps()// 返回ServerSocketChannel關(guān)聯(lián)的ServerSocket對(duì)象public ServerSocket socket()
3. SocketChannel類(lèi)

SockeChannel可以被看作是 Socket的替代類(lèi),SockeChannel不僅繼承了 SelectableChannel,而且實(shí)現(xiàn)了 ByteChannel。SockeChannel同樣通過(guò)它的靜態(tài)方法 open()來(lái)創(chuàng)建

public static SocketChannel open() throws IOException// 帶參數(shù)的構(gòu)造方法還會(huì)建立與遠(yuǎn)程服務(wù)器的連接public static SocketChannel open(SocketAddress remote) throws IOException

SocketChannel的主要方法如下:

// 返回ServerSocketChannel所能產(chǎn)生的事件,這個(gè)方法總是返回以下值// SelectionKey.OP_CONNECT | SelectionKey.OP_READ | SelectionKey.OP_WRITEpublic final int validOps()// 返回SocketChannel關(guān)聯(lián)的Socket對(duì)象public Socket socket()// 建立遠(yuǎn)程連接,當(dāng)處于非阻塞模式時(shí),如果立即連接成功返回true,否則返回falsepublic boolean connect(SocketAddress remote) throws IOException// 判斷底層Socket是否已經(jīng)建立遠(yuǎn)程連接public boolean isConnected()// 判斷是否正在進(jìn)行遠(yuǎn)程連接,如果遠(yuǎn)程連接操作已經(jīng)開(kāi)始,但還沒(méi)有完成,則返回true,否則返回false// 也就是說(shuō),無(wú)論底層Socket還沒(méi)有開(kāi)始連接,或者已經(jīng)連接成功,該方法都會(huì)返回falsepublic boolean isConnectionPending()// 試圖完成連接遠(yuǎn)程服務(wù)器的操作// 非阻塞模式下,建立連接從調(diào)用connect()方法開(kāi)始,到調(diào)用finishConnect()方法結(jié)束// 如果在調(diào)用此方法之前連接已經(jīng)建立,則立即返回true,否則立即返回false// 阻塞模式下,如果連接操作還沒(méi)有完成,則會(huì)進(jìn)入阻塞狀態(tài),直到連接完成,或者出現(xiàn)IO異常public boolean finishConnect) throws IOException// 從Channel讀入若干字節(jié),存放到參數(shù)指定的ByteBuffer// 假設(shè)ByteBuffer剩余容量為r,阻塞模式下,該方法會(huì)爭(zhēng)取讀到r字節(jié)// 如果輸入流中不足r字節(jié),就進(jìn)入阻塞狀態(tài),直到讀入了r字節(jié),或者讀到了輸入流末尾,或者出現(xiàn)了IO異常// 非阻塞模式下,該方法奉行能讀到多少數(shù)據(jù)就讀多少數(shù)據(jù)的原則// 通道中的可讀數(shù)據(jù),有可能不足r字節(jié),或者為0字節(jié),總是立即返回// 該方法返回實(shí)際上讀入的字節(jié)數(shù),有可能為0,如果返回-1,表示讀到了輸入流的末尾public int read(ByteBuffer dst) throws IOException// 把參數(shù)src指定的ByteBuffer的字節(jié)寫(xiě)到Channel// 假設(shè)ByteBuffer剩余容量為r,阻塞模式下,該方法會(huì)爭(zhēng)取輸出r字節(jié)// 如果底層網(wǎng)絡(luò)的輸出緩沖區(qū)不能容納r字節(jié),就進(jìn)入阻塞狀態(tài),直到輸出了r字節(jié),或者出現(xiàn)了IO異常// 非阻塞模式下,該方法奉行能輸出多少數(shù)據(jù)就輸出多少數(shù)據(jù)的原則,有可能不足r字節(jié),或者為0字節(jié),總是立即返回// 該方法返回實(shí)際上輸出的字節(jié),有可能為0public int write(ByteBuffer src) throws IOException
Selector 類(lèi)

只要 ServerSockerChannel以及 SocketChannelSelector注冊(cè)了特定的事件,Selector就會(huì)監(jiān)控這些事件是否發(fā)生。SelectableChannelregister()方法負(fù)責(zé)注冊(cè)事件,該方法返回 SelectionKey對(duì)象,該對(duì)象是用于跟蹤這些被注冊(cè)事件的句柄

Selector對(duì)象中會(huì)包含三種類(lèi)型的 SelectionKey的集合:

all-keys:當(dāng)前所有向 Selector注冊(cè)的 SelectionKey的集合,Selectorkeys()方法返回該集合selected-keys:相關(guān)事件已經(jīng)被 Selector捕獲的 SelectionKey的集合,SelectorselectedKeys()方法返回該集合cancelled-keys:已經(jīng)被取消的 SelectionKey的集合,Selector沒(méi)有提供訪問(wèn)這種集合的方法

當(dāng)執(zhí)行 SelectableChannelregiste()方法,會(huì)新建一個(gè) SelectionKey并加入 Selectorall-keys集合中。如果關(guān)閉了與 SelectionKey對(duì)象關(guān)聯(lián)的 Channel對(duì)象,或者調(diào)用了 SelectionKey對(duì)象的 cancel()方法,那么這個(gè) SelectionKey對(duì)象就會(huì)被加入 cancelled-keys集合,表示已經(jīng)被取消,在程序下一次執(zhí)行 Selectorselect()方法時(shí),被取消的 SelectionKey對(duì)象將從所有的集合(包括 all-keys集合、selected-keys集合和 cancelled-keys集合)中被刪除

在執(zhí)行 Selectorselect()方法時(shí),如果與 SelectionKey相關(guān)的事件發(fā)生了,這個(gè) SelectionKey就被加入 selected-keys集合中。程序直接調(diào)用 selected-keys集合的 remove()方法,或者調(diào)用它的 Iteratorremove()方法,都可以從 selected-keys集合中刪除一個(gè) SelectionKey對(duì)象

程序不允許直接通過(guò)集合接口的 remove()方法刪除 all-keys集合中的 SelectionKey對(duì)象,這會(huì)導(dǎo)致 UnsupportedOperationException

Selector類(lèi)的主要方法如下:

// Selector的靜態(tài)工廠方法,創(chuàng)建一個(gè)Selector對(duì)象public static Selector open() throws IOException// 判斷Selector是否處于打開(kāi)狀態(tài),Selector對(duì)象創(chuàng)建后就處于打開(kāi)狀態(tài),當(dāng)調(diào)用close()方法就進(jìn)入關(guān)閉狀態(tài)public boolean isOpen()// 返回Seleclor的all-keys集合,包含了所有與Seclector關(guān)聯(lián)的SelectionKey對(duì)象public Set keys()// 返回相關(guān)事件已經(jīng)發(fā)生的SelectionKey對(duì)象的數(shù)目// 該方法采用非阻塞的工作方式,返回當(dāng)前相關(guān)事件已經(jīng)發(fā)生的SelectionKey對(duì)象的數(shù)目,如果沒(méi)有,就立即返回0public int selectNow() throws IOException// 返回相關(guān)事件已經(jīng)發(fā)生的SelectionKey對(duì)象的數(shù)目// 該方法采用阻塞的工作方式,如果一個(gè)也沒(méi)有,就進(jìn)入阻塞狀態(tài),直到出現(xiàn)以下情況之一,就會(huì)從select()返回:// 1.至少有一個(gè)SelectionKey的相關(guān)事件已經(jīng)發(fā)生// 2.其他線程調(diào)用了Selector的wakeup()方法// 3.當(dāng)前執(zhí)行select()方法的線程被其他線程中斷// 4.超出了等待時(shí)間public int select() throws IOExceptionpublic int select(long timeout) throws IOException// 喚醒執(zhí)行Selector的select()方法 public Selector wakeup()// 關(guān)閉 Selector// 如果有其他線程正執(zhí)行這個(gè)Selector的select()方法并且處于阻塞狀態(tài),這個(gè)線程會(huì)立即返回// close()方法使得Selector占用的所有資源都被釋敗,所有關(guān)聯(lián)的SelectionKey都被取消public void close() throws IOException
SelectionKey 類(lèi)

SelectionKey中定義了四種事件,分別用四個(gè) int 類(lèi)型的常量來(lái)表示:

SelectionKey.OP_ACCEPT:接收連接就緒事件,表示服務(wù)器監(jiān)聽(tīng)到了客戶連接,服務(wù)器可以接收這個(gè)連接了,常量值為 16SeiectionKey.OP_CONNECT:連接就緒事件表示客戶與服務(wù)器的連接已經(jīng)建立成功,常量值為 8SelectionKey.OP_READ:讀就緒事件,表示通道中已經(jīng)有了可讀數(shù)據(jù),可以執(zhí)行讀操作了,常量值為 1SelectionKey.OP_WRITE:寫(xiě)就緒事件表示已經(jīng)可以向通道寫(xiě)數(shù)據(jù)了,常量值為 4

以上常量分別占據(jù)不同的二進(jìn)制位,因此可以通過(guò)二進(jìn)制的或運(yùn)算來(lái)將它們進(jìn)行任意組合

一個(gè) SelectionKey對(duì)象中包含兩種類(lèi)型的事件:

所有感興趣的事件:通過(guò) SelectableChannelregister()方法注冊(cè)事件時(shí),可以在參數(shù)中指定 SelectionKey感興趣的事件

SelectionKey key = socketChannel.register(selector,SelectionKey.OP_CONNECT | SelectionKey.OP_READ);

該代碼表示這個(gè) SelectionKey對(duì)讀就緒和寫(xiě)就緒事件感興趣,與之關(guān)聯(lián)的 Selector對(duì)象會(huì)負(fù)責(zé)監(jiān)控這些事件

SelectionKey的帶參數(shù)的 interestOps(int ops)方法也可以為 SelectionKey對(duì)象增加一個(gè)感興趣的事件,如下代碼所示:

key.interestOps(SelectionKey.OP_WRITE);

所有已經(jīng)發(fā)生的事件:SeletionKeyreadyOps()方法返回所有已經(jīng)發(fā)生的事件,例如假定返回值為 SelectionKey.OP_WRITE | SelectionKey.OP_READ,表示讀就緒和寫(xiě)就緒事件已經(jīng)發(fā)生了,這意味著與之關(guān)聯(lián)的 SocketChannel對(duì)象可以進(jìn)行讀操作和寫(xiě)操作了

SelectionKey的主要方法如下:

// 返回與這個(gè)SelectionKey對(duì)象關(guān)聯(lián)的SelectableChannel對(duì)象public SelectableChannel channel()// 返回與這個(gè)SelectionKey對(duì)象關(guān)聯(lián)的Selector對(duì)象public Selector selector()// 判斷這個(gè)SelectionKey是否有效// 當(dāng)SelectionKey對(duì)象創(chuàng)建后,它就一直處于有效狀態(tài)// 如果調(diào)用了它的cancel()方法,或關(guān)閉了與它關(guān)聯(lián)的SelectableChannel或Selector對(duì)象,它就失效public boolean isValid()// 使SelectionKey對(duì)象失效public void cancel()// 返回這個(gè)SelectionKey感興趣的事件public int interestOps()// 為SelectionKey增加感興趣的事件public SelectionKey interestOps(int ops)// 返回已經(jīng)就緒的事件public int readyOps()// 判斯與之關(guān)聯(lián)的SocketChannel的讀就緒事件是否已經(jīng)發(fā)生public final boolean isReadable()// 判斷與之關(guān)聯(lián)的SocketChannel的寫(xiě)就緒事件是否已經(jīng)發(fā)生public final boolean isWritable()// 判斷與之關(guān)聯(lián)的SocketChannel的連接就緒事件是否已經(jīng)發(fā)生public final boolean isConnectable()// 判斷與之關(guān)聯(lián)的ServerSocketChannel的接收連接就緒事件是否已經(jīng)發(fā)生public final boolean isAcceptable()// 使SelectionKey關(guān)聯(lián)一個(gè)附件,一個(gè)SelectionKey對(duì)象只能關(guān)聯(lián)一個(gè)Object類(lèi)型的附件// 如果多次調(diào)用該方法,則只有最后一個(gè)附件與SelectionKey對(duì)象關(guān)聯(lián)public final Object attach(Object obj)// 返回與SelectionKey對(duì)象關(guān)聯(lián)的附件public final Object attachment()
Channels 類(lèi)

Channels類(lèi)是一個(gè)簡(jiǎn)單的工具類(lèi),提供了通道與傳統(tǒng)的基于 IO 的流、ReaderWriter之間進(jìn)行轉(zhuǎn)換的靜態(tài)方法

ReadableByteChannel newChannel(InputStream in) // 輸入流轉(zhuǎn)換成讀通道WritableByteChannel newChannel(OutputStream out) // 輸出流轉(zhuǎn)換成寫(xiě)通道InputStream newInputStream(AsynchronousByteChannel ch) // 異步通道轉(zhuǎn)換成輸入流InputStream newInputStream(ReadableByteChannel ch) // 讀通道轉(zhuǎn)換成輸入流OutputStream newOutputStream(AsynchronousByteChannel ch) // 異步通道轉(zhuǎn)換成輸出流OutputStream newOutputStream(WritableByteChannel ch) // 寫(xiě)通道轉(zhuǎn)換成輸出流Reader newReader(ReadableByteChannel ch,String csName) // 讀通道轉(zhuǎn)換成Reader,參數(shù)csName指定字符編碼Reader newReader(ReadableByteChannel ch,Charset charset)//讀通道轉(zhuǎn)換成Reader.參數(shù)charset指定字符編碼Reader newReader(ReadableByteChannel ch,CharsetDecoder dec, int minBufferCap) // 讀通道轉(zhuǎn)換成 Reader,參數(shù)dec指定字符解碼器,參數(shù)minBufferCap指定內(nèi)部字節(jié)緩沖區(qū)的最小容量Writer newWriter(WritableByeChannel ch, String csName) // 寫(xiě)通道轉(zhuǎn)換Writer.參數(shù)csName指定字符編碼Writer newWriter(WritableByeChannel ch, Charset charset) // / 寫(xiě)通道轉(zhuǎn)換Writer.參數(shù)charset指定字符編碼Writer newWriter(WritableByeChannel ch, CharsetEncoder enc, int minBufferCap) // 寫(xiě)通道轉(zhuǎn)換成Writer,參數(shù)dec指定字符解碼器,參數(shù)minBufferCap指定內(nèi)部字節(jié)緩沖區(qū)的最小容量
Socket 選項(xiàng)

從 JDK7 開(kāi)始,SocketChannel、ServerSocketChannelAsynchronousSocketChannel、AsynchronousServerSocketChannelDatagramChannel都實(shí)現(xiàn)了新的 NetworkChannel接口。NetworkChannel接口的主要作用是設(shè)置和讀取各種 Socket 選項(xiàng)

NetworkChannel接口提供了用于設(shè)置和讀取這些選項(xiàng)的方法:

 T getOption(SocketOption name) // 獲取特定的Socket選項(xiàng)值 NetworkChannel setOption(SocketOption name, T value) // 設(shè)置特定的Socket選項(xiàng)Set> supportedOptions() // 獲取所有支持的Socket選項(xiàng)

SocketOptionl類(lèi)是一個(gè)泛型類(lèi),SocketOption中的 T代表特定選項(xiàng)的取值類(lèi)型,可選值包括 Integer、BooleanNetworkInterface

StandardSocketOptions類(lèi)提供了以下表示特定選項(xiàng)的常量:

SocketOption  --  StandardSocketOptions.IP_MULTICAST_IFSocketOption  --  StandardSocketOptions.IP_MULTICAST_LOOPSocketOption  --  StandardSocketOptions.IP_MULTICAST_TTLSocketOption  --  StandardSocketOptions.IP_TOSSocketOption  --  StandardSocketOptions.SO_BROADCASTSocketOption  --  StandardSocketOptions.SO_KEEPALIVESocketOption  --  StandardSocketOptions.SO_LINGERSocketOption  --  StandardSocketOptions.SO_RCVBUFSocketOption  --  StandardSocketOptions.SO_REUSEADDRSocketOption  --  StandardSocketOptions.SO_REUSEPORTSocketOption  --  StandardSocketOptions.SO_SNDBUFSocketOption  --  StandardSocketOptions.TCP_NODELAY

關(guān)鍵詞:

相關(guān)閱讀