DatagramPacket发送必须指定目标地址和端口,否则抛出Unresolved address异常;接收构造器不接受地址参数,来源信息需receive后调用getAddress()和getPort()获取。

用 DatagramPacket 发送数据时必须指定目标地址和端口
不指定 InetAddress 和端口的 DatagramPacket 只能用于接收,不能发出去——UDP 是无连接协议,没有“默认目的地”这种概念。发送包必须明确告诉系统“发给谁”,否则 send() 会抛出 java.net.SocketException: Unresolved address。
-
DatagramPacket(byte[] buf, int length, InetAddress addr, int port)是最常用、最直接的发送构造器 - 如果数据在 byte 数组中不是从索引 0 开始(比如复用缓冲区),要用带
offset的版本:DatagramPacket(byte[] buf, int offset, int length, InetAddress addr, int port) -
InetAddress.getByName("127.0.0.1")或InetAddress.getByName("localhost")都行,但注意后者可能解析为 IPv6 地址(如::1),若对方只监听 IPv4,会导致收不到 - 端口号必须是
0–65535范围内的整数;小于 1024 的端口在 Linux/macOS 上通常需要 root 权限
DatagramPacket 接收构造器不能带地址参数
接收用的 DatagramPacket 构造器如果传了 InetAddress 和端口,不会报错,但那些参数会被忽略——因为接收方不关心“谁发的”,只管把网卡上收到的 UDP 包往缓冲区里塞。真正获取来源信息,得靠 packet.getAddress() 和 packet.getPort() 在 receive() 之后调用。
- 正确接收构造器:
DatagramPacket(byte[] buf, int length)或DatagramPacket(byte[] buf, int offset, int length) - 缓冲区大小要合理:太小会截断数据(
getLength()返回实际接收字节数,不是缓冲区长度);太大浪费内存,但 Java UDP 默认最大接收 65507 字节(IP 包总长减去 IP+UDP 头) - 别用
buf.length直接转字符串,要用packet.getLength()—— 否则可能包含之前残留的垃圾字节
发送端 DatagramSocket 不需要绑定端口,但有隐含行为
发送端用 new DatagramSocket() 看似“没端口”,其实系统会自动分配一个临时端口(ephemeral port),这个端口会成为该 socket 的源端口。这意味着:对方回包时,可以原路返回到这个端口;你也能用同一个 socket 接收响应(只要没 close)。
- 如果发送后还想收回复,不要调用
socket.close()过早 - 想显式控制源端口(例如做 NAT 穿透或日志追踪),可用
new DatagramSocket(new InetSocketAddress("0.0.0.0", 50000))(Java 7+)或DatagramSocket(int port, InetAddress addr) - 多线程共用一个
DatagramSocket是安全的,但多个线程同时send()或receive()可能导致顺序混乱,建议加锁或分离读写线程
常见错误:发出去却收不到,先查这三处
UDP 丢包不报错,问题往往藏在配置或网络层,而不是代码语法。
- 防火墙或路由器拦截了目标端口(特别是 Windows Defender 防火墙默认阻止入站 UDP)
- 接收端
DatagramSocket绑定的是127.0.0.1,但发送端用了localhost(IPv6 解析失败)或外网 IP,导致地址不匹配 - 接收端没调用
socket.receive(packet),或者调用前 packet 的length被设为 0(常见于复用 packet 对象但忘了重置setLength())
packet.getLength() 初始值是构造时传入的 length,但 receive() 会覆盖它;而发送端的 packet 一旦构造完成,其 getLength() 就固定为构造时传入的 length,不会随数组内容变化——这个不对称性容易被忽略。

















