网络编程套接字

网络编程套接字

Socket(套接字)是网络编程的核心工具,也是操作系统提供给应用程序的网络通信接口。简单来说,Socket 文件是操作系统对网卡的抽象封装,读写 Socket 文件,本质上就是借助网卡完成数据的收发操作。

在网络协议中,UDP 是一种无连接、不可靠的传输层协议,而 Java 提供了 DatagramSocketDatagramPacket 两个类来支持 UDP 编程。接下来,我们就详细拆解 UDP 编程的核心知识点,并通过完整的示例代码落地实践。

一、UDP 数据报

UDP 编程的核心是两个类:DatagramSocket(用于收发数据报的套接字)和 DatagramPacket(用于封装数据报的载体)。

DatagramSocket

DatagramSocket 是 UDP 对应的 Socket 实现,专门用于发送和接收 UDP 数据报,它就像一个「数据收发中转站」,负责建立应用程序与网络层的连接。

(1)核心构造方法

方法签名方法说明
DatagramSocket()创建一个 UDP 数据报套接字,绑定到本机任意随机端口(适用于客户端,无需固定端口)
DatagramSocket(int port)创建一个 UDP 数据报套接字,绑定到本机指定端口(适用于服务端,需要固定端口供客户端访问)

(2)常用核心方法

方法签名方法说明
void receive(DatagramPacket p)从此套接字接收数据报(无数据时会阻塞等待,直到收到数据)
void send(DatagramPacket p)从此套接字发送数据报(直接发送,不阻塞等待对方确认)
void close()关闭此数据报套接字,释放相关资源

DatagramPacket

DatagramPacket 是 UDP 数据报的「载体」,负责封装 UDP 通信中的数据内容、目标地址、端口号等信息。所有通过 DatagramSocket 收发的数据,都必须封装在这个对象中。

(1)核心构造方法
UDP 的收发数据报构造方法有所区分,需注意区分使用场景:

方法签名方法说明
DatagramPacket(byte[] buf, int length)用于接收数据报:接收到的数据会存入字节数组 buf,仅保留前 length 长度的内容
DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)用于发送数据报:从字节数组 bufoffset 下标开始,取 length 长度的数据作为发送内容;address 指定接收端的 IP 和端口

(2)常用核心方法

方法签名方法说明
InetAddress getAddress()接收场景:获取发送端的 IP 地址;发送场景:获取接收端的 IP 地址
int getPort()接收场景:获取发送端的端口号;发送场景:获取接收端的端口号
byte[] getData()获取数据报中封装的原始字节数据

创建 SocketAddress

构造发送用的 DatagramPacket 时,需要传入 SocketAddress 对象,通常我们使用 InetSocketAddress 来快速创建(指定 IP 和端口):

// 示例:创建指向 本地回环地址 127.0.0.1:8888 的 Socket 地址
SocketAddress address = new InetSocketAddress("127.0.0.1", 8888);

UDP 实战:实现回声服务器

回声服务器(Echo Server)的核心功能是:接收客户端发送的消息,不做任何处理,直接原封不动地返回给客户端。下面我们分别实现服务端和客户端代码,一步一步完成 UDP 通信。

UDP 服务端代码

服务端需要固定端口,循环接收客户端请求,并返回响应结果,核心步骤是「接收请求 → 构造响应 → 发送响应」。

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

public class UdpEchoServer {
    // 声明 DatagramSocket 实例,负责收发数据
    private DatagramSocket socket = null;

    /**
     * 构造方法:绑定指定端口,创建 UDP 套接字
     * @param port 服务端监听端口
     * @throws SocketException 端口被占用时抛出异常
     */
    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }

    /**
     * 启动服务端,循环处理客户端请求
     * @throws IOException 数据收发异常时抛出
     */
    public void start() throws IOException {
        System.out.println("服务器启动...");
        
        // 无限循环,持续处理请求(服务端通常不主动退出)
        while (true) {

            // 1. 创建空白的 UDP 数据包,接收客户端请求
            DatagramPacket requestPacket = new DatagramPacket(new byte[1024],
            1024);
            socket.receive(requestPacket);

            // 2. 解析请求数据,转为字符串(方便阅读和处理)
            String request = new String(requestPacket.getData(), 0,
                    requestPacket.getLength());

            // 3. 根据请求构造响应
            String response = process(request);

            // 4. 封装响应数据包,发送给客户端
            DatagramPacket responsePacket = new DatagramPacket(
                    response.getBytes(), // 响应数据字节数组
                    response.getBytes().length, // 响应数据长度
                    requestPacket.getSocketAddress()
            );
            socket.send(responsePacket); // 发送响应

            // 5. 打印日志
            System.out.printf("[客户端 %s:%d] 请求:%s | 响应:%s\n",
                    requestPacket.getAddress().getHostAddress(),
                    requestPacket.getPort(),
                    request,
                    response);
        }

        }

    private String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        UdpEchoServer server = new UdpEchoServer(9090);
        server.start();
    }
}

UDP 客户端代码

客户端需要指定服务端的 IP 和端口,读取用户输入并发送给服务端,然后接收服务端的响应并打印。

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

public class UdpEchoClient {
    // 声明 DatagramSocket 实例,负责收发数据
    private DatagramSocket socket = null;
    // 服务端 IP 地址
    private String serverIP;
    // 服务端监听端口
    private int serverPort;

    /**
     * 构造方法:初始化客户端,指定服务端地址和端口
     * @param serverIP 服务端 IP 地址
     * @param serverPort 服务端监听端口
     * @throws SocketException 创建套接字异常时抛出
     */
    public UdpEchoClient(String serverIP, int serverPort) throws SocketException {
        // 客户端无需绑定固定端口,由操作系统自动分配随机端口(避免端口冲突)
        socket = new DatagramSocket();
        this.serverIP = serverIP;
        this.serverPort = serverPort;
    }

    /**
     * 启动客户端,循环与服务端通信
     * @throws IOException 数据收发异常时抛出
     */
    public void start() throws IOException {
        System.out.println("客户端启动...");
        Scanner scanner = new Scanner(System.in);

        // 无限循环,持续发送用户输入的消息
        while (true) {
            // 1. 读取用户控制台输入
            System.out.println("-> ");
            String request = scanner.next();

            // 2. 封装请求数据包,发送给服务端
            DatagramPacket requestPacket = new DatagramPacket(
                    request.getBytes(),
                    request.getBytes().length,
                    InetAddress.getByName(serverIP),
                    serverPort
            );
            socket.send(requestPacket); // 发送请求

            // 3. 接受服务器响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[1024],
            1024);
            socket.receive(responsePacket);

            // 4.打印结果
            String response = new String(responsePacket.getData(), 0,
            responsePacket.getLength());
            System.out.println(response);

            // 关闭资源
            scanner.close();
            socket.close();
        }
    }

    public static void main(String[] args) throws IOException {
        UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);
        client.start();
    }
}

总结

  1. UDP 是无连接、不可靠的传输协议,Java 中通过 DatagramSocket(收发)和 DatagramPacket(封装)实现 UDP 编程。
  2. DatagramSocket 服务端需绑定固定端口,客户端由操作系统分配随机端口,避免冲突。
  3. UDP 通信中,服务端无法主动保存客户端地址,需从接收的 DatagramPacket 中获取客户端的 IP 和端口,才能返回响应。
  4. DatagramPacket 的收发构造方法需区分使用,接收时仅需传入缓冲区,发送时需指定目标地址和端口。
文件IO 2026-01-30

评论区