4. 实现UDP广播发送端
好,咱们进入实战环节。这一章我们来做UDP广播的发送端。
说白了,发送端就是那个「喊话的人」。它要把数据包扔给网络里的所有人,而不是只跟某一个人私聊。这个过程中有几个关键动作:打开一个特殊的「喇叭口」(设置广播选项)、写好「喊话地址」(构造广播地址)、然后大声喊出去(发送数据)。当然,还得防着喊岔劈了(错误处理)。
我个人习惯先把整体流程画出来,这样心里有数。你看下面这张图:
嗯,流程不复杂,但每一步都有坑。下面我带你手写一遍。
4.1 创建套接字与设置SO_BROADCAST
先创建一个UDP套接字。这个跟普通UDP一样,没什么特别的。
import socket
# 创建UDP套接字
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
关键来了——设置SO_BROADCAST选项。这一步很多人会忘掉。
为什么必须设置这个?因为操作系统默认不允许发送广播数据包,怕你误操作把网络搞炸了。你想想看,要是随便一个程序都能发广播,那网络里得乱成什么样?
所以你得主动告诉系统:「我知道我在干什么,请允许我发广播。」
# 设置广播选项 —— 这一步不能省!
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
4.2 构造广播地址
广播地址有两种写法:
- 受限广播地址:
255.255.255.255—— 发给当前网络的所有设备 - 子网广播地址:比如
192.168.1.255—— 只发给特定子网
我个人习惯用受限广播地址,省事。但要注意,有些路由器会过滤掉255.255.255.255的广播包,这时候就得用子网广播地址了。
# 广播地址和端口
BROADCAST_IP = '255.255.255.255'
PORT = 9999
# 构造目标地址
dest_addr = (BROADCAST_IP, PORT)
4.3 发送广播数据
发送数据用sendto()方法。注意,数据必须是bytes类型,字符串要编码一下。
# 要发送的数据
message = "Hello, 这是广播消息!"
data = message.encode('utf-8')
# 发送广播
try:
sent = sock.sendto(data, dest_addr)
print(f"成功发送 {sent} 字节到 {dest_addr}")
except Exception as e:
print(f"发送失败: {e}")
finally:
sock.close()
这里有个细节:sendto()返回的是实际发送的字节数。如果网络拥堵或者MTU限制,可能发送的字节数会小于你传入的数据长度。不过对于UDP广播来说,数据一般不会太大,我建议控制在1400字节以内,避免IP分片。
4.4 完整的发送端代码
把上面几段拼起来,就是一个完整的发送端了:
import socket
def udp_broadcast_sender(message, port=9999):
"""
UDP广播发送端
:param message: 要发送的字符串消息
:param port: 目标端口
"""
# 1. 创建套接字
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2. 设置广播选项 —— 这是关键!
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
# 3. 构造广播地址
dest_addr = ('255.255.255.255', port)
# 4. 编码并发送
data = message.encode('utf-8')
try:
sent = sock.sendto(data, dest_addr)
print(f"[✓] 发送成功: {sent} 字节 -> {dest_addr}")
return True
except socket.error as e:
print(f"[✗] 发送失败: {e}")
return False
finally:
sock.close()
# 使用示例
if __name__ == '__main__':
udp_broadcast_sender("系统广播:服务器即将重启!")
4.5 错误处理详解
实际项目中,网络环境很复杂。我遇到过好几种异常情况:
| 错误类型 | 常见原因 | 处理方法 |
|---|---|---|
PermissionError |
没有设置SO_BROADCAST | 检查setsockopt是否执行 |
OSError: [Errno 101] |
网络接口不可达 | 检查网卡是否在线 |
OSError: [Errno 90] |
消息太长(超过MTU) | 拆分数据,控制在1400字节内 |
socket.timeout |
发送超时(极少出现) | 设置超时重试机制 |
📌 我的经验: 在Windows上,如果没设置SO_BROADCAST,错误信息是「An attempt was made to access a socket in a way forbidden by its access permissions」。在Linux上则是「Permission denied」。看到这类错误,第一反应就是检查广播选项有没有开。
4.6 进阶:设置发送超时
有时候网络状况不好,sendto()可能会卡住。我建议加个超时:
# 设置2秒超时
sock.settimeout(2.0)
try:
sent = sock.sendto(data, dest_addr)
except socket.timeout:
print("发送超时,网络可能不通")
except Exception as e:
print(f"其他错误: {e}")
嗯,到这里发送端就讲完了。核心就三件事:开广播权限、写对地址、处理异常。下一章我们来做接收端,看看另一边怎么收数据。
可可精品资料库