NIO经典demo源码

server:

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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
package com.test.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

/**
* Created by lifei on 16/11/8.
*/
public class NIOServer {
private int flag = 0;/*标识数字*/
private int blockSize = 4096;/*缓冲区大小*/
private ByteBuffer sendBuffer = ByteBuffer.allocate(blockSize);/*接受数据缓冲区*/
private ByteBuffer receiveBuffer = ByteBuffer.allocate(blockSize);/*发送数据缓冲区*/
private Selector selector;

public NIOServer(int port) {
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();// 打开服务器套接字通道
serverSocketChannel.configureBlocking(false);//// 服务器配置为非阻塞
ServerSocket serverSocket = serverSocketChannel.socket(); // 检索与此通道关联的服务器套接字
serverSocket.bind(new InetSocketAddress(port));//绑定ip和端口
selector = Selector.open();//打开选择器
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);// 注册到selector,等待连接
System.out.println("Server start -> " + port);
} catch (IOException e) {
e.printStackTrace();
}
}

//监听
public void listen(){
while (true){
try {
selector.select();//如果查询不到,会阻塞 选择一组键,并且相应的通道已经打开
Set<SelectionKey> selectionKeys = selector.selectedKeys();// 返回此选择器的已选择键集。
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
iterator.remove();
//业务逻辑
handleKey(selectionKey);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

// 处理请求
public void handleKey(SelectionKey selectionKey){
try {
// 接受请求
ServerSocketChannel server;
SocketChannel client;
String receiveText;
String sendText;
int count;
if (selectionKey.isAcceptable()){// 测试此键的通道是否已准备好接受新的套接字连接。
server = (ServerSocketChannel) selectionKey.channel();// 返回为之创建此键的通道。
client = server.accept();// 接受到此通道套接字的连接。 此方法返回的套接字通道(如果有)将处于阻塞模式。
client.configureBlocking(false);// 配置为非阻塞
client.register(selector,SelectionKey.OP_READ);// 注册到selector,等待连接
} else if (selectionKey.isReadable()){
client = (SocketChannel) selectionKey.channel();// 返回为之创建此键的通道。
receiveBuffer.clear();//todo 将缓冲区清空以备下次读取
count = client.read(receiveBuffer);//读取服务器发送来的数据到缓冲区中
if (count > 0){
receiveText = new String(receiveBuffer.array(),0,count);
System.out.println("服务端接收到客户端的信息:"+receiveText);
client.register(selector,SelectionKey.OP_WRITE);
}
} else if (selectionKey.isWritable()){
sendBuffer.clear();//将缓冲区清空以备下次写入
client = (SocketChannel) selectionKey.channel();// 返回为之创建此键的通道。
sendText = "msg send to clent ..."+(flag++);//发送的数据
sendBuffer.put(sendText.getBytes());//向缓冲区中输入数据
sendBuffer.flip();//将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位
client.write(sendBuffer);//输出到通道
System.out.println("服务端发送数据给客户端:"+sendText);
client.register(selector,SelectionKey.OP_READ);
}
} catch (Exception e){
e.printStackTrace();
}
}

public static void main(String[] args) {
int port = 7080;
NIOServer server = new NIOServer(port);
server.listen();
}


}
```

# client:

```java
package com.test.nio;

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;

/**
* Created by lifei on 16/11/8.
* http://developer.51cto.com/art/201112/307685.htm
*/
public class NIOClient {
private static int flag = 0;/*标识数字*/
private static int blockSize = 4096;/*缓冲区大小*/
private static ByteBuffer sendBuffer = ByteBuffer.allocate(blockSize);/*接受数据缓冲区*/
private static ByteBuffer receiveBuffer = ByteBuffer.allocate(blockSize); /*服务器端地址*/
private final static InetSocketAddress serverAddress = new InetSocketAddress("127.0.0.1",7080);

public static void main(String[] args) {
try {
SocketChannel socketChannel = SocketChannel.open();// 打开socket通道
socketChannel.configureBlocking(false);// 设置为非阻塞方式
Selector selector = Selector.open();//打开选择器
socketChannel.register(selector, SelectionKey.OP_CONNECT);// 注册连接服务端socket动作
socketChannel.connect(serverAddress);// 连接

// 分配缓冲区大小内存
Set<SelectionKey> selectionKeys;
Iterator<SelectionKey> iterator;
SelectionKey selectionKey;
SocketChannel client;
String receiveText;
String sendText;
int count;

while (true){
try {
selector.select();//todo 选择一组键,其相应的通道已为 I/O 操作准备就绪。 此方法执行处于阻塞模式的选择操作。
selectionKeys = selector.selectedKeys();//返回此选择器的已选择键集。
iterator = selectionKeys.iterator();
while (iterator.hasNext()){
selectionKey = iterator.next();
if (selectionKey.isConnectable()){
System.out.println("client connet");
client = (SocketChannel) selectionKey.channel();
if (client.isConnectionPending()){// 判断此通道上是否正在进行连接操作。 完成套接字通道的连接过程。
client.finishConnect();
System.out.println("客户端完成连接操作");
sendBuffer.clear();
sendText = "Hello server...";
sendBuffer.put(sendText.getBytes());
sendBuffer.flip();
client.write(sendBuffer);
}
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);
}
} else if (selectionKey.isWritable()){
sendBuffer.clear();
client = (SocketChannel) selectionKey.channel();
sendText = "Msg to server ..."+(flag++);
sendBuffer.put(sendText.getBytes());
sendBuffer.flip();//将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位
client.write(sendBuffer);
System.out.println("客户端发送数据给服务端"+sendText);
client.register(selector,SelectionKey.OP_READ);
}
}
selectionKeys.clear();
} catch (Exception e){

}

}
} catch (IOException e) {
e.printStackTrace();
}
}
}