2. 网络协议与Java网络API总览

Java网络编程的根基是TCP/IP协议族。理解协议分层与Java API的对应关系,是选择正确通信方式的前提。本节从协议要点出发,梳理InetAddress、Socket、ServerSocket、DatagramSocket等核心类的职责边界与选型依据。

一、TCP/IP协议要点

TCP/IP并非单一协议,而是一个四层模型:链路层、网络层、传输层、应用层。Java网络API主要工作在传输层与应用层之间。

  • IP协议:负责主机到主机的寻址与路由,提供无连接、不可靠的数据报服务。IPv4地址为32位,IPv6为128位。
  • TCP协议:面向连接、可靠、基于字节流。通过三次握手建立连接,四次挥手释放连接。提供流量控制、拥塞控制、超时重传机制。
  • UDP协议:无连接、不可靠、基于数据报。不保证顺序与到达,但开销小、延迟低,适合实时性要求高的场景。
  • HTTP协议:应用层协议,通常基于TCP。请求-响应模型,无状态,通过方法(GET/POST/PUT/DELETE)和状态码表达语义。

端口号用于区分同一主机上的不同服务。0~1023为知名端口,1024~49151为注册端口,49152~65535为动态/私有端口。Java中端口用int表示,有效范围0~65535。

二、Java核心网络类职责

InetAddress:表示IP地址,封装主机名与IP的映射。不包含端口信息。常用方法:getByName(String host)、getLocalHost()、getAllByName(String host)。它不负责建立连接,仅用于地址解析与表示。

Socket:客户端TCP套接字,代表一个已建立的连接端点。通过new Socket(host, port)发起连接,获取输入输出流进行读写。Socket是面向连接的,内部维护本地地址、远程地址、端口及连接状态。

ServerSocket:服务端TCP套接字,用于监听端口并接受连接。调用accept()阻塞等待客户端连接,返回一个新的Socket实例代表该连接。ServerSocket本身不传输数据,只负责建立连接。

DatagramSocket:UDP套接字,用于发送和接收数据报。不建立连接,每个数据报独立寻址。配合DatagramPacket使用,包中需指定目标地址与端口。

DatagramPacket:UDP数据报载体,包含数据字节数组、长度、目标地址与端口。发送时需填充目标地址,接收时由系统填充来源地址。

三、协议与API选择关系

选择哪种API,取决于通信需求:需要可靠传输、顺序保证、大数据量传输时选TCP(Socket/ServerSocket);需要低延迟、容忍丢包、广播/组播时选UDP(DatagramSocket)。HTTP则是在TCP之上封装的应用层协议,Java中可用HttpURLConnection或HttpClient(Java 11+)处理。

Java网络API与协议对应关系 应用层 HTTP HttpURLConnection / HttpClient DNS InetAddress 解析 应用协议 FTP / SMTP / 自定义 传输层 TCP — 面向连接 · 可靠 · 字节流 Socket 客户端连接端点 ServerSocket 服务端监听/accept UDP — 无连接 · 数据报 · 低延迟 DatagramSocket 收发数据报 DatagramPacket 数据载体 网络层 IP 协议 寻址 · 路由 · 无连接 InetAddress IP地址封装 · 主机名解析 选型决策 可靠传输 · 顺序保证 → TCP (Socket/ServerSocket) 低延迟 · 容忍丢包 → UDP (DatagramSocket) 请求-响应 · 无状态 → HTTP (HttpClient)

四、TCP通信示例

以下是一个最小可运行的TCP回声服务端与客户端示例,展示ServerSocket与Socket的配合方式。

// 服务端
try (ServerSocket server = new ServerSocket(8080)) {
    System.out.println("监听 8080...");
    try (Socket conn = server.accept();
         BufferedReader in = new BufferedReader(
             new InputStreamReader(conn.getInputStream()));
         PrintWriter out = new PrintWriter(conn.getOutputStream(), true)) {
        String line;
        while ((line = in.readLine()) != null) {
            out.println("echo: " + line);
        }
    }
}

// 客户端
try (Socket socket = new Socket("127.0.0.1", 8080);
     PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
     BufferedReader in = new BufferedReader(
         new InputStreamReader(socket.getInputStream()))) {
    out.println("hello");
    System.out.println(in.readLine()); // echo: hello
}

关键点:accept()返回的Socket代表一条独立连接,服务端通常为每个连接分配线程或使用NIO多路复用。输入流读取到null表示对端关闭连接。

五、UDP通信示例

// 接收端
try (DatagramSocket socket = new DatagramSocket(9090)) {
    byte[] buf = new byte[1024];
    DatagramPacket packet = new DatagramPacket(buf, buf.length);
    socket.receive(packet); // 阻塞直到收到数据报
    String msg = new String(packet.getData(), 0, packet.getLength());
    System.out.println("收到: " + msg + " 来自 " + packet.getAddress());
}

// 发送端
try (DatagramSocket socket = new DatagramSocket()) {
    byte[] data = "ping".getBytes(StandardCharsets.UTF_8);
    DatagramPacket packet = new DatagramPacket(
        data, data.length, InetAddress.getByName("127.0.0.1"), 9090);
    socket.send(packet);
}

UDP没有连接建立过程,发送端无需知道接收端是否在线。每个数据报最大理论长度受IP层限制,实际使用中建议控制在1472字节以内以避免IP分片。

六、HTTP协议要点与Java处理

HTTP基于TCP,默认端口80(HTTPS为443)。请求由请求行、请求头、空行、请求体组成;响应由状态行、响应头、空行、响应体组成。Java 11引入的java.net.http.HttpClient支持HTTP/2与异步请求,是当前推荐方式。

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://example.com/api"))
    .header("Accept", "application/json")
    .GET()
    .build();
HttpResponse<String> response = client.send(
    request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode()); // 200
System.out.println(response.body());

七、选型对照

维度TCP (Socket/ServerSocket)UDP (DatagramSocket)HTTP (HttpClient)
连接方式面向连接无连接基于TCP
可靠性可靠,保证顺序不可靠,可能丢包乱序可靠
传输单位字节流数据报请求/响应消息
典型场景文件传输、长连接、RPC实时音视频、DNS、游戏Web API、REST服务
Java核心类Socket、ServerSocketDatagramSocket、DatagramPacketHttpClient、HttpURLConnection
选型建议:优先考虑HTTP(HttpClient)处理Web服务调用;需要自定义二进制协议或长连接时用TCP;对延迟极度敏感且能容忍丢包时用UDP。InetAddress是所有方式共用的地址解析工具,不直接参与数据传输。
常见误区:ServerSocket的accept()返回新Socket后,原ServerSocket继续监听,不要误以为它只能接受一个连接。UDP的receive()会阻塞,需设置setSoTimeout()避免永久等待。Socket读写默认阻塞,高并发场景应结合线程池或NIO。