前面用netty的方式写过一个聊天的demo,这次用nio来写一下,熟悉一下Java nio的Selector,与Selector相关的对象主要有 Selector、Channel、SelectionKey,这三者之间的关系和Selector的处理流程如下图:
多个channel可以注册到一个Selector上,调用channel的register的方法,指定需要关注的事件。
一个线程处理多个客户端的请求:
public class NioTest12 {
public static void main(String[] args) throws Exception {
int[] ports = new int[5];
ports[0] = 50000;
ports[1] = 50001;
ports[2] = 50002;
ports[3] = 50003;
ports[4] = 50004;
Selector selector = Selector.open();
for (int i = 0; i < ports.length; i++) {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//设置为非阻塞模式
serverSocketChannel.configureBlocking(false);
InetSocketAddress address = new InetSocketAddress(ports[i]);
//绑定端口
serverSocketChannel.bind(address);
//channel注册到selector,最终channel被包装成SelectionKeyImpl对象,以数组的方式存在Selector中:
// The list of SelectableChannels serviced by this Selector
// protected SelectionKeyImpl[] channelArray;
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("监听端口:" + ports[i]);
}
while (true) {
int numbers = selector.select();
System.out.println("numbers:" + numbers);
Set<SelectionKey> selectionKeys = selector.selectedKeys();
System.out.println("selectionKeys:" + selectionKeys);
Iterator<SelectionKey> iter = selectionKeys.iterator();
while (iter.hasNext()) {
// SelectionKey 保存着 SelectableChannel 与 Selector 之间的关联
SelectionKey selectionKey = iter.next();
if (selectionKey.isAcceptable()) {
ServerSocketChannel serverSocketChannel = (ServerSocketChannel)selectionKey.channel();
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
iter.remove();
System.out.println("获得客户端连接:" + socketChannel);
} else if (selectionKey.isReadable()) {
SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
int bytesRead = 0;
while (true) {
ByteBuffer buffer = ByteBuffer.allocate(512);
buffer.clear();
int read = socketChannel.read(buffer);
if (read <= 0) {
break;
}
buffer.flip();
socketChannel.write(buffer);
bytesRead += read;
}
System.out.println("读取:" + bytesRead + ", 来自于:" + socketChannel);
iter.remove();
}
}
}
}
}
聊天demo的服务端:
public class NioTest13 {
public static void main(String[] args) throws Exception {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
ServerSocket serverSocket = serverSocketChannel.socket();
serverSocket.bind(new InetSocketAddress(8899));
Map<String, SocketChannel> clientMap = new HashMap<>();
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
try {
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
selectionKeys.forEach(selectionKey -> {
final SocketChannel client;
try {
if (selectionKey.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel)selectionKey.channel();
client = server.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
String key = "【" + UUID.randomUUID().toString() + "】";
clientMap.put(key, client);
} else if (selectionKey.isReadable()) {
client = (SocketChannel)selectionKey.channel();
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
int count = client.read(readBuffer);
if (count > 0) {
readBuffer.flip();
Charset charset = Charset.forName("utf-8");
String receivedMessage = String.valueOf(charset.decode(readBuffer).array());
System.out.println(client + ": " + receivedMessage);
String senderKey = null;
for (Map.Entry<String, SocketChannel> entry : clientMap.entrySet()) {
if (client == entry.getValue()) {
senderKey = entry.getKey();
break;
}
}
for (Map.Entry<String, SocketChannel> entry : clientMap.entrySet()) {
SocketChannel channel = entry.getValue();
ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
writeBuffer.put((senderKey + ": " + receivedMessage).getBytes());
writeBuffer.flip();
channel.write(writeBuffer);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
});
selectionKeys.clear();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}