⑴ java nio 開發實例
首先了解下所謂的java nio是個什麼東西!
傳統的並發型伺服器設計是利用阻塞型網路I/O 以多線程的模式來實現的 然而由
於系統常常在進行網路讀寫時處於阻塞狀態 會大大影響系統的性能 自Java 開始引入
了NIO(新I/O) API 通過使用非阻塞型I/O 實現流暢的網路讀寫操作 為開發高性能並發
型伺服器程序提供了一個很好的解決方案 這就罩笑答是java nio
首先來看下傳統的阻塞型網路 I/O的不足
Java 平台傳統的I/O 系統都是基於Byte(位元組)和Stream(數據流)的 相應的I/O 操
作都是阻塞型的 所以伺服器程序也採用阻塞型I/O 進行數據的讀 寫操作 本文以TCP
長連接模式來討論並發型伺服器的相關設計 為了實現伺服器程序的並發性要求 系統由一
個單獨的主線程來監聽用戶發起的連接請求 一直處於阻塞狀態 當有用戶連接請求到來時
程序都會啟一個新的線程來統一處理用戶數據的讀 寫操作
這種模式的優點是簡單 實用 易管理 然而缺點也是顯而易見的 由於是為每一個客
戶端分配一個線程來處理輸入 輸出數據 其線程與客戶機的比例近似為 隨著線程
數量的不斷增加 伺服器啟動了大量的並發線程 會大大加大系統對線程的管理開銷 這將
成為吞吐量瓶頸的主要原因 其次由於底層的I/O 操作採用的同步模式 I/O 操作的阻塞管
理粒度是以服務於請求的線程為單位的 有可能大量的線程會閑置 處於盲等狀態升派 造成I/O
資源利用率不高 影響整個系統的性能
對於並發型伺服器 系統用在阻塞型I/O 等待和線程間切換的時間遠遠多於CPU 在內
存中處理數據的時間 因此傳統的阻塞型物慧I/O 已經成為制約系統性能的瓶頸 Java 版本
後推出的NIO 工具包 提供了非阻塞型I/O 的非同步輸入輸出機制 為提高系統的性能提供
了可實現的基礎機制
NIO 包及工作原理
針對傳統I/O 工作模式的不足 NIO 工具包提出了基於Buffer(緩沖區) Channel(通
道) Selector(選擇器)的新模式 Selector(選擇器) 可選擇的Channel(通道)和
SelectionKey(選擇鍵)配合起來使用 可以實現並發的非阻塞型I/O 能力
NIO 工具包的成員
Buffer(緩沖器)
Buffer 類是一個抽象類 它有 個子類分別對應於七種基本的數據類型 ByteBuffer
CharBuffer DoubleBuffer FloatBuffer IntBuffer LongBuffer 和ShortBuffer 每一個Buffer
對象相當於一個數據容器 可以把它看作內存中的一個大的數組 用來存儲和提取所有基本
類型(boolean 型除外)的數據 Buffer 類的核心是一塊內存區 可以直接對其執行與內存有關
的操作 利用操作系統特性和能力提高和改善Java 傳統I/O 的性能
Channel(通道)
Channel 被認為是NIO 工具包的一大創新點 是(Buffer)緩沖器和I/O 服務之間的通道
具有雙向性 既可以讀入也可以寫出 可以更高效的傳遞數據 我們這里主要討論
ServerSocketChannel 和SocketChannel 它們都繼承了SelectableChannel 是可選擇的通道
分別可以工作在同步和非同步兩種方式下(這里的可選擇不是指可以選擇兩種工作方式 而是
指可以有選擇的注冊自己感興趣的事件) 當通道工作在同步方式時 它的功能和編程方法
與傳統的ServerSocket Socket 對象相似 當通道工作在非同步工作方式時 進行輸入輸出處
理不必等到輸入輸出完畢才返回 並且可以將其感興趣的(如 接受操作 連接操作 讀出
操作 寫入操作)事件注冊到Selector 對象上 與Selector 對象協同工作可以更有效率的支
持和管理並發的網路套接字連接
Selector(選擇器)和SelectionKey(選擇鍵)
各類 Buffer 是數據的容器對象 各類Channel 實現在各類Buffer 與各類I/O 服務間傳輸
數據 Selector 是實現並發型非阻塞I/O 的核心 各種可選擇的通道將其感興趣的事件注冊
到Selector 對象上 Selector 在一個循環中不斷輪循監視這各些注冊在其上的Socket 通道
SelectionKey 類則封裝了SelectableChannel 對象在Selector 中的注冊信息 當Selector 監測
到在某個注冊的SelectableChannel 上發生了感興趣的事件時 自動激活產生一個SelectionKey
對象 在這個對象中記錄了哪一個SelectableChannel 上發生了哪種事件 通過對被激活的
SelectionKey 的分析 外界可以知道每個SelectableChannel 發生的具體事件類型 進行相應的
處理
NIO 工作原理
通過上面的討論 我們可以看出在並發型伺服器程序中使用NIO 實際上是通過網路事
件驅動模型實現的 我們應用Select 機制 不用為每一個客戶端連接新啟線程處理 而是將
其注冊到特定的Selector 對象上 這就可以在單線程中利用Selector 對象管理大量並發的網
絡連接 更好的利用了系統資源 採用非阻塞I/O 的通信方式 不要求阻塞等待I/O 操作完
成即可返回 從而減少了管理I/O 連接導致的系統開銷 大幅度提高了系統性能
當有讀或寫等任何注冊的事件發生時 可以從Selector 中獲得相應的
SelectionKey 從SelectionKey 中可以找到發生的事件和該事件所發生的具體的
SelectableChannel 以獲得客戶端發送過來的數據 由於在非阻塞網路I/O 中採用了事件觸
發機制 處理程序可以得到系統的主動通知 從而可以實現底層網路I/O 無阻塞 流暢地讀
寫 而不像在原來的阻塞模式下處理程序需要不斷循環等待 使用NIO 可以編寫出性能更
好 更易擴展的並發型伺服器程序
並發型伺服器程序的實現代碼
應用 NIO 工具包 基於非阻塞網路I/O 設計的並發型伺服器程序與以往基於阻塞I/O 的
實現程序有很大不同 在使用非阻塞網路I/O 的情況下 程序讀取數據和寫入數據的時機不
是由程序員控制的 而是Selector 決定的 下面便給出基於非阻塞網路I/O 的並發型伺服器
程序的核心代碼片段
import java io * //引入Java io包
import * //引入包
import java nio channels * //引入Java nio channels包
import java util * //引入Java util包
public class TestServer implements Runnable
{
/**
* 伺服器Channel對象 負責接受用戶連接
*/
private ServerSocketChannel server
/**
* Selector對象 負責監控所有的連接到伺服器的網路事件的發生
*/
private Selector selector
/**
* 總的活動連接數
*/
private int activeSockets
/**
* 伺服器Channel綁定的埠號
*/
private int port
/**
*
* 構造函數
*/
public TestServer()throws IOException
{
activeSockets=
port= //初始化伺服器Channel綁定的埠號為
selector= Selector open() //初始化Selector對象
server=ServerSocketChannel open() //初始化伺服器Channel對象
ServerSocket socket=server socket() //獲取伺服器Channel對應的//ServerSocket對象
socket bind(new InetSocketAddress(port)) //把Socket綁定到監聽埠 上
nfigureBlocking(false) //將伺服器Channel設置為非阻塞模式
server register(selector SelectionKey OP_ACCEPT) //將伺服器Channel注冊到
Selector對象 並指出伺服器Channel所感興趣的事件為可接受請求操作
}
public void run()
{
while(true)
{
try
{
/**
*應用Select機制輪循是否有用戶感興趣的新的網路事件發生 當沒有
* 新的網路事件發生時 此方法會阻塞 直到有新的網路事件發生為止
*/
selector select()
}
catch(IOException e)
{
continue //當有異常發生時 繼續進行循環操作
}
/**
* 得到活動的網路連接選擇鍵的集合
*/
Set<SelectionKey> keys=selector selectedKeys()
activeSockets=keys size() //獲取活動連接的數目
if(activeSockets== )
{
continue //如果連接數為 則繼續進行循環操作
}
/**
/**
* 應用For—Each循環遍歷整個選擇鍵集合
*/
for(SelectionKey key :keys)
{
/**
* 如果關鍵字狀態是為可接受 則接受連接 注冊通道 以接受更多的*
事件 進行相關的伺服器程序處理
*/
if(key isAcceptable())
{
doServerSocketEvent(key)
continue
}
/**
* 如果關鍵字狀態為可讀 則說明Channel是一個客戶端的連接通道
* 進行相應的讀取客戶端數據的操作
*/
if(key isReadable())
{
doClientReadEvent(key)
continue
}
/**
* 如果關鍵字狀態為可寫 則也說明Channel是一個客戶端的連接通道
* 進行相應的向客戶端寫數據的操作
*/
if(key isWritable())
{
doClinetWriteEvent(key)
continue
}
}
}
}
/**
* 處理伺服器事件操作
* @param key 伺服器選擇鍵對象
*/
private void doServerSocketEvent(SelectionKey key)
{
SocketChannel client=null
try
{
ServerSocketChannel server=(ServerSocketChannel)key channel()
client=server accept()
if(client==null)
{
return
}
nfigureBlocking(false) //將客戶端Channel設置為非阻塞型
/**
/**
* 將客戶端Channel注冊到Selector對象上 並且指出客戶端Channel所感
* 興趣的事件為可讀和可寫
*/
client register(selector SelectionKey OP_READ|SelectionKey OP_READ)
}catch(IOException e)
{
try
{
client close()
}catch(IOException e ){}
}
}
/**
* 進行向客戶端寫數據操作
* @param key 客戶端選擇鍵對象
*/
private void doClinetWriteEvent(SelectionKey key)
{
代碼實現略
}
/**
* 進行讀取客戶短數據操作
* @param key 客戶端選擇鍵對象
*/
private void doClientReadEvent(SelectionKey key)
{
代碼實現略
}
}
從上面對代碼可以看出 使用非阻塞性I/O進行並發型伺服器程序設計分三個部分
向Selector對象注冊感興趣的事件 從Selector中獲取所感興趣的事件 根據不同的事件進
行相應的處理
結語
通過使用NIO 工具包進行並發型伺服器程序設計 一個或者很少幾個Socket 線程就可
以處理成千上萬個活動的Socket 連接 大大降低了伺服器端程序的開銷 同時網路I/O 採取
非阻塞模式 線程不再在讀或寫時阻塞 操作系統可以更流暢的讀寫數據並可以更有效地向
CPU 傳遞數據進行處理 以便更有效地提高系統的性能
看到這里相信你看了不止 分鍾了吧 我說 分鍾其實就是想讓大家能夠輕松的讀下去(雞蛋 )
好了 到這里大家應該對java nio有個初步的了解了吧~~~
lishixin/Article/program/Java/hx/201311/27190
⑵ java中IO和NIO的區別和適用場景
以前在遠標學過nio是new io的簡稱,從jdk1.4就被引入了,可以說不是什麼新東西了。nio的主回要作用就是用來解答決速度差異的。舉個例子:計算機處理的速度,和用戶按鍵盤的速度。這兩者的速度相差懸殊。如果按照經典的方法:一個用戶設定一個線程,專門等待用戶的輸入,無形中就造成了嚴重的資源浪費:每一個線程都需要珍貴的cpu時間片,由於速度差異造成了在這個交互線程中的cpu都用來等待。 在以前的 Java IO 中,都是阻塞式 IO,NIO 引入了非阻塞式 IO。
⑶ java nio網上的例子都是死循環,CUP 100%,誰給我個真正能用的例子
看什抄么孫衛琴啊,都是垃圾,你好好看看安裝好的JDK中的demo程序,那才是精品,其中有一個文件夾,全是nio的例子,保證運行,而且nio也不用非要用什麼socket好吧,讀文件也能用哈,只要是IO的流都可以
⑷ java.nio的描述
定義作為數據容器的緩沖區,並提供其他 NIO 包的概述。
NIO API 的集中抽象為:
緩沖區,它們是數據容器;
字元集及其相關解碼器 和編碼器,
它們在位元組和 Unicode字元之間進行轉換;
各種類型的通道,它們表示到能夠執行 IO 操作的
實體的連接;以及選擇器 和選擇鍵,它們與
可橋升簡選擇信道 一起定義了多路的、無阻塞的
I/O 設施。
java.nio 包定義了緩沖區類,這些類用於所有 NIO API。java.nio.charset包中定義了字元集API,java.nio.channels包中定義了信道和選擇器 API。每個子包都具有自己的服務提供程序介面(SPI) 子包,SPI 子包的內容可用於擴展平台的默認實現或構造替代實現。
緩沖敏褲區
描述
Buffer 位置,界限和容量;
清除,反轉,重笑模繞和標記/重置
ByteBuffer Get/put,壓縮,查看;分配,包裝
MappedByteBuffer 映射到文件的位元組緩沖區
CharBuffer Get/put,壓縮;分配,包裝
DoubleBuffer ' '
FloatBuffer ' '
IntBuffer ' '
LongBuffer ' '
ShortBuffer ' '
ByteOrder 位元組順序的類型安全的枚舉
⑸ 什麼是Java NIO,它的工作原理是什麼
Java NIO是在jdk1.4開始使用的,它既可以說成「新I/O」,也可以說成非阻塞式I/O。
1. 由一個專門的內線程來處理所有的 IO 事件,並負責分容發。
2. 事件驅動機制:事件到的時候觸發,而不是同步的去監視事件。
3. 線程通訊:線程之間通過 wait,notify 等方式通訊。保證每次上下文切換都是有意義的。減少無謂的線程切換。
⑹ 求java nio網路編程的小例子,要求客戶端一直與伺服器保持連接
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.net.*;
import java.io.*;
public class chatClient extends Frame {
/**
* @param args
*/
TextField tfTxT=new TextField();
TextArea taContent=new TextArea();
Socket s=null;
DataOutputStream dos=null;
DataInputStream dis=null;
private boolean bConnected =false;
public static void main(String[] args) {
new chatClient().lunachFrame();
}
private class RecvThread implements Runnable{
public void run() {
try{
while(bConnected){
String str=dis.readUTF();
taContent.setText(taContent.getText()+str+'\n');
}
}catch(IOException e){
e.printStackTrace();
}
}
}
public void lunachFrame(){
this.setLocation(400, 300);
this.setSize(300,300);
//this.setLayout(new FlowLayout());
this.add(tfTxT,"South");
this.add(taContent,"North");
pack();
tfTxT.addActionListener(new TFListener());
this.addWindowListener(new WindowClose());
this.setVisible(true);
connect();
new Thread(new RecvThread()).start();
}
public void connect(){
try {
s= new Socket("127.0.0.1",8888);
dos =new DataOutputStream(s.getOutputStream());
dis =new DataInputStream(s.getInputStream());
System.out.println("connected!");
bConnected=true;
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void disconnect(){
try {
dos.close();
s.close();
} catch (Exception e) {
// TODO 自動生成 catch 塊
e.printStackTrace();
}
}
class WindowClose extends WindowAdapter{
@Override
public void windowClosing(WindowEvent e) {
// TODO 自動生成方法存根
System.exit(0);
disconnect();
}
}
private class TFListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
String str=tfTxT.getText().trim();//trim去掉兩邊空格
//taContent.setText(str);
tfTxT.setText("");
try {
dos.writeUTF(str);
dos.flush();
//dos.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
======================================
import java.io.IOException;
import java.net.*;
import java.io.*;
import java.util.*;
public class ChatServer {
List<Client> clients=new ArrayList<Client>();
Client c=null;
public static void main(String[] args){
new ChatServer().start();
}
public void start(){
boolean started=false;
ServerSocket ss=null;
DataInputStream dis=null;
try{
ss=new ServerSocket(8888);
started =true;
}catch(Exception e)
{
e.printStackTrace();
}
try{
while(started){
Socket s=ss.accept();
c=new Client(s);//啟動線程,實行run()方法
System.out.println("a client connected!");
new Thread(c).start();//啟動start方法,循環.start是Thread中的方法與這上面的start無關
clients.add(c);
//dis.close();
}
} catch (Exception e) {
//e.printStackTrace();
}
finally{
try {
ss.close();
} catch (IOException e) {
// TODO 自動生成 catch 塊
e.printStackTrace();
}
}
}
class Client implements Runnable{
private Socket s;
private DataInputStream dis =null;
private boolean bConnected =false;
private DataOutputStream dos=null;
public Client(Socket s){
this.s=s;
try {
dis=new DataInputStream(s.getInputStream());
dos =new DataOutputStream(s.getOutputStream());
bConnected =true;
} catch (IOException e) {
// TODO 自動生成 catch 塊
e.printStackTrace();
}
}
public void send(String str)throws Exception{
dos.writeUTF(str);
}
public void run() {
try{
while(bConnected){
String str = dis.readUTF();
System.out.println(str);
for(int i=0;i<clients.size();i++){
c=clients.get(i);
c.send(str);
}
/*for(Iterator<Client> it=clients.iterator();it.hasNext();){
Client c=it.next();
c.send(str);
}*/
}
}catch(SocketException e){
clients.remove(this);
System.out.println("客戶下線了");
}
catch(EOFException e){
System.out.println("Client closed");
}
catch (Exception e){
//e.printStackTrace();
}
finally{
try {
if(dis !=null) dis.close();
if(dos !=null) dos.close();
if(s!=null) s.close();
} catch (Exception e1) {
// TODO 自動生成 catch 塊
//e1.printStackTrace();
}
}
}
}
}
第一個是客戶端,
第二個是server端
⑺ Java NIO怎麼理解通道和非阻塞
nio引入了buffer、channel、selector等概念。
通道相當於之前的I/O流。
「通道」太抽象了。java解釋不清的東西只能看它底層是怎麼解釋的——操作系統的I/O控制,通道控制方式?
I/O設備:CPU——通道——設備控制器——I/O設備
(通道和設備控制器的關系是多對多,設備控制器和I/O設備的關系也是多對多。)
I/O過程,參考察握http://www.nbrkb.net/lwt/jsjsj/asm/INTR&DMA.htm:
1.CPU在執行用戶程序時遇到I/O請求,根據用戶的I/O請求生成通道程序(也可以是事先編好的)。放到內存中,並把該通道程序首地址放入CAW中。
2.CPU執行「啟動I/O」指令,啟動通道工作。
3.通道接收「啟動I/O」指令信號,從CAW(記錄下一條通道指令存放的地址)中取出通道程序首地址,並根據此地址取出通道程序的第一條指令,放入CCW(記錄正在執行的通道指令)中;同時向CPU發回答信號,通知「啟動I/O」指令完成物唯完畢,CPU可繼續執行。
4.與此同時,通道開始執行通道程序,進行物理I/O操作。當執行完一條指令後,如果還有下一條指令則繼續執行;否則表示傳輸完成,同時自行停止,通知CPU轉去處理通道結束事件,並從CCW中得到有關通道狀態。
如此一罩沒培來,主處理器只要發出一個I/O操作命令,剩下的工作完全由通道負責。I/O操作結束後,I/O通道會發出一個中斷請求,表示相應操作已完成。
通道控制方式是對數據塊進行處理的,並非位元組。
通道控制方式就是非同步I/O
I/O分兩段:1.數據從I/O設備到內核緩沖區。2.數據從內核緩沖區到應用緩沖區
I/O類型:
1.非同步I/O不會產生阻塞,程序不會等待I/O完成,繼續執行代碼,等I/O完成了再執行一個什麼回調函數,代碼執行效率高。很容易聯想到ajax。這個一般用於I/O操作不影響之後的代碼執行。
2.阻塞I/O,程序發起I/O操作後,進程阻塞,CPU轉而執行其他進程,I/O的兩個步驟完成後,向CPU發送中斷信號,進程就緒,等待執行。
3.非阻塞I/O並非都不阻塞,其實是第一步不阻塞,第二部阻塞。程序發起I/O操作後,進程一直檢查第一步是否完成,CPU一直在循環詢問,完成後,進程阻塞直到完成第二步。明白了!這個是「站著茅坑不拉屎」,CPU利用率最低的。邏輯和操作系統的程序直接控制方式一樣。
阻塞不阻塞描述的是發生I/O時當前線程的狀態。
以上是操作系統的I/O,那麼java的nio又是怎樣的呢?
個人覺得是模仿了通道控制方式。
先看看nio的示例代碼:
服務端TestReadServer.java
import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set; public class TestReadServer { /*標識數字*/ private int flag = 0; /*緩沖區大小*/ private int BLOCK = 1024*1024*10; /*接受數據緩沖區*/ private ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK); /*發送數據緩沖區*/ private ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK); private Selector selector; public TestReadServer(int port) throws IOException { // 打開伺服器套接字通道 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // 伺服器配置為非阻塞 serverSocketChannel.configureBlocking(false); // 檢索與此通道關聯的伺服器套接字 ServerSocket serverSocket = serverSocketChannel.socket(); // 進行服務的綁定 serverSocket.bind(new InetSocketAddress(port)); // 通過open()方法找到Selector selector = Selector.open(); // 注冊到selector,等待連接 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("Server Start----"+port+":"); } // 監聽 private void listen() throws IOException { while (true) { // 選擇一組鍵,並且相應的通道已經打開 selector.select(); // 返回此選擇器的已選擇鍵集。 Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectionKeys.iterator(); while (iterator.hasNext()) { SelectionKey selectionKey = iterator.next(); iterator.remove(); handleKey(selectionKey); } } } // 處理請求 private void handleKey(SelectionKey selectionKey) throws IOException { // 接受請求 ServerSocketChannel server = null; SocketChannel client = null; String receiveText; String sendText; int count=0; // 測試此鍵的通道是否已准備好接受新的套接字連接。 if (selectionKey.isAcceptable()) { // 返回為之創建此鍵的通道。 server = (ServerSocketChannel) selectionKey.channel(); // 接受到此通道套接字的連接。 // 此方法返回的套接字通道(如果有)將處於阻塞模式。 client = server.accept(); // 配置為非阻塞 client.configureBlocking(false); // 注冊到selector,等待連接 client.register(selector, SelectionKey.OP_READ); } else if (selectionKey.isReadable()) { // 返回為之創建此鍵的通道。 client = (SocketChannel) selectionKey.channel(); //將緩沖區清空以備下次讀取 receivebuffer.clear(); //讀取伺服器發送來的數據到緩沖區中 System.out.println(System.currentTimeMillis()); count = client.read(receivebuffer); System.out.println(System.currentTimeMillis() + "~"+count); } } /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // TODO Auto-generated method stub int port = 1234; TestReadServer server = new TestReadServer(port); server.listen(); } }客戶端TestReadClient.javaimport java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set; public class TestReadClient { /*標識數字*/ private static int flag = 0; /*緩沖區大小*/ private static int BLOCK = 1024*1024*10; /*接受數據緩沖區*/ private static ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK); /*發送數據緩沖區*/ private static ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK); /*伺服器端地址*/ private final static InetSocketAddress SERVER_ADDRESS = new InetSocketAddress( "localhost", 1234); public static void main(String[] args) throws IOException { // TODO Auto-generated method stub // 打開socket通道 SocketChannel socketChannel = SocketChannel.open(); // 設置為非阻塞方式 socketChannel.configureBlocking(false); // 打開選擇器 Selector selector = Selector.open(); // 注冊連接服務端socket動作 socketChannel.register(selector, SelectionKey.OP_CONNECT); // 連接 socketChannel.connect(SERVER_ADDRESS); // 分配緩沖區大小內存 Set<SelectionKey> selectionKeys; Iterator<SelectionKey> iterator; SelectionKey selectionKey; SocketChannel client; String receiveText; String sendText; int count=0; while (true) { //選擇一組鍵,其相應的通道已為 I/O 操作準備就緒。 //此方法執行處於阻塞模式的選擇操作。 selector.select(); //返回此選擇器的已選擇鍵集。 selectionKeys = selector.selectedKeys(); //System.out.println(selectionKeys.size()); iterator = selectionKeys.iterator(); while (iterator.hasNext()) { selectionKey = iterator.next(); if (selectionKey.isConnectable()) { System.out.println("client connect"); client = (SocketChannel) selectionKey.channel(); // 判斷此通道上是否正在進行連接操作。 // 完成套接字通道的連接過程。 if (client.isConnectionPending()) { client.finishConnect(); System.out.println("完成連接!"); sendbuffer.clear(); BufferedInputStream br = new BufferedInputStream(new FileInputStream(new File("D:\BigData.zip"))); byte[] b = new byte[BLOCK]; br.read(b); sendbuffer.put(b); sendbuffer.flip(); System.out.println(System.currentTimeMillis()); client.write(sendbuffer); System.out.println(System.currentTimeMillis()); } client.register(selector, SelectionKey.OP_READ); } else if (selectionKey.isReadable()) { client = (SocketChannel) selectionKey.channel(); //將緩沖區清空以備下次讀取 receivebuffer.clear(); //讀取伺服器發送來的數據到緩沖區中 count=client.read(receivebuffer); if(count>0){ receiveText = new String( receivebuffer.array(),0,count); System.out.println("客戶端接受伺服器端數據--:"+receiveText); client.register(selector, SelectionKey.OP_WRITE); } } } selectionKeys.clear(); } } }例子是TestReadClient向TestReadServer發送一個本地文件。TestReadServer收到後每次列印讀取到的位元組數。
如何體現非同步I/O?
看看TestReadClient中的:
if (selectionKey.isConnectable()) { System.out.println("client connect"); client = (SocketChannel) selectionKey.channel(); // 判斷此通道上是否正在進行連接操作。 // 完成套接字通道的連接過程。 if (client.isConnectionPending()) { client.finishConnect();如果沒有client.finishConnect();這句等待完成socket連接,可能會報異常:java.nio.channels.NotYetConnectedException
非同步的才不會管你有沒有連接成功,都會執行下面的代碼。這里需要人為的干預。
如果要證明是java的nio單獨使用非阻塞I/O,真沒辦法!!!阻塞非阻塞要查看進程。。。
不過還有種說法,叫非同步非阻塞。上面那段,是用非同步方式創建連接,進程當然沒有被阻塞。使用了finishConnect()這是人為將程序中止,等待連接創建完成(是模仿阻塞將當前進程阻塞掉,還是模仿非阻塞不斷輪詢訪問,不重要了反正是程序卡住沒往下執行)。
所以,創建連接的過程用非同步非阻塞I/O可以解釋的通。那read/write的過程呢?
根據上面例子的列印結果,可以知道這個過程是同步的,沒執行完是不會執行下面的代碼的。至於底下是使用阻塞I/O還是非阻塞I/O,對於應用級程序來說不重要了。
阻塞還是非阻塞,對於正常的開發(創立連接,從連接中讀寫數據)並沒有多少的提升,操作過程都類似。
那NIO憑什麼成為高性能架構的基礎,比起IO,性能優越在哪裡,接著猜。。。
java nio有意模仿操作系統的通道控制方式,那他的底層是不是就是直接使用操作系統的通道?
通道中的數據是以塊為單位的,之前的流是以位元組為單位的,同樣的數據流操作外設的次數較多。代碼中channel都是針對ByteBuffer對象進行read/write的,而ByteBuffer又是ByteBuffer.allocate(BLOCK);這樣創建的,是一個連續的塊空間。
那ByteBuffer是不是也是模擬操作系統的緩存?
緩存在io也有,如BufferedInputStream。CPU和外設的速度差很多,緩存為了提高CPU使用率,等外設將數據讀入緩存後,CPU再統一操作,不用外設讀一次,CPU操作一次,CPU的效率會被拉下來。。。
⑻ Java NIO和IO的區別
JavaNIO和IO之間的主要差別,我會更詳細地描述表中每部分的差異。
IONIO
面向流面向緩沖
阻塞IO非阻塞IO
無選擇器
面向流與面向緩沖
JavaNIO和IO之間第一個最大的區別是,IO是面向流的,NIO是面向緩沖區的。JavaIO面向流意味著每次從流中讀一個或多個位元組,直至讀取所有位元組,它們沒有被緩存在任何地方。此外,它不能前後移動流中的數據。如果需要前後移動從流中讀取的數據,需要先將它緩存到一個緩沖區。JavaNIO的緩沖導向方法略有不同。數據讀取到一個它稍後處理的緩沖區,需要時可在緩沖區中前後移動。這就增加了處理過程中的靈活性。但是,還需要檢查是否該緩沖區中包含所有您需要處理的數據。而且,需確保當更多的數據讀入緩沖區時,不要覆蓋緩沖區里尚未處理的數據。
阻塞與非阻塞IO
JavaIO的各種流是阻塞的。這意味著,當一個線程調用read()或write()時,該線程被阻塞,直到有一些數據被讀取,或數據完全寫入。該線程在此期間不能再干任何事情了。JavaNIO的非阻塞模式,使一個線程從某通道發送請求讀取數據,但是它僅能得到目前可用的數據,如果目前沒有數據可運棗用時,就什麼都不會獲取。而不是保持線程阻塞,所以直至數據變的可以讀取之前,該線程可以繼續做其他的事情。非阻塞寫也是如此。一個線程請求寫入一些數據到某通道,但不需要等待它完全寫入,這個線程同時可以去做別的事情。線程通常將非阻塞IO的空閑時間用於在其它通道上執行IO操作,所以一個單獨的線程現在可以管理多個輸入和輸出通道(channel)。
選擇器(Selectors)
JavaNIO的選擇器允許一個單獨的線程來監視多個輸入通道,你可以注冊多個通道使用一個選擇器,然後使用一個單獨的線程來「選擇」通道:這些通道里已經有可以處理的輸入,或者選擇已准備寫入的通道。這種選兄和擇機制,使得一個單獨的線程很容易來管理多個通道。
NIO和IO如何影響應用程序的設計
無論您選擇IO或NIO工具箱,可能會影響您應用程序設計的以下幾個方面:
1.對NIO或IO類的API調用。
2.數據處理。
3.用來處理數據的線程數。
API調用
當然,使用NIO的API調用時看起來與羨悄盯使用IO時有所不同,但這並不意外,因為並不是僅從一個InputStream逐位元組讀取,而是數據必須先讀入緩沖區再處理。
數據處理
使用純粹的NIO設計相較IO設計,數據處理也受到影響。
在IO設計中,我們從InputStream或Reader逐位元組讀取數據。假設你正在處理一基於行的文本數據流,例如:
Name:Anna
Age:25
Email:[email protected]
Phone:1234567890
該文本行的流可以這樣處理:
BufferedReaderreader=newBufferedReader(newInputStreamReader(input));
StringnameLine=reader.readLine();
StringageLine=reader.readLine();
StringemailLine=reader.readLine();
StringphoneLine=reader.readLine();
請注意處理狀態由程序執行多久決定。換句話說,一旦reader.readLine()方法返回,你就知道肯定文本行就已讀完,readline()阻塞直到整行讀完,這就是原因。你也知道此行包含名稱;同樣,第二個readline()調用返回的時候,你知道這行包含年齡等。正如你可以看到,該處理程序僅在有新數據讀入時運行,並知道每步的數據是什麼。一旦正在運行的線程已處理過讀入的某些數據,該線程不會再回退數據(大多如此)。下圖也說明了這條原則:
(JavaIO:從一個阻塞的流中讀數據)而一個NIO的實現會有所不同,下面是一個簡單的例子:
ByteBufferbuffer=ByteBuffer.allocate(48);
intbytesRead=inChannel.read(buffer);
注意第二行,從通道讀取位元組到ByteBuffer。當這個方法調用返回時,你不知道你所需的所有數據是否在緩沖區內。你所知道的是,該緩沖區包含一些位元組,這使得處理有點困難。
假設第一次read(buffer)調用後,讀入緩沖區的數據只有半行,例如,「Name:An」,你能處理數據嗎?顯然不能,需要等待,直到整行數據讀入緩存,在此之前,對數據的任何處理毫無意義。
所以,你怎麼知道是否該緩沖區包含足夠的數據可以處理呢?好了,你不知道。發現的方法只能查看緩沖區中的數據。其結果是,在你知道所有數據都在緩沖區里之前,你必須檢查幾次緩沖區的數據。這不僅效率低下,而且可以使程序設計方案雜亂不堪。例如:
ByteBufferbuffer=ByteBuffer.allocate(48);
intbytesRead=inChannel.read(buffer);
while(!bufferFull(bytesRead)){
bytesRead=inChannel.read(buffer);
}
bufferFull()方法必須跟蹤有多少數據讀入緩沖區,並返回真或假,這取決於緩沖區是否已滿。換句話說,如果緩沖區准備好被處理,那麼表示緩沖區滿了。
bufferFull()方法掃描緩沖區,但必須保持在bufferFull()方法被調用之前狀態相同。如果沒有,下一個讀入緩沖區的數據可能無法讀到正確的位置。這是不可能的,但卻是需要注意的又一問題。
如果緩沖區已滿,它可以被處理。如果它不滿,並且在你的實際案例中有意義,你或許能處理其中的部分數據。但是許多情況下並非如此。下圖展示了「緩沖區數據循環就緒」:
3)用來處理數據的線程數
NIO可讓您只使用一個(或幾個)單線程管理多個通道(網路連接或文件),但付出的代價是解析數據可能會比從一個阻塞流中讀取數據更復雜。
如果需要管理同時打開的成千上萬個連接,這些連接每次只是發送少量的數據,例如聊天伺服器,實現NIO的伺服器可能是一個優勢。同樣,如果你需要維持許多打開的連接到其他計算機上,如P2P網路中,使用一個單獨的線程來管理你所有出站連接,可能是一個優勢。一個線程多個連接的設計方案如
JavaNIO:單線程管理多個連接
如果你有少量的連接使用非常高的帶寬,一次發送大量的數據,也許典型的IO伺服器實現可能非常契合。下圖說明了一個典型的IO伺服器設計:
JavaIO:一個典型的IO伺服器設計-一個連接通過一個線程處理
⑼ 介紹一下Java NIO,NIO讀取文件都有哪些方法
NIO也就是New I/O,是一組擴展Java IO操作的API集, 於Java 1.4起被引入,Java 7中NIO又提供了一些新的文件系統API,叫.
NIO2提供兩種主要的文件讀取方法:
使用buffer和channel類
使用Path 和 File 類
NIO讀取文件有以下三種方式:
1. 舊的NIO方式,使用BufferedReader
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class WithoutNIOExample
{
public static void main(String[] args)
{
BufferedReader br = null;
String sCurrentLine = null;
try
{
br = new BufferedReader(
new FileReader("test.txt"));
while ((sCurrentLine = br.readLine()) != null)
{
System.out.println(sCurrentLine);
}
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
try
{
if (br != null)
br.close();
} catch (IOException ex)
{
ex.printStackTrace();
}
}
}
}
2. 使用buffer讀取小文件
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class ReadFileWithFileSizeBuffer
{
public static void main(String args[])
{
try
{
RandomAccessFile aFile = new RandomAccessFile(
"test.txt","r");
FileChannel inChannel = aFile.getChannel();
long fileSize = inChannel.size();
ByteBuffer buffer = ByteBuffer.allocate((int) fileSize);
inChannel.read(buffer);
buffer.rewind();
buffer.flip();
for (int i = 0; i < fileSize; i++)
{
System.out.print((char) buffer.get());
}
inChannel.close();
aFile.close();
}
catch (IOException exc)
{
System.out.println(exc);
System.exit(1);
}
}
}
3. 分塊讀取大文件
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class ReadFileWithFixedSizeBuffer
{
public static void main(String[] args) throws IOException
{
RandomAccessFile aFile = new RandomAccessFile
("test.txt", "r");
FileChannel inChannel = aFile.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
while(inChannel.read(buffer) > 0)
{
buffer.flip();
for (int i = 0; i < buffer.limit(); i++)
{
System.out.print((char) buffer.get());
}
buffer.clear(); // do something with the data and clear/compact it.
}
inChannel.close();
aFile.close();
}
}
4. 使用MappedByteBuffer讀取文件
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class ReadFileWithMappedByteBuffer
{
public static void main(String[] args) throws IOException
{
RandomAccessFile aFile = new RandomAccessFile
("test.txt", "r");
FileChannel inChannel = aFile.getChannel();
MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
buffer.load();?
for (int i = 0; i < buffer.limit(); i++)
{
System.out.print((char) buffer.get());
}
buffer.clear(); // do something with the data and clear/compact it.
inChannel.close();
aFile.close();
}
}
⑽ Java NIO與IO的區別和比較
J2SE1.4以上版本中發布了全新的I/O類庫。本文將通過一些實例來簡單介紹NIO庫提供的一些新特性:非阻塞I/O,字元轉換,緩沖以及通道。
一. 介紹NIO
NIO包(java.nio.*)引入了四個關鍵的抽象數據類型,它們共同解決傳統的I/O類中的一些問題。
1. Buffer:它是包含數據且用於讀寫的線形表結構。其中還提供了一個特殊類用於內存映射文件的I/O操作。
2. Charset:它提供Unicode字元串影射到位元組序列以及逆影射的操作。
3. Channels:包含socket,file和pipe三種管道,它實際上是雙向交流的通道。
4. Selector:它將多元非同步I/O操作集中到一個或多個線程中(它可以被看成是Unix中select()函數或Win32中WaitForSingleEvent()函數的面向對象版本)。
二. 回顧傳統
在介紹NIO之前,有必要了解傳統的I/O操作的方式。以網路應用為例,傳統方式需要監聽一個ServerSocket,接受請求的連接為其提供服務(服務通常包括了處理請求並發送響應)圖一是伺服器的生命周期圖,其中標有粗黑線條的部分表明會發生I/O阻塞。
圖一
可以分析創建伺服器的每個具體步驟。首先創建ServerSocket
ServerSocket server=new ServerSocket(10000);
然後接受新的連接請求
Socket newConnection=server.accept();
對於accept方法的調用將造成阻塞,直到ServerSocket接受到一個連接請求為止。一旦連接請求被接受,伺服器可以讀客戶socket中的請求。
InputStream in = newConnection.getInputStream();
InputStreamReader reader = new InputStreamReader(in);
BufferedReader buffer = new BufferedReader(reader);
Request request = new Request();
while(!request.isComplete()) {
String line = buffer.readLine();
request.addLine(line);
}
這樣的操作有兩個問題,首先BufferedReader類的readLine()方法在其緩沖區未滿時會造成線程阻塞,只有一定數據填滿了緩沖區或者客戶關閉了套接字,方法才會返回。其次,它回產生大量的垃圾,BufferedReader創建了緩沖區來從客戶套接字讀入數據,但是同樣創建了一些字元串存儲這些數據。雖然BufferedReader內部提供了StringBuffer處理這一問題,但是所有的String很快變成了垃圾需要回收。
同樣的問題在發送響應代碼中也存在
Response response = request.generateResponse();
OutputStream out = newConnection.getOutputStream();
InputStream in = response.getInputStream();
int ch;
while(-1 != (ch = in.read())) {
out.write(ch);
}
newConnection.close();
類似的,讀寫操作被阻塞而且向流中一次寫入一個字元會造成效率低下,所以應該使用緩沖區,但是一旦使用緩沖,流又會產生更多的垃圾。
傳統的解決方法
通常在Java中處理阻塞I/O要用到線程(大量的線程)。一般是實現一個線程池用來處理請求,如圖二
圖二
線程使得伺服器可以處理多個連接,但是它們也同樣引發了許多問題。每個線程擁有自己的棧空間並且佔用一些CPU時間,耗費很大,而且很多時間是浪費在阻塞的I/O操作上,沒有有效的利用CPU。
三. 新I/O
1. Buffer
傳統的I/O不斷的浪費對象資源(通常是String)。新I/O通過使用Buffer讀寫數據避免了資源浪費。Buffer對象是線性的,有序的數據集合,它根據其類別只包含唯一的數據類型。
java.nio.Buffer 類描述
java.nio.ByteBuffer 包含位元組類型。 可以從ReadableByteChannel中讀在 WritableByteChannel中寫
java.nio.MappedByteBuffer 包含位元組類型,直接在內存某一區域映射
java.nio.CharBuffer 包含字元類型,不能寫入通道
java.nio.DoubleBuffer 包含double類型,不能寫入通道
java.nio.FloatBuffer 包含float類型
java.nio.IntBuffer 包含int類型
java.nio.LongBuffer 包含long類型
java.nio.ShortBuffer 包含short類型
可以通過調用allocate(int capacity)方法或者allocateDirect(int capacity)方法分配一個Buffer。特別的,你可以創建MappedBytesBuffer通過調用FileChannel.map(int mode,long position,int size)。直接(direct)buffer在內存中分配一段連續的塊並使用本地訪問方法讀寫數據。非直接(nondirect)buffer通過使用Java中的數組訪問代碼讀寫數據。有時候必須使用非直接緩沖例如使用任何的wrap方法(如ByteBuffer.wrap(byte[]))在Java數組基礎上創建buffer。
2. 字元編碼
向ByteBuffer中存放數據涉及到兩個問題:位元組的順序和字元轉換。ByteBuffer內部通過ByteOrder類處理了位元組順序問題,但是並沒有處理字元轉換。事實上,ByteBuffer沒有提供方法讀寫String。
Java.nio.charset.Charset處理了字元轉換問題。它通過構造CharsetEncoder和CharsetDecoder將字元序列轉換成位元組和逆轉換。
3. 通道(Channel)
你可能注意到現有的java.io類中沒有一個能夠讀寫Buffer類型,所以NIO中提供了Channel類來讀寫Buffer。通道可以認為是一種連接,可以是到特定設備,程序或者是網路的連接。通道的類等級結構圖如下
圖三
圖中ReadableByteChannel和WritableByteChannel分別用於讀寫。
GatheringByteChannel可以從使用一次將多個Buffer中的數據寫入通道,相反的,ScatteringByteChannel則可以一次將數據從通道讀入多個Buffer中。你還可以設置通道使其為阻塞或非阻塞I/O操作服務。
為了使通道能夠同傳統I/O類相容,Channel類提供了靜態方法創建Stream或Reader
4. Selector
在過去的阻塞I/O中,我們一般知道什麼時候可以向stream中讀或寫,因為方法調用直到stream准備好時返回。但是使用非阻塞通道,我們需要一些方法來知道什麼時候通道准備好了。在NIO包中,設計Selector就是為了這個目的。SelectableChannel可以注冊特定的事件,而不是在事件發生時通知應用,通道跟蹤事件。然後,當應用調用Selector上的任意一個selection方法時,它查看注冊了的通道看是否有任何感興趣的事件發生。圖四是selector和兩個已注冊的通道的例子
圖四
並不是所有的通道都支持所有的操作。SelectionKey類定義了所有可能的操作位,將要用兩次。首先,當應用調用SelectableChannel.register(Selector sel,int op)方法注冊通道時,它將所需操作作為第二個參數傳遞到方法中。然後,一旦SelectionKey被選中了,SelectionKey的readyOps()方法返回所有通道支持操作的數位的和。SelectableChannel的validOps方法返回每個通道允許的操作。注冊通道不支持的操作將引發IllegalArgumentException異常。下表列出了SelectableChannel子類所支持的操作。
ServerSocketChannel OP_ACCEPT
SocketChannel OP_CONNECT, OP_READ, OP_WRITE
DatagramChannel OP_READ, OP_WRITE
Pipe.SourceChannel OP_READ
Pipe.SinkChannel OP_WRITE
四. 舉例說明
1. 簡單網頁內容下載
這個例子非常簡單,類SocketChannelReader使用SocketChannel來下載特定網頁的HTML內容。
package examples.nio;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.net.InetSocketAddress;
import java.io.IOException;
public class SocketChannelReader{
private Charset charset=Charset.forName("UTF-8");//創建UTF-8字元集
private SocketChannel channel;
public void getHTMLContent(){
try{
connect();
sendRequest();
readResponse();
}catch(IOException e){
System.err.println(e.toString());
}finally{
if(channel!=null){
try{
channel.close();
}catch(IOException e){}
}
}
}
private void connect()throws IOException{//連接到CSDN
InetSocketAddress socketAddress=
new InetSocketAddress("http://www.csdn.net",80/);
channel=SocketChannel.open(socketAddress);
//使用工廠方法open創建一個channel並將它連接到指定地址上
//相當與SocketChannel.open().connect(socketAddress);調用
}
private void sendRequest()throws IOException{
channel.write(charset.encode("GET "
+"/document"
+"\r\n\r\n"));//發送GET請求到CSDN的文檔中心
//使用channel.write方法,它需要CharByte類型的參數,使用
//Charset.encode(String)方法轉換字元串。
}
private void readResponse()throws IOException{//讀取應答
ByteBuffer buffer=ByteBuffer.allocate(1024);//創建1024位元組的緩沖
while(channel.read(buffer)!=-1){
buffer.flip();//flip方法在讀緩沖區位元組操作之前調用。
System.out.println(charset.decode(buffer));
//使用Charset.decode方法將位元組轉換為字元串
buffer.clear();//清空緩沖
}
}
public static void main(String [] args){
new SocketChannelReader().getHTMLContent();
}
2. 簡單的加法伺服器和客戶機
伺服器代碼
package examples.nio;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.net.InetSocketAddress;
import java.io.IOException;
/**
* SumServer.java
*
*
* Created: Thu Nov 06 11:41:52 2003
*
* @author starchu1981
* @version 1.0
*/
public class SumServer {
private ByteBuffer _buffer=ByteBuffer.allocate(8);
private IntBuffer _intBuffer=_buffer.asIntBuffer();
private SocketChannel _clientChannel=null;
private ServerSocketChannel _serverChannel=null;
public void start(){
try{
openChannel();
waitForConnection();
}catch(IOException e){
System.err.println(e.toString());
}
}
private void openChannel()throws IOException{
_serverChannel=ServerSocketChannel.open();
_serverChannel.socket().bind(new InetSocketAddress(10000));
System.out.println("伺服器通道已經打開");
}
private void waitForConnection()throws IOException{
while(true){
_clientChannel=_serverChannel.accept();
if(_clientChannel!=null){
System.out.println("新的連接加入");
processRequest();
_clientChannel.close();
}
}
}
private void processRequest()throws IOException{
_buffer.clear();
_clientChannel.read(_buffer);
int result=_intBuffer.get(0)+_intBuffer.get(1);
_buffer.flip();
_buffer.clear();
_intBuffer.put(0,result);
_clientChannel.write(_buffer);
}
public static void main(String [] args){
new SumServer().start();
}
} // SumServer
客戶代碼
package examples.nio;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.channels.SocketChannel;
import java.net.InetSocketAddress;
import java.io.IOException;
/**
* SumClient.java
*
*
* Created: Thu Nov 06 11:26:06 2003
*
* @author starchu1981
* @version 1.0
*/
public class SumClient {
private ByteBuffer _buffer=ByteBuffer.allocate(8);
private IntBuffer _intBuffer;
private SocketChannel _channel;
public SumClient() {
_intBuffer=_buffer.asIntBuffer();
} // SumClient constructor
public int getSum(int first,int second){
int result=0;
try{
_channel=connect();
sendSumRequest(first,second);
result=receiveResponse();
}catch(IOException e){System.err.println(e.toString());
}finally{
if(_channel!=null){
try{
_channel.close();
}catch(IOException e){}
}
}
return result;
}
private SocketChannel connect()throws IOException{
InetSocketAddress socketAddress=
new InetSocketAddress("localhost",10000);
return SocketChannel.open(socketAddress);
}
private void sendSumRequest(int first,int second)throws IOException{
_buffer.clear();
_intBuffer.put(0,first);
_intBuffer.put(1,second);
_channel.write(_buffer);
System.out.println("發送加法請求 "+first+"+"+second);
}
private int receiveResponse()throws IOException{
_buffer.clear();
_channel.read(_buffer);
return _intBuffer.get(0);
}
public static void main(String [] args){
SumClient sumClient=new SumClient();
System.out.println("加法結果為 :"+sumClient.getSum(100,324));
}
} // SumClient
3. 非阻塞的加法伺服器
首先在openChannel方法中加入語句
_serverChannel.configureBlocking(false);//設置成為非阻塞模式
重寫WaitForConnection方法的代碼如下,使用非阻塞方式
private void waitForConnection()throws IOException{
Selector acceptSelector = SelectorProvider.provider().openSelector();
/*在伺服器套接字上注冊selector並設置為接受accept方法的通知。
這就告訴Selector,套接字想要在accept操作發生時被放在ready表
上,因此,允許多元非阻塞I/O發生。*/
SelectionKey acceptKey = ssc.register(acceptSelector,
SelectionKey.OP_ACCEPT);
int keysAdded = 0;