App Inventor 2 UrsAI2UDP 拓展 - UDP广播通信协议

« 返回首页 Iot 专题

拓展下载:

UrsAI2UDP.zip

demo下载:

CLOUD_REMOTE_VIDEO_CAR.aia

原作者开发动机

对于一个项目,应该开发一个与 ESP8266(项目)通信的 Android 应用程序。为了轻松开发应用程序,选择了 MIT App Inventor 2。

项目中可用的 ESP8266 设备的 IP 地址不应固定。应用程序本身应该确定哪些设备当前处于活动状态以及可以从哪些地址寻址它们。

要完成此任务(名称服务),您可以使用 UDP 的广播功能。您只需将广播数据报发送到先前约定的端口,发送到本地网络上的所有设备,请求它们的连接数据。在约定端口上监听的设备随后将其 IP 和可能的其他数据返回给发送者。发送者收集答案,从而知道所有活动设备。由于 UDP 数据包传递无法保证,因此最好重复此过程并使用响应的联合集。

为了能够有针对性地寻址项目的设备,您可以同意不同的端口,也可以在响应中输入设备标识符。然后,接收器会过滤掉它感兴趣的设备。

问题是:App Inventor 没有内置 UDP,而且我找不到可用的扩展。所以自己动手吧。

注意:

如果智能手机和远程站位于同一个(本地)网络中,则扩展可以完美运行。如果智能手机仅通过蜂窝网络连接,则通常无法访问。原因是智能手机不直接连接到互联网,而只连接到移动电话提供商的本地网络。

udp

您自己的 LAN 中的设备可以通过您自己的路由器上的端口转发从外部访问(如果需要,可以通过 DDNS 访问)。您将无法影响提供商的路由器。因此,从智能手机向 LAN 中的设备发送数据是可行的,而反过来则不行。

TCP 也是如此。如果智能手机仅连接到蜂窝网络,则只能从智能手机建立连接。LAN 中的设备必须充当服务器,等待传入的连接请求。智能手机充当客户端,即它发起连接。建立连接后,路由器会在内部协调地址和端口。然后可以双向传输数据。

注意:

在 UNIX 系统上,系统保留 1023(含)以下的端口号。这些限制已保留用于 LINUX,因此也用于 Android。使用这些端口号需要 root 授权。这同样适用于 Apple 的 iOS。

因此,此扩展无法使用低于 1024 的端口号运行。

重要提示:

某些通过 UDP 控制的设备会向发送方发送响应。使用接收数据包的地址作为目标地址。为了能够接收此数据包,应用程序的发送和接收地址必须相同。参数 LocalPort(= 发送数据的端口)不能保留默认设置 0,而必须进行相应设置。

用法

扩展包含两个组件。如果将其导入 App Inventor 项目,则“扩展”标题下会出现两个条目。

udp

  • UDPListener:该组件负责侦听传入的UDP数据报,并将其传递给App Inventor项目。通过UDPListener,开发者可以轻松接收来自其他设备或服务器的UDP数据,实现数据的实时处理和响应。

  • UDPXmitter:该组件用于发送UDP数据报。开发者可以通过配置目标IP地址和端口,将数据发送到指定的设备或服务器,实现与其他设备的通信。

应用场景参考

  • 物联网设备控制:通过WIFI UDP通信,可以实现对物联网设备的远程控制和数据采集。
  • 实时数据传输:在需要实时数据传输的应用中,如实时监控、实时游戏等,UDP通信可以提供高效的传输方式。
  • 智能家居系统:在智能家居系统中,通过UDP通信可以实现设备之间的快速响应和联动。

在技术应用中或控制设备时,通常需要使用二进制数据。在 4.3 版中,通过 UrsAI2ByteArray 扩展大大简化了二进制数据的发送和接收(另请参阅示例 UDPBinaryTest)。

传输数据报

已经实现了四种不同的方法来发送数据报。它们应该可以减少 App Inventor 中必要程序块的大小和数量。过多和过大的块很快就会使 AI2 项目变得混乱。

目标地址通常相同。对于这些情况,目标地址数据(RemoteHost、RemotePort)可以存储在设计器属性中。Xmit 和 XmitAsync 发送到存储在设计器中的地址。这样可以使块保持较小。实际上只需要一条“线”。

udp

XmitTo 和 XmitToAsync 允许独立指定目标地址。

这些方法在错误处理类型上也有所不同。以“… Async”结尾的方法使用 AfterXmit 和 XmitFailure 事件确认发送消息。每次传输后都会触发 AfterXmit。如果您只对错误情况感兴趣,请使用 XmitFailure 事件,该事件在发生错误时也会触发。其他两种方法将错误代码作为返回值返回。

通常不需要指定要从哪个本地端口发送数据报。如果在特殊情况下需要这样做,可以在设计器中定义本地端口号 (LocalPort)。如果值为“0”(默认值),系统会寻找一个空闲端口进行内部发送。

当然,每个块都可以覆盖所有设计器属性。

属性/参数

发送消息需要三个细节:目标地址、发送者地址和消息本身。网络地址由两个部分组成:IP(地址)和端口(编号)。端口通常连接到某个服务(另请参阅 Wikipedia:TCP 和 UDP 端口号列表)。

此扩展仅考虑 IPv4 地址。此地址是四个数字的组合,每个数字都在 0..255 范围内。完整的网络地址以“IP:端口”的形式给出,例如“192.168.178.35:2003”。

一些地址具有特殊含义。有关 UDP 地址方案,请参阅 UdpClient:地址解析/地址方案。有关 Java 中的端点,请参阅 UdpClient:地址解析/Java

在网络上相互通信的设备称为“端点”或“主机”(它们并不完全相同,但这两个术语通常被同等使用)。因此发送者被称为“本地主机”,而接收者被称为“远程主机”。

发送方地址

一般来说,无需担心发送方地址。如果您不输入任何内容,扩展程序将使用地址“0.0.0.0:0”。此地址意味着系统(更准确地说是“服务提供商”,即网络固件)可以选择一个可以到达目标地址的网络接口。它还会查找任何空闲端口。

如果您想在特殊情况下指定发送方地址,可以通过 LocalPort 属性执行此操作。Android 设备通常一次只有一个活动网络接口,移动数据连接或 WiFi。因此,指定本地网络接口的方式未实现。

目标地址

必须始终指定目标地址。这可以通过设计器属性 RemoteHostRemotePort 或作为 Xmit… 方法中的参数完成。

使用 RemoteHost,可以指定 IP 地址或收件人的名称(例如“www.google.de”)。扩展程序会尝试确定关联的 IP。对于设计器属性 RemoteHost,检索结果显示在属性 RemoteIP 中。如果无法确定 IP,则 RemoteIP 为空。然后无法发送到此主机(将导致 UnknownRemoteHost(代码 1)错误)。

由于 IP 地址很少更改,因此只有在输入名称时才能确定属于主机名的 IP。如果意外发生 UnknownRemoteHost(代码 1)错误,您可以尝试再次设置 RemoteHost 属性。然后重新确定关联的 IP 地址。

二进制模式

在 AI2 应用程序中,消息通常由文本组成。非文本信息转换为文本:

udp

数字转换成文字,收件人收到四位数字“1234”。

udp

列表被传输为 JSON 格式。接收方收到字符串 [1234, "Ulli"] 。

但是,某些接收方需要某些字节序列,例如字节 123(十六进制 7B)而不是字符串“123”。如果打开二进制模式,则消息将被解释为逗号分隔的字节规范序列,并且消息将作为字节序列发送(详细信息请参阅二进制数据)。

Xmit 字节数组

发送字节数组需要扩展 UrsAI2ByteArray。使用它可以轻松管理二进制序列。使用 XmitByteArray … 方法发送字节数组,并指定 UrsAI2ByteArray 组件。例如:

udp

