淺析Java之BIO、NIO、AIO
本文將介紹Java中幾種常見的網(wǎng)絡(luò)編程模型

對(duì)于一個(gè)網(wǎng)絡(luò)IO而言,其操作流程大體可分為兩個(gè)階段。這里以read操作為例進(jìn)行說明,write操作同理
- 數(shù)據(jù)準(zhǔn)備階段:等待數(shù)據(jù)從網(wǎng)絡(luò)中到達(dá),并將數(shù)據(jù)拷貝到內(nèi)核的Socket接收緩沖區(qū)
- 數(shù)據(jù)拷貝階段:將數(shù)據(jù)從內(nèi)核空間拷貝到用戶空間
對(duì)于阻塞IO、非阻塞IO而言,其描述的是在第一個(gè)階段——數(shù)據(jù)準(zhǔn)備階段,發(fā)起IO請(qǐng)求的進(jìn)程/線程是否會(huì)被阻塞。具體地,對(duì)于阻塞IO而言,如果內(nèi)核接收緩沖區(qū)沒有數(shù)據(jù),則進(jìn)程/線程會(huì)一直等待,直到內(nèi)核接收緩沖區(qū)有數(shù)據(jù)為止;而對(duì)于非阻塞IO而言,如果內(nèi)核接收緩沖區(qū)沒有數(shù)據(jù),則進(jìn)程/線程會(huì)立即返回,而不是一直進(jìn)行等待。故對(duì)于非阻塞IO而言,可通過輪詢的方式檢查當(dāng)前是否有數(shù)據(jù)到達(dá)
而對(duì)于同步、異步IO而言,其描述的是在第二個(gè)階段——數(shù)據(jù)拷貝階段,是否需要由用戶線程來執(zhí)行。具體地,同步IO是由用戶線程的內(nèi)核態(tài)來執(zhí)行第二階段;而異步IO則是由內(nèi)核自己完成第二個(gè)階段。在內(nèi)核完成數(shù)據(jù)拷貝后通知用戶線程,同時(shí)將數(shù)據(jù)以回調(diào)的形式傳遞給用戶線程。顯然對(duì)于同步IO而言,第二個(gè)階段的數(shù)據(jù)拷貝是由用戶線程參與完成的,故在第二個(gè)階段會(huì)發(fā)生阻塞;而對(duì)于異步IO而言,數(shù)據(jù)拷貝由于是內(nèi)核自己完成的,故在第二個(gè)階段不會(huì)發(fā)生阻塞
網(wǎng)絡(luò)IO模型大致可分為兩類五種,其中同步IO有四種,異步IO有一種:
- 對(duì)于Bloking IO阻塞IO模型而言,其在Java中就是經(jīng)典的BIO。其特征為阻塞同步IO
- 對(duì)于Non-Blocking IO非阻塞IO模型而言,由于需要用戶線程不斷發(fā)起系統(tǒng)調(diào)用,頻繁地在內(nèi)核空間與用戶空間之間進(jìn)行切換,性能較低,故很少得到應(yīng)用
- 相比較Non-Blocking IO非阻塞IO模型,取而代之的是Multiplexing IO多路復(fù)用IO技術(shù)。其將前者頻繁地輪詢操作交由操作系統(tǒng)內(nèi)核來完成,是目前高并發(fā)網(wǎng)絡(luò)應(yīng)用的主流技術(shù)手段。其在Java中對(duì)應(yīng)的就是Java 1.4中引入的NIO,其特征為非阻塞同步IO
- 對(duì)于Signal-Driven IO信號(hào)驅(qū)動(dòng)IO模型而言,由于其不適用于TCP協(xié)議,故也很少被使用
- 對(duì)于Asynchronous IO異步IO模型來說,其在Java中對(duì)應(yīng)的就是Java 1.7中引入的AIO,其特征為非阻塞異步IO

在Java 1.4之前BIO是Java網(wǎng)絡(luò)編程唯一的選擇,其特點(diǎn)是阻塞同步。由于accept、read方法均是阻塞操作。如果沒有連接請(qǐng)求,accept方法阻塞;如果無數(shù)據(jù)可讀取,read方法阻塞。故服務(wù)端側(cè)需要為每一個(gè)客戶端連接都提供一個(gè)線程。顯然在BIO模型下,如果存在大量客戶端連接,勢(shì)必增大服務(wù)端的壓力。甚至在極端情況下服務(wù)端會(huì)由于開啟的線程過多而最終宕機(jī),故為了保險(xiǎn)起見。最佳的實(shí)踐方式是通過線程池來提供、維護(hù)與客戶端通信所需的線程

