mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
691 字
2 分钟
Java网络编程详解
2026-02-05

用Java写一个简单的TCP聊天室是我大学时候最有成就感的事情之一。Socket编程虽然基础,但理解TCP/IP协议栈对排查网络问题帮助真的很大。这篇文章从基础概念讲起,一步步带你实现网络通信。

Java网络编程详解#

网络编程基础概念#

IP地址 & 端口号#

IP地址定位设备,端口号定位应用。打个比方:IP是门牌号,端口是房间号。

  • IPv4:32位,如 192.168.1.1
  • IPv6:128位,如 2001:0db8:85a3:0000:0000:8a2e:0370:7334
  • 端口范围:0-65535,0-1023是系统保留端口,别乱用

TCP vs UDP#

特性TCPUDP
连接方式面向连接(先握手)无连接(直接发)
可靠性可靠(有重传机制)不可靠(丢了不管)
速度
适用场景文件传输、网页直播、游戏、DNS

实战经验:TCP写起来相对简单,因为不用操心丢包的问题。UDP虽然快,但得自己实现可靠性,业务复杂度上去之后,直接上TCP或者Netty更省心。

Java网络编程核心类#

1. InetAddress#

// 获取本机IP
InetAddress localHost = InetAddress.getLocalHost();
System.out.println("本机: " + localHost);
// 获取域名对应的IP
InetAddress baidu = InetAddress.getByName("www.baidu.com");
System.out.println("百度IP: " + baidu.getHostAddress());
// 检查可达性(会发ICMP包)
boolean reachable = baidu.isReachable(5000);

2. Socket & ServerSocket(TCP)#

服务端

public class TCPServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("服务端启动,监听8080端口...");
while (true) {
// accept() 是阻塞的,直到有客户端连接
Socket socket = serverSocket.accept();
// 每个客户端开一个线程处理
new Thread(() -> handleClient(socket)).start();
}
}
private static void handleClient(Socket socket) {
try (BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(
socket.getOutputStream(), true)) {
String message;
while ((message = in.readLine()) != null) {
System.out.println("收到: " + message);
out.println("服务端回复: " + message);
if ("bye".equals(message)) break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

客户端

public class TCPClient {
public static void main(String[] args) throws IOException {
try (Socket socket = new Socket("localhost", 8080);
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(
socket.getOutputStream(), true);
BufferedReader keyboard = new BufferedReader(
new InputStreamReader(System.in))) {
System.out.println("已连接到服务器");
String message;
while ((message = keyboard.readLine()) != null) {
out.println(message);
String response = in.readLine();
System.out.println("服务端回复: " + response);
if ("bye".equals(message)) break;
}
}
}
}

3. DatagramSocket & DatagramPacket(UDP)#

服务端

public class UDPServer {
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket(8080);
byte[] buffer = new byte[1024];
while (true) {
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet); // 阻塞等待
String message = new String(packet.getData(), 0, packet.getLength());
System.out.println("收到: " + message + " 来自: " + packet.getAddress());
// 回复
String response = "服务端收到: " + message;
byte[] responseData = response.getBytes();
DatagramPacket responsePacket = new DatagramPacket(
responseData, responseData.length,
packet.getAddress(), packet.getPort());
socket.send(responsePacket);
}
}
}

客户端

public class UDPClient {
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket();
InetAddress serverAddr = InetAddress.getByName("localhost");
BufferedReader keyboard = new BufferedReader(
new InputStreamReader(System.in));
String message;
while ((message = keyboard.readLine()) != null) {
// 发送
byte[] data = message.getBytes();
DatagramPacket packet = new DatagramPacket(
data, data.length, serverAddr, 8080);
socket.send(packet);
// 接收回复
byte[] buffer = new byte[1024];
DatagramPacket responsePacket = new DatagramPacket(buffer, buffer.length);
socket.receive(responsePacket);
String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
System.out.println("服务端回复: " + response);
if ("bye".equals(message)) break;
}
socket.close();
}
}

HTTP客户端#

方式一:HttpURLConnection(Java原生)#

URL url = new URL("https://api.github.com/users/octocat");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "application/json");
int responseCode = conn.getResponseCode();
System.out.println("响应码: " + responseCode);
BufferedReader in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line;
StringBuilder content = new StringBuilder();
while ((line = in.readLine()) != null) {
content.append(line);
}
in.close();
conn.disconnect();
System.out.println("响应内容: " + content);

方式二:HttpClient(Java 11+,推荐)#

Java 11引入了新的HttpClient,API比HttpURLConnection友好太多了:

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.github.com/users/octocat"))
.header("Accept", "application/json")
.build();
HttpResponse<String> response = client.send(
request, HttpResponse.BodyHandlers.ofString());
System.out.println("响应码: " + response.statusCode());
System.out.println("响应内容: " + response.body());

实战贴士#

  1. 资源管理:Socket、Stream这些用完一定要关,用try-with-resources最省心
  2. 超时设置socket.setSoTimeout(5000) 设置读取超时,避免网络问题导致线程卡死
  3. 编码问题:网络传输统一用UTF-8,不然乱码问题查到你怀疑人生
  4. 连接池:高并发场景下不要频繁创建销毁连接,用连接池复用
  5. 优先用HttpClient:Java 11+的项目直接用HttpClient,别再用HttpURLConnection了

常见坑#

  • 连接泄漏:忘了关连接,线上应用连接数一直涨,最后把数据库连接池打满了。这个我踩过,场面一度非常尴尬。
  • 没设超时:网络一卡,线程就永远挂在那了,慢慢就把线程池耗尽了。
  • 编码不一致:服务端用GBK,客户端用UTF-8,出来的全是乱码,debug半天才发现。
  • 端口被占用:启动时报Address already in use,换个端口或者kill掉占用进程。
  • 防火墙拦截:开发环境跑得好好的,部署到服务器就连不上,十有八九是防火墙没开端口。
分享

如果这篇文章对你有帮助,欢迎分享给更多人!

部分信息可能已经过时

目录