首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java TCP 网络通信实战:基于 Socket 与 HttpClient 的完整示例

     編輯:Java 相關知識

TCP 与 UDP 核心差异速览

特性TCPUDP
连接模式面向连接(三次握手/四次挥手)无连接
可靠性有序、无丢失、无重复(重传+序列号+ACK)尽力而为,可能丢包/乱序/重复
流量控制滑动窗口无
适用场景HTTP、RPC、数据库、即时通讯核心链路视频流、DNS、广播、可容忍丢包的实时场景

原生 Socket 实现:多线程 Echo 服务端

服务端

public class TcpServer {
    private static final int PORT = 8080;
    private static final ExecutorService POOL = Executors.newCachedThreadPool();

    public static void main(String[] args) throws IOException {
        try (ServerSocket server = new ServerSocket(PORT)) {
            System.out.println("Server listening on " + PORT);
            while (true) {
                Socket client = server.accept();
                POOL.submit(() -> handleClient(client));
            }
        }
    }

    private static void handleClient(Socket socket) {
        try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
             PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8), true)) {
            String msg;
            while ((msg = in.readLine()) != null) {
                System.out.println("Received: " + msg);
                out.println("Echo: " + msg);
                if ("bye".equalsIgnoreCase(msg)) break;
            }
        } catch (IOException e) {
            log.error("Client error", e);
        } finally {
            try { socket.close(); } catch (IOException ignored) {}
        }
    }
}

客户端

public class TcpClient {
    public static void main(String[] args) throws IOException {
        String host = "127.0.0.1";
        int port = 8080;
        try (Socket socket = new Socket(host, port);
             BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
             PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8), true);
             BufferedReader console = new BufferedReader(new InputStreamReader(System.in))) {
            String line;
            while ((line = console.readLine()) != null) {
                out.println(line);
                System.out.println("Server: " + in.readLine());
                if ("bye".equalsIgnoreCase(line)) break;
            }
        }
    }
}

进阶:Apache HttpClient 传输序列化对象

HttpClient 4.5+ 封装了连接池、重试、拦截器等企业级特性,适合 REST/RPC 场景。以下示例演示客户端发送序列化对象,服务端反序列化处理并返回结果。

共享 DTO(需实现 Serializable)

public class RequestPayload implements Serializable {
    private String command;
    private Map params;
    // getter/setter/构造器
}

public class ResponsePayload implements Serializable {
    private int code;
    private String message;
    private Object data;
    // getter/setter/构造器
}

客户端发送

CloseableHttpClient client = HttpClients.custom()
    .setConnectionManager(new PoolingHttpClientConnectionManager())
    .evictIdleConnections(30, TimeUnit.SECONDS)
    .build();

HttpPost post = new HttpPost("https://localhost:8080/api/process");
post.setHeader("Content-Type", "application/x-java-serialized-object");

RequestPayload req = new RequestPayload("QUERY", Map.of("id", 1001));
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
     ObjectOutputStream oos = new ObjectOutputStream(bos)) {
    oos.writeObject(req);
    post.setEntity(new ByteArrayEntity(bos.toByteArray()));
    
    try (CloseableHttpResponse resp = client.execute(post);
         ObjectInputStream ois = new ObjectInputStream(resp.getEntity().getContent())) {
        ResponsePayload result = (ResponsePayload) ois.readObject();
        System.out.println("Result: " + result.getData());
    }
}

服务端 Servlet 处理

@WebServlet("/api/process")
public class ProcessServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        resp.setContentType("application/x-java-serialized-object");
        try (ObjectInputStream ois = new ObjectInputStream(req.getInputStream());
             ObjectOutputStream oos = new ObjectOutputStream(resp.getOutputStream())) {
            RequestPayload payload = (RequestPayload) ois.readObject();
            // 业务处理...
            ResponsePayload result = new ResponsePayload(200, "OK", "Processed: " + payload.getCommand());
            oos.writeObject(result);
        } catch (ClassNotFoundException | IOException e) {
            log.error("Deserialize/process failed", e);
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
        }
    }
}

常见坑与最佳实践

  1. main 为何 static? JVM 启动时无实例,需静态入口。非静态字段(如 BufferedReader br)不应声明为 static,否则多客户端并发会数据错乱。
  2. BufferedReader 判空:readLine() 返回 null 表示流结束,而非空字符串,直接 if (br.readLine().equals(...)) 会 NPE。
  3. 连接生命周期:客户端 connect 按钮事件中 new Socket() 会阻塞 UI 线程,必须放入后台线程(SwingWorker/CompletableFuture),否则界面冻结。
  4. 序列化版本控制:DTO 必须显式声明 private static final long serialVersionUID,防止版本不一致导致 InvalidClassException。
  5. 安全警示:Java 原生反序列化存在高危漏洞(CVE-2015-4852 等),生产环境严禁反序列化不可信数据。建议改用 JSON (Jackson)、Protobuf、Avro 等安全协议。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved