UDP 实验 - 两台手机在局域网里互发消息

« 返回 UDP 拓展文档  IoT 专题

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

中途验证:这一步做完还看不到效果,要等下一步发送。

事件自带发送方的 RemoteIPRemotePort —— 这是 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 明显卡一下。所有发送方法都有这一对。

⚠️ AfterXmitSuccess 只代表”数据包扔出去了”,不代表对方收到了。 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

六、下一步


相关文档

文档反馈