一、NIO Echo Server
- NioEchoServer.java
java
public class NioEchoServer {
public static void main(String[] args) {
try {
// 1. 创建 Selector
Selector selector = Selector.open();
// 2. 创建 ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false); // 设置为非阻塞
serverSocketChannel.bind(new InetSocketAddress(8080)); // 绑定端口
// 3. 将 ServerSocketChannel 注册到 Selector,关注 ACCEPT 事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("[NIO Echo Server] start on port 8080...");
// 4. 循环等待事件
while (true) {
int result = selector.select();
if (result == 0) {
continue;
}
// 5. 获取事件的 SelectionKey 集合
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
// 6. 处理事件
if (selectionKey.isAcceptable()) {
// 有连接
System.out.println("[NIO Echo Server] accept");
handleAccept(selectionKey, selector);
} else if (selectionKey.isReadable()) {
// 有数据可读
System.out.println("[NIO Echo Server] read");
handleRead(selectionKey);
}
iterator.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void handleAccept(SelectionKey selectionKey, Selector selector) {
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
try {
// 接受连接
SocketChannel socketChannel = serverSocketChannel.accept();
if (socketChannel == null) return;
socketChannel.configureBlocking(false);
// 将 SocketChannel 注册到 Selector,关注 READ 事件,并附加一个 ByteBuffer
socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
System.out.println("[NIO Echo Server] client connect: " + socketChannel.getRemoteAddress());
} catch (IOException e) {
e.printStackTrace();
}
}
private static void handleRead(SelectionKey selectionKey) {
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
ByteBuffer byteBuffer = (ByteBuffer) selectionKey.attachment();
try {
int bytesRead = socketChannel.read(byteBuffer);
if (bytesRead == -1) {
System.out.println("[NIO Echo Server] client disconnect: " + socketChannel.getRemoteAddress());
socketChannel.close();
return;
}
// 切换为读模式
byteBuffer.flip();
if (byteBuffer.hasRemaining()) {
byte[] receivedData = new byte[byteBuffer.remaining()];
byteBuffer.get(receivedData);
String message = new String(receivedData);
System.out.println("[NIO Echo Server] read: " + message);
byteBuffer.clear();
byteBuffer.put(receivedData);
byteBuffer.flip();
}
while (byteBuffer.hasRemaining()) {
socketChannel.write(byteBuffer);
}
byteBuffer.clear();
} catch (IOException e) {
e.printStackTrace();
try {
System.out.println("[NIO Echo Server] client disconnect: " + socketChannel.getRemoteAddress());
socketChannel.close();
} catch (IOException ex) {
e.printStackTrace();
}
}
}
}
二、Test Client
1、简单的阻塞客户端
java
try (Socket socket = new Socket("localhost", 8080)) {
PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("enter message: ");
String message = scanner.nextLine();
if ("exit".equalsIgnoreCase(message)) {
break;
}
// 发送消息
printWriter.println(message);
// 接收消息
String response = bufferedReader.readLine();
System.out.println("echo: " + response);
}
} catch (IOException e) {
e.printStackTrace();
}
-
Server 输出结果
[NIO Echo Server] start on port 8080...
[NIO Echo Server] accept
[NIO Echo Server] client connect: /127.0.0.1:59465
[NIO Echo Server] read
[NIO Echo Server] read: 123[NIO Echo Server] read
[NIO Echo Server] read: 456 -
Client 输出结果
enter message: 123
echo: 123
enter message: 456
echo: 456
2、多线程并发客户端
java
private static final int THREAD_COUNT = 3;
private static final int MESSAGES_PER_THREAD = 3;
java
ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT);
CountDownLatch countDownLatch = new CountDownLatch(THREAD_COUNT);
for (int i = 0; i < THREAD_COUNT; i++) {
final int clientId = i + 1;
executorService.submit(() -> {
try (Socket socket = new Socket("localhost", 8080)) {
PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
for (int j = 0; j < MESSAGES_PER_THREAD; j++) {
String message = "client-" + clientId + "-message-" + (j + 1);
System.out.println("[client " + clientId + "] send message: " + message);
printWriter.println(message);
String response = bufferedReader.readLine();
System.out.println("[client " + clientId + "] received: " + response);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
});
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
executorService.shutdown();
-
Server 输出结果
[NIO Echo Server] start on port 8080...
[NIO Echo Server] accept
[NIO Echo Server] client connect: /127.0.0.1:60733
[NIO Echo Server] accept
[NIO Echo Server] client connect: /127.0.0.1:60734
[NIO Echo Server] accept
[NIO Echo Server] client connect: /127.0.0.1:60732
[NIO Echo Server] read
[NIO Echo Server] read: client-3-message-1[NIO Echo Server] read
[NIO Echo Server] read: client-2-message-1[NIO Echo Server] read
[NIO Echo Server] read: client-1-message-1[NIO Echo Server] read
[NIO Echo Server] read: client-3-message-2[NIO Echo Server] read
[NIO Echo Server] read: client-2-message-2[NIO Echo Server] read
[NIO Echo Server] read: client-1-message-2[NIO Echo Server] read
[NIO Echo Server] read: client-3-message-3[NIO Echo Server] read
[NIO Echo Server] read: client-2-message-3[NIO Echo Server] read
[NIO Echo Server] read: client-1-message-3[NIO Echo Server] read
[NIO Echo Server] client disconnect: /127.0.0.1:60732
[NIO Echo Server] read
[NIO Echo Server] client disconnect: /127.0.0.1:60734
[NIO Echo Server] read
[NIO Echo Server] client disconnect: /127.0.0.1:60733 -
Client 输出结果
[client 1] send message: client-1-message-1
[client 3] send message: client-3-message-1
[client 2] send message: client-2-message-1
[client 3] received: client-3-message-1
[client 2] received: client-2-message-1
[client 1] received: client-1-message-1
[client 2] send message: client-2-message-2
[client 3] send message: client-3-message-2
[client 3] received: client-3-message-2
[client 2] received: client-2-message-2
[client 1] send message: client-1-message-2
[client 1] received: client-1-message-2
[client 3] send message: client-3-message-3
[client 2] send message: client-2-message-3
[client 3] received: client-3-message-3
[client 2] received: client-2-message-3
[client 1] send message: client-1-message-3
[client 1] received: client-1-message-3
三、Test NIO Client
1、NIO 异步客户端
java
try {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("localhost", 8080));
while (!socketChannel.finishConnect()) {
System.out.println("connecting...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("connect server");
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("enter message: ");
String message = scanner.nextLine();
if ("exit".equalsIgnoreCase(message)) {
break;
}
// 发送消息
byteBuffer.clear();
byteBuffer.put(message.getBytes());
byteBuffer.flip();
while (byteBuffer.hasRemaining()) {
socketChannel.write(byteBuffer);
}
// 接收消息
byteBuffer.clear();
int bytesRead;
while ((bytesRead = socketChannel.read(byteBuffer)) == 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (bytesRead == -1) {
System.out.println("disconnected");
break;
}
byteBuffer.flip();
byte[] responseBytes = new byte[byteBuffer.remaining()];
byteBuffer.get(responseBytes);
String response = new String(responseBytes);
System.out.println("echo: " + response);
}
} catch (IOException e) {
e.printStackTrace();
}
-
Server 输出结果
[NIO Echo Server] start on port 8080...
[NIO Echo Server] accept
[NIO Echo Server] client connect: /127.0.0.1:61864
[NIO Echo Server] read
[NIO Echo Server] read: 123
[NIO Echo Server] read
[NIO Echo Server] read: 456 -
Client 输出结果
connect server
enter message: 123
echo: 123
enter message: 456
echo: 456
2、NIO Selector 异步客户端
java
public class NioEchoClient {
public static void main(String[] args) {
try {
Selector selector = Selector.open();
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
// 注册连接事件
socketChannel.register(selector, SelectionKey.OP_CONNECT);
socketChannel.connect(new InetSocketAddress("localhost", 8080));
handleWrite(socketChannel);
while (true) {
int readyChannels = selector.select();
if (readyChannels == 0) {
continue;
}
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
iterator.remove();
if (selectionKey.isConnectable()) {
handleConnect(selectionKey, selector);
} else if (selectionKey.isReadable()) {
handleRead(selectionKey);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void handleWrite(SocketChannel socketChannel) {
new Thread(() -> {
Scanner scanner = new Scanner(System.in);
try {
while (socketChannel.isConnected()) {
System.out.print("enter message: ");
String message = scanner.nextLine();
if ("exit".equalsIgnoreCase(message)) {
break;
}
ByteBuffer byteBuffer = ByteBuffer.wrap((message).getBytes());
while (byteBuffer.hasRemaining() && socketChannel.isConnected()) {
try {
socketChannel.write(byteBuffer);
} catch (IOException e) {
e.printStackTrace();
break;
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
scanner.close();
try {
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
private static void handleConnect(SelectionKey selectionKey, Selector selector) {
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
try {
if (socketChannel.finishConnect()) {
System.out.println("connect server");
socketChannel.register(selector, SelectionKey.OP_READ);
} else {
System.out.println("connect server fail");
socketChannel.close();
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("connect server error");
try {
socketChannel.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
private static void handleRead(SelectionKey selectionKey) {
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
ByteBuffer byteBuffer = (ByteBuffer) selectionKey.attachment();
if (byteBuffer == null) {
byteBuffer = ByteBuffer.allocate(1024);
selectionKey.attach(byteBuffer);
}
try {
int bytesRead = socketChannel.read(byteBuffer);
if (bytesRead == -1) {
System.out.println("disconnected");
socketChannel.close();
return;
}
byteBuffer.flip();
byte[] responseBytes = new byte[byteBuffer.remaining()];
byteBuffer.get(responseBytes);
byteBuffer.clear();
String response = new String(responseBytes);
System.out.println("echo: " + response);
} catch (IOException e) {
e.printStackTrace();
try {
System.out.println("disconnected");
socketChannel.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
-
Server 输出结果
[NIO Echo Server] start on port 8080...
[NIO Echo Server] accept
[NIO Echo Server] client connect: /127.0.0.1:65429
[NIO Echo Server] read
[NIO Echo Server] read: 123
[NIO Echo Server] read
[NIO Echo Server] read: 456 -
Client 输出结果
connect server
enter message: 123
echo: 123
enter message: 456
echo: 456