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 协议栈中的位置:
性能考量[编辑 | 编辑源代码]
- 缓冲区复用:避免频繁创建新缓冲区
- 线程模型:考虑使用 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;
}
常见问题[编辑 | 编辑源代码]
数学原理[编辑 | 编辑源代码]
UDP 校验和计算(RFC 1071):
其中:
- 表示按位取反
- 表示左移运算
- 求和使用16位补码运算