這里給出BIO模型下的服務(wù)端編程示例
/**
?*?BIO下的服務(wù)端
?*?@author?Aaron?Zhu
?*?@date?2022-03-15
?*/
public?class?Server?{
????public?static?void?main(String[]?args)?{
????????try{
????????????ServerSocket?ss?=?new?ServerSocket(9999);
????????????while?(true)?{
????????????????//?阻塞等待客戶端連接
????????????????Socket?socket?=?ss.accept();
????????????????new?MsgHandler(?socket?).start();
????????????}
????????}catch?(IOException?e)?{
????????????System.out.println("[Server]:?Happen?Exception:?"+e.getMessage());
????????}
????}
}
class?MsgHandler?extends?Thread?{
????private?Socket?socket;
????public?MsgHandler(Socket?socket)?{
????????this.socket?=?socket;
????}
????@Override
????public?void?run()?{
????????try?{
????????????InputStream?inputStream?=?socket.getInputStream();
????????????BufferedReader?bufferedReader?=?new?BufferedReader(?new?InputStreamReader(inputStream));
????????????System.out.println("[Server]:?客戶端上線");
????????????String?msg;
????????????//?阻塞等待客戶端的輸入
????????????while?(?(msg=bufferedReader.readLine())?!=?null?)?{
????????????????if(?"Bye".equals(msg)?)?{
????????????????????break;
????????????????}
????????????????System.out.println("[Server]:?"?+?msg);
????????????}
????????????bufferedReader.close();
????????????socket.close();
????????}?catch?(Exception?e)?{
????????????System.out.println("[Server]:?Happen?Exception:?"+e.getMessage());
????????}
????????System.out.println("[Server]:?客戶端下線");
????}
}
這里給出BIO模型下的客戶端編程示例
/**
?*?BIO下的客戶端
?*?@author?Aaron?Zhu
?*?@date?2022-03-15
?*/
public?class?Client?{
????public?static?void?main(String[]?args)?{
????????try?(
????????????Socket?socket?=?new?Socket("127.0.0.1",?9999);
????????????PrintStream?printStream?=?new?PrintStream(socket.getOutputStream());
????????????Scanner?scanner?=?new?Scanner(System.in)?)?{
????????????while?(true)?{
????????????????System.out.print("[Client]:?請(qǐng)說:");
????????????????String?msg?=?scanner.nextLine();
????????????????if(?"Bye".equals(msg)?)?{
????????????????????break;
????????????????}
????????????????printStream.println(msg);
????????????????printStream.flush();
????????????}
????????????socket.shutdownOutput();
????????}?catch?(Exception?e)?{
????????????System.out.println("[Client]:?Happen?Exception:?"+e.getMessage());
????????}
????}
}
NIOJava從1.4開始引入了非阻塞同步的NIO。不同于BIO,其支持面向Buffer緩沖區(qū)的、基于Channel通道的IO操作。在NIO模型中,有三個(gè)核心部分:Buffer緩沖區(qū)、Channel通道、Selector選擇器
「1. Buffer緩沖區(qū)」
本質(zhì)上是一塊可以讀取、寫入數(shù)據(jù)的內(nèi)存空間,其被包裝為NIO Buffer對(duì)象。同時(shí)為了進(jìn)一步方便操作,還提供了一組相應(yīng)的API進(jìn)行操作、管理。為了方便理解,這里通過實(shí)踐的方式加強(qiáng)對(duì)緩沖區(qū)的認(rèn)識(shí)
在Buffer中有兩個(gè)最重要的游標(biāo):position、limit。前者表示下一個(gè)將要被寫入或讀取的元素索引,當(dāng)調(diào)用get/put方法時(shí)會(huì)自動(dòng)更新。其中,初始化值為0;后者表示讀取、寫入元素時(shí)的界限。當(dāng)向Buffer寫完數(shù)據(jù)、準(zhǔn)備讀取時(shí),必須調(diào)用flip方法將其切換為可讀模式。具體地,其會(huì)將buffer的limit設(shè)置為pos的值、將pos設(shè)置0,以實(shí)現(xiàn)從頭讀取。與此同時(shí),可以通過clear方法將Buffer恢復(fù)至初始狀態(tài)。具體地,將pos的值歸零、limit設(shè)置為Buffer的容量值。這樣即可再次寫入數(shù)據(jù)以覆蓋歷史數(shù)據(jù)
@Test
public?void?test1()?{
????System.out.println("---------------Test?1?:?實(shí)例化---------------");
????ByteBuffer?buffer?=?ByteBuffer.allocate(15);
????System.out.println("capacity:?"?+?buffer.capacity());
????System.out.println("position:?"?+?buffer.position());
????System.out.println("limit:?"?+?buffer.limit());
????System.out.println("---------------Test?2?:?寫數(shù)據(jù)---------------");
????String?msg?=?"BobAaronTina";
????buffer.put(?msg.getBytes()?);
????System.out.println("capacity:?"?+?buffer.capacity());
????System.out.println("position:?"?+?buffer.position());
????System.out.println("limit:?"?+?buffer.limit());
????System.out.println("---------------Test?3?:?切換為可讀模式---------------");
????//?將buffer的limit設(shè)置為pos的值,?將pos設(shè)置0
????buffer.flip();
????System.out.println("capacity:?"?+?buffer.capacity());
????System.out.println("position:?"?+?buffer.position());
????System.out.println("limit:?"?+?buffer.limit());
????System.out.println("---------------Test?4?:?讀取數(shù)據(jù)---------------");
????byte[]?bytes?=?new?byte[3];
????buffer.get(?bytes?);
????String?res?=?new?String(?bytes?);
????System.out.println("res?:?"+res);
????System.out.println("capacity:?"?+?buffer.capacity());
????System.out.println("position:?"?+?buffer.position());
????System.out.println("limit:?"?+?buffer.limit());
????System.out.println("---------------Test?5?:?清空緩沖區(qū)---------------");
????//?將pos的值歸零、limit設(shè)置為Buffer的容量值
????buffer.clear();
????System.out.println("capacity:?"?+?buffer.capacity());
????System.out.println("position:?"?+?buffer.position());
????System.out.println("limit:?"?+?buffer.limit());
}
測(cè)試結(jié)果,如下所示

