UDP 实验:局域网互发消息
这个实验做完你会得到什么
两台手机连同一个 WiFi,在 A 上打一句话,B 立刻收到 —— 不需要任何服务器、 不需要注册账号、不用连外网。再改一行就变成广播,一台发全网都收到。
UDP 是”发出去就不管”的协议,没有连接的概念:发送方不需要知道对方在不在线, 接收方只管守着一个端口。做局域网设备发现、ESP32 上报、遥控小车都靠它。
| 项目 | 说明 |
|---|---|
| 难度 | ★★☆☆☆ |
| 用时 | 约 35 分钟 |
| 前置 | 无。不需要任何硬件,两台手机(或一台手机 + 电脑)即可 |
一、器材清单
| 器材 | 数量 | 说明 |
|---|---|---|
| Android 手机 | 2 | 或者一台手机 + 一台装了网络调试助手的电脑 |
| WiFi 路由器 | 1 | 两台设备必须连同一个局域网,这是唯一的硬性要求 |
手机连的是同一个 WiFi,不是同一个热点名就行 —— 有些路由器开了「AP 隔离」, 会禁止同网设备互访,那样 UDP 也通不了。排错表里有验证办法。
二、导入扩展
UrsAI2UDP.zip(解压后得到 .aix 导入)
包里有两个组件,各管一头:
| 组件 | 职责 |
|---|---|
UDPListener |
守在某个端口上收数据 |
UDPXmitter |
往某个地址+端口发数据 |
一台设备既收又发的话,两个都拖进去。本实验就是这样做。
三、界面组件
| 组件 | 命名 | 关键属性 |
|---|---|---|
| 文本输入框 | 文本输入框_对方IP |
提示「对方 IP,如 192.168.1.23」 |
| 文本输入框 | 文本输入框_内容 |
提示「输入要发送的消息」 |
| 按钮 | 按钮_开始监听 |
文本「开始监听」 |
| 按钮 | 按钮_发送 |
文本「发送」 |
| 标签 | 标签_本机 |
显示本机 IP,供对方填 |
| 标签 | 标签_消息 |
高度 300,显示收到的消息 |
| 标签 | 标签_状态 |
文本「未启动」 |
| UDPListener | UDPListener1 |
不可见 |
| UDPXmitter | UDPXmitter1 |
不可见,LocalPort 留默认 |
四、分步搭代码块
第 1 步:先把本机 IP 显示出来
两边要互相知道对方 IP,所以第一步是让 App 自报家门。
global 端口 = 5000
when Screen1.Initialize() {
标签_本机.Text = join("本机地址:", UDPListener1.LocalHost, " 端口 ", 端口)
标签_状态.Text = "未启动"
}
中途验证:两台手机上都显示出各自的 IP(形如 192.168.x.x)。
把 A 的 IP 记下来填到 B 的输入框里,反之亦然。
LocalHost给的是本机在局域网里的地址。显示成127.0.0.1或空白说明没连上 WiFi (只有移动数据),UDP 局域网通信在这种情况下不可能成功。
第 2 步:开始监听
global 端口 = 5000
when 按钮_开始监听.Click() {
UDPListener1.Start(端口)
标签_状态.Text = join("正在监听端口 ", 端口)
}
when UDPListener1.ListenerFailure(ErrorCode) {
标签_状态.Text = join("监听失败,错误码 ", ErrorCode)
}
中途验证:点「开始监听」后状态栏显示”正在监听端口 5000”,且没有跳出失败。
端口两边必须一样。1024 以下是系统保留端口,普通 App 用不了, 挑 5000~60000 之间的随便一个即可。
同一个端口同一台设备上只能被一个 App 占用,报失败多半是上次没停就重开了。
第 3 步:收到消息就显示
when UDPListener1.DataReceived(Data, RemoteIP, RemotePort) {
标签_消息.Text = join(标签_消息.Text, "\n[", RemoteIP, "] ", Data)
}
中途验证:这一步做完还看不到效果,要等下一步发送。
事件自带发送方的
RemoteIP和RemotePort—— 这是 UDP 很实用的一点: 不用对方在消息里自报身份,收到就知道是谁发的,可以直接回给他。
第 4 步:发送
global 端口 = 5000
when 按钮_发送.Click() {
if isEmpty(文本输入框_对方IP.Text) {
标签_状态.Text = "请先填对方 IP"
} else {
UDPXmitter1.XmitToAsync(文本输入框_对方IP.Text, 端口, 文本输入框_内容.Text)
文本输入框_内容.Text = ""
}
}
when UDPXmitter1.AfterXmit(Success, ErrorCode) {
if Success {
标签_状态.Text = "已发送"
} else {
标签_状态.Text = join("发送失败,错误码 ", ErrorCode)
}
}
中途验证:两台手机都点了「开始监听」之后,在 A 上输入 B 的 IP 和一句话, 点发送 —— B 的消息区出现这句话,A 的状态栏显示”已发送”。
用
XmitToAsync不用XmitTo:带Async的是异步发送,不会卡住界面。 同步版在网络不畅时会让 App 明显卡一下。所有发送方法都有这一对。⚠️
AfterXmit的Success只代表”数据包扔出去了”,不代表对方收到了。 UDP 没有送达确认 —— 这是协议本身的特性,不是扩展的缺陷。要确认送达, 得让对方回一条”收到”(应用层自己做)。
第 5 步:改一行变广播
把目标 IP 换成广播地址,同网段所有在监听这个端口的设备都会收到:
global 端口 = 5000
when 按钮_广播.Click() {
UDPXmitter1.XmitToAsync("255.255.255.255", 端口, join("大家好,我是 ", UDPListener1.LocalHost))
}
中途验证:点广播,所有在监听的设备(包括第三、第四台)都收到消息。
广播地址
255.255.255.255只在本地网段内传播,不会跑到外网去。 这是做「局域网设备发现」的标准手法:新设备上线先广播一声, 已有设备收到后用RemoteIP回复,双方就互相认识了。想让自己也收到自己的广播,把
DropSentToYourself设为false(默认丢弃)。
第 6 步:退出时停掉监听
when Screen1.BackPressed() {
UDPListener1.Stop()
}
不停会占着端口,下次进来
Start直接失败。这是这个实验最常见的”第二次就跑不通”。
五、排错表
| 现象 | 原因与处理 |
|---|---|
本机 IP 显示 127.0.0.1 或空 |
没连 WiFi,只开了移动数据。UDP 局域网通信必须连 WiFi |
| 监听启动就失败 | 端口被占(上次没 Stop);或端口小于 1024。换个 5000+ 的端口,或重启 App |
| 显示”已发送”但对方收不到 | ① 对方没点「开始监听」;② 两边端口不一致;③ 路由器开了 AP 隔离(很多公共 WiFi 默认开),换手机热点试一次就能确认;④ IP 填错 |
| 只有一个方向能通 | 通常是只有一边启动了监听。两边都要 Start |
| 收到的中文是乱码 | BinaryMode 被设成了 true。发文本时保持默认(false) |
| 广播收不到 | 部分路由器过滤广播包;改用对方的具体 IP 试。或对方 DropSentToYourself 影响自收 |
| App 发送时卡一下 | 用了同步的 XmitTo,换成 XmitToAsync |
六、下一步
- ByteArray 实验 —— 发二进制帧而不是文本, 和硬件通信时必用
- 把接收端接上 ESP32 或 Arduino, 就是最简单的局域网远程控制
- 完整示例工程:CLOUD_REMOTE_VIDEO_CAR.aia(视频小车)
相关文档
- UrsAI2UDP 拓展参考
- 低功耗蓝牙 BLE / 经典蓝牙 SPP:不走 WiFi 的另外两条路
扫码添加客服咨询