Java网络编程中的IO(Input/Output)模型是管理计算机对外部数据读取和写入操作的重要机制。Java提供了多种IO模型来满足不同的网络通信需求。
// 客户端
try (Socket socket = new Socket("localhost", 8080);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
// 阻塞直到数据到达
String response = in.readLine();
System.out.println("Response: " + response);
}
// 服务端
try (ServerSocket serverSocket = new ServerSocket(8080);
Socket clientSocket = serverSocket.accept(); // 阻塞等待连接
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)) {
out.println("Hello from blocking server!");
}
package com.example.helloword;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
/**
* 服务端功能
* 监听新连接:通过 ServerSocketChannel 接受客户端连接,并注册到 Selector。
* 处理消息:读取客户端消息并广播给其他用户。
* 上下线通知:当客户端连接或断开时,广播通知所有用户。
* 异常处理:检测客户端异常断开并清理资源。
*/
public class GroupChatServer {
private static final int PORT = 6667;
private ServerSocketChannel serverSocketChannel;
private Selector selector;
public GroupChatServer() {
try {
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(PORT));
serverSocketChannel.configureBlocking(false);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务器启动,监听端口:" + PORT);
} catch (IOException e) {
e.printStackTrace();
}
}
public void listen() {
try {
while (true) {
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove(); // 防止重复处理
if (key.isAcceptable()) {
handleAccept(key);
} else if (key.isReadable()) {
handleRead(key);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void handleAccept(SelectionKey key) throws IOException {
SocketChannel clientChannel = serverSocketChannel.accept();
clientChannel.configureBlocking(false);
clientChannel.register(selector, SelectionKey.OP_READ);
String msg = "[" + clientChannel.getRemoteAddress() + "] 上线了";
System.out.println(msg);
broadcast(msg, clientChannel); // 广播上线通知
}
private void handleRead(SelectionKey key) {
SocketChannel clientChannel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
try {
int len = clientChannel.read(buffer);
if (len > 0) {
String msg = new String(buffer.array(), 0, len).trim();
System.out.println("收到消息: " + msg);
broadcast(msg, clientChannel); // 广播消息
} else if (len == -1) { // 客户端正常关闭
handleDisconnect(clientChannel, "下线");
}
} catch (IOException e) {
handleDisconnect(clientChannel, "异常断开");
}
}
private void handleDisconnect(SocketChannel clientChannel, String reason) {
try {
String msg = "[" + clientChannel.getRemoteAddress() + "] " + reason;
System.out.println(msg);
broadcast(msg, clientChannel);
clientChannel.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
private void broadcast(String msg, SocketChannel excludeChannel) throws IOException {
for (SelectionKey key : selector.keys()) {
Channel targetChannel = key.channel();
if (targetChannel instanceof SocketChannel && targetChannel != excludeChannel) {
SocketChannel dest = (SocketChannel) targetChannel;
ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
dest.write(buffer);
}
}
}
public static void main(String[] args) {
new GroupChatServer().listen();
}
}
package com.example.helloword;
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.Scanner;
import java.util.Set;
/**
* 客户端功能
* 连接服务器:初始化时自动连接服务器。
* 发送消息:读取用户输入并发送到服务器。
* 接收消息:监听服务器广播的消息并实时显示。
* 退出机制:输入 exit 退出群聊并关闭连接。
*/
public class GroupChatClient {
private static final String HOST = "127.0.0.1";
private static final int PORT = 6667;
private SocketChannel socketChannel;
private Selector selector;
private String username;
public GroupChatClient() {
try {
selector = Selector.open();
socketChannel = SocketChannel.open(new InetSocketAddress(HOST, PORT));
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
username = "用户" + socketChannel.getLocalAddress().toString().substring(1).replaceAll("[:.]", "");
System.out.println(username + " 已连接到服务器");
} catch (IOException e) {
e.printStackTrace();
}
}
public void sendMsg(String msg) {
if ("exit".equalsIgnoreCase(msg)) {
try {
socketChannel.close();
selector.close();
System.out.println("已退出群聊");
System.exit(0);
} catch (IOException e) {
e.printStackTrace();
}
return;
}
try {
String formattedMsg = username + ": " + msg;
ByteBuffer buffer = ByteBuffer.wrap(formattedMsg.getBytes());
socketChannel.write(buffer);
} catch (IOException e) {
e.printStackTrace();
}
}
public void readMsg() {
try {
while (true) {
selector.select();
System.out.println("==> readMsg");
Set<SelectionKey> keys = selector.selectedKeys();
for (SelectionKey key : keys) {
if (key.isReadable()) {
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int len = channel.read(buffer);
if (len > 0) {
String msg = new String(buffer.array(), 0, len).trim();
System.out.println(msg);
} else if (len == -1) {
System.out.println("服务端已关闭连接");
key.cancel(); // 取消 SelectionKey 的注册
channel.close(); // 关闭通道
System.exit(0);
}
}
}
keys.clear();
}
} catch (IOException e) {
System.out.println("与服务器断开连接");
System.exit(0);
} finally {
// 关闭资源
try {
if (socketChannel != null) socketChannel.close();
if (selector != null) selector.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
public static void main(String[] args) {
GroupChatClient client = new GroupChatClient();
// 启动线程监听服务器消息
new Thread(client::readMsg).start();
// 读取用户输入并发送
Scanner scanner = new Scanner(System.in);
while (true) {
String msg = scanner.nextLine();
client.sendMsg(msg);
}
}
}
AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open();
server.bind(new InetSocketAddress(8080));
server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
@Override
public void completed(AsynchronousSocketChannel client, Void attachment) {
server.accept(null, this); // 继续接收新连接
ByteBuffer buffer = ByteBuffer.allocate(1024);
client.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer buffer) {
// 处理读取的数据
}
});
}
});
模型 | BIO | NIO | AIO |
---|---|---|---|
阻塞性 | 同步阻塞 | 同步非阻塞 | 异步非阻塞 |
线程模型 | 1 连接 1 线程 | 多路复用(单线程/线程池) | 回调驱动 |
复杂度 | 低 | 中高 | 高 |
适用场景 | 低并发、简单应用 | 高并发、实时响应(如 Netty) | 特定场景(如文件操作) |
接口 | Socket ServerSocket |
SocketChanne ServerSocketChannel |
AsynchronousSocketChannel AsynchronousServerSocketChannel |
实践建议:
为什么主流框架(如 Netty)选择 NIO 而非 AIO?