在Buffer中,可通過mark、reset方法分別實(shí)現(xiàn)將當(dāng)前pos值保存到mark變量中、將pos設(shè)置為mark變量的值。與此同時(shí),還可以通過hasRemaining、remaining方法分別實(shí)現(xiàn)判斷buffer中是否還有剩余元素、返回剩余元素的數(shù)量
@Test
public?void?test2()?{
????ByteBuffer?buffer?=?ByteBuffer.allocate(15);
????//?1.?寫數(shù)據(jù)
????String?msg?=?"USHelloChina";
????buffer.put(?msg.getBytes()?);
????//?2.?切換為讀模式
????buffer.flip();
????System.out.println("----------?第一次讀取?----------");
????for?(int?i=0;i<2;i++)?{
????????char?ch?=?(char)?buffer.get();
????????System.out.println("char?:?"?+?ch);
????}
????System.out.println("position:?"?+?buffer.position());
????System.out.println("limit:?"?+?buffer.limit());
????System.out.println("----------?標(biāo)記位置?----------");
????//?將當(dāng)前pos值保存到mark變量中
????buffer.mark();
????System.out.println("----------?第二次讀取?----------");
????byte[]?bytes?=?new?byte[5];
????buffer.get(?bytes?);
????String?res?=?new?String(?bytes?);
????System.out.println("res?:?"+res);
????System.out.println("Buffer?Info:?"?+?buffer.toString());
????System.out.println("----------?回到標(biāo)記位置?----------");
????//?將pos設(shè)置為mark變量的值
????buffer.reset();
????System.out.println("position:?"?+?buffer.position());
????System.out.println("limit:?"?+?buffer.limit());
????System.out.println("----------?第三次讀取?----------");
????//?判斷buffer中是否還有剩余元素
????if?(?buffer.hasRemaining()?)?{
????????//?返回剩余元素的數(shù)量
????????int?size?=?buffer.remaining();
????????bytes?=?new?byte[size];
????????buffer.get(?bytes?);
????????res?=?new?String(?bytes?);
????????System.out.println("size?:?"+size);
????????System.out.println("res?:?"+res);
????????System.out.println("Buffer?Info:?"?+?buffer.toString());
????}
}????
測(cè)試結(jié)果,如下所示

