4. 实现UDP广播发送端

好,咱们进入实战环节。这一章我们来做UDP广播的发送端。

说白了,发送端就是那个「喊话的人」。它要把数据包扔给网络里的所有人,而不是只跟某一个人私聊。这个过程中有几个关键动作:打开一个特殊的「喇叭口」(设置广播选项)、写好「喊话地址」(构造广播地址)、然后大声喊出去(发送数据)。当然,还得防着喊岔劈了(错误处理)。

我个人习惯先把整体流程画出来,这样心里有数。你看下面这张图:

UDP广播发送端核心流程 1. 创建UDP套接字 socket(AF_INET, SOCK_DGRAM, 0) 2. 设置广播选项 setsockopt(SO_BROADCAST) 3. 构造广播地址 255.255.255.255 4. 发送广播数据 sendto() 5. 错误处理 try-except / perror ⚠️ 关键前提 必须设置SO_BROADCAST

嗯,流程不复杂,但每一步都有坑。下面我带你手写一遍。

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)
⚠️ 我曾经踩过的坑: 有一次我忘了设置SO_BROADCAST,结果sendto()一直返回错误,我还以为是地址写错了。折腾了半小时才发现是没开广播权限。记住,不设置这个选项,sendto()会直接报错,不会默默忽略。

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)
💡 小技巧: 端口号建议选1024以上的,避免跟系统服务冲突。我一般用9000-10000之间的端口,好记。

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}")

嗯,到这里发送端就讲完了。核心就三件事:开广播权限、写对地址、处理异常。下一章我们来做接收端,看看另一边怎么收数据。


可可精品资料库