在传统的Socket编程中,每个客户端与服务器之间的通信都需要创建一个新的线程来处理。然而,在高并发的环境下,创建大量线程会消耗过多的系统资源,导致性能下降。Java NIO(New Input/Output)是Java 1.4版本引入的一种非阻塞IO模型,使用NIO编程可以实现为多个并发连接服务的高效网络应用。
架构
NIO采用了IO多路复用的机制,通过一个或者几个Selector轮询一组非阻塞的通道,实现多个通道的同时读写。当某个通道有数据可读/可写时,就会通知选择器,从而避免了创建大量线程来处理连接。
NIO Socket编程实践
以下示例展示了NIO Socket编程的一个简单实践,包括客户端和服务器端的实现:
服务器端
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 Server {
private Selector selector;
private ByteBuffer buffer = ByteBuffer.allocate(1024);
public static void main(String[] args) throws IOException {
new Server().runServer();
}
public void runServer() throws IOException {
// 创建选择器
selector = Selector.open();
// 创建ServerSocketChannel并绑定端口
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.bind(new InetSocketAddress(8888));
// 将ServerSocketChannel注册到选择器上,并监听连接事件
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("Server started...");
// 循环监听事件
while (true) {
selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> iterator = keys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()) {
// 处理连接请求
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
System.out.println("Accepted connection from " + socketChannel.getRemoteAddress());
} else if (key.isReadable()) {
// 处理读事件
SocketChannel socketChannel = (SocketChannel) key.channel();
buffer.clear();
int bytesRead = socketChannel.read(buffer);
if (bytesRead == -1) {
socketChannel.close();
} else if (bytesRead > 0) {
buffer.flip();
byte[] data = new byte[buffer.limit()];
buffer.get(data);
System.out.println("Received message: " + new String(data));
}
}
}
}
}
}
客户端
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;
public class Client {
private ByteBuffer buffer = ByteBuffer.allocate(1024);
public static void main(String[] args) throws IOException {
new Client().runClient();
}
public void runClient() throws IOException {
// 连接服务器
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888));
socketChannel.configureBlocking(false);
System.out.println("Client started...");
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("Enter message: ");
String message = scanner.nextLine();
if (message.equalsIgnoreCase("exit")) {
socketChannel.close();
break;
}
buffer.clear();
buffer.put(message.getBytes());
buffer.flip();
socketChannel.write(buffer);
}
}
}
在上述示例中,服务器端监听到Accept事件时接受客户端连接,并注册到选择器上。服务器端监听到Read事件时读取客户端传递的数据,并打印出来。客户端连接服务器后,可以通过控制台输入消息,将其发送给服务器。
总结
通过NIO编程,我们可以实现高并发的网络应用。使用选择器来管理多个通道的读写事件,避免了线程的创建和切换,提高了系统的性能。在实际应用中,可以根据需求对NIO进行更深入的学习和应用。
本文来自极简博客,作者:红尘紫陌,转载请注明原文链接:NIO中的Socket编程实践:客户端与服务器通信