网络编程
BIO(Blocking I/O)
传统阻塞式 IO,一个连接对应一个线程。
// 服务端
ServerSocket server = new ServerSocket(8080);
while (true) {
Socket socket = server.accept(); // 阻塞等待连接
new Thread(() -> handle(socket)).start(); // 每个连接新建线程
}
private void handle(Socket socket) {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true)) {
String line;
while ((line = reader.readLine()) != null) { // 阻塞读
writer.println("Echo: " + line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 客户端
Socket socket = new Socket("localhost", 8080);
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
writer.println("hello");
System.out.println(reader.readLine());缺点:连接数增多时线程数线性增长,资源耗尽。
NIO(Non-blocking I/O)
Java 1.4 引入,基于 Channel、Buffer、Selector。
核心组件
| 组件 | 说明 |
|---|---|
Channel | 双向数据通道,替代 Stream |
Buffer | 数据读写缓冲区 |
Selector | 多路复用器,单线程管理多个 Channel |
// Selector 模型
Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(8080));
serverChannel.configureBlocking(false); // 非阻塞
serverChannel.register(selector, SelectionKey.OP_ACCEPT); // 注册感兴趣的事件
while (true) {
selector.select(); // 阻塞直到有事件就绪
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
if (key.isAcceptable()) {
SocketChannel client = serverChannel.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buf = ByteBuffer.allocate(1024);
int n = client.read(buf);
if (n == -1) { client.close(); continue; }
buf.flip(); // 切换为读模式
byte[] data = new byte[buf.remaining()];
buf.get(data);
System.out.println("收到: " + new String(data));
}
}
}Buffer 操作
ByteBuffer buf = ByteBuffer.allocate(1024);
// 写模式:position=0, limit=capacity
buf.put("hello".getBytes());
// 切换到读模式:limit=position, position=0
buf.flip();
byte[] data = new byte[buf.remaining()];
buf.get(data);
// 重置为写模式(保留未读数据)
buf.compact();
// 重置为写模式(丢弃数据)
buf.clear();
// 标记与重置
buf.mark(); // 标记当前 position
buf.reset(); // 回到 mark 位置AIO(Asynchronous I/O,NIO.2)
Java 7 引入,真正异步,操作完成后回调。
AsynchronousServerSocketChannel server =
AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(8080));
server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
@Override
public void completed(AsynchronousSocketChannel client, Void att) {
server.accept(null, this); // 继续接受下一个连接
ByteBuffer buf = ByteBuffer.allocate(1024);
client.read(buf, buf, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer n, ByteBuffer buffer) {
buffer.flip();
client.write(buffer); // 回写数据
}
@Override
public void failed(Throwable e, ByteBuffer buffer) { }
});
}
@Override
public void failed(Throwable e, Void att) { }
});IO 模型对比
| 模型 | 特点 | 适用场景 |
|---|---|---|
| BIO | 同步阻塞,一连接一线程 | 连接数少、并发低 |
| NIO | 同步非阻塞,多路复用 | 高并发、长连接 |
| AIO | 异步非阻塞,回调通知 | 超高并发(实际使用少) |
生产环境高并发网络编程推荐使用 Netty,它对 NIO 进行了深度封装。
UDP
// 服务端
DatagramSocket server = new DatagramSocket(8080);
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
server.receive(packet); // 阻塞接收
String msg = new String(packet.getData(), 0, packet.getLength());
// 客户端
DatagramSocket client = new DatagramSocket();
byte[] data = "hello".getBytes();
DatagramPacket packet = new DatagramPacket(
data, data.length,
InetAddress.getByName("localhost"), 8080);
client.send(packet);URL 与 HTTP
// 简单 HTTP GET(Java 11+)
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users"))
.header("Authorization", "Bearer " + token)
.GET()
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
// 异步请求
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println);
// POST JSON
HttpRequest post = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString("{\"name\":\"Alice\"}"))
.build();InetAddress
InetAddress addr = InetAddress.getByName("www.example.com");
addr.getHostAddress(); // IP 地址字符串
addr.getHostName(); // 主机名
addr.isReachable(3000); // 是否可达
InetAddress local = InetAddress.getLocalHost();