12.1 网络编程基础

12.1.1 网络编程概述

网络编程是指编写能够在网络上运行的程序,使得运行在不同计算机上的程序能够相互通信。Java提供了强大的网络编程支持,包括Socket编程、URL处理、HTTP通信等。

网络编程的基本概念

  1. IP地址:网络中每台计算机的唯一标识
  2. 端口号:标识计算机上的特定服务或应用程序
  3. 协议:数据传输的规则,如TCP、UDP、HTTP等
  4. Socket:网络通信的端点
  5. 客户端/服务器模型:网络应用的基本架构
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&param2=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 选择建议

  1. 传统Socket编程

    • 适合连接数较少(<1000)的应用
    • 客户端-服务器交互频繁
    • 对实时性要求高的应用
  2. NIO编程

    • 高并发服务器(>1000连接)
    • 聊天服务器、游戏服务器
    • 需要处理大量长连接的应用
  3. HTTP编程

    • Web应用开发
    • RESTful API调用
    • 微服务间通信

12.7.3 性能优化要点

  1. 缓冲区管理

    • 使用合适的缓冲区大小(通常8KB-64KB)
    • 重用缓冲区对象
    • 使用直接内存缓冲区(DirectByteBuffer)
  2. 连接管理

    • 使用连接池
    • 设置合适的超时时间
    • 及时关闭不用的连接
  3. 线程管理

    • 使用线程池而不是为每个连接创建线程
    • 合理设置线程池大小
    • 使用异步处理减少阻塞
  4. 数据处理

    • 批量处理数据
    • 使用高效的序列化方式
    • 压缩大数据传输

12.7.4 安全考虑

  1. 输入验证:验证所有网络输入数据
  2. 加密传输:使用SSL/TLS加密敏感数据
  3. 访问控制:实现适当的身份验证和授权
  4. 防护攻击:防范DDoS、SQL注入等攻击

下一章预告

下一章我们将学习Java反射机制,包括: - 反射的基本概念和原理 - Class类的使用 - 动态创建对象和调用方法 - 注解的定义和处理 - 反射的实际应用场景

练习题

基础练习

  1. 简单聊天程序

    • 实现一个基于Socket的简单聊天程序
    • 支持多个客户端同时连接
    • 实现消息广播功能
  2. 文件传输工具

    • 使用Socket实现文件传输
    • 支持断点续传
    • 显示传输进度
  3. HTTP客户端

    • 实现一个简单的HTTP客户端
    • 支持GET和POST请求
    • 处理重定向和Cookie

进阶练习

  1. NIO聊天服务器

    • 使用NIO实现高并发聊天服务器
    • 支持私聊和群聊
    • 实现用户认证和房间管理
  2. 简单Web服务器

    • 实现一个支持静态文件的Web服务器
    • 支持多种MIME类型
    • 实现基本的HTTP缓存机制
  3. 网络代理服务器

    • 实现一个简单的HTTP代理服务器
    • 支持请求转发和响应缓存
    • 实现访问日志记录

挑战练习

  1. 分布式缓存系统

    • 设计并实现一个简单的分布式缓存
    • 支持数据分片和复制
    • 实现一致性哈希算法
  2. 微服务通信框架

    • 实现一个简单的RPC框架
    • 支持服务注册和发现
    • 实现负载均衡和故障转移 “`