Java中的TCP协议

Java 中的 TCP 协议 主要通过 java.net 包中的 SocketServerSocket 两个核心类来实现。

Java 并没有直接“实现 TCP 协议”(TCP 协议栈由操作系统内核完成),而是提供了高级抽象,让开发者通过面向流的 socket 接口使用 TCP 的可靠、面向连接、字节流传输特性。

1. 核心类对比(2026 年视角仍未变化)

类名角色主要用途是否阻塞式(默认)典型构造方法示例
java.net.Socket客户端 / 已连接的端点主动发起连接、读写数据new Socket("127.0.0.1", 8080)
java.net.ServerSocket服务端监听绑定端口、监听连接、产生新 Socketnew ServerSocket(8080)
Socket 的流getInputStream() / getOutputStream()用于实际的字节读写

2. TCP 在 Java 中的关键特性(开发者视角)

  • 三次握手:隐藏在 Socket 构造器 / ServerSocket.accept() 内部完成
  • 可靠传输:重传、确认、流量控制、拥塞控制 → 全部由 OS 内核处理,Java 不感知
  • 字节流:无消息边界(不像 UDP 有数据报边界)
  • 有序性:保证发送顺序 = 接收顺序
  • 全双工:读写可同时进行(但应用层需自行处理并发)
  • 优雅关闭shutdownInput() / shutdownOutput() / close() 支持半关闭

3. 最经典的单线程阻塞式 Echo Server & Client 示例(2026 年仍是最基础面试写法)

Server 端(EchoServer.java)

import java.io.*;
import java.net.*;

public class EchoServer {
    public static void main(String[] args) {
        int port = 8888;
        try (ServerSocket serverSocket = new ServerSocket(port)) {
            System.out.println("TCP Echo Server started on port " + port);

            while (true) {  // 持续接受新连接
                // 阻塞等待客户端连接(三次握手在此完成)
                Socket clientSocket = serverSocket.accept();
                System.out.println("New client connected: " + clientSocket.getInetAddress());

                // 为每个客户端分配一个线程(生产中强烈推荐线程池)
                new Thread(() -> handleClient(clientSocket)).start();
            }
        } catch (IOException e) {
            System.err.println("Server error: " + e.getMessage());
        }
    }

    private static void handleClient(Socket socket) {
        try (
            socket;                             // Java 9+ 自动关闭
            BufferedReader in = new BufferedReader(
                new InputStreamReader(socket.getInputStream()));
            PrintWriter out = new PrintWriter(
                socket.getOutputStream(), true)
        ) {
            String line;
            while ((line = in.readLine()) != null) {
                System.out.println("Received: " + line);
                out.println("Echo: " + line);   // 回显
                out.flush();
            }
        } catch (IOException e) {
            System.err.println("Client error: " + e.getMessage());
        } finally {
            System.out.println("Client disconnected");
        }
    }
}

Client 端(EchoClient.java)

import java.io.*;
import java.net.*;
import java.util.Scanner;

public class EchoClient {
    public static void main(String[] args) {
        String host = "localhost";
        int port = 8888;

        try (
            Socket socket = new Socket(host, port);
            PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
            BufferedReader in = new BufferedReader(
                new InputStreamReader(socket.getInputStream()));
            Scanner scanner = new Scanner(System.in)
        ) {
            System.out.println("Connected to " + host + ":" + port);
            System.out.println("Type messages (type 'bye' to exit):");

            String userInput;
            while (!(userInput = scanner.nextLine()).equalsIgnoreCase("bye")) {
                out.println(userInput);         // 发送
                String response = in.readLine(); // 接收
                System.out.println("Server reply: " + response);
            }
            out.println("bye");
        } catch (UnknownHostException e) {
            System.err.println("Don't know about host " + host);
        } catch (IOException e) {
            System.err.println("I/O error: " + e.getMessage());
        }
    }
}

4. 生产环境中必须知道的进阶点(面试常问)

问题/场景推荐做法(2026 年主流)关键类/方法
处理多个客户端使用线程池(Executors.newFixedThreadPool / newCachedThreadPool)或 NIO / NettyExecutorService
非阻塞 / 高并发切换到 NIO(java.nio.channels)或直接使用 Netty / Vert.xSelector, SocketChannel, ServerSocketChannel
心跳检测定时发送 PING/PONG,或使用 SO_KEEPALIVE + 自定义应用层心跳setKeepAlive(true), setSoTimeout()
半包/粘包TCP 是流式 → 必须自己定义消息边界(长度前缀、定长、分隔符、协议如 Protobuf/JSON Length)
优雅关闭连接先 shutdownOutput() → 读完剩余数据 → 再 close()shutdownInput(), shutdownOutput()
超时控制connect timeout / read timeout / write timeoutsetSoTimeout(), Socket connect() 重载
TLS / SSL使用 SSLSocket / SSLServerSocket(或直接用 Netty 的 SslHandler)SSLSocketFactory
零拷贝优化Java NIO 的 transferTo() / transferFrom() 或 Netty 的零拷贝支持FileChannel.transferTo()

5. 常见面试追问一览

  • Socket 和 ServerSocket 区别?
  • accept() 做了什么?(完成三次握手,返回新的 Socket)
  • close() 和 shutdownOutput() 区别?
  • 如何判断客户端异常断开?(read 返回 -1 或抛异常)
  • 为什么生产不用单线程 accept + 处理?(阻塞导致无法接受新连接)
  • BIO / NIO / AIO / Netty 对比?
  • TCP 粘包怎么解决?(最少说出 4 种方案)

需要哪一部分更详细的代码示例(线程池版、多路复用 NIO 版、Netty 版、心跳版、Protobuf 定长协议版等),或者想看某个具体场景的完整实现?可以直接告诉我。

文章已创建 5245

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部