事實(shí)上,Buffer實(shí)例所使用的內(nèi)存分為兩種:堆內(nèi)內(nèi)存(即非直接內(nèi)存)、堆外內(nèi)存(即直接內(nèi)存)
@Test
public?void?test3()?{
????//?創(chuàng)建堆內(nèi)內(nèi)存的Buffer實(shí)例,?實(shí)現(xiàn)類為?HeapByteBuffer
????Buffer?buffer1?=?ByteBuffer.allocate(15);
????//?該Buffer是否為直接內(nèi)存
????boolean?b1?=?buffer1.isDirect();
????System.out.println("是否為直接內(nèi)存:?"?+?b1);
????//?創(chuàng)建堆外內(nèi)存的Buffer實(shí)例,?實(shí)現(xiàn)類為DirectByteBuffer
????ByteBuffer?buffer2?=?ByteBuffer.allocateDirect(15);
????//?該Buffer是否為直接內(nèi)存
????boolean?b2?=?buffer2.isDirect();
????System.out.println("是否為直接內(nèi)存:?"?+?b2);
}
測(cè)試結(jié)果,如下所示

「2. Channel通道」
相比較傳統(tǒng)的單向流(輸入流、輸出流)而言,通道是雙向的。既可以讀數(shù)據(jù),也可以寫數(shù)據(jù)
「3. Selector選擇器」
Selector選擇器,也被稱作為多路復(fù)用器,是Java NIO中最重要的部分。其可以同時(shí)管理多個(gè)通道,并確定其中的哪些通道已經(jīng)準(zhǔn)備好進(jìn)行讀取、寫入操作。換言之,利用選擇器可以實(shí)現(xiàn)通過一個(gè)線程管理多個(gè)通道,即管理多個(gè)客戶端連接。NIO模型的示意圖如下所示

這里給出NIO模型下的服務(wù)端編程示例
/**
?*?NIO下的服務(wù)端
?*?@author?Aaron?Zhu
?*?@date?2022-03-23
?*/
public?class?Server?{
????public?static?void?main(String[]?args)?throws?IOException?{
????????//?獲取通道
????????ServerSocketChannel?serverSocketChannel?=?ServerSocketChannel.open();
????????//?切換為非阻塞模式
????????serverSocketChannel.configureBlocking(false);
????????//?綁定監(jiān)聽端口
????????serverSocketChannel.bind(?new?InetSocketAddress(8585)?);
????????//?獲取選擇器Selector
????????Selector?selector?=?Selector.open();
????????//?將通道注冊(cè)到選擇器中,?并開始監(jiān)聽客戶端連接類型的事件
????????serverSocketChannel.register(?selector,?SelectionKey.OP_ACCEPT);
????????//?使用Selector選擇器輪詢已經(jīng)就緒的事件
????????while?(?selector.select()>0?)?{
????????????//?獲取迭代器以進(jìn)行事件的遍歷、處理
????????????Iterator?it?=?selector.selectedKeys().iterator();
????????????while?(?it.hasNext()?)?{
????????????????//?通過迭代器獲取當(dāng)前事件
????????????????SelectionKey?selectionKey?=?it.next();
????????????????//?事件類型?為?接受連接事件
????????????????if(?selectionKey.isAcceptable()?)?{
????????????????????System.out.println("[Server]:?客戶端上線");
????????????????????//?獲取當(dāng)前連接進(jìn)來的客戶端通道
????????????????????SocketChannel?socketChannel?=?serverSocketChannel.accept();
????????????????????//?切換為非阻塞模式
????????????????????socketChannel.configureBlocking(false);
????????????????????//?將該客戶端通道注冊(cè)到選擇器中,?并開始監(jiān)聽讀就緒類型的事件
????????????????????socketChannel.register(selector,?SelectionKey.OP_READ);
????????????????}?else?if(?selectionKey.isReadable()?)?{????//?事件類型?為?讀就緒事件
????????????????????StringBuilder?sb?=?new?StringBuilder();
????????????????????//?通過事件獲取相應(yīng)的客戶端通道
????????????????????SocketChannel?socketChannel?=?(SocketChannel)?selectionKey.channel();
????????????????????//?創(chuàng)建一個(gè)Buffer實(shí)例,?用于存放從通道中讀取的數(shù)據(jù)
????????????????????ByteBuffer?buffer?=?ByteBuffer.allocate(5);
????????????????????int?len?=?0;
????????????????????//?從客戶端通道不停地讀取數(shù)據(jù)
????????????????????while?(?(len=socketChannel.read(buffer))?>?0?)?{
????????????????????????//?切換為讀模式
????????????????????????buffer.flip();
????????????????????????//?處理Buffer中的數(shù)據(jù)
????????????????????????String?temp?=?new?String(buffer.array(),?0,?len);
????????????????????????sb.append(?temp?);
????????????????????????//?清除Buffer
????????????????????????buffer.clear();
????????????????????}
????????????????????//?客戶端通道斷開連接
????????????????????if(?len==-1?)?{
????????????????????????//?取消注冊(cè)當(dāng)前事件的客戶端通道
????????????????????????selectionKey.cancel();
????????????????????????//?關(guān)閉客戶端通道
????????????????????????socketChannel.close();
????????????????????????System.out.println("[Server]:?客戶端下線");
????????????????????}
????????????????????System.out.println("[Server]:?"?+?sb.toString());
????????????????}
????????????????//?事件處理完畢后,移除當(dāng)前事件
????????????????it.remove();
????????????}
????????}
????}
}
這里給出NIO模型下的客戶端編程示例
/**
?*?NIO下的客戶端
?*?@author?Aaron?Zhu
?*?@date?2022-03-24
?*/
public?class?Client?{
????public?static?void?main(String[]?args)?throws?IOException?{
????????//?獲取客戶端通道
????????SocketChannel?socketChannel?=?SocketChannel.open(?new?InetSocketAddress("127.0.0.1",?8585));
????????//?切換為非阻塞模式
????????socketChannel.configureBlocking(false);
????????//?創(chuàng)建Buffer實(shí)例
????????int?bufferSize?=?5;
????????ByteBuffer?buffer?=?ByteBuffer.allocate(bufferSize);
????????Scanner?scanner?=?new?Scanner(System.in);
????????while?(true)?{
????????????System.out.print("[Client]:?請(qǐng)說:");
????????????String?msg?=?scanner.nextLine();
????????????if(?"Bye".equals(msg)?)?{
????????????????System.out.println("[Client]:?客戶端下線");
????????????????break;
????????????}
????????????byte[]?bytes?=?msg.getBytes();
????????????int?start?=?0;
????????????//?將客戶端的消息分批寫入Buffer、客戶端通道
????????????while?(?start?????????????????int?length?=?start?+?bufferSize?<=?bytes.length???bufferSize?:?bytes.length-start;
????????????????//?將客戶端消息部分寫入Buffer
????????????????buffer.put(bytes,?start,?length);
????????????????//?切換為讀模式
????????????????buffer.flip();
????????????????//?將客戶端消息部分寫入通道
????????????????socketChannel.write(?buffer?);
????????????????//?清除Buffer
????????????????buffer.clear();
????????????????//?更新下一次數(shù)據(jù)寫入的起點(diǎn)
????????????????start?=?start?+?length;
????????????}
????????}
????????//?關(guān)閉客戶端通道
????????socketChannel.close();
????}
}
AIOJava從1.7開始引入了非阻塞異步的AIO,作為對(duì)NIO的改進(jìn)、增強(qiáng),故其也被稱為NIO 2.0。其分別引入了服務(wù)端異步Socket通道AsynchronousServerSocketChannel、客戶端異步Socket通道AsynchronousSocketChannel,前者負(fù)責(zé)服務(wù)端Socket的創(chuàng)建、監(jiān)聽;后者負(fù)責(zé)客戶端消息的讀寫操作。同時(shí)提供了一個(gè)CompletionHandler,作為消息處理回調(diào)接口
這里給出AIO模型下的服務(wù)端編程示例
/**
?*?AIO下的服務(wù)端
?*?@author?Aaron?Zhu
?*?@date?2022-03-27
?*/
public?class?Server?{
????public?static?void?main(String[]?args)?throws?IOException?{
????????//?獲取通道
????????AsynchronousServerSocketChannel?serverSocketChannel?=?AsynchronousServerSocketChannel.open();
????????//?綁定監(jiān)聽端口
????????serverSocketChannel.bind(?new?InetSocketAddress(9696)?);
????????//?準(zhǔn)備接受客戶端連接請(qǐng)求
????????serverSocketChannel.accept(?serverSocketChannel,?new?ServerHandler()?);
????????while?(true)?{
????????}
????}
}
當(dāng)接受到客戶端的連接請(qǐng)求后,我們需要提供一個(gè)相應(yīng)的CompletionHandler實(shí)現(xiàn)類。進(jìn)行業(yè)務(wù)邏輯處理。具體地,通過實(shí)現(xiàn)completed方法用于接受客戶端請(qǐng)求、建立連接后的業(yè)務(wù)處理邏輯,通過實(shí)現(xiàn)failed方法用于進(jìn)行服務(wù)端發(fā)生異常的處理邏輯。具體實(shí)現(xiàn)如下所示
/**
?*?@author?Aaron?Zhu
?*?@date?2022-03-27
?*/
public?class?ServerHandler?implements?CompletionHandler<AsynchronousSocketChannel,?AsynchronousServerSocketChannel>?{
????/**
?????*?接受客戶端請(qǐng)求、建立連接后,?業(yè)務(wù)處理邏輯
?????*?@param?socketChannel?客戶端通道
?????*?@param?serverSocketChannel?服務(wù)端通道
?????*/
????@Override
????public?void?completed(AsynchronousSocketChannel?socketChannel,?AsynchronousServerSocketChannel?serverSocketChannel)?{
????????//?準(zhǔn)備接受下一個(gè)客戶端的連接請(qǐng)求
????????serverSocketChannel.accept(serverSocketChannel,?this);
????????//?創(chuàng)建Buffer實(shí)例
????????ByteBuffer?byteBuffer?=?ByteBuffer.allocate(2048);
????????socketChannel.read(?byteBuffer,?byteBuffer,?new?CompletionHandler()?{
????????????@Override
????????????public?void?completed(Integer?result,?ByteBuffer?attachment)?{
????????????????//?客戶端通道斷開連接
????????????????if(result?==?-1)?{
????????????????????try?{
????????????????????????//?關(guān)閉客戶端通道
????????????????????????socketChannel.close();
????????????????????}?catch?(IOException?e)?{
????????????????????????System.out.println("[Server]:?happen?exception,?"+e.getMessage());
????????????????????}
????????????????????return;
????????????????}
????????????????//?切換為讀模式
????????????????attachment.flip();
????????????????//?處理Buffer中的數(shù)據(jù)
????????????????String?msg?=?new?String(attachment.array(),?0,?result);
????????????????//?清除Buffer
????????????????attachment.clear();
????????????????System.out.println("[Server]:?"?+?msg);
????????????????//?準(zhǔn)備下一次讀
????????????????socketChannel.read(attachment,?attachment,?this);
????????????}
????????????@Override
????????????public?void?failed(Throwable?exc,?ByteBuffer?attachment)?{
????????????????System.out.println("[Server]:?happen?exception,?"+exc.getMessage());
????????????}
????????}?);
????}
????/**
?????*?服務(wù)端發(fā)生異常的處理邏輯
?????*?@param?exc
?????*?@param?serverSocketChannel
?????*/
????@Override
????public?void?failed(Throwable?exc,?AsynchronousServerSocketChannel?serverSocketChannel)?{
????????System.out.println("[Server]:?happen?exception,?"+exc.getMessage());
????}
}
這里給出AIO模型下的客戶端編程示例
/**
?*?AIO下的客戶端
?*?@author?Aaron?Zhu
?*?@date?2022-03-27
?*/
public?class?Client?{
????public?static?void?main(String[]?args)?throws?IOException?{
????????//?獲取通道
????????AsynchronousSocketChannel?socketChannel?=?AsynchronousSocketChannel.open();
????????//?請(qǐng)求連接服務(wù)端
????????socketChannel.connect(?new?InetSocketAddress("127.0.0.1",?9696));
????????//?創(chuàng)建Buffer實(shí)例
????????ByteBuffer?buffer?=?ByteBuffer.allocate(1024);
????????Scanner?scanner?=?new?Scanner(System.in);
????????while?(true)?{
????????????System.out.print("[Client]:?請(qǐng)說:");
????????????String?msg?=?scanner.nextLine();
????????????if(?"Bye".equals(msg)?)?{
????????????????System.out.println("[Client]:?客戶端下線");
????????????????break;
????????????}
????????????byte[]?bytes?=?msg.getBytes();
????????????//?將客戶端消息寫入Buffer
????????????buffer.put(?msg.getBytes()?);
????????????//?切換為讀模式
????????????buffer.flip();
????????????//?將客戶端消息部分寫入通道
????????????socketChannel.write(?buffer?);
????????????//?清除Buffer
????????????buffer.clear();
????????}
????????//?關(guān)閉客戶端通道
????????socketChannel.close();
????}
}
參考文獻(xiàn)- 鳳凰架構(gòu) 周志明著
