👉 你在浏览器输入 www.taobao.com
,页面是怎么“蹦”出来的?
👉 你用微信发一条消息,对方是怎么“秒收”的?
👉 面试官问:“说说 Java 的网络编程?” 你脑子里闪过 Socket
、ServerSocket
,但串不起来,说不完整……
想象一下你给朋友打电话:
在 Java 世界里,网络编程就是让两个程序像打电话一样通信。
而 Java 提供了一套标准 API,让我们可以用代码实现这个过程。
✅ 网络编程 = 通过代码,让不同机器上的程序互相传递数据
就像每个人有手机号,每台联网的设备都有一个 IP 地址,比如 192.168.1.100
或 127.0.0.1
(本机)。
一台电脑可能运行多个网络程序(微信、浏览器、游戏)。 IP 地址只能定位到电脑,端口用来定位具体的应用。
💡 端口就像公司总机的“分机号”: 总机是 IP,分机是 Port。
常见端口:
80
:HTTP(网页)443
:HTTPS3306
:MySQL6379
:Redis8080
:常用作 Web 服务器测试端口通信双方必须遵守相同的“语言规则”,否则鸡同鸭讲。
Java 网络编程最常用的是:
✅ 我们重点讲 TCP,因为它是 Java 网络编程的基石。
TCP 通信就像“客服热线”:
[客户端] --- 拨号 ---> [服务器]
| |
| 发送请求 | 接收请求
| ---------------------> |
| |
| 等待响应 | 处理并返回
| <--------------------- |
| |
| 接收响应 |
| |
Java 用两个核心类实现 TCP 通信:
角色 | Java 类 | 作用 |
---|---|---|
客户端 | Socket | 发起连接,收发数据 |
服务器 | ServerSocket | 监听端口,接收连接 |
功能:客户端发啥,服务器原样返回。
public class EchoServer {
public static void main(String[] args) throws IOException {
// 1. 创建服务器,监听 8888 端口
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("服务器启动,等待连接...");
while (true) {
// 2. 接收客户端连接(阻塞)
Socket clientSocket = serverSocket.accept();
System.out.println("客户端已连接:" + clientSocket.getInetAddress());
// 3. 获取输入输出流
BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream())
);
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
// 4. 读取客户端消息,原样返回
String msg;
while ((msg = in.readLine()) != null) {
System.out.println("收到:" + msg);
out.println("ECHO: " + msg); // 原样返回
}
// 5. 关闭资源
clientSocket.close();
}
}
}
public class EchoClient {
public static void main(String[] args) throws IOException {
// 1. 连接服务器(IP: 127.0.0.1,端口: 8888)
Socket socket = new Socket("127.0.0.1", 8888);
// 2. 获取输入输出流
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream())
);
BufferedReader console = new BufferedReader(
new InputStreamReader(System.in)
);
// 3. 发送消息,接收响应
String input;
while ((input = console.readLine()) != null) {
out.println(input); // 发送
String response = in.readLine(); // 接收
System.out.println("服务器返回:" + response);
}
socket.close();
}
}
// 客户端输入
Hello
服务器返回:ECHO: Hello
Java Network
服务器返回:ECHO: Java Network
上面的服务器只能服务一个客户端,第二个连不上。
问题:accept()
和 readLine()
都是阻塞的!
解决方案:每来一个客户端,就开一个线程处理。
// 在服务器端,修改处理逻辑
while (true) {
Socket clientSocket = serverSocket.accept();
System.out.println("新客户端:" + clientSocket.getInetAddress());
// 开启新线程处理
new Thread(() -> {
try (BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)) {
String msg;
while ((msg = in.readLine()) != null) {
System.out.println("收到:" + msg);
out.println("ECHO: " + msg);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
💡 实际项目中,会用 线程池 替代
new Thread()
,避免创建过多线程。
UDP 不建立连接,直接发数据包,速度快但不保证送达。
核心类:
DatagramSocket
:发送/接收数据包DatagramPacket
:数据包(含数据、IP、端口)public class UDPServer {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket(9999);
byte[] buffer = new byte[1024];
while (true) {
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet); // 阻塞接收
String msg = new String(packet.getData(), 0, packet.getLength());
System.out.println("收到:" + msg + " 来自 " + packet.getAddress() + ":" + packet.getPort());
}
}
}
public class UDPClient {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket();
String msg = "Hello UDP!";
byte[] data = msg.getBytes();
InetAddress serverAddr = InetAddress.getByName("127.0.0.1");
DatagramPacket packet = new DatagramPacket(data, data.length, serverAddr, 9999);
socket.send(packet);
socket.close();
}
}
✅ UDP 适用于:视频直播、在线游戏、DNS 查询等对速度要求高、能容忍少量丢包的场景。
区别点 | TCP | UDP |
---|---|---|
是否连接 | 面向连接(三次握手) | 无连接 |
可靠性 | 可靠,保证送达、有序 | 不可靠,可能丢包、乱序 |
速度 | 较慢(建立连接、确认机制) | 快(直接发) |
用途 | 文件传输、网页、邮件 | 视频、游戏、广播 |
实现类 | Socket / ServerSocket | DatagramSocket / DatagramPacket |
💬 高分回答: “TCP 像打电话,必须接通才能说话,保证每句话都听到;UDP 像发短信,发了就不管了,但速度快。我们项目中 HTTP 用 TCP,实时推送用 WebSocket(底层也是 TCP),只有对延迟极度敏感的场景才考虑 UDP。”
答:
Socket
是网络通信的“端点”,它封装了:
它是应用层与传输层之间的接口,Java 通过 Socket
类提供了统一的编程方式。
答: 因为
accept()
和read()
都是阻塞的。如果不开启新线程,一个客户端连接后,服务器就卡在读数据上,其他客户端无法连接。 用多线程(或线程池)可以让每个客户端独立处理,实现并发服务。 我们项目中用的是线程池,避免频繁创建线程导致资源耗尽。
答:
Selector
监听多个通道,一个线程可处理多个连接,适合高并发。
我们项目中普通文件服务用 BIO,网关和消息中间件用 Netty(基于 NIO),支撑了上万并发。问题 | 答案 |
---|---|
核心类 | Socket(客户端)、ServerSocket(服务器) |
TCP 特点 | 面向连接、可靠、有序、慢 |
UDP 特点 | 无连接、不可靠、快、可能丢包 |
阻塞问题 | 用多线程或线程池解决 |
实际应用 | HTTP、RPC、聊天室、文件传输 |
怎么说 | “我写过 TCP 回声服务器,支持多客户端,理解阻塞与线程模型” |
网络编程不是“古董”,而是“一切分布式系统的基石”。 从 Spring Boot 到 Dubbo,从 Redis 到 Kafka,底层都是 Socket 通信。 掌握它,你才能真正理解“一个请求是怎么跨过网络,被另一台机器处理的”。
希望这篇能帮你彻底搞懂 Java 网络编程!