5.2 KiB
5.2 KiB
BroadcastServer 技术文档
本模块实现了一个基于 UDP 广播的发现服务,可用于局域网内设备或玩家的自动发现。主要包括一个广播服务器(BroadcastServer)和一个客户端发现函数(find_players)。
模块依赖
from socket import *
import json
from appPublic.sockPackage import get_free_local_addr
from appPublic.background import Background
外部依赖说明:
socket: 提供底层网络通信支持。json: 用于序列化/反序列化服务信息。appPublic.sockPackage.get_free_local_addr: 获取本地可用 IP 地址。appPublic.background.Background: 在后台线程中运行任务。
⚠️ 注意:
appPublic是自定义公共库,请确保其已安装并可导入。
常量定义
BUFSIZE = 1024
BUFSIZE: UDP 数据包最大接收缓冲区大小,单位为字节(1KB),适用于大多数局域网小数据传输场景。
类:BroadcastServer
一个 UDP 服务器,监听广播请求,并返回预设的服务信息(如主机名、端口等)给请求方。
构造函数:__init__(self, port, info)
参数:
| 参数 | 类型 | 描述 |
|---|---|---|
port |
int | 服务器绑定的 UDP 端口号 |
info |
dict | 要广播的服务信息(例如:{"name": "GameServer", "game_port": 8000}) |
功能:
- 创建 UDP 套接字(IPv4, UDP)。
- 设置套接字为阻塞模式(默认行为)。
- 绑定到所有本地地址的指定端口 (
''表示0.0.0.0)。 - 启动后台线程运行
run()方法。
示例初始化:
server_info = {
"name": "Player1",
"game_port": 6000,
"status": "waiting"
}
bc_server = BroadcastServer(9999, server_info)
方法:run()
在后台持续运行,处理来自客户端的广播探测请求。
流程:
- 循环等待接收 UDP 数据包(最多
BUFSIZE字节)。 - 收到任意数据后,将
self.info序列化为 JSON 并编码为 UTF-8 发送回请求者的地址。 - 异常捕获打印日志但不中断服务。
📌 协议约定:任何发往该端口的数据都会触发响应 —— 即“有问必答”模型。
响应格式(JSON):
{
"name": "Player1",
"game_port": 6000,
"status": "waiting"
}
方法:stop()
安全停止服务器。
功能:
- 设置运行标志
run_flg = False,终止run()循环。 - 关闭 UDP 套接字资源。
- 不主动等待线程结束(需业务层控制时序)。
示例调用:
bc_server.stop()
函数:find_players(port)
向局域网广播查询消息,寻找活跃的广播服务节点(如游戏主机)。
参数:
| 参数 | 类型 | 描述 |
|---|---|---|
port |
int | 目标广播服务器监听的端口号 |
返回值:
list[dict]: 找到的设备信息列表,每个元素包含原始信息 +'ip'字段。
[
{
"name": "Player1",
"game_port": 6000,
"status": "waiting",
"ip": "192.168.1.105"
},
...
]
实现细节:
- 使用
get_free_local_addr()获取本机 IP,用于过滤自身响应。 - 创建 UDP 客户端套接字并启用广播权限:
udpCliSock.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) - 绑定任意端口(
('', 0))。 - 向
255.255.255.255:{port}发送广播消息'findplayers'。 - 接收响应,超时时间为 5 秒。
- 对每条有效响应:
- 过滤非本地回环且非自身的 IP;
- 解码 JSON 数据;
- 添加来源 IP 到结果中;
- 超时或异常时退出循环,返回收集结果。
✅ 支持跨子网?仅限局域网内支持广播的环境(通常不可跨路由)。
使用示例
启动广播服务(服务端)
info = {"name": "MyGameHost", "game_port": 8888}
server = BroadcastServer(port=9999, info=info)
# ... 保持运行一段时间
# server.stop() # 显式关闭
搜索局域网中的服务(客户端)
players = find_players(9999)
for p in players:
print(f"Found {p['name']} at {p['ip']}:{p['game_port']}")
输出示例:
Found MyGameHost at 192.168.1.105:8888
Found Player2 at 192.168.1.106:8889
注意事项与限制
| 项目 | 说明 |
|---|---|
| 协议 | UDP,不可靠传输,适合低延迟发现 |
| 广播地址 | 固定使用 255.255.255.255,适用于大多数局域网 |
| 安全性 | 无认证机制,仅适用于受信任内网 |
| 性能 | 单线程处理,适合轻量级发现场景 |
| 编码 | 默认使用 UTF-8 编码传输 JSON |
| 异常处理 | 已捕获异常防止崩溃,但建议上层监控 |
可能的改进方向
- ✅ 添加日志系统替代
print - 🔧 支持多播替代广播以提高效率
- ⏱️ 可配置超时时间与重试次数
- 🛡️ 加入消息校验(如 magic header)
- 🔄 支持定期心跳广播(主动宣告)
版权与许可
© 2025 作者保留所有权利。
仅供内部学习与开发使用,遵循项目整体开源协议(如有)。
📌 提示:部署前请测试网络环境是否允许广播通信。