12.1 网络编程基础
12.1.1 网络编程概述
网络编程是指编写能够在网络上运行的程序,使得运行在不同计算机上的程序能够相互通信。Java提供了强大的网络编程支持,包括Socket编程、URL处理、HTTP通信等。
网络编程的基本概念
- IP地址:网络中每台计算机的唯一标识
- 端口号:标识计算机上的特定服务或应用程序
- 协议:数据传输的规则,如TCP、UDP、HTTP等
- Socket:网络通信的端点
- 客户端/服务器模型:网络应用的基本架构
import java.net.*;
import java.io.*;
// 网络基础信息演示
public class NetworkBasicsDemo {
public static void main(String[] args) {
demonstrateInetAddress();
demonstrateURL();
demonstrateNetworkInterface();
}
// InetAddress演示
public static void demonstrateInetAddress() {
System.out.println("=== InetAddress演示 ===");
try {
// 获取本地主机信息
InetAddress localHost = InetAddress.getLocalHost();
System.out.println("本地主机名: " + localHost.getHostName());
System.out.println("本地IP地址: " + localHost.getHostAddress());
// 根据主机名获取IP地址
InetAddress[] addresses = InetAddress.getAllByName("www.baidu.com");
System.out.println("\nwww.baidu.com的IP地址:");
for (InetAddress addr : addresses) {
System.out.println(" " + addr.getHostAddress());
}
// 根据IP地址获取主机名
InetAddress addr = InetAddress.getByName("8.8.8.8");
System.out.println("\n8.8.8.8的主机名: " + addr.getHostName());
// 检查地址可达性
boolean reachable = addr.isReachable(3000);
System.out.println("8.8.8.8是否可达: " + reachable);
// 特殊地址检查
InetAddress loopback = InetAddress.getByName("127.0.0.1");
System.out.println("\n127.0.0.1是否为回环地址: " + loopback.isLoopbackAddress());
InetAddress multicast = InetAddress.getByName("224.0.0.1");
System.out.println("224.0.0.1是否为组播地址: " + multicast.isMulticastAddress());
} catch (Exception e) {
System.err.println("InetAddress演示出错: " + e.getMessage());
}
}
// URL演示
public static void demonstrateURL() {
System.out.println("\n=== URL演示 ===");
try {
URL url = new URL("https://www.example.com:8080/path/to/resource?param1=value1¶m2=value2#section");
System.out.println("完整URL: " + url.toString());
System.out.println("协议: " + url.getProtocol());
System.out.println("主机: " + url.getHost());
System.out.println("端口: " + url.getPort());
System.out.println("路径: " + url.getPath());
System.out.println("查询字符串: " + url.getQuery());
System.out.println("锚点: " + url.getRef());
System.out.println("文件部分: " + url.getFile());
// URL连接
URL simpleUrl = new URL("http://httpbin.org/get");
URLConnection connection = simpleUrl.openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
System.out.println("\n连接信息:");
System.out.println("内容类型: " + connection.getContentType());
System.out.println("内容长度: " + connection.getContentLength());
System.out.println("最后修改时间: " + new java.util.Date(connection.getLastModified()));
} catch (Exception e) {
System.err.println("URL演示出错: " + e.getMessage());
}
}
// 网络接口演示
public static void demonstrateNetworkInterface() {
System.out.println("\n=== 网络接口演示 ===");
try {
java.util.Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface ni = interfaces.nextElement();
System.out.println("\n接口名称: " + ni.getName());
System.out.println("显示名称: " + ni.getDisplayName());
System.out.println("是否启用: " + ni.isUp());
System.out.println("是否回环: " + ni.isLoopback());
System.out.println("是否虚拟: " + ni.isVirtual());
System.out.println("是否点对点: " + ni.isPointToPoint());
// 获取MAC地址
byte[] mac = ni.getHardwareAddress();
if (mac != null) {
StringBuilder macStr = new StringBuilder();
for (int i = 0; i < mac.length; i++) {
macStr.append(String.format("%02X", mac[i]));
if (i < mac.length - 1) {
macStr.append("-");
}
}
System.out.println("MAC地址: " + macStr.toString());
}
// 获取IP地址
java.util.Enumeration<InetAddress> addresses = ni.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress addr = addresses.nextElement();
System.out.println("IP地址: " + addr.getHostAddress());
}
}
} catch (Exception e) {
System.err.println("网络接口演示出错: " + e.getMessage());
}
}
}
12.2 TCP Socket编程
12.2.1 TCP服务器端编程
import java.net.*;
import java.io.*;
import java.util.concurrent.*;
import java.util.*;
// TCP服务器
public class TCPServer {
private ServerSocket serverSocket;
private ExecutorService threadPool;
private volatile boolean running = false;
private final Set<ClientHandler> clients = Collections.synchronizedSet(new HashSet<>());
public TCPServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
threadPool = Executors.newCachedThreadPool();
System.out.println("TCP服务器启动,监听端口: " + port);
}
public void start() {
running = true;
// 主线程接受客户端连接
Thread acceptThread = new Thread(() -> {
while (running) {
try {
Socket clientSocket = serverSocket.accept();
System.out.println("新客户端连接: " + clientSocket.getRemoteSocketAddress());
ClientHandler handler = new ClientHandler(clientSocket, this);
clients.add(handler);
threadPool.submit(handler);
} catch (IOException e) {
if (running) {
System.err.println("接受连接时出错: " + e.getMessage());
}
}
}
});
acceptThread.start();
// 服务器控制台
Scanner scanner = new Scanner(System.in);
System.out.println("服务器已启动,输入'quit'退出,'clients'查看客户端列表");
while (running) {
String command = scanner.nextLine();
switch (command.toLowerCase()) {
case "quit":
stop();
break;
case "clients":
System.out.println("当前连接的客户端数量: " + clients.size());
synchronized (clients) {
for (ClientHandler client : clients) {
System.out.println(" " + client.getClientAddress());
}
}
break;
default:
// 广播消息给所有客户端
broadcastMessage("服务器消息: " + command);
break;
}
}
scanner.close();
}
public void stop() {
running = false;
try {
// 关闭所有客户端连接
synchronized (clients) {
for (ClientHandler client : clients) {
client.close();
}
clients.clear();
}
// 关闭服务器Socket
if (serverSocket != null && !serverSocket.isClosed()) {
serverSocket.close();
}
// 关闭线程池
threadPool.shutdown();
if (!threadPool.awaitTermination(5, TimeUnit.SECONDS)) {
threadPool.shutdownNow();
}
System.out.println("服务器已关闭");
} catch (Exception e) {
System.err.println("关闭服务器时出错: " + e.getMessage());
}
}
public void removeClient(ClientHandler client) {
clients.remove(client);
System.out.println("客户端断开连接: " + client.getClientAddress());
}
public void broadcastMessage(String message) {
synchronized (clients) {
Iterator<ClientHandler> iterator = clients.iterator();
while (iterator.hasNext()) {
ClientHandler client = iterator.next();
if (!client.sendMessage(message)) {
iterator.remove();
}
}
}
}
public static void main(String[] args) {
try {
TCPServer server = new TCPServer(8888);
server.start();
} catch (IOException e) {
System.err.println("启动服务器失败: " + e.getMessage());
}
}
}
// 客户端处理器
class ClientHandler implements Runnable {
private Socket socket;
private BufferedReader reader;
private PrintWriter writer;
private TCPServer server;
private String clientAddress;
public ClientHandler(Socket socket, TCPServer server) throws IOException {
this.socket = socket;
this.server = server;
this.clientAddress = socket.getRemoteSocketAddress().toString();
// 设置Socket选项
socket.setSoTimeout(30000); // 30秒超时
socket.setKeepAlive(true);
// 创建输入输出流
reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"), true);
// 发送欢迎消息
sendMessage("欢迎连接到TCP服务器!");
}
@Override
public void run() {
try {
String message;
while ((message = reader.readLine()) != null) {
System.out.println("收到来自 " + clientAddress + " 的消息: " + message);
// 处理特殊命令
if ("bye".equalsIgnoreCase(message.trim())) {
sendMessage("再见!");
break;
} else if (message.startsWith("echo ")) {
// 回显消息
String echoMsg = message.substring(5);
sendMessage("回显: " + echoMsg);
} else if ("time".equalsIgnoreCase(message.trim())) {
// 发送当前时间
sendMessage("当前时间: " + new Date());
} else {
// 广播给其他客户端
server.broadcastMessage(clientAddress + ": " + message);
}
}
} catch (SocketTimeoutException e) {
System.out.println("客户端 " + clientAddress + " 超时");
} catch (IOException e) {
System.out.println("客户端 " + clientAddress + " 连接异常: " + e.getMessage());
} finally {
close();
server.removeClient(this);
}
}
public boolean sendMessage(String message) {
try {
writer.println(message);
return !writer.checkError();
} catch (Exception e) {
return false;
}
}
public String getClientAddress() {
return clientAddress;
}
public void close() {
try {
if (reader != null) reader.close();
if (writer != null) writer.close();
if (socket != null && !socket.isClosed()) socket.close();
} catch (IOException e) {
System.err.println("关闭客户端连接时出错: " + e.getMessage());
}
}
}
12.2.2 TCP客户端编程
import java.net.*;
import java.io.*;
import java.util.concurrent.*;
// TCP客户端
public class TCPClient {
private Socket socket;
private BufferedReader reader;
private PrintWriter writer;
private BufferedReader consoleReader;
private volatile boolean connected = false;
public void connect(String host, int port) throws IOException {
System.out.println("正在连接到服务器 " + host + ":" + port + "...");
socket = new Socket();
socket.connect(new InetSocketAddress(host, port), 5000); // 5秒连接超时
// 设置Socket选项
socket.setSoTimeout(0); // 无限等待
socket.setKeepAlive(true);
socket.setTcpNoDelay(true);
// 创建输入输出流
reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"), true);
consoleReader = new BufferedReader(new InputStreamReader(System.in));
connected = true;
System.out.println("连接成功!");
// 启动接收消息的线程
Thread receiveThread = new Thread(this::receiveMessages);
receiveThread.setDaemon(true);
receiveThread.start();
// 主线程处理用户输入
handleUserInput();
}
private void receiveMessages() {
try {
String message;
while (connected && (message = reader.readLine()) != null) {
System.out.println("[服务器] " + message);
}
} catch (IOException e) {
if (connected) {
System.err.println("接收消息时出错: " + e.getMessage());
}
}
}
private void handleUserInput() {
System.out.println("\n可用命令:");
System.out.println(" echo <消息> - 回显消息");
System.out.println(" time - 获取服务器时间");
System.out.println(" bye - 断开连接");
System.out.println(" 其他消息会广播给所有客户端\n");
try {
String input;
while (connected && (input = consoleReader.readLine()) != null) {
if (input.trim().isEmpty()) {
continue;
}
sendMessage(input);
if ("bye".equalsIgnoreCase(input.trim())) {
break;
}
}
} catch (IOException e) {
System.err.println("读取用户输入时出错: " + e.getMessage());
} finally {
disconnect();
}
}
public void sendMessage(String message) {
if (connected && writer != null) {
writer.println(message);
if (writer.checkError()) {
System.err.println("发送消息失败");
disconnect();
}
}
}
public void disconnect() {
connected = false;
try {
if (reader != null) reader.close();
if (writer != null) writer.close();
if (consoleReader != null) consoleReader.close();
if (socket != null && !socket.isClosed()) socket.close();
System.out.println("已断开连接");
} catch (IOException e) {
System.err.println("断开连接时出错: " + e.getMessage());
}
}
public static void main(String[] args) {
TCPClient client = new TCPClient();
try {
String host = args.length > 0 ? args[0] : "localhost";
int port = args.length > 1 ? Integer.parseInt(args[1]) : 8888;
client.connect(host, port);
} catch (IOException e) {
System.err.println("连接失败: " + e.getMessage());
} catch (NumberFormatException e) {
System.err.println("端口号格式错误");
}
}
}
12.3 UDP Socket编程
12.3.1 UDP服务器和客户端
import java.net.*;
import java.io.*;
import java.util.concurrent.*;
import java.util.*;
// UDP服务器
public class UDPServer {
private DatagramSocket socket;
private volatile boolean running = false;
private ExecutorService threadPool;
private final Map<InetSocketAddress, Long> clients = new ConcurrentHashMap<>();
public UDPServer(int port) throws SocketException {
socket = new DatagramSocket(port);
threadPool = Executors.newCachedThreadPool();
System.out.println("UDP服务器启动,监听端口: " + port);
}
public void start() {
running = true;
// 启动接收数据的线程
Thread receiveThread = new Thread(this::receiveData);
receiveThread.start();
// 启动客户端清理线程
Thread cleanupThread = new Thread(this::cleanupClients);
cleanupThread.setDaemon(true);
cleanupThread.start();
// 服务器控制台
Scanner scanner = new Scanner(System.in);
System.out.println("UDP服务器已启动,输入'quit'退出,'clients'查看客户端列表");
while (running) {
String command = scanner.nextLine();
switch (command.toLowerCase()) {
case "quit":
stop();
break;
case "clients":
System.out.println("当前活跃客户端数量: " + clients.size());
clients.forEach((addr, time) -> {
long seconds = (System.currentTimeMillis() - time) / 1000;
System.out.println(" " + addr + " (最后活跃: " + seconds + "秒前)");
});
break;
default:
// 广播消息给所有客户端
broadcastMessage("服务器广播: " + command);
break;
}
}
scanner.close();
}
private void receiveData() {
byte[] buffer = new byte[1024];
while (running) {
try {
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
// 提交到线程池处理
threadPool.submit(() -> handlePacket(packet));
} catch (IOException e) {
if (running) {
System.err.println("接收数据时出错: " + e.getMessage());
}
}
}
}
private void handlePacket(DatagramPacket packet) {
try {
String message = new String(packet.getData(), 0, packet.getLength(), "UTF-8");
InetSocketAddress clientAddr = new InetSocketAddress(packet.getAddress(), packet.getPort());
// 更新客户端活跃时间
clients.put(clientAddr, System.currentTimeMillis());
System.out.println("收到来自 " + clientAddr + " 的消息: " + message);
// 处理消息
String response = processMessage(message, clientAddr);
if (response != null) {
sendMessage(response, clientAddr);
}
} catch (Exception e) {
System.err.println("处理数据包时出错: " + e.getMessage());
}
}
private String processMessage(String message, InetSocketAddress clientAddr) {
message = message.trim();
if ("ping".equalsIgnoreCase(message)) {
return "pong";
} else if ("time".equalsIgnoreCase(message)) {
return "当前时间: " + new Date();
} else if (message.startsWith("echo ")) {
return "回显: " + message.substring(5);
} else if ("clients".equalsIgnoreCase(message)) {
return "当前在线客户端数量: " + clients.size();
} else {
// 广播给其他客户端
String broadcastMsg = clientAddr + ": " + message;
clients.keySet().forEach(addr -> {
if (!addr.equals(clientAddr)) {
sendMessage(broadcastMsg, addr);
}
});
return "消息已广播";
}
}
private void sendMessage(String message, InetSocketAddress clientAddr) {
try {
byte[] data = message.getBytes("UTF-8");
DatagramPacket packet = new DatagramPacket(
data, data.length, clientAddr.getAddress(), clientAddr.getPort());
socket.send(packet);
} catch (IOException e) {
System.err.println("发送消息到 " + clientAddr + " 时出错: " + e.getMessage());
}
}
private void broadcastMessage(String message) {
clients.keySet().forEach(addr -> sendMessage(message, addr));
}
private void cleanupClients() {
while (running) {
try {
Thread.sleep(30000); // 每30秒清理一次
long now = System.currentTimeMillis();
clients.entrySet().removeIf(entry -> {
long lastActive = entry.getValue();
return (now - lastActive) > 60000; // 60秒未活跃则移除
});
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
public void stop() {
running = false;
if (socket != null && !socket.isClosed()) {
socket.close();
}
threadPool.shutdown();
try {
if (!threadPool.awaitTermination(5, TimeUnit.SECONDS)) {
threadPool.shutdownNow();
}
} catch (InterruptedException e) {
threadPool.shutdownNow();
}
System.out.println("UDP服务器已关闭");
}
public static void main(String[] args) {
try {
int port = args.length > 0 ? Integer.parseInt(args[0]) : 9999;
UDPServer server = new UDPServer(port);
server.start();
} catch (Exception e) {
System.err.println("启动UDP服务器失败: " + e.getMessage());
}
}
}
// UDP客户端
class UDPClient {
private DatagramSocket socket;
private InetAddress serverAddress;
private int serverPort;
private volatile boolean running = false;
public UDPClient(String host, int port) throws Exception {
socket = new DatagramSocket();
serverAddress = InetAddress.getByName(host);
serverPort = port;
System.out.println("UDP客户端已创建,服务器地址: " + host + ":" + port);
}
public void start() {
running = true;
// 启动接收消息的线程
Thread receiveThread = new Thread(this::receiveMessages);
receiveThread.setDaemon(true);
receiveThread.start();
// 发送心跳包
Thread heartbeatThread = new Thread(this::sendHeartbeat);
heartbeatThread.setDaemon(true);
heartbeatThread.start();
// 处理用户输入
handleUserInput();
}
private void receiveMessages() {
byte[] buffer = new byte[1024];
while (running) {
try {
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
String message = new String(packet.getData(), 0, packet.getLength(), "UTF-8");
System.out.println("[服务器] " + message);
} catch (IOException e) {
if (running) {
System.err.println("接收消息时出错: " + e.getMessage());
}
}
}
}
private void sendHeartbeat() {
while (running) {
try {
Thread.sleep(30000); // 每30秒发送一次心跳
sendMessage("ping");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
private void handleUserInput() {
Scanner scanner = new Scanner(System.in);
System.out.println("\n可用命令:");
System.out.println(" ping - 发送心跳包");
System.out.println(" time - 获取服务器时间");
System.out.println(" echo <消息> - 回显消息");
System.out.println(" clients - 查看在线客户端数量");
System.out.println(" quit - 退出");
System.out.println(" 其他消息会广播给所有客户端\n");
while (running) {
String input = scanner.nextLine();
if ("quit".equalsIgnoreCase(input.trim())) {
break;
}
if (!input.trim().isEmpty()) {
sendMessage(input);
}
}
scanner.close();
stop();
}
public void sendMessage(String message) {
try {
byte[] data = message.getBytes("UTF-8");
DatagramPacket packet = new DatagramPacket(
data, data.length, serverAddress, serverPort);
socket.send(packet);
} catch (IOException e) {
System.err.println("发送消息失败: " + e.getMessage());
}
}
public void stop() {
running = false;
if (socket != null && !socket.isClosed()) {
socket.close();
}
System.out.println("UDP客户端已关闭");
}
public static void main(String[] args) {
try {
String host = args.length > 0 ? args[0] : "localhost";
int port = args.length > 1 ? Integer.parseInt(args[1]) : 9999;
UDPClient client = new UDPClient(host, port);
client.start();
} catch (Exception e) {
System.err.println("启动UDP客户端失败: " + e.getMessage());
}
}
}
12.4 HTTP编程
12.4.1 HTTP客户端
import java.net.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
// HTTP客户端工具类
public class HTTPClient {
private int connectTimeout = 5000;
private int readTimeout = 10000;
private Map<String, String> defaultHeaders = new HashMap<>();
public HTTPClient() {
// 设置默认请求头
defaultHeaders.put("User-Agent", "Java-HTTPClient/1.0");
defaultHeaders.put("Accept", "*/*");
defaultHeaders.put("Connection", "close");
}
// GET请求
public HTTPResponse get(String urlString) throws IOException {
return get(urlString, null);
}
public HTTPResponse get(String urlString, Map<String, String> headers) throws IOException {
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
try {
// 设置请求方法和超时
connection.setRequestMethod("GET");
connection.setConnectTimeout(connectTimeout);
connection.setReadTimeout(readTimeout);
// 设置请求头
setHeaders(connection, headers);
// 发送请求并获取响应
return getResponse(connection);
} finally {
connection.disconnect();
}
}
// POST请求
public HTTPResponse post(String urlString, String data) throws IOException {
return post(urlString, data, null);
}
public HTTPResponse post(String urlString, String data, Map<String, String> headers) throws IOException {
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
try {
// 设置请求方法和属性
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setConnectTimeout(connectTimeout);
connection.setReadTimeout(readTimeout);
// 设置请求头
if (headers == null) {
headers = new HashMap<>();
}
if (!headers.containsKey("Content-Type")) {
headers.put("Content-Type", "application/x-www-form-urlencoded");
}
setHeaders(connection, headers);
// 发送请求数据
if (data != null) {
try (OutputStreamWriter writer = new OutputStreamWriter(
connection.getOutputStream(), "UTF-8")) {
writer.write(data);
writer.flush();
}
}
// 获取响应
return getResponse(connection);
} finally {
connection.disconnect();
}
}
// JSON POST请求
public HTTPResponse postJSON(String urlString, String jsonData) throws IOException {
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "application/json");
return post(urlString, jsonData, headers);
}
// 文件上传
public HTTPResponse uploadFile(String urlString, File file, String fieldName) throws IOException {
String boundary = "----" + System.currentTimeMillis();
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
try {
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setConnectTimeout(connectTimeout);
connection.setReadTimeout(readTimeout);
// 设置multipart请求头
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
setHeaders(connection, null);
// 构建multipart数据
try (DataOutputStream out = new DataOutputStream(connection.getOutputStream())) {
// 文件部分
out.writeBytes("--" + boundary + "\r\n");
out.writeBytes("Content-Disposition: form-data; name=\"" + fieldName +
"\"; filename=\"" + file.getName() + "\"\r\n");
out.writeBytes("Content-Type: application/octet-stream\r\n\r\n");
// 写入文件内容
try (FileInputStream fileInput = new FileInputStream(file)) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = fileInput.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
}
out.writeBytes("\r\n--" + boundary + "--\r\n");
out.flush();
}
return getResponse(connection);
} finally {
connection.disconnect();
}
}
private void setHeaders(HttpURLConnection connection, Map<String, String> headers) {
// 设置默认请求头
for (Map.Entry<String, String> entry : defaultHeaders.entrySet()) {
connection.setRequestProperty(entry.getKey(), entry.getValue());
}
// 设置自定义请求头
if (headers != null) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
connection.setRequestProperty(entry.getKey(), entry.getValue());
}
}
}
private HTTPResponse getResponse(HttpURLConnection connection) throws IOException {
int responseCode = connection.getResponseCode();
String responseMessage = connection.getResponseMessage();
// 获取响应头
Map<String, List<String>> responseHeaders = connection.getHeaderFields();
// 读取响应体
String responseBody;
InputStream inputStream = responseCode >= 400 ?
connection.getErrorStream() : connection.getInputStream();
if (inputStream != null) {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(inputStream, "UTF-8"))) {
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
responseBody = sb.toString();
}
} else {
responseBody = "";
}
return new HTTPResponse(responseCode, responseMessage, responseHeaders, responseBody);
}
// 设置超时时间
public void setConnectTimeout(int timeout) {
this.connectTimeout = timeout;
}
public void setReadTimeout(int timeout) {
this.readTimeout = timeout;
}
// 设置默认请求头
public void setDefaultHeader(String name, String value) {
defaultHeaders.put(name, value);
}
// 演示方法
public static void main(String[] args) {
HTTPClient client = new HTTPClient();
try {
System.out.println("=== HTTP GET请求演示 ===");
HTTPResponse response = client.get("http://httpbin.org/get");
System.out.println("状态码: " + response.getStatusCode());
System.out.println("响应体: " + response.getBody());
System.out.println("\n=== HTTP POST请求演示 ===");
String postData = "name=张三&age=25";
response = client.post("http://httpbin.org/post", postData);
System.out.println("状态码: " + response.getStatusCode());
System.out.println("响应体: " + response.getBody());
System.out.println("\n=== JSON POST请求演示 ===");
String jsonData = "{\"name\":\"李四\",\"age\":30}";
response = client.postJSON("http://httpbin.org/post", jsonData);
System.out.println("状态码: " + response.getStatusCode());
System.out.println("响应体: " + response.getBody());
} catch (IOException e) {
System.err.println("HTTP请求失败: " + e.getMessage());
}
}
}
// HTTP响应类
class HTTPResponse {
private int statusCode;
private String statusMessage;
private Map<String, List<String>> headers;
private String body;
public HTTPResponse(int statusCode, String statusMessage,
Map<String, List<String>> headers, String body) {
this.statusCode = statusCode;
this.statusMessage = statusMessage;
this.headers = headers;
this.body = body;
}
public int getStatusCode() {
return statusCode;
}
public String getStatusMessage() {
return statusMessage;
}
public Map<String, List<String>> getHeaders() {
return headers;
}
public String getHeader(String name) {
List<String> values = headers.get(name);
return values != null && !values.isEmpty() ? values.get(0) : null;
}
public String getBody() {
return body;
}
public boolean isSuccess() {
return statusCode >= 200 && statusCode < 300;
}
@Override
public String toString() {
return "HTTPResponse{" +
"statusCode=" + statusCode +
", statusMessage='" + statusMessage + '\'' +
", bodyLength=" + (body != null ? body.length() : 0) +
'}';
}
}
12.4.2 简单HTTP服务器
import java.net.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
import java.text.SimpleDateFormat;
// 简单HTTP服务器
public class SimpleHTTPServer {
private ServerSocket serverSocket;
private ExecutorService threadPool;
private volatile boolean running = false;
private final Map<String, HTTPHandler> handlers = new HashMap<>();
private String documentRoot = "./www";
public SimpleHTTPServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
threadPool = Executors.newFixedThreadPool(10);
// 注册默认处理器
registerDefaultHandlers();
System.out.println("HTTP服务器启动,监听端口: " + port);
System.out.println("文档根目录: " + new File(documentRoot).getAbsolutePath());
}
private void registerDefaultHandlers() {
// 根路径处理器
handlers.put("/", (request, response) -> {
response.setContentType("text/html; charset=UTF-8");
response.setBody("<h1>欢迎访问简单HTTP服务器</h1>" +
"<p>当前时间: " + new Date() + "</p>" +
"<p><a href='/info'>服务器信息</a></p>" +
"<p><a href='/echo?msg=Hello'>回显测试</a></p>");
});
// 服务器信息处理器
handlers.put("/info", (request, response) -> {
response.setContentType("application/json; charset=UTF-8");
String info = "{" +
"\"server\": \"SimpleHTTPServer/1.0\"," +
"\"java_version\": \"" + System.getProperty("java.version") + "\"," +
"\"os\": \"" + System.getProperty("os.name") + "\"," +
"\"time\": \"" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "\"" +
"}";
response.setBody(info);
});
// 回显处理器
handlers.put("/echo", (request, response) -> {
String message = request.getParameter("msg");
if (message == null) {
message = "No message provided";
}
response.setContentType("text/plain; charset=UTF-8");
response.setBody("Echo: " + message);
});
// POST测试处理器
handlers.put("/post", (request, response) -> {
if ("POST".equals(request.getMethod())) {
response.setContentType("application/json; charset=UTF-8");
String result = "{" +
"\"method\": \"" + request.getMethod() + "\"," +
"\"content_type\": \"" + request.getHeader("Content-Type") + "\"," +
"\"body_length\": " + (request.getBody() != null ? request.getBody().length() : 0) + "," +
"\"body\": \"" + (request.getBody() != null ? request.getBody().replace("\"", "\\\"") : "") + "\"" +
"}";
response.setBody(result);
} else {
response.setStatus(405, "Method Not Allowed");
response.setBody("Only POST method is allowed");
}
});
}
public void start() {
running = true;
while (running) {
try {
Socket clientSocket = serverSocket.accept();
threadPool.submit(new HTTPRequestHandler(clientSocket));
} catch (IOException e) {
if (running) {
System.err.println("接受连接时出错: " + e.getMessage());
}
}
}
}
public void stop() {
running = false;
try {
if (serverSocket != null && !serverSocket.isClosed()) {
serverSocket.close();
}
threadPool.shutdown();
if (!threadPool.awaitTermination(5, TimeUnit.SECONDS)) {
threadPool.shutdownNow();
}
System.out.println("HTTP服务器已关闭");
} catch (Exception e) {
System.err.println("关闭服务器时出错: " + e.getMessage());
}
}
public void addHandler(String path, HTTPHandler handler) {
handlers.put(path, handler);
}
public void setDocumentRoot(String documentRoot) {
this.documentRoot = documentRoot;
}
// HTTP请求处理器
private class HTTPRequestHandler implements Runnable {
private Socket socket;
public HTTPRequestHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream(), "UTF-8"));
PrintWriter writer = new PrintWriter(
new OutputStreamWriter(socket.getOutputStream(), "UTF-8"), true)) {
// 解析HTTP请求
HTTPRequest request = parseRequest(reader);
if (request == null) {
return;
}
System.out.println("收到请求: " + request.getMethod() + " " + request.getPath());
// 创建响应对象
HTTPResponse response = new HTTPResponse();
try {
// 查找处理器
HTTPHandler handler = handlers.get(request.getPath());
if (handler != null) {
handler.handle(request, response);
} else {
// 尝试提供静态文件
serveStaticFile(request, response);
}
} catch (Exception e) {
response.setStatus(500, "Internal Server Error");
response.setBody("服务器内部错误: " + e.getMessage());
}
// 发送响应
sendResponse(writer, response);
} catch (IOException e) {
System.err.println("处理HTTP请求时出错: " + e.getMessage());
} finally {
try {
socket.close();
} catch (IOException e) {
// 忽略关闭异常
}
}
}
private HTTPRequest parseRequest(BufferedReader reader) throws IOException {
// 读取请求行
String requestLine = reader.readLine();
if (requestLine == null || requestLine.trim().isEmpty()) {
return null;
}
String[] parts = requestLine.split(" ");
if (parts.length != 3) {
return null;
}
String method = parts[0];
String uri = parts[1];
String version = parts[2];
// 解析URI
String path = uri;
Map<String, String> parameters = new HashMap<>();
int queryIndex = uri.indexOf('?');
if (queryIndex != -1) {
path = uri.substring(0, queryIndex);
String query = uri.substring(queryIndex + 1);
parseQueryString(query, parameters);
}
// 读取请求头
Map<String, String> headers = new HashMap<>();
String line;
while ((line = reader.readLine()) != null && !line.trim().isEmpty()) {
int colonIndex = line.indexOf(':');
if (colonIndex != -1) {
String name = line.substring(0, colonIndex).trim();
String value = line.substring(colonIndex + 1).trim();
headers.put(name, value);
}
}
// 读取请求体
String body = null;
String contentLengthStr = headers.get("Content-Length");
if (contentLengthStr != null) {
try {
int contentLength = Integer.parseInt(contentLengthStr);
if (contentLength > 0) {
char[] buffer = new char[contentLength];
int totalRead = 0;
while (totalRead < contentLength) {
int read = reader.read(buffer, totalRead, contentLength - totalRead);
if (read == -1) break;
totalRead += read;
}
body = new String(buffer, 0, totalRead);
// 如果是表单数据,解析参数
String contentType = headers.get("Content-Type");
if (contentType != null && contentType.startsWith("application/x-www-form-urlencoded")) {
parseQueryString(body, parameters);
}
}
} catch (NumberFormatException e) {
// 忽略无效的Content-Length
}
}
return new HTTPRequest(method, path, version, headers, parameters, body);
}
private void parseQueryString(String query, Map<String, String> parameters) {
if (query == null || query.trim().isEmpty()) {
return;
}
String[] pairs = query.split("&");
for (String pair : pairs) {
int equalIndex = pair.indexOf('=');
if (equalIndex != -1) {
try {
String name = URLDecoder.decode(pair.substring(0, equalIndex), "UTF-8");
String value = URLDecoder.decode(pair.substring(equalIndex + 1), "UTF-8");
parameters.put(name, value);
} catch (UnsupportedEncodingException e) {
// 忽略解码错误
}
}
}
}
private void serveStaticFile(HTTPRequest request, HTTPResponse response) {
String path = request.getPath();
if (path.equals("/")) {
path = "/index.html";
}
File file = new File(documentRoot + path);
if (!file.exists() || !file.isFile()) {
response.setStatus(404, "Not Found");
response.setContentType("text/html; charset=UTF-8");
response.setBody("<h1>404 - 文件未找到</h1><p>请求的文件不存在: " + path + "</p>");
return;
}
try {
// 读取文件内容
StringBuilder content = new StringBuilder();
try (BufferedReader fileReader = new BufferedReader(
new FileReader(file, java.nio.charset.StandardCharsets.UTF_8))) {
String line;
while ((line = fileReader.readLine()) != null) {
content.append(line).append("\n");
}
}
// 设置Content-Type
String contentType = getContentType(file.getName());
response.setContentType(contentType);
response.setBody(content.toString());
} catch (IOException e) {
response.setStatus(500, "Internal Server Error");
response.setBody("读取文件时出错: " + e.getMessage());
}
}
private String getContentType(String fileName) {
String extension = "";
int dotIndex = fileName.lastIndexOf('.');
if (dotIndex != -1) {
extension = fileName.substring(dotIndex + 1).toLowerCase();
}
switch (extension) {
case "html":
case "htm":
return "text/html; charset=UTF-8";
case "css":
return "text/css; charset=UTF-8";
case "js":
return "application/javascript; charset=UTF-8";
case "json":
return "application/json; charset=UTF-8";
case "xml":
return "application/xml; charset=UTF-8";
case "txt":
return "text/plain; charset=UTF-8";
case "jpg":
case "jpeg":
return "image/jpeg";
case "png":
return "image/png";
case "gif":
return "image/gif";
default:
return "application/octet-stream";
}
}
private void sendResponse(PrintWriter writer, HTTPResponse response) {
// 状态行
writer.println("HTTP/1.1 " + response.getStatusCode() + " " + response.getStatusMessage());
// 响应头
writer.println("Server: SimpleHTTPServer/1.0");
writer.println("Date: " + new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH).format(new Date()));
if (response.getContentType() != null) {
writer.println("Content-Type: " + response.getContentType());
}
String body = response.getBody();
if (body != null) {
writer.println("Content-Length: " + body.getBytes(java.nio.charset.StandardCharsets.UTF_8).length);
}
writer.println("Connection: close");
writer.println(); // 空行分隔头部和正文
// 响应体
if (body != null) {
writer.print(body);
}
writer.flush();
}
}
public static void main(String[] args) {
try {
int port = args.length > 0 ? Integer.parseInt(args[0]) : 8080;
SimpleHTTPServer server = new SimpleHTTPServer(port);
// 添加自定义处理器
server.addHandler("/hello", (request, response) -> {
String name = request.getParameter("name");
if (name == null) name = "World";
response.setContentType("text/html; charset=UTF-8");
response.setBody("<h1>Hello, " + name + "!</h1>");
});
System.out.println("访问 http://localhost:" + port + " 查看服务器");
server.start();
} catch (Exception e) {
System.err.println("启动HTTP服务器失败: " + e.getMessage());
}
}
}
// HTTP请求类
class HTTPRequest {
private String method;
private String path;
private String version;
private Map<String, String> headers;
private Map<String, String> parameters;
private String body;
public HTTPRequest(String method, String path, String version,
Map<String, String> headers, Map<String, String> parameters, String body) {
this.method = method;
this.path = path;
this.version = version;
this.headers = headers;
this.parameters = parameters;
this.body = body;
}
public String getMethod() { return method; }
public String getPath() { return path; }
public String getVersion() { return version; }
public Map<String, String> getHeaders() { return headers; }
public String getHeader(String name) { return headers.get(name); }
public Map<String, String> getParameters() { return parameters; }
public String getParameter(String name) { return parameters.get(name); }
public String getBody() { return body; }
}
// HTTP响应类(服务器端)
class HTTPResponse {
private int statusCode = 200;
private String statusMessage = "OK";
private String contentType;
private String body;
public void setStatus(int code, String message) {
this.statusCode = code;
this.statusMessage = message;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public void setBody(String body) {
this.body = body;
}
public int getStatusCode() { return statusCode; }
public String getStatusMessage() { return statusMessage; }
public String getContentType() { return contentType; }
public String getBody() { return body; }
}
// HTTP处理器接口
interface HTTPHandler {
void handle(HTTPRequest request, HTTPResponse response) throws Exception;
}
12.5 NIO网络编程
12.5.1 NIO基础概念
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.net.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
// NIO基础演示
public class NIOBasicsDemo {
public static void main(String[] args) {
demonstrateBuffer();
demonstrateChannel();
demonstrateSelector();
}
// Buffer演示
public static void demonstrateBuffer() {
System.out.println("=== Buffer演示 ===");
// 创建ByteBuffer
ByteBuffer buffer = ByteBuffer.allocate(10);
System.out.println("初始状态: " + bufferInfo(buffer));
// 写入数据
buffer.put("Hello".getBytes());
System.out.println("写入Hello后: " + bufferInfo(buffer));
// 切换到读模式
buffer.flip();
System.out.println("flip后: " + bufferInfo(buffer));
// 读取数据
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
System.out.println("读取的数据: " + new String(data));
System.out.println("读取后: " + bufferInfo(buffer));
// 清空buffer
buffer.clear();
System.out.println("clear后: " + bufferInfo(buffer));
// 演示mark和reset
buffer.put("ABCDEFGHIJ".getBytes());
buffer.flip();
buffer.get(); // 读取A
buffer.get(); // 读取B
buffer.mark(); // 标记当前位置
buffer.get(); // 读取C
buffer.get(); // 读取D
System.out.println("mark后读取CD: " + bufferInfo(buffer));
buffer.reset(); // 回到mark位置
System.out.println("reset后: " + bufferInfo(buffer));
// 演示compact
buffer.compact();
System.out.println("compact后: " + bufferInfo(buffer));
}
private static String bufferInfo(ByteBuffer buffer) {
return String.format("position=%d, limit=%d, capacity=%d, remaining=%d",
buffer.position(), buffer.limit(), buffer.capacity(), buffer.remaining());
}
// Channel演示
public static void demonstrateChannel() {
System.out.println("\n=== Channel演示 ===");
try {
// 文件Channel演示
String fileName = "nio_test.txt";
String content = "这是NIO Channel测试内容\nLine 2\nLine 3";
// 写入文件
try (FileChannel writeChannel = FileChannel.open(
Paths.get(fileName),
StandardOpenOption.CREATE,
StandardOpenOption.WRITE,
StandardOpenOption.TRUNCATE_EXISTING)) {
ByteBuffer buffer = ByteBuffer.wrap(content.getBytes("UTF-8"));
int bytesWritten = writeChannel.write(buffer);
System.out.println("写入字节数: " + bytesWritten);
}
// 读取文件
try (FileChannel readChannel = FileChannel.open(
Paths.get(fileName), StandardOpenOption.READ)) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = readChannel.read(buffer);
buffer.flip();
String readContent = StandardCharsets.UTF_8.decode(buffer).toString();
System.out.println("读取字节数: " + bytesRead);
System.out.println("读取内容:\n" + readContent);
}
// 文件复制演示
String copyFileName = "nio_test_copy.txt";
try (FileChannel sourceChannel = FileChannel.open(
Paths.get(fileName), StandardOpenOption.READ);
FileChannel destChannel = FileChannel.open(
Paths.get(copyFileName),
StandardOpenOption.CREATE,
StandardOpenOption.WRITE,
StandardOpenOption.TRUNCATE_EXISTING)) {
long transferred = sourceChannel.transferTo(0, sourceChannel.size(), destChannel);
System.out.println("文件复制完成,传输字节数: " + transferred);
}
// 清理文件
new File(fileName).delete();
new File(copyFileName).delete();
} catch (IOException e) {
System.err.println("Channel演示出错: " + e.getMessage());
}
}
// Selector演示
public static void demonstrateSelector() {
System.out.println("\n=== Selector演示 ===");
try {
// 创建Selector
Selector selector = Selector.open();
// 创建ServerSocketChannel
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.bind(new InetSocketAddress(9999));
// 注册到Selector
SelectionKey serverKey = serverChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("Selector创建成功,注册了ServerSocketChannel");
System.out.println("ServerChannel是否阻塞: " + serverChannel.isBlocking());
System.out.println("注册的操作: " + getOperationString(serverKey.interestOps()));
// 模拟客户端连接
Thread clientThread = new Thread(() -> {
try {
Thread.sleep(1000);
SocketChannel client = SocketChannel.open();
client.connect(new InetSocketAddress("localhost", 9999));
ByteBuffer buffer = ByteBuffer.wrap("Hello NIO Server".getBytes());
client.write(buffer);
Thread.sleep(1000);
client.close();
} catch (Exception e) {
System.err.println("客户端连接出错: " + e.getMessage());
}
});
clientThread.start();
// 处理事件
long endTime = System.currentTimeMillis() + 5000; // 5秒后结束
while (System.currentTimeMillis() < endTime) {
int readyChannels = selector.select(1000);
if (readyChannels > 0) {
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isAcceptable()) {
System.out.println("处理ACCEPT事件");
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
if (client != null) {
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
System.out.println("客户端连接成功: " + client.getRemoteAddress());
}
} else if (key.isReadable()) {
System.out.println("处理READ事件");
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = client.read(buffer);
if (bytesRead > 0) {
buffer.flip();
String message = StandardCharsets.UTF_8.decode(buffer).toString();
System.out.println("收到消息: " + message);
} else if (bytesRead == -1) {
System.out.println("客户端断开连接");
key.cancel();
client.close();
}
}
keyIterator.remove();
}
}
}
// 清理资源
selector.close();
serverChannel.close();
clientThread.join();
System.out.println("Selector演示完成");
} catch (Exception e) {
System.err.println("Selector演示出错: " + e.getMessage());
}
}
private static String getOperationString(int ops) {
List<String> operations = new ArrayList<>();
if ((ops & SelectionKey.OP_ACCEPT) != 0) operations.add("ACCEPT");
if ((ops & SelectionKey.OP_CONNECT) != 0) operations.add("CONNECT");
if ((ops & SelectionKey.OP_READ) != 0) operations.add("READ");
if ((ops & SelectionKey.OP_WRITE) != 0) operations.add("WRITE");
return String.join(", ", operations);
}
}
12.5.2 NIO服务器实现
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.net.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
// NIO服务器
public class NIOServer {
private Selector selector;
private ServerSocketChannel serverChannel;
private volatile boolean running = false;
private final Map<SocketChannel, ClientSession> sessions = new ConcurrentHashMap<>();
private ExecutorService workerPool;
public NIOServer(int port) throws IOException {
selector = Selector.open();
serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.bind(new InetSocketAddress(port));
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
workerPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
System.out.println("NIO服务器启动,监听端口: " + port);
}
public void start() {
running = true;
while (running) {
try {
int readyChannels = selector.select(1000);
if (readyChannels > 0) {
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
keyIterator.remove();
if (!key.isValid()) {
continue;
}
try {
if (key.isAcceptable()) {
handleAccept(key);
} else if (key.isReadable()) {
handleRead(key);
} else if (key.isWritable()) {
handleWrite(key);
}
} catch (Exception e) {
System.err.println("处理事件时出错: " + e.getMessage());
closeChannel(key);
}
}
}
// 清理断开的连接
cleanupSessions();
} catch (IOException e) {
if (running) {
System.err.println("Selector操作出错: " + e.getMessage());
}
}
}
}
private void handleAccept(SelectionKey key) throws IOException {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
if (client != null) {
client.configureBlocking(false);
SelectionKey clientKey = client.register(selector, SelectionKey.OP_READ);
// 创建客户端会话
ClientSession session = new ClientSession(client);
sessions.put(client, session);
clientKey.attach(session);
System.out.println("新客户端连接: " + client.getRemoteAddress());
// 发送欢迎消息
session.sendMessage("欢迎连接到NIO服务器!\n");
}
}
private void handleRead(SelectionKey key) throws IOException {
SocketChannel client = (SocketChannel) key.channel();
ClientSession session = (ClientSession) key.attachment();
if (session == null) {
closeChannel(key);
return;
}
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = client.read(buffer);
if (bytesRead > 0) {
buffer.flip();
session.appendData(buffer);
// 处理完整的消息
String message;
while ((message = session.getNextMessage()) != null) {
final String finalMessage = message;
final ClientSession finalSession = session;
// 提交到工作线程池处理
workerPool.submit(() -> processMessage(finalMessage, finalSession));
}
} else if (bytesRead == -1) {
System.out.println("客户端断开连接: " + client.getRemoteAddress());
closeChannel(key);
}
}
private void handleWrite(SelectionKey key) throws IOException {
SocketChannel client = (SocketChannel) key.channel();
ClientSession session = (ClientSession) key.attachment();
if (session != null && session.hasDataToWrite()) {
ByteBuffer buffer = session.getWriteBuffer();
if (buffer != null) {
client.write(buffer);
if (!buffer.hasRemaining()) {
session.writeComplete();
// 如果没有更多数据要写,取消写事件
if (!session.hasDataToWrite()) {
key.interestOps(SelectionKey.OP_READ);
}
}
}
}
}
private void processMessage(String message, ClientSession session) {
message = message.trim();
System.out.println("收到消息: " + message + " 来自: " + session.getRemoteAddress());
String response;
if ("quit".equalsIgnoreCase(message)) {
response = "再见!\n";
session.sendMessage(response);
session.close();
return;
} else if ("time".equalsIgnoreCase(message)) {
response = "当前时间: " + new Date() + "\n";
} else if (message.startsWith("echo ")) {
response = "回显: " + message.substring(5) + "\n";
} else if ("clients".equalsIgnoreCase(message)) {
response = "当前在线客户端数量: " + sessions.size() + "\n";
} else if ("help".equalsIgnoreCase(message)) {
response = "可用命令:\n" +
" time - 获取服务器时间\n" +
" echo <消息> - 回显消息\n" +
" clients - 查看在线客户端数量\n" +
" broadcast <消息> - 广播消息\n" +
" quit - 退出\n";
} else if (message.startsWith("broadcast ")) {
String broadcastMsg = message.substring(10);
broadcastMessage(session.getRemoteAddress() + ": " + broadcastMsg + "\n", session);
response = "消息已广播\n";
} else {
response = "未知命令: " + message + ",输入 'help' 查看帮助\n";
}
session.sendMessage(response);
}
private void broadcastMessage(String message, ClientSession excludeSession) {
sessions.values().forEach(session -> {
if (session != excludeSession && !session.isClosed()) {
session.sendMessage(message);
}
});
}
private void closeChannel(SelectionKey key) {
try {
SocketChannel client = (SocketChannel) key.channel();
ClientSession session = sessions.remove(client);
if (session != null) {
session.close();
}
key.cancel();
client.close();
} catch (IOException e) {
System.err.println("关闭连接时出错: " + e.getMessage());
}
}
private void cleanupSessions() {
sessions.entrySet().removeIf(entry -> {
ClientSession session = entry.getValue();
if (session.isClosed()) {
try {
entry.getKey().close();
} catch (IOException e) {
// 忽略关闭异常
}
return true;
}
return false;
});
}
public void stop() {
running = false;
try {
// 关闭所有客户端连接
sessions.values().forEach(ClientSession::close);
sessions.clear();
// 关闭服务器
if (selector != null) selector.close();
if (serverChannel != null) serverChannel.close();
// 关闭工作线程池
workerPool.shutdown();
if (!workerPool.awaitTermination(5, TimeUnit.SECONDS)) {
workerPool.shutdownNow();
}
System.out.println("NIO服务器已关闭");
} catch (Exception e) {
System.err.println("关闭服务器时出错: " + e.getMessage());
}
}
public static void main(String[] args) {
try {
int port = args.length > 0 ? Integer.parseInt(args[0]) : 8888;
NIOServer server = new NIOServer(port);
// 添加关闭钩子
Runtime.getRuntime().addShutdownHook(new Thread(server::stop));
server.start();
} catch (Exception e) {
System.err.println("启动NIO服务器失败: " + e.getMessage());
}
}
}
// 客户端会话类
class ClientSession {
private final SocketChannel channel;
private final ByteBuffer readBuffer = ByteBuffer.allocate(1024);
private final Queue<ByteBuffer> writeQueue = new ConcurrentLinkedQueue<>();
private final StringBuilder messageBuffer = new StringBuilder();
private volatile boolean closed = false;
private String remoteAddress;
public ClientSession(SocketChannel channel) {
this.channel = channel;
try {
this.remoteAddress = channel.getRemoteAddress().toString();
} catch (IOException e) {
this.remoteAddress = "unknown";
}
}
public void appendData(ByteBuffer buffer) {
String data = StandardCharsets.UTF_8.decode(buffer).toString();
messageBuffer.append(data);
}
public String getNextMessage() {
int newlineIndex = messageBuffer.indexOf("\n");
if (newlineIndex != -1) {
String message = messageBuffer.substring(0, newlineIndex);
messageBuffer.delete(0, newlineIndex + 1);
return message;
}
return null;
}
public void sendMessage(String message) {
if (!closed) {
ByteBuffer buffer = ByteBuffer.wrap(message.getBytes(StandardCharsets.UTF_8));
writeQueue.offer(buffer);
// 请求写事件
try {
SelectionKey key = channel.keyFor(channel.provider().openSelector());
if (key != null && key.isValid()) {
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
key.selector().wakeup();
}
} catch (Exception e) {
// 忽略异常
}
}
}
public boolean hasDataToWrite() {
return !writeQueue.isEmpty();
}
public ByteBuffer getWriteBuffer() {
return writeQueue.peek();
}
public void writeComplete() {
writeQueue.poll();
}
public String getRemoteAddress() {
return remoteAddress;
}
public boolean isClosed() {
return closed;
}
public void close() {
closed = true;
writeQueue.clear();
}
}
12.5.3 NIO客户端实现
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.net.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
// NIO客户端
public class NIOClient {
private SocketChannel socketChannel;
private Selector selector;
private volatile boolean running = false;
private final Queue<String> messageQueue = new ConcurrentLinkedQueue<>();
private final StringBuilder messageBuffer = new StringBuilder();
public NIOClient() throws IOException {
selector = Selector.open();
}
public boolean connect(String host, int port) {
try {
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
// 开始连接
boolean connected = socketChannel.connect(new InetSocketAddress(host, port));
if (!connected) {
// 注册连接事件
socketChannel.register(selector, SelectionKey.OP_CONNECT);
// 等待连接完成
while (!socketChannel.finishConnect()) {
selector.select(1000);
Set<SelectionKey> keys = selector.selectedKeys();
for (SelectionKey key : keys) {
if (key.isConnectable()) {
SocketChannel channel = (SocketChannel) key.channel();
if (channel.finishConnect()) {
System.out.println("连接成功: " + host + ":" + port);
// 注册读事件
key.interestOps(SelectionKey.OP_READ);
break;
}
}
}
keys.clear();
}
} else {
System.out.println("连接成功: " + host + ":" + port);
socketChannel.register(selector, SelectionKey.OP_READ);
}
return true;
} catch (IOException e) {
System.err.println("连接失败: " + e.getMessage());
return false;
}
}
public void start() {
running = true;
// 启动消息处理线程
Thread messageThread = new Thread(this::processMessages);
messageThread.setDaemon(true);
messageThread.start();
// 启动用户输入线程
Thread inputThread = new Thread(this::handleUserInput);
inputThread.setDaemon(true);
inputThread.start();
// 主事件循环
while (running) {
try {
int readyChannels = selector.select(1000);
if (readyChannels > 0) {
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
keyIterator.remove();
if (!key.isValid()) {
continue;
}
try {
if (key.isReadable()) {
handleRead(key);
} else if (key.isWritable()) {
handleWrite(key);
}
} catch (Exception e) {
System.err.println("处理事件时出错: " + e.getMessage());
disconnect();
break;
}
}
}
// 检查是否有消息要发送
if (!messageQueue.isEmpty() && socketChannel.isConnected()) {
SelectionKey key = socketChannel.keyFor(selector);
if (key != null && key.isValid()) {
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
}
}
} catch (IOException e) {
if (running) {
System.err.println("Selector操作出错: " + e.getMessage());
disconnect();
}
}
}
}
private void handleRead(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = channel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
String data = StandardCharsets.UTF_8.decode(buffer).toString();
messageBuffer.append(data);
// 处理完整的消息
processReceivedMessages();
} else if (bytesRead == -1) {
System.out.println("服务器断开连接");
disconnect();
}
}
private void handleWrite(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
String message = messageQueue.poll();
if (message != null) {
ByteBuffer buffer = ByteBuffer.wrap(message.getBytes(StandardCharsets.UTF_8));
channel.write(buffer);
if (!buffer.hasRemaining()) {
// 写入完成,如果没有更多消息,取消写事件
if (messageQueue.isEmpty()) {
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
}
} else {
// 没有完全写入,重新放回队列
messageQueue.offer(message);
}
} else {
// 没有消息要发送,取消写事件
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
}
}
private void processReceivedMessages() {
int newlineIndex;
while ((newlineIndex = messageBuffer.indexOf("\n")) != -1) {
String message = messageBuffer.substring(0, newlineIndex);
messageBuffer.delete(0, newlineIndex + 1);
if (!message.trim().isEmpty()) {
System.out.println("[服务器] " + message);
}
}
}
private void processMessages() {
// 这个方法可以用于处理接收到的消息
// 当前实现中,消息直接在handleRead中处理
}
private void handleUserInput() {
try (Scanner scanner = new Scanner(System.in)) {
System.out.println("连接成功!输入消息发送到服务器,输入 'quit' 退出:");
while (running) {
if (scanner.hasNextLine()) {
String input = scanner.nextLine();
if ("quit".equalsIgnoreCase(input.trim())) {
sendMessage(input + "\n");
disconnect();
break;
} else if (!input.trim().isEmpty()) {
sendMessage(input + "\n");
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
break;
}
}
} catch (Exception e) {
System.err.println("用户输入处理出错: " + e.getMessage());
}
}
public void sendMessage(String message) {
if (running && socketChannel != null && socketChannel.isConnected()) {
messageQueue.offer(message);
// 唤醒selector以处理写事件
selector.wakeup();
}
}
public void disconnect() {
running = false;
try {
if (socketChannel != null) {
socketChannel.close();
}
if (selector != null) {
selector.close();
}
System.out.println("已断开连接");
} catch (IOException e) {
System.err.println("断开连接时出错: " + e.getMessage());
}
}
public boolean isConnected() {
return running && socketChannel != null && socketChannel.isConnected();
}
public static void main(String[] args) {
String host = args.length > 0 ? args[0] : "localhost";
int port = args.length > 1 ? Integer.parseInt(args[1]) : 8888;
try {
NIOClient client = new NIOClient();
if (client.connect(host, port)) {
// 添加关闭钩子
Runtime.getRuntime().addShutdownHook(new Thread(client::disconnect));
client.start();
} else {
System.err.println("无法连接到服务器");
}
} catch (Exception e) {
System.err.println("启动NIO客户端失败: " + e.getMessage());
}
}
}
12.6 网络编程最佳实践
12.6.1 性能优化
import java.net.*;
import java.io.*;
import java.util.concurrent.*;
import java.nio.*;
import java.nio.channels.*;
// 网络编程性能对比演示
public class NetworkPerformanceComparison {
public static void main(String[] args) {
int clientCount = 100;
int messagesPerClient = 10;
System.out.println("=== 网络编程性能对比 ===");
System.out.println("客户端数量: " + clientCount);
System.out.println("每客户端消息数: " + messagesPerClient);
// 测试传统IO
testTraditionalIO(clientCount, messagesPerClient);
// 测试NIO
testNIO(clientCount, messagesPerClient);
}
// 传统IO性能测试
public static void testTraditionalIO(int clientCount, int messagesPerClient) {
System.out.println("\n--- 传统IO测试 ---");
long startTime = System.currentTimeMillis();
try {
// 启动简单的Echo服务器
Thread serverThread = new Thread(() -> {
try (ServerSocket serverSocket = new ServerSocket(9001)) {
ExecutorService pool = Executors.newFixedThreadPool(50);
while (!Thread.currentThread().isInterrupted()) {
try {
Socket client = serverSocket.accept();
pool.submit(() -> {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(client.getInputStream()));
PrintWriter writer = new PrintWriter(
client.getOutputStream(), true)) {
String line;
while ((line = reader.readLine()) != null) {
writer.println("Echo: " + line);
if ("quit".equals(line)) {
break;
}
}
} catch (IOException e) {
// 忽略客户端断开异常
} finally {
try {
client.close();
} catch (IOException e) {
// 忽略
}
}
});
} catch (IOException e) {
if (!Thread.currentThread().isInterrupted()) {
System.err.println("服务器接受连接出错: " + e.getMessage());
}
}
}
pool.shutdown();
} catch (IOException e) {
System.err.println("启动传统IO服务器失败: " + e.getMessage());
}
});
serverThread.setDaemon(true);
serverThread.start();
// 等待服务器启动
Thread.sleep(1000);
// 创建客户端
ExecutorService clientPool = Executors.newFixedThreadPool(clientCount);
CountDownLatch latch = new CountDownLatch(clientCount);
for (int i = 0; i < clientCount; i++) {
final int clientId = i;
clientPool.submit(() -> {
try (Socket socket = new Socket("localhost", 9001);
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream()))) {
for (int j = 0; j < messagesPerClient; j++) {
String message = "Client-" + clientId + "-Message-" + j;
writer.println(message);
reader.readLine(); // 读取回显
}
writer.println("quit");
} catch (IOException e) {
System.err.println("客户端" + clientId + "出错: " + e.getMessage());
} finally {
latch.countDown();
}
});
}
// 等待所有客户端完成
latch.await();
clientPool.shutdown();
serverThread.interrupt();
long endTime = System.currentTimeMillis();
System.out.println("传统IO总耗时: " + (endTime - startTime) + "ms");
System.out.println("平均每个连接耗时: " + (endTime - startTime) / clientCount + "ms");
} catch (Exception e) {
System.err.println("传统IO测试出错: " + e.getMessage());
}
}
// NIO性能测试
public static void testNIO(int clientCount, int messagesPerClient) {
System.out.println("\n--- NIO测试 ---");
long startTime = System.currentTimeMillis();
try {
// 启动NIO Echo服务器
Thread serverThread = new Thread(() -> {
try {
Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.bind(new InetSocketAddress(9002));
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (!Thread.currentThread().isInterrupted()) {
int ready = selector.select(1000);
if (ready > 0) {
var keys = selector.selectedKeys();
var iterator = keys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
try {
int bytesRead = client.read(buffer);
if (bytesRead > 0) {
buffer.flip();
String message = new String(buffer.array(), 0, buffer.limit());
if (message.trim().equals("quit")) {
client.close();
key.cancel();
} else {
// 回显消息
String echo = "Echo: " + message;
ByteBuffer writeBuffer = ByteBuffer.wrap(echo.getBytes());
client.write(writeBuffer);
}
} else if (bytesRead == -1) {
client.close();
key.cancel();
}
} catch (IOException e) {
client.close();
key.cancel();
}
}
}
}
}
selector.close();
serverChannel.close();
} catch (IOException e) {
System.err.println("NIO服务器出错: " + e.getMessage());
}
});
serverThread.setDaemon(true);
serverThread.start();
// 等待服务器启动
Thread.sleep(1000);
// 创建NIO客户端
ExecutorService clientPool = Executors.newFixedThreadPool(clientCount);
CountDownLatch latch = new CountDownLatch(clientCount);
for (int i = 0; i < clientCount; i++) {
final int clientId = i;
clientPool.submit(() -> {
try {
SocketChannel channel = SocketChannel.open();
channel.connect(new InetSocketAddress("localhost", 9002));
for (int j = 0; j < messagesPerClient; j++) {
String message = "Client-" + clientId + "-Message-" + j + "\n";
ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
channel.write(buffer);
// 读取回显
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
channel.read(readBuffer);
}
// 发送quit消息
ByteBuffer quitBuffer = ByteBuffer.wrap("quit".getBytes());
channel.write(quitBuffer);
channel.close();
} catch (IOException e) {
System.err.println("NIO客户端" + clientId + "出错: " + e.getMessage());
} finally {
latch.countDown();
}
});
}
// 等待所有客户端完成
latch.await();
clientPool.shutdown();
serverThread.interrupt();
long endTime = System.currentTimeMillis();
System.out.println("NIO总耗时: " + (endTime - startTime) + "ms");
System.out.println("平均每个连接耗时: " + (endTime - startTime) / clientCount + "ms");
} catch (Exception e) {
System.err.println("NIO测试出错: " + e.getMessage());
}
}
}
12.6.2 网络编程最佳实践总结
// 网络编程最佳实践示例
public class NetworkBestPractices {
// 1. 连接池管理
public static class ConnectionPool {
private final Queue<Socket> availableConnections = new ConcurrentLinkedQueue<>();
private final Set<Socket> usedConnections = ConcurrentHashMap.newKeySet();
private final String host;
private final int port;
private final int maxConnections;
public ConnectionPool(String host, int port, int maxConnections) {
this.host = host;
this.port = port;
this.maxConnections = maxConnections;
}
public Socket getConnection() throws IOException {
Socket connection = availableConnections.poll();
if (connection == null || connection.isClosed()) {
if (usedConnections.size() < maxConnections) {
connection = new Socket(host, port);
} else {
throw new IOException("连接池已满");
}
}
usedConnections.add(connection);
return connection;
}
public void returnConnection(Socket connection) {
if (connection != null && !connection.isClosed()) {
usedConnections.remove(connection);
availableConnections.offer(connection);
}
}
public void close() {
// 关闭所有连接
availableConnections.forEach(this::closeQuietly);
usedConnections.forEach(this::closeQuietly);
}
private void closeQuietly(Socket socket) {
try {
if (socket != null && !socket.isClosed()) {
socket.close();
}
} catch (IOException e) {
// 忽略关闭异常
}
}
}
// 2. 超时处理
public static class TimeoutHandler {
public static void setSocketTimeouts(Socket socket) throws SocketException {
socket.setSoTimeout(30000); // 读取超时30秒
socket.setKeepAlive(true); // 启用keep-alive
socket.setTcpNoDelay(true); // 禁用Nagle算法
socket.setSoLinger(true, 5); // 关闭时等待5秒
}
public static void setServerSocketTimeouts(ServerSocket serverSocket) throws SocketException {
serverSocket.setSoTimeout(60000); // 接受连接超时60秒
serverSocket.setReuseAddress(true); // 允许地址重用
}
}
// 3. 异常处理
public static class ExceptionHandler {
public static void handleNetworkException(Exception e, String operation) {
if (e instanceof ConnectException) {
System.err.println(operation + " - 连接被拒绝: " + e.getMessage());
} else if (e instanceof SocketTimeoutException) {
System.err.println(operation + " - 连接超时: " + e.getMessage());
} else if (e instanceof UnknownHostException) {
System.err.println(operation + " - 未知主机: " + e.getMessage());
} else if (e instanceof IOException) {
System.err.println(operation + " - IO异常: " + e.getMessage());
} else {
System.err.println(operation + " - 未知异常: " + e.getMessage());
e.printStackTrace();
}
}
}
// 4. 缓冲区优化
public static class BufferOptimization {
// 使用合适的缓冲区大小
public static final int OPTIMAL_BUFFER_SIZE = 8192; // 8KB
public static BufferedInputStream createOptimizedInputStream(InputStream in) {
return new BufferedInputStream(in, OPTIMAL_BUFFER_SIZE);
}
public static BufferedOutputStream createOptimizedOutputStream(OutputStream out) {
return new BufferedOutputStream(out, OPTIMAL_BUFFER_SIZE);
}
// NIO缓冲区重用
private static final ThreadLocal<ByteBuffer> BUFFER_CACHE =
ThreadLocal.withInitial(() -> ByteBuffer.allocateDirect(OPTIMAL_BUFFER_SIZE));
public static ByteBuffer getReusableBuffer() {
ByteBuffer buffer = BUFFER_CACHE.get();
buffer.clear();
return buffer;
}
}
// 5. 资源管理
public static class ResourceManager {
public static void closeQuietly(AutoCloseable... resources) {
for (AutoCloseable resource : resources) {
if (resource != null) {
try {
resource.close();
} catch (Exception e) {
// 记录日志但不抛出异常
System.err.println("关闭资源时出错: " + e.getMessage());
}
}
}
}
// 使用try-with-resources的网络操作模板
public static <T> T executeWithSocket(String host, int port,
SocketOperation<T> operation) throws IOException {
try (Socket socket = new Socket(host, port)) {
TimeoutHandler.setSocketTimeouts(socket);
return operation.execute(socket);
}
}
}
@FunctionalInterface
public interface SocketOperation<T> {
T execute(Socket socket) throws IOException;
}
// 6. 线程安全的消息处理
public static class ThreadSafeMessageHandler {
private final BlockingQueue<String> messageQueue = new LinkedBlockingQueue<>();
private final ExecutorService processingPool = Executors.newFixedThreadPool(4);
private volatile boolean running = true;
public void start() {
for (int i = 0; i < 4; i++) {
processingPool.submit(this::processMessages);
}
}
public void addMessage(String message) {
if (running) {
messageQueue.offer(message);
}
}
private void processMessages() {
while (running) {
try {
String message = messageQueue.poll(1, TimeUnit.SECONDS);
if (message != null) {
// 处理消息
handleMessage(message);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
private void handleMessage(String message) {
// 实际的消息处理逻辑
System.out.println("处理消息: " + message);
}
public void shutdown() {
running = false;
processingPool.shutdown();
try {
if (!processingPool.awaitTermination(5, TimeUnit.SECONDS)) {
processingPool.shutdownNow();
}
} catch (InterruptedException e) {
processingPool.shutdownNow();
}
}
}
}
12.7 本章小结
12.7.1 网络编程技术对比
特性 | 传统IO (BIO) | NIO | HTTP编程 |
---|---|---|---|
编程模型 | 阻塞式,一线程一连接 | 非阻塞式,事件驱动 | 请求-响应模式 |
性能 | 连接数受线程数限制 | 可处理大量并发连接 | 适合短连接 |
复杂度 | 简单易懂 | 相对复杂 | 中等 |
适用场景 | 连接数少,交互频繁 | 高并发,连接数多 | Web应用,API调用 |
资源消耗 | 内存消耗大 | 内存消耗小 | 中等 |
12.7.2 选择建议
传统Socket编程:
- 适合连接数较少(<1000)的应用
- 客户端-服务器交互频繁
- 对实时性要求高的应用
NIO编程:
- 高并发服务器(>1000连接)
- 聊天服务器、游戏服务器
- 需要处理大量长连接的应用
HTTP编程:
- Web应用开发
- RESTful API调用
- 微服务间通信
12.7.3 性能优化要点
缓冲区管理:
- 使用合适的缓冲区大小(通常8KB-64KB)
- 重用缓冲区对象
- 使用直接内存缓冲区(DirectByteBuffer)
连接管理:
- 使用连接池
- 设置合适的超时时间
- 及时关闭不用的连接
线程管理:
- 使用线程池而不是为每个连接创建线程
- 合理设置线程池大小
- 使用异步处理减少阻塞
数据处理:
- 批量处理数据
- 使用高效的序列化方式
- 压缩大数据传输
12.7.4 安全考虑
- 输入验证:验证所有网络输入数据
- 加密传输:使用SSL/TLS加密敏感数据
- 访问控制:实现适当的身份验证和授权
- 防护攻击:防范DDoS、SQL注入等攻击
下一章预告
下一章我们将学习Java反射机制,包括: - 反射的基本概念和原理 - Class类的使用 - 动态创建对象和调用方法 - 注解的定义和处理 - 反射的实际应用场景
练习题
基础练习
简单聊天程序:
- 实现一个基于Socket的简单聊天程序
- 支持多个客户端同时连接
- 实现消息广播功能
文件传输工具:
- 使用Socket实现文件传输
- 支持断点续传
- 显示传输进度
HTTP客户端:
- 实现一个简单的HTTP客户端
- 支持GET和POST请求
- 处理重定向和Cookie
进阶练习
NIO聊天服务器:
- 使用NIO实现高并发聊天服务器
- 支持私聊和群聊
- 实现用户认证和房间管理
简单Web服务器:
- 实现一个支持静态文件的Web服务器
- 支持多种MIME类型
- 实现基本的HTTP缓存机制
网络代理服务器:
- 实现一个简单的HTTP代理服务器
- 支持请求转发和响应缓存
- 实现访问日志记录
挑战练习
分布式缓存系统:
- 设计并实现一个简单的分布式缓存
- 支持数据分片和复制
- 实现一致性哈希算法
微服务通信框架:
- 实现一个简单的RPC框架
- 支持服务注册和发现
- 实现负载均衡和故障转移 “`