NIO技術-4-Socket通道

1、Socket通道介紹java

Socket通道即套接字通道,是一種基於TCP鏈接協議傳輸數據的通道。Socket通道能夠以分阻塞模式運行,而且能夠被Reactor設計模式使用,由於Socket通道是可供選擇的,其繼承SelectableChannel類。設計模式

非阻塞I/O是複雜的、高性能的程序構建的基礎。數組

使用NIO的Socket通道就沒有必須像舊的Socket同樣爲每一個Socket建立一個線程,避免了N個線程之間切換上下文的性能開銷,使用NIO的Socket通道能夠一個或幾個線程就能夠管理成百上千個已鏈接的Socket,而且性能消耗很是小。緩存

1. ServerSocketChannel:負責監聽某個端口,等待客戶端Socket鏈接進來服務器

2. SocketChannel:能夠理解爲一對一對使用,客戶端使用SocketChannel鏈接服務器,服務器也會有個對等的SocketChannel網絡

3. DatagramChannel:基於UDP發送數據報socket

全部的Socket通道類被建立後都有一個關聯的Socket;可是使用舊的方法直接建立Socket並不會關聯一個通道,雖然它們有getChannel()方法,可是這個方法會返回null。性能

2、ServerSocketChannel.net

ServerSocketChannel有以下方法可供使用:線程

1. open():打開一個ServerSocketChannel,用於建立ServerSocketChannel

2. validOps():返回SelectionKey.OP_ACCEPT,通常用不到,可用於驗證註冊到選擇器時指定的興趣是否位OP_ACCEPT

3. socket():返回與通道關聯的ServerSocket

4. accept():等待接受一個Socket鏈接,非阻塞模式下可能返回null

//打開一個ServerSocketChannel  
ServerSocketChannel ssc = ServerSocketChannel.open();  
//將通道關聯的ServerSocket綁定65535端口  
//jdk1.7以後能夠ssc.bind(new InetSocketAddress(65535))  
ssc.socket().bind(new InetSocketAddress(65535));  
//設置非阻塞模式  
ssc.configureBlocking(false);

3、SocketChannel

SocketChannel有以下方法可供使用:

open():打開一個SocketChannel
open(SocketAddress remote):同open(),並指定鏈接服務器地址
validOps():返回SelectionKey.OP_READ | SelectionKey.OP_WRITE | SelectionKey.OP_CONNECT,做用同ServerSocketChannel的validOps()
socket():返回通道關聯的Socket
isConnected():檢查是否已鏈接,返回true or false
isConnectionPending():檢查是否處於請求鏈接狀態
connect(SocketAddress remote):鏈接指定地址服務器
finishConnect():強制完成鏈接,完成鏈接則返回true,返回false
read(ByteBuffer dst):將通道的數據讀到ByteBuffer
read(ByteBuffer[] dsts):將通道數據分散讀到ByteBuffer數組
read(ByteBuffer[] dsts, int offset, int length):將通道數據分散讀到ByteBuffer數組,並指定讀取數據的開始位置和讀取的長度
write(ByteBuffer src):將字節緩衝區的數據寫入通道
write(ByteBuffer[] srcs):將分散的字節緩衝區的數據彙集寫入通道
write(ByteBuffer[] srcs, int offset, int length):同上一個方法,並指定寫入數據的開始位置和長度
SocketChannel sc = SocketChannel.open();  
sc.configureBlocking(false);  
sc.connect(new InetSocketAddress("127.0.0.1", 65535));  
//輪詢直到成功鏈接爲止  
while(!sc.isConnected()){  
      
}

4、DatagramChannel

open():打開一個DatagramChannel
validOps():返回SelectionKey.OP_READ | SelectionKey.OP_WRITE
socket():返回關聯的DatagramSocket
isConnected():是否已鏈接
connect(SocketAddress remote)》:鏈接某個地址
disconnect():斷開鏈接
receive(ByteBuffer dst):接收數據,從通道讀入緩衝區
send(ByteBuffer src, SocketAddress target):將緩衝區的數據發送到指定網絡地址
read(ByteBuffer dst)
read(ByteBuffer[] dsts)
read(ByteBuffer[] dsts, int offset, int length)
write(ByteBuffer src)
write(ByteBuffer[] srcs)
write(ByteBuffer[] srcs, int offset, int length)

這麼須要說明下,DatagramChannel提供了connect方法,別被這樣的方法套住了,DatagramSocket是基於UDP的,UDP是無鏈接的,connection方法只是綁定某個地址,只能從這個地址接收數據或發送數據到這個地址。

DEMO:

Java簡單版飛鴿傳書:http://download.csdn.net/detail/abc_key/7497543

Java簡單聊天室:http://download.csdn.net/detail/abc_key/7459723

Java NIO聊天室 中:若客戶端強制關閉,服務器會報「java.io.IOException: 遠程主機強迫關閉了一個現有的鏈接。而且服務器會在報錯後中止運行,錯誤的意思就是客戶端關閉了,可是服務器還在從這個套接字通道讀取數據,便拋出IOException,致使這種狀況出現的緣由就是,客戶端異常關閉後,服務器的選擇器會獲取到與客戶端套接字對應的套接字通道SelectionKey,而且這個key的興趣是OP_READ,執行從這個通道讀取數據時,客戶端已套接字已關閉,因此會出現「java.io.IOException: 遠程主機強迫關閉了一個現有的鏈接」的錯誤。解決這種問題也很簡單,就是服務器在讀取數據時,若發生異常,則取消當前key並關閉通道,以下代碼:

//獲取此key對應的套接字通道  
SocketChannel channel = (SocketChannel) key.channel();  
//建立一個大小爲1024k的緩存區  
ByteBuffer buffer = ByteBuffer.allocate(1024);  
StringBuffer sb = new StringBuffer();  
//將通道的數據讀到緩存區  
int count = 0;  
try{  
    count = channel.read(buffer);  
}catch(IOException e){  
    key.cancel();  
    channel.socket().close();  
    channel.close();  
    return;  
}

因在catch中取消了key,readMsg返回後,run方法繼續往下走,以前的代碼會報「java.nio.channels.CancelledKeyException」錯誤,因此須要判斷當前key是否有效,

以前的代碼:

//若此key的通道的行爲是"讀"  
if (key.isReadable()) {  
    readMsg(key);  
}  
//若次key的通道的行爲是"寫"  
if (key.isWritable()) {  
    writeMsg(key);  
}

修復後的代碼:

//若此key的通道的行爲是"讀"  
if (key.isValid() && key.isReadable()) {  
    readMsg(key);  
}  
//若次key的通道的行爲是"寫"  
if (key.isValid() && key.isWritable()) {  
    writeMsg(key);  
}

這樣改良以後的聊天室服務端,客戶端異常強制關閉後,服務器便會妥善處理了,也不會報錯,更不會中止運行。改良以後還算比較穩定運行的。

相關文章
相關標籤/搜索