使用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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class NIOServer {
private static final int PORT = 8080;
public static void main(String[] args) {
try (Selector selector = Selector.open();
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {
serverSocketChannel.bind(new InetSocketAddress(PORT));
serverSocketChannel.configureBlocking(false);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("Server started on port " + PORT);
while (true) {
selector.select();
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = client.read(buffer);
if (bytesRead > 0) {
buffer.flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
String message = new String(data);
System.out.println("Received from client: " + message);
ByteBuffer responseBuffer = ByteBuffer.wrap("Message received".getBytes());
client.write(responseBuffer);
} else if (bytesRead == -1) {
client.close();
}
}
keyIterator.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
NIO在网络通信方面相比于传统Java IO的优势
- 非阻塞I/O
- 传统IO:传统的Java IO是阻塞式的,当一个线程执行
read()
或write()
操作时,该线程会被阻塞,直到操作完成。这意味着在高并发场景下,每个客户端连接都需要一个独立的线程来处理,线程数量随着客户端的增加而急剧增加,消耗大量系统资源。
- NIO:NIO采用非阻塞I/O,线程在执行
read()
或write()
操作时,如果数据尚未准备好,线程不会被阻塞,而是可以继续执行其他任务。这样可以使用少量的线程处理大量的客户端连接,大大提高了系统的并发处理能力。
- 多路复用
- 传统IO:对于多个客户端连接,需要为每个连接分配一个线程,每个线程处理一个连接的I/O操作,线程管理成本高。
- NIO:NIO通过Selector实现多路复用,一个Selector可以同时监控多个Channel的I/O事件。当某个Channel有事件发生时,Selector会通知对应的线程进行处理。这种方式使得一个线程可以处理多个客户端连接的I/O操作,优化了资源利用。
- 缓冲区管理
- 传统IO:传统IO使用字节流或字符流进行数据读写,数据的处理相对简单直接,但在处理大数据量时效率较低。
- NIO:NIO使用ByteBuffer进行数据存储和传输,ByteBuffer提供了更灵活和高效的缓冲区管理机制。可以通过
flip()
、rewind()
等方法灵活操作缓冲区,并且支持直接内存访问(Direct Memory Access, DMA),减少数据在用户空间和内核空间之间的拷贝,提高数据传输效率。
- 提高响应速度
- 传统IO:由于阻塞特性,在高并发场景下,新的客户端连接可能需要等待已有线程处理完当前任务才能得到响应,响应速度会受到影响。
- NIO:非阻塞和多路复用的特性使得服务器可以及时响应多个客户端的请求,不会因为某个连接的I/O操作未完成而阻塞其他连接的处理,从而提高了整体的响应速度。