在当今互联的世界中,网络编程已成为Java开发者必备的技能之一。无论是构建微服务架构、实现实时通信应用,还是开发分布式系统,网络编程都扮演着核心角色。本文将带你走进Java网络编程的世界,从基础概念到实际应用,一步步构建你的网络编程能力。
一、网络编程基础概念
1.1 TCP vs UDP
-
TCP:面向连接、可靠传输、数据顺序保证
-
UDP:无连接、快速、可能丢包
1.2 核心概念
-
IP地址:设备的网络标识
-
端口号:应用程序的通信端点(0-65535)
-
Socket:网络通信的端点抽象
二、简单的TCP客户端/服务器示例
2.1 基础TCP服务器
java
import java.io.*;
import java.net.*;
public class SimpleTCPServer {
public static void main(String[] args) {
final int PORT = 8888;
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
System.out.println("服务器启动,监听端口: " + PORT);
while (true) {
// 等待客户端连接
Socket clientSocket = serverSocket.accept();
System.out.println("客户端连接: " + clientSocket.getInetAddress());
// 处理客户端请求
handleClient(clientSocket);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void handleClient(Socket clientSocket) {
try (BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(
clientSocket.getOutputStream(), true)) {
String request;
while ((request = in.readLine()) != null) {
System.out.println("收到: " + request);
out.println("服务器回复: " + request.toUpperCase());
}
} catch (IOException e) {
System.out.println("客户端断开连接");
}
}
}
2.2 基础TCP客户端
java
import java.io.*;
import java.net.*;
public class SimpleTCPClient {
public static void main(String[] args) {
final String SERVER_ADDRESS = "localhost";
final int PORT = 8888;
try (Socket socket = new Socket(SERVER_ADDRESS, PORT);
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(
socket.getOutputStream(), true);
BufferedReader userInput = new BufferedReader(
new InputStreamReader(System.in))) {
System.out.println("已连接到服务器");
String userInputLine;
while ((userInputLine = userInput.readLine()) != null) {
out.println(userInputLine);
System.out.println("服务器回复: " + in.readLine());
if ("exit".equalsIgnoreCase(userInputLine)) {
break;
}
}
} catch (UnknownHostException e) {
System.err.println("未知主机: " + SERVER_ADDRESS);
} catch (IOException e) {
System.err.println("IO异常: " + e.getMessage());
}
}
}
三、多线程服务器实现
单线程服务器无法处理并发连接,让我们升级为多线程版本:
java
import java.io.*;
import java.net.*;
import java.util.concurrent.*;
public class MultiThreadedServer {
private static final int PORT = 8888;
private static final ExecutorService threadPool =
Executors.newCachedThreadPool();
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
System.out.println("多线程服务器启动,端口: " + PORT);
while (true) {
Socket clientSocket = serverSocket.accept();
threadPool.execute(new ClientHandler(clientSocket));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
static class ClientHandler implements Runnable {
private final Socket clientSocket;
public ClientHandler(Socket socket) {
this.clientSocket = socket;
}
@Override
public void run() {
try (BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(
clientSocket.getOutputStream(), true)) {
out.println("欢迎连接到服务器!当前线程: " +
Thread.currentThread().getName());
String inputLine;
while ((inputLine = in.readLine()) != null) {
if ("exit".equalsIgnoreCase(inputLine)) {
out.println("再见!");
break;
}
out.println("回声: " + inputLine);
}
} catch (IOException e) {
System.out.println("客户端异常断开");
} finally {
try {
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
四、UDP编程示例
java
import java.net.*;
import java.io.*;
public class UDPExample {
// UDP 服务器
static class UDPServer {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket(8888);
byte[] buffer = new byte[1024];
System.out.println("UDP服务器启动...");
while (true) {
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
String received = new String(packet.getData(), 0, packet.getLength());
System.out.println("收到: " + received);
// 发送响应
String response = "UDP响应: " + received.toUpperCase();
byte[] responseData = response.getBytes();
DatagramPacket responsePacket = new DatagramPacket(
responseData, responseData.length,
packet.getAddress(), packet.getPort()
);
socket.send(responsePacket);
}
}
}
// UDP 客户端
static class UDPClient {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket();
InetAddress address = InetAddress.getByName("localhost");
BufferedReader userInput = new BufferedReader(
new InputStreamReader(System.in));
while (true) {
System.out.print("输入消息: ");
String message = userInput.readLine();
// 发送消息
byte[] sendData = message.getBytes();
DatagramPacket sendPacket = new DatagramPacket(
sendData, sendData.length, address, 8888);
socket.send(sendPacket);
// 接收响应
byte[] receiveData = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(
receiveData, receiveData.length);
socket.receive(receivePacket);
String response = new String(receivePacket.getData(),
0, receivePacket.getLength());
System.out.println("服务器响应: " + response);
if ("exit".equalsIgnoreCase(message)) {
break;
}
}
socket.close();
}
}
}
五、实战:简单的聊天室服务器
java
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
public class ChatRoomServer {
private static final int PORT = 9999;
private static final Set<ClientHandler> clients =
ConcurrentHashMap.newKeySet();
public static void main(String[] args) {
ExecutorService pool = Executors.newCachedThreadPool();
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
System.out.println("聊天室服务器启动,端口: " + PORT);
while (true) {
Socket clientSocket = serverSocket.accept();
ClientHandler client = new ClientHandler(clientSocket);
clients.add(client);
pool.execute(client);
}
} catch (IOException e) {
e.printStackTrace();
}
}
static class ClientHandler implements Runnable {
private Socket socket;
private PrintWriter out;
private String clientName;
public ClientHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
// 获取客户端名称
out.println("请输入你的名字:");
clientName = in.readLine();
broadcast(clientName + " 加入了聊天室");
String message;
while ((message = in.readLine()) != null) {
if ("/exit".equalsIgnoreCase(message)) {
break;
}
broadcast(clientName + ": " + message);
}
} catch (IOException e) {
System.out.println(clientName + " 异常断开");
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
clients.remove(this);
broadcast(clientName + " 离开了聊天室");
}
}
private void broadcast(String message) {
System.out.println(message);
for (ClientHandler client : clients) {
if (client != this) {
client.out.println(message);
}
}
}
}
}
六、最佳实践与注意事项
6.1 资源管理
-
始终使用try-with-resources确保资源关闭
-
注意处理各种IO异常
6.2 性能考虑
-
使用线程池管理连接
-
考虑使用NIO处理大量并发连接
-
合理设置缓冲区大小
6.3 安全性
-
验证输入数据
-
防止缓冲区溢出
-
使用SSL/TLS进行加密通信
七、下一步学习方向
-
Java NIO:学习非阻塞IO,处理高并发场景
-
Netty框架:企业级网络应用框架
-
HTTP客户端:Java 11+的HttpClient API
-
WebSocket:实现实时双向通信
-
RPC框架:如gRPC、Dubbo等
结语
Java网络编程是一个既基础又强大的技能。从简单的Socket编程开始,逐步深入到多线程、NIO,再到使用成熟的网络框架,这个过程充满挑战但也极具价值。掌握网络编程不仅能让你更好地理解网络通信的本质,还能为构建分布式系统打下坚实基础。