Java 网络编程实战:Socket、ServerSocket 与 TCP/UDP 通信
Java java.net 包提供底层网络通信支持,核心协议:TCP(面向连接、可靠、流式)和 UDP(无连接、数据报、不可靠但低延迟)。
TCP Socket 编程模型
服务端流程
ServerSocket server = new ServerSocket(port);绑定端口监听Socket client = server.accept();阻塞等待客户端连接- 通过
client.getInputStream()/getOutputStream()通信 - 处理完关闭
client.close(),循环 accept 下一个
客户端流程
Socket socket = new Socket(host, port);连接服务端- 获取流进行读写
socket.close()断开
完整 Echo 服务端
public class EchoServer {
public static void main(String[] args) throws IOException {
try (ServerSocket server = new ServerSocket(8888)) {
System.out.println("服务端启动,监听 8888...");
while (true) {
try (Socket client = server.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream(), StandardCharsets.UTF_8));
PrintWriter out = new PrintWriter(new OutputStreamWriter(client.getOutputStream(), StandardCharsets.UTF_8), true)) {
String line;
while ((line = in.readLine()) != null) {
System.out.println("收到: " + line);
out.println("Echo: " + line); // autoFlush=true
if ("bye".equalsIgnoreCase(line)) break;
}
} catch (IOException e) {
System.err.println("客户端异常: " + e.getMessage());
}
}
}
}
}
对应客户端
public class EchoClient {
public static void main(String[] args) throws IOException {
try (Socket socket = new Socket("localhost", 8888);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8), true);
BufferedReader console = new BufferedReader(new InputStreamReader(System.in))) {
String input;
while ((input = console.readLine()) != null) {
out.println(input);
System.out.println("服务端回复: " + in.readLine());
if ("bye".equalsIgnoreCase(input)) break;
}
}
}
}
核心类 API 速查
ServerSocket
| 方法 | 说明 |
|---|---|
new ServerSocket(int port) | 绑定端口,回log默认 50 |
new ServerSocket(port, backlog) | 指定等待队列长度 |
accept() | 阻塞等待连接,返回新 Socket |
setSoTimeout(ms) | 设置 accept 超时,避免永久阻塞 |
bind(SocketAddress, backlog) | 无参构造后绑定 |
Socket
| 方法 | 说明 |
|---|---|
new Socket(host, port) | 连接指定主机端口 |
connect(SocketAddress, timeout) | 带超时连接 |
getInputStream()/getOutputStream() | 获取字节流 |
setSoTimeout(ms) | 读操作超时 |
setTcpNoDelay(true) | 禁用 Nagle 算法,降低延迟 |
close() | 关闭连接(发送 FIN) |
InetAddress
InetAddress local = InetAddress.getLocalHost(); // 本机地址
InetAddress google = InetAddress.getByName("www.google.com"); // DNS 解析
System.out.println(google.getHostAddress()); // IP 字符串
UDP 编程
基于 DatagramSocket 和 DatagramPacket,无连接、面向数据报。
UDP 服务端
try (DatagramSocket server = new DatagramSocket(9999)) {
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
while (true) {
server.receive(packet); // 阻塞接收
String msg = new String(packet.getData(), 0, packet.getLength(), StandardCharsets.UTF_8);
System.out.println("来自 " + packet.getAddress() + ":" + packet.getPort() + " -> " + msg);
// 回复
byte[] reply = ("ACK: " + msg).getBytes(StandardCharsets.UTF_8);
DatagramPacket replyPacket = new DatagramPacket(reply, reply.length, packet.getAddress(), packet.getPort());
server.send(replyPacket);
}
}
UDP 客户端
try (DatagramSocket client = new DatagramSocket()) {
InetAddress serverAddr = InetAddress.getByName("localhost");
String msg = "Hello UDP";
byte[] buf = msg.getBytes(StandardCharsets.UTF_8);
DatagramPacket packet = new DatagramPacket(buf, buf.length, serverAddr, 9999);
client.send(packet);
// 接收回复
byte[] replyBuf = new byte[1024];
DatagramPacket reply = new DatagramPacket(replyBuf, replyBuf.length);
client.receive(reply);
System.out.println("服务端回复: " + new String(reply.getData(), 0, reply.getLength(), StandardCharsets.UTF_8));
}
TCP vs UDP 选型
| 维度 | TCP | UDP |
|---|---|---|
| 连接 | 三次握手建立连接 | 无连接 |
| 可靠性 | 保证有序、不丢、不重 | 不保证,可能丢包/乱序/重复 |
| 流量控制 | 滑动窗口 | 无 |
| 开销 | 头部 20+ 字节、状态维护 | 头部 8 字节、无状态 |
| 适用 | HTTP、数据库、文件传输、IM | 直播、游戏、DNS、SNMP、实时音视频 |
高并发服务端架构演进
- 单线程阻塞:一次处理一个,吞吐极低
- 多线程/线程池:每连接一线程,C10K 问题(线程开销大)
- NIO 非阻塞:
Selector+ServerSocketChannel,单线程管理多连接 - Netty 框架:生产级 NIO 框架,解决粘包/半包、内存管理、编解码
- 虚拟线程:Java 21+
Thread.ofVirtual(),轻量级线程,阻塞式写法达成高并发
最佳实践
- 显式指定字符集(UTF-8),避免平台默认编码差异
- 使用
try-with-resources确保 Socket/流关闭 - 设置合理超时(连接超时、读超时),防止死锁
- 应用层协议设计:定长头部 + 长度字段 或 分隔符/换行符,解决 TCP 粘包
- 心跳机制检测死连接
- 优雅关闭:半关闭(
shutdownOutput())发送 FIN,继续接收对端数据
java
- 精通 Java 运算符:类型、语法与实战示例
- Java 注释:类型、用法与最佳实践
- 精通 Java if…else:控制流全解析
- 精通 Java 增强 for 循环:语法、示例与最佳实践
- 精通 Java 字符串:创建、方法与最佳实践
- 精通 Java 接口:概念、实现与最佳实践
- 精通 Java 匿名内部类:定义、语法与实战示例
- 精通 Java Try-With-Resources:自动资源管理详解
- Java 注解详解:类型、放置位置与实战示例
- Java 断言实战指南:尽早发现 Bug 与保障代码可靠性
- 深入理解 Java Vector:用法、特性及与 ArrayList 的对比
- Java 网络编程实战:Socket、ServerSocket 与 TCP/UDP 通信