XmitData 是 https://bbs.tsingfun.com/thread-1648-1-1.html 扩展的一个实例。这是块概述中的最后一项:

udp

发送到多播组

发送到多播组与“正常”发送没有什么不同。作为 RemoteHost,必须指定组的 IP 地址。

参考

代码块 功能 注解
属性
指定用于发送消息的端口号。 如果值为 0 或负数,Android 操作系统将确定一个空闲端口。

建议使用 默认值为 0。然后,操作系统将选择一个空闲端口进行发送。有效值为 0...65535。

此规范影响扩展实例的所有 Xmit... 方法。
定义(默认)目标地址。 可以指定 IP 地址或主机名。

此地址由 XmitXmitAsync 方法使用。
返回属于designer 属性 RemoteHost 如果无法确定 IP 地址,则返回空字符串。

发送数据报失败 (错误 UnknownRemoteHost (代码 1)。
定义(默认)目标端口号。 此端口由 XmitXmitAsync 方法使用。

有效值为 1...65535,但不应使用低于 1024 的端口号。这些数字中有许多是为特殊用途保留的。根据网络配置,使用它们可能会导致 问题。
指定是否应将发送的消息转换为字节数组。 有关二进制模式的详细信息,请参阅 二进制数据
返回所有网络接口的 IP 地址列表。 大多数情况下,此列表仅包含一个与 LocalHost 中的条目匹配的条目。
返回默认网络接口的 IP 地址。 如果无法建立与互联网的连接,则返回空字符串。

确切地说,尝试访问互联网地址“8.8.8.8”(Google 的公共 DNS 服务器)。Android 操作系统会为此选择合适的网络接口。
有关上次发生错误的信息。
LastAction:上次执行的方法的名称。
LastErrorCode错误代码
LastErrorMessage:简短的错误描述。
如果上次执行的操作没有发生错误,则 LastErrorCode 的值为 0,而 LastErrorMessage 返回一个空字符串。
方法
将消息发送到由 RemoteHostRemotePort 定义的目标地址。

返回值为错误代码
 
将消息发送到指定的目标地址。

返回值为错误代码
 
将消息发送到 RemoteHostRemotePort 定义的目标地址。

传输是否成功通过 AfterXmitXmitFailure 事件进行传达。
另请参阅 RemoteHostRemoteIP。  
将消息发送到指定的目标地址。

传输成功通过 AfterXmitXmitFailure 事件传达。
另请参阅 RemoteHostRemoteIP。  
ByteArray 发送到由 RemoteHostRemotePort 定义的目标地址。

返回值为 错误代码
 
ByteArray 到指定的目标地址。

返回值为 错误代码
 
ByteArray 发送到由 RemoteHostRemotePort 定义的目标地址。

传输是否成功通过 AfterXmitXmitFailure 事件进行传达。
另请参阅 RemoteHostRemoteIP。  
ByteArray 发送到指定的目标地址。

传输成功通过 AfterXmitXmitFailure 事件传达。
另请参阅 RemoteHostRemoteIP。  
事件
使用异步方法报告传输结果。

成功 :如果传输成功,则为 true,否则为 false
ErrorCode错误代码
触发事件在每次使用 XmitAsyncXmitToAsync 方法进行传输后。
如果使用异步方法发生传输错误,除了 AfterXmit 事件外,还会触发 XmitFailure

ErrorCode错误代码..
如果您只对错误感兴趣,则无需检查 AfterXmit 事件中的 Success

错误代码

代码 消息 含义
0   消息已成功发送。
1 未知远程主机 收件人的 IP 地址无效或无法确定。
2 本地端口无效 LocalPort 关联的消息无效或端口无效。
LocalPort 无效或端口正在使用中。
3 无效的远程端口  RemotePort 无效。
4 发送失败 传输失败。
5 二进制转换失败  无法将指定的字符串转换为字节数组。
8 无效的数据类型 指定的组件不是UrsAI2ByteArray类型.

接收数据报

数据报通过 UDPListener 组件接收。启动后,此组件会以连续循环的方式检查数据报包是否已到达。接收到的数据报通过事件 DataReceived 发布。

UDPListener 组件有两个方法 Start 和 Stop,分别用于启动和停止对 UDP 数据报的监听。有两个事件:DataReceived 在收到数据报时触发,ListenerFailure 表示接收循环因错误而终止。isRunning 属性可用于随时查询接收循环是否处于活动状态。

广播消息

UDP 允许发送广播消息。在这种情况下,还会接收应用程序本身发送的数据报。属性 DropSentToYourself 用于过滤掉发送者为您自己的 IP 地址的数据报。

多屏应用

打开第二个屏幕不会中断消息接收!第一个屏幕仍会接收事件 DataReceived 和 ListenerFailure。事件发生时,将执行与事件相关的块。如果不希望这样,则必须在打开第二个屏幕之前停止侦听器,并在返回后重新启动。

正在运行的侦听器也会阻止相关端口。在第二个屏幕中,不能在此端口上启动其他侦听器。如果要在与第一个屏幕相同的端口上接收数据报,则必须在打开第二个屏幕之前停止第一个屏幕的侦听器,并在第二个屏幕上再次打开。

扩展会在屏幕关闭时进行清理,即停止正在运行的侦听器。最终没有剩余的正在运行的侦听器。但清理会在后台延迟。

因此,OtherScreenClosed 事件发生在侦听器内部停止并再次释放端口之前。如果您想使用 OtherScreenClosed 事件(这是合适的),在第二个屏幕使用的同一端口上重新启动侦听器

udp

在关闭屏幕之前,第二个屏幕中的监听器必须停止。

在第二个屏幕上捕获 BackPressed 事件非常重要:

udp

二进制模式

在 AI2 应用程序中,消息通常由文本组成。但在某些情况下,数据报由任意字节序列(字节数组)组成。如果打开 BinaryMmode,则接收的字节不会直接转换为文本。各个字节以分号分隔的十进制数字表示。

如果侦听器收到内容(十六进制)为 48 61 6C 6C 6F 的数据报,这通常会被翻译成测试“Hallo”。如果打开二进制模式,则输出为“72;97;108;108;111”(详细信息请参阅下面的二进制数据)。

接收字节数组

要接收字节数组,必须为 ReceivingByteArray 属性分配 UrsAI2ByteArray 扩展的实例。这可以在设计器中或通过指令块完成。

udp

RcvData 是 UrsAI2ByteArray 扩展的一个实例。这是块概述中的最后一项:

udp

当 UDP 数据包(数据报)到达时,接收到的数据也会复制到指定的 UrsAI2ByteArray 实例中。然后触发 DataReceived 事件。只要代码执行在 DataReceived 事件中,数据仅在 UrsAI2ByteArray 组件中可用。如果退出事件方法,数据可能会被其他接收到的数据包覆盖。

接收多播数据包

StartMulticast 方法可用于接收多播数据包。扩展将自动加入指定的多播组。

udp

参考

代码块 功能 注解
属性
指定是否应将收到的消息解释为字节数组。 将收到的数据包转换为带有十进制数字序列的字符串。

有关二进制模式的详细信息,请参阅二进制数据
DropSentToYourself 属性控制接收广播数据报时的行为。默认情况下,该块设置为忽略从您自己的 IP 发送的消息。如果仍要接收这些数据,则必须将 DropSentToYourself 设置为 false 默认值为 true
指定 UrsAI2ByteArray 组件,其中接收的数据应以二进制形式提供。 只要代码执行在 DataReceived 事件中,数据仅在 UrsAI2ByteArray 组件中可用。如果退出事件方法,数据可能会被其他接收到的数据包覆盖。
删除 UrsAI2ByteArray 组件。  
属性 isRunning 可用于查询监听器当前是否处于活动状态。  
返回所有网络接口的 IP 地址列表。 大多数情况下,此列表仅包含一个与 LocalHost 中的条目匹配的条目。
返回默认网络接口的 IP 地址。 如果无法建立与互联网的连接,则返回空字符串。

确切地说,尝试访问互联网地址“8.8.8.8”(Google 的公共 DNS 服务器)。Android 操作系统会为此选择合适的网络接口。
有关上次发生错误的信息。
LastAction:上次执行的方法的名称。
LastErrorCode错误代码
LastErrorMessage:简短的错误描述。
如果上次执行的操作没有发生错误,则 LastErrorCode 的值为 0,而 LastErrorMessage 返回一个空字符串。
方法
Start 开始侦听发送到指定端口(UDP 服务器)的数据报。  如果启动不成功,则会触发 ListenerFailure 事件。

您不应使用低于 1024 的端口号。这些数字中的许多是为特殊目的保留的。根据网络配置,它们的使用可能会导致问题。
Start 开始侦听发送到指定端口(UDP 服务器)的数据报。 您不应使用低于 1024 的端口号。这些数字中的许多是为特殊目的保留的。根据网络配置,它们的使用可能会导致问题。
Stop 停止服务器。 多次调用 Stop 并不重要,也不会导致错误。
事件
如果监听器进程无法启动或由于错误而结束,则会触发 ListenerFailure 事件。

ErrorCode:终止原因。
原因可能是网络连接终止。
当收到数据报时,将触发 DataReceived 事件。

Data 包含数据报数据。RemoteIPRemotePort 是发送方地址。
 

错误代码

代码 消息 含义
0   无错误。
6 服务器线程中止 由于(网络)错误,服务器线程中止。
7 监听器已运行 此监听器已启动。
8 数据类型无效 指定的组件不是UrsAI2ByteArray类型.

二进制数据

在 4.3 版中,通过 UrsAI2ByteArray 扩展(见上文),二进制数据的发送和接收已得到显著简化。

传输

udp

如果将 BinaryMode 设置为 true,则 Xmit 方法将接受带有编码字节的字符串,这些字节以逗号 (‘,’) 或分号分隔。

对于十六进制输入,每个字节可以编码为“0xff”或“0xFF”或“0Xff”或“0XFF”或“#ff”或“#FF”,对于十进制输入,或“255”,对于八进制输入,或“0377”。

如果需要,您可以混合使用:“0xFF;255,#ff”有效。

您可以在数字前后插入空格:“0xFF;255,#ff”也有效。

尾随逗号或分号将被忽略:“0xFF;255,#ff”和“0xFF;255,#ff;”相同。

算法

此算法用于转换:

  1. 用分号替换所有逗号
  2. 使用分号拆分字符串
  3. 删除前导和尾随空格
  4. 使用 Integer.decode() 转换为整数
  5. 检查值是否小于 0 或大于 255。

接收

udp

如果将 BinaryMode 设置为 true,则接收到的数据包将被转换为以分号分隔的十进制数字字符串。

如果侦听器收到一个数据报,其内容(十六进制)为 48 61 6C 6C 6F,这通常会被转换为测试“Hallo”。如果打开二进制模式,则输出为“72;97;108;108;111”。您可以使用 text.Split 获取字节列表:

udp

UrsAI2UDP 拓展用法

udp 发送和接收文本 (URSAI2UDPTest)

此示例展示了如何发送和接收文本。

udp udp

示例中的块并不难理解。大多数块涉及输入项的验证。

udp 发送和接收字节数组 (UDPBinaryTest)

此示例展示了如何发送和接收字节数组。

udp

在数据字段中,要发送的字节被指定为由空格分隔的两位十六进制数字序列(不带前导“0x”!)。在示例中,这些是文本“Hello”的 ASCII 代码。在 ASCII 终端中,接收到的数据以文本表示形式显示。然后,ASCII 终端发送文本“Ulli”。ASCII 代码以十六进制格式显示在应用程序中。

使用以下过程将文本输入传输到 UrsAI2ByteArray 组件:

udp