BIO 方式使得整個處理過程和鏈接是綁定的,只要鏈接創建,不管客戶端是否有消息發送,都要進行等待處理,必定程度上浪費了服務器端的硬件資源,所以就有了 NIO 方式。Java 對於 NIO 方式的支持是經過 Channel和 Selector 方式來實現,採用的方法爲向 Channel註冊感興趣的事件,而後經過 Selector 來獲取到發生了事件的 key,如發生了相應的事件,則進行相應的處理,不然則不作任何處理,是典型的Reactor 模式,按照這樣的方式,就不用像 BIO 方式同樣,即便在沒有消息的狀況下也須要佔據一個線程來阻塞讀取消息,從而提高服務器的使用效率, 爲實現 TCP/IP+NIO 方式的系統間通信, Java 提供了 SocketChannel和 ServerSocketChannel兩個關鍵的類,網絡 IO 的操做則改成經過ByteBuffer 來實現,具體的基於 java實現TCP/IP+NIO 方式的通信的方法以下所示。java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
package
com.flyoung;
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.util.Iterator;
import
java.util.Set;
import
java.nio.channels.SocketChannel;
public
class
NIOServer {
/*標誌數字*/
private static int flag = 0;
/*定義緩衝區大小*/
private static int block = 4096;
/*接收緩衝區*/
private static ByteBuffer receiveBuffer = ByteBuffer.allocate(block);
/*發送緩衝區*/
private static ByteBuffer sendBuffer = ByteBuffer.allocate(block);
/*定義Selector*/
private
Selector selector;
public
NIOServer(
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 -----8888:"
);
}
//監聽
public
void
listen()
throws
IOException{
while
(
true
){
//監控全部註冊的 channel ,當其中有註冊的 IO 操做能夠進行時,該函數返回,並將對應的 SelectionKey 加入 selected-key set
selector.select();
//Selected-key set 表明了全部經過 select() 方法監測到能夠進行 IO 操做的 channel ,這個集合能夠經過 selectedKeys() 拿到
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while
(iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
handleKey(selectionKey);
iterator.remove();
}
}
}
//處理請求
public
void
handleKey(SelectionKey selectionKey)
throws
IOException{
//接受請求
ServerSocketChannel serverSocketChannel =
null
;
SocketChannel socketChannel =
null
;
String receiveText;
String sendText;
int
count;
//測試此鍵的通道是否準備好接受新的套接字鏈接
if
(selectionKey.isAcceptable()){
//返回建立此鍵的通道
serverSocketChannel = (ServerSocketChannel)selectionKey.channel();
//接受客戶端創建鏈接的請求,並返回 SocketChannel 對象
socketChannel = serverSocketChannel.accept();
//配置爲非阻塞
socketChannel.configureBlocking(
false
);
//註冊到selector
socketChannel.register(selector, SelectionKey.OP_READ);
}
else
if
(selectionKey.isReadable()){
//返回爲之建立此鍵的通道
socketChannel = (SocketChannel)selectionKey.channel();
//將緩衝區清空,以備下次讀取
receiveBuffer.clear();
//將發送來的數據讀取到緩衝區
count = socketChannel.read(receiveBuffer);
if
(count>
0
){
receiveText =
new
String(receiveBuffer.array(),
0
,count);
System.out.println(
"服務器端接受到的數據---"
+receiveText);
socketChannel.register(selector, SelectionKey.OP_WRITE);
}
}
else
if
(selectionKey.isWritable()) {
//將緩衝區清空以備下次寫入
sendBuffer.clear();
// 返回爲之建立此鍵的通道。
socketChannel = (SocketChannel) selectionKey.channel();
sendText=
"message from server--"
+ flag++;
//向緩衝區中輸入數據
sendBuffer.put(sendText.getBytes());
//將緩衝區各標誌復位,由於向裏面put了數據標誌被改變要想從中讀取數據發向服務器,就要復位
sendBuffer.flip();
//輸出到通道
socketChannel.write(sendBuffer);
System.out.println(
"服務器端向客戶端發送數據--:"
+sendText);
socketChannel.register(selector, SelectionKey.OP_READ);
}
}
public
static
void
main(String[] args)
throws
IOException {
int
port =
8888
;
NIOServer server =
new
NIOServer(port);
server.listen();
}
}
|