跳转到内容

Java DatagramPacket

来自代码酷


Java DatagramPacket 是 Java 网络编程中用于实现无连接数据包传输的核心类,属于 java.net 包。它通常与 DatagramSocket 配合使用,支持 UDP(用户数据报协议)通信。与面向连接的 TCP 不同,UDP 不保证数据包的顺序或可靠性,但具有低延迟和高吞吐量的特点,适用于实时应用(如视频流、在线游戏等)。

概述[编辑 | 编辑源代码]

DatagramPacket 表示一个数据包,包含以下核心信息:

  • 数据缓冲区(byte[]):存储实际传输的数据
  • 长度:数据包的有效数据长度
  • 目标地址(IP + 端口):用于发送数据包
  • 源地址(IP + 端口):接收数据包时自动填充

类签名[编辑 | 编辑源代码]

public final class DatagramPacket extends Object

构造函数[编辑 | 编辑源代码]

DatagramPacket 提供两种主要构造函数:

接收数据包[编辑 | 编辑源代码]

// 创建用于接收的包(不指定目标地址)
DatagramPacket(byte[] buf, int length)

发送数据包[编辑 | 编辑源代码]

// 创建用于发送的包(指定目标地址)
DatagramPacket(byte[] buf, int length, InetAddress address, int port)

核心方法[编辑 | 编辑源代码]

方法 描述
byte[] getData() 返回数据缓冲区
int getLength() 返回有效数据长度
InetAddress getAddress() 返回目标/源地址
int getPort() 返回目标/源端口
void setData(byte[] buf) 设置数据缓冲区
void setLength(int length) 设置有效数据长度

基础示例[编辑 | 编辑源代码]

发送端代码[编辑 | 编辑源代码]

import java.net.*;

public class UDPSender {
    public static void main(String[] args) throws Exception {
        DatagramSocket socket = new DatagramSocket();
        String message = "Hello UDP!";
        byte[] data = message.getBytes();
        
        InetAddress address = InetAddress.getByName("localhost");
        DatagramPacket packet = new DatagramPacket(
            data, data.length, address, 9876);
        
        socket.send(packet);
        System.out.println("Sent: " + message);
        socket.close();
    }
}

接收端代码[编辑 | 编辑源代码]

import java.net.*;

public class UDPReceiver {
    public static void main(String[] args) throws Exception {
        DatagramSocket socket = new DatagramSocket(9876);
        byte[] buffer = new byte[1024];
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
        
        socket.receive(packet);
        String received = new String(
            packet.getData(), 0, packet.getLength());
        System.out.println("Received: " + received);
        socket.close();
    }
}

执行结果[编辑 | 编辑源代码]

先运行接收端,再运行发送端:

接收端输出: Received: Hello UDP!
发送端输出: Sent: Hello UDP!

高级应用[编辑 | 编辑源代码]

数据包分片[编辑 | 编辑源代码]

UDP 最大包长受 MTU 限制(通常 1500 字节)。大文件需要分片传输:

// 发送分片示例
byte[] largeData = /* 大文件数据 */;
int chunkSize = 1400; // 小于MTU
for (int i = 0; i < largeData.length; i += chunkSize) {
    int length = Math.min(chunkSize, largeData.length - i);
    DatagramPacket chunk = new DatagramPacket(
        largeData, i, length, address, port);
    socket.send(chunk);
}

校验和验证[编辑 | 编辑源代码]

UDP 不保证可靠性,可手动添加校验:

// 添加简单校验和
byte[] originalData = /* 原始数据 */;
byte checksum = calculateChecksum(originalData);
byte[] packetData = new byte[originalData.length + 1];
System.arraycopy(originalData, 0, packetData, 0, originalData.length);
packetData[packetData.length - 1] = checksum;

// 接收端验证
byte receivedChecksum = packetData[packetData.length - 1];
if (receivedChecksum != calculateChecksum(
    Arrays.copyOf(packetData, packetData.length - 1))) {
    throw new IOException("Checksum mismatch");
}

协议分析[编辑 | 编辑源代码]

DatagramPacket 在 UDP 协议栈中的位置:

sequenceDiagram participant App as 应用程序 participant Java as Java API participant OS as 操作系统 participant Net as 网络 App->>Java: 创建DatagramPacket Java->>OS: 调用系统socket API OS->>Net: 发送UDP数据报 Net-->>OS: 接收UDP数据报 OS-->>Java: 填充DatagramPacket Java-->>App: 返回数据

性能考量[编辑 | 编辑源代码]

  • 缓冲区复用:避免频繁创建新缓冲区
  • 线程模型:考虑使用 NIO 的非阻塞模式处理高并发
  • 超时设置:通过 setSoTimeout() 避免无限等待

安全实践[编辑 | 编辑源代码]

  • 验证数据包来源(防止IP欺骗):
if (!packet.getAddress().equals(trustedAddress)) {
    throw new SecurityException("Untrusted source");
}
  • 限制数据包大小:
if (packet.getLength() > MAX_ALLOWED_SIZE) {
    throw new IOException("Packet too large");
}

实际应用场景[编辑 | 编辑源代码]

网络游戏位置同步[编辑 | 编辑源代码]

客户端每秒发送多次玩家位置数据包:

// 游戏客户端发送位置数据
class PlayerPosition {
    float x, y, z;
    long timestamp;
}

ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeFloat(x); dos.writeFloat(y); dos.writeFloat(z);
dos.writeLong(System.currentTimeMillis());

DatagramPacket packet = new DatagramPacket(
    baos.toByteArray(), baos.size(), serverAddress, port);
socket.send(packet);

IoT 设备状态上报[编辑 | 编辑源代码]

传感器设备定期发送状态数据:

// 温度传感器数据包结构
byte[] createSensorPacket(float temp, int battery) {
    byte[] data = new byte[5];
    data[0] = (byte)(temp * 10); // 0.1度精度
    data[1] = (byte)((int)(temp * 100) % 10);
    System.arraycopy(
        ByteBuffer.allocate(4).putInt(battery).array(),
        0, data, 2, 3); // 3字节存储电量
    return data;
}

常见问题[编辑 | 编辑源代码]

模板:Q&A

模板:Q&A

数学原理[编辑 | 编辑源代码]

UDP 校验和计算(RFC 1071): checksum=i=1n(datai8)+datai+1

其中:

  • x 表示按位取反
  • 表示左移运算
  • 求和使用16位补码运算

参见[编辑 | 编辑源代码]