Java 网络编程核心:Socket、TCP/UDP 与 HTTP 实战
学习目标
本章将深入探讨 Java 网络编程的核心机制。重点掌握套接字(Socket)编程模型,理解 TCP/IP 与 UDP 协议的区别,并学会使用 URL 和 URLConnection 处理 HTTP 请求。通过实际代码示例,你将能够独立构建客户端与服务器的通信逻辑,解决开发中常见的网络交互问题。
网络编程概述
Java 网络编程本质上是处理不同设备间数据交换的机制。它允许应用程序跨越物理边界进行通信,无论是局域网内的文件传输,还是互联网上的 Web 请求。
主要分类
根据传输协议的不同,Java 网络编程主要分为三类:
- TCP/IP 通信:面向连接、可靠的流式传输,适合对数据完整性要求高的场景。
- UDP 通信:无连接、不可靠的数据报传输,适合实时性要求高但允许少量丢包的场景。
- HTTP 通信:基于 TCP 的应用层协议,用于访问 Web 资源。
套接字编程基础
套接字(Socket)是网络通信的端点。在 Java 中,Socket 代表客户端连接,而 ServerSocket 代表服务器监听端口。
Socket 与 ServerSocket 核心方法
- Socket:负责建立连接后获取输入输出流。
getInputStream():读取对方发送的数据。getOutputStream():向对方发送数据。close():关闭连接释放资源。
- ServerSocket:负责监听指定端口等待连接。
accept():阻塞等待直到有客户端连接。close():关闭服务器监听。
简单 TCP 通信示例
下面是一个最基础的 TCP 回声服务实现。服务器启动后等待连接,收到消息后原样返回。
import java.io.*;
import java.net.*;
// 服务器端
public class TCPServer {
public static void main(String[] args) {
try {
// 创建服务器端套接字,监听 8888 端口
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("服务器端启动成功,等待客户端连接...");
// accept() 会阻塞,直到有客户端连接
Socket socket = serverSocket.accept();
System.out.println("客户端连接成功:" + socket.getInetAddress().getHostAddress());
// 获取输入流和输出流
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
// 读取客户端消息
String message = reader.readLine();
System.out.println("客户端消息:" + message);
// 发送响应
writer.println("服务器端已收到消息:" + message);
// 关闭资源
reader.close();
writer.close();
socket.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
对应的客户端代码如下:
import java.io.*;
import java.net.*;
// 客户端
public class TCPClient {
public static void main(String[] args) {
try {
// 连接到 localhost 的 8888 端口
Socket socket = new Socket("localhost", 8888);
System.out.println("客户端连接成功");
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
// 发送数据
writer.println("Hello, Server!");
// 读取响应
String response = reader.readLine();
System.out.println("服务器端响应:" + response);
// 关闭资源
reader.close();
writer.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果通常如下:
服务器端:
服务器端启动成功,等待客户端连接...
客户端连接成功:127.0.0.1
客户端消息:Hello, Server!
客户端:
客户端连接成功
服务器端响应:服务器端已收到消息:Hello, Server!
注意:在实际生产环境中,单线程处理一个连接会导致并发性能瓶颈。通常需要使用多线程或 NIO 来处理多个客户端请求。
TCP/IP 通信进阶
为了支持多客户端同时连接,服务器端需要引入线程池或手动管理线程。
多线程服务器实现
每个客户端连接分配一个独立线程处理,这样主线程可以继续接受新连接。
import java.io.*;
import java.net.*;
public class MultiThreadTCPServer {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("多线程服务器启动成功");
while (true) {
Socket socket = serverSocket.accept();
System.out.println("新客户端接入:" + socket.getInetAddress().getHostAddress());
// 为每个连接开启新线程
new Thread(() -> {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
String message = reader.readLine();
System.out.println("收到消息:" + message);
writer.println("服务端回复:" + message);
reader.close();
writer.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
UDP 通信实现
UDP 不需要建立连接,直接发送数据包。适用于对速度敏感但对可靠性要求不高的场景。
DatagramSocket 与 DatagramPacket
- DatagramSocket:UDP 套接字,负责发送和接收数据包。
- DatagramPacket:封装了数据、目标地址和端口。
UDP 示例代码
import java.io.*;
import java.net.*;
// UDP 服务器端
public class UDPServer {
public static void main(String[] args) {
try {
DatagramSocket socket = new DatagramSocket(8888);
System.out.println("UDP 服务器启动");
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
// 接收数据包
socket.receive(packet);
String message = new String(packet.getData(), 0, packet.getLength());
System.out.println("收到消息:" + message);
// 构造响应包
String response = "服务端已收到:" + message;
byte[] responseData = response.getBytes();
DatagramPacket responsePacket = new DatagramPacket(responseData, responseData.length,
packet.getAddress(), packet.getPort());
socket.send(responsePacket);
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.*;
import java.net.*;
// UDP 客户端
public class UDPClient {
public static void main(String[] args) {
try {
DatagramSocket socket = new DatagramSocket();
InetAddress address = InetAddress.getByName("localhost");
String message = "Hello, UDP!";
byte[] buffer = message.getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, address, 8888);
socket.send(packet);
// 接收响应
byte[] responseBuffer = new byte[1024];
DatagramPacket responsePacket = new DatagramPacket(responseBuffer, responseBuffer.length);
socket.receive(responsePacket);
String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
System.out.println("服务端响应:" + response);
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
HTTP 通信实现
HTTP 是基于 TCP 的应用层协议,Java 提供了 URL 和 URLConnection 类来简化操作。
基本用法
import java.io.*;
import java.net.*;
public class HTTPExample {
public static void main(String[] args) {
try {
URL url = new URL("https://www.baidu.com");
URLConnection connection = url.openConnection();
// 设置请求头,模拟浏览器
connection.setRequestProperty("User-Agent", "Mozilla/5.0");
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
reader.close();
System.out.println("响应内容长度:" + response.toString().length());
} catch (IOException e) {
e.printStackTrace();
}
}
}
实际应用场景:文件上传
网络编程最常见的应用之一是文件传输。这里演示一个简单的 TCP 文件上传流程。
文件上传服务器端
import java.io.*;
import java.net.*;
public class FileUploadServer {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(8888);
Socket socket = serverSocket.accept();
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
FileOutputStream fileOutputStream = new FileOutputStream("uploaded_file.txt");
// 先读取文件名
String fileName = reader.readLine();
System.out.println("接收到文件:" + fileName);
// 读取文件内容
byte[] buffer = new byte[1024];
int length;
while ((length = dataInputStream.read(buffer)) > 0) {
fileOutputStream.write(buffer, 0, length);
}
fileOutputStream.close();
dataInputStream.close();
reader.close();
socket.close();
serverSocket.close();
System.out.println("文件保存成功");
} catch (IOException e) {
e.printStackTrace();
}
}
}
文件上传客户端
import java.io.*;
import java.net.*;
public class FileUploadClient {
public static void main(String[] args) {
try {
Socket socket = new Socket("localhost", 8888);
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
FileInputStream fileInputStream = new FileInputStream("test.txt");
// 发送文件名
String fileName = "test.txt";
writer.println(fileName);
// 发送文件内容
byte[] buffer = new byte[1024];
int length;
while ((length = fileInputStream.read(buffer)) > 0) {
dataOutputStream.write(buffer, 0, length);
}
fileInputStream.close();
dataOutputStream.close();
writer.close();
socket.close();
System.out.println("文件发送成功");
} catch (IOException e) {
e.printStackTrace();
}
}
}
结语
Java 网络编程虽然底层细节较多,但掌握了 Socket、Stream 以及协议特性后,构建分布式系统的基础就稳固了。在实际开发中,除了原生 API,也可以考虑使用 Netty 等高性能框架来应对高并发场景。希望这些示例能帮助你更好地理解网络通信的本质。


