第 2 篇:Android 控制屏为什么与机器人主控使用 TCP 长连接?
上一篇,我们把服务型机器人的整体软件架构串了起来:
Android 控制屏
↓ TCP
Linux 机器人主控
↓ 串口 / CAN
MCU
↓
底盘、电机、灯光、柜门、传感器
其中:
Android 负责业务与人机交互;
Linux 主控负责机器人核心任务和设备控制;
MCU 负责更加底层的硬件执行。
那么接下来就会遇到一个非常实际的问题:
Android 控制屏和 Linux 机器人主控之间,到底应该怎么通信?
在实际服务机器人项目中,一个非常典型的方案就是:
Android 控制屏
↕
TCP 长连接
↕
Linux 机器人主控
通常由机器人主控启动 TCP Server,Android 控制屏作为 TCP Client 主动连接。
看到这里,很多人可能会产生疑问:
现在 HTTPS、WebSocket、MQTT 都已经非常成熟了,为什么机器人内部通信还会直接使用 TCP?
这一篇,我们就把这条链路彻底串起来。
一、先别急着选协议,先看 Android 和主控之间到底在传什么
Android 控制屏可能会向机器人发送很多控制指令,例如:
- 开始任务
- 暂停任务
- 继续任务
- 停止任务
- 前往某个点位
- 返回充电桩
- 打开柜门
- 关闭柜门
- 切换工作模式
而机器人主控也会不断向 Android 返回状态:
- 机器人当前状态
- 任务状态
- 导航状态
- 当前位置
- 当前点位
- 电量
- 设备状态
- 故障状态
- 指令执行结果
这就意味着:
Android 和机器人主控之间,并不是偶尔通信一次。
而是需要进行长期、持续、双向的通信。
例如:
用户点击:
前往会议室
Android 向主控发送:
GO_TO_POINT
pointId = meeting_room
主控接收到以后开始执行导航任务。
随后可能不断向 Android 返回:
NAVIGATION_START
↓
NAVIGATING
↓
NAVIGATING
↓
ARRIVED
Android 根据这些状态不断刷新界面。
所以这里天然存在一个需求:
Android 和机器人主控之间需要保持一条长期存在的双向通信通道。
这就是长连接。
二、为什么不直接使用 HTTP?
如果之前主要做普通 Android App,我们最熟悉的通信方式通常是:
Android App
↓
HTTPS
↓
Spring Boot / 业务服务器
例如:
GET /device/status
POST /task/start
HTTP 非常适合普通业务接口。
但是机器人内部控制场景有一点不一样。
假设 Android 需要实时知道机器人当前的运行状态。
如果只使用普通 HTTP,那么 Android 就需要不断询问:
Android:
机器人现在什么状态?
↓
主控:运行中。
过一会再问:
机器人现在什么状态?
↓
主控:运行中。
再过一会:
机器人现在什么状态?
↓
主控:已经到达。
这种方式其实就是: 轮询。
例如:
每 500ms 请求一次:
GET /robot/status
理论上当然可以做。
但是对于机器人内部这种高频、持续的状态通信来说,就显得比较笨重。
因为 Android 控制屏和 Linux 主控本身就在同一台机器人上。
两者之间通常处于稳定的局域网环境。
既然如此,与其不断:
请求
↓
响应
↓
等待
↓
再次请求
不如直接建立一条长期连接:
Android
↕
TCP
↕
Linux 主控
连接建立以后:哪一方有消息,哪一方直接发送。
三、TCP 为什么适合机器人内部通信?
TCP 有几个特点,非常适合这种场景。
1. 双向通信
TCP 连接建立以后,双方都可以主动发送数据。
例如:
Android → 主控:
START_TASK
主控 → Android:
TASK_STARTED
随后主控还可以继续主动发送:
TASK_RUNNING
TASK_PROGRESS
TASK_FINISHED
也就是说:Android 不需要不断询问机器人状态。
主控状态发生变化以后,可以主动推送给 Android。
2. 长连接
TCP 连接建立以后,只要双方不主动关闭,并且网络没有异常,就可以一直保持。
例如机器人开机:
Android App 启动
↓
连接 Linux 主控
↓
TCP Connected
↓
保持连接
↓
持续收发消息
机器人运行几个小时,这条连接都可以一直存在。
这非常适合机器人这种:
控制端和主控长期同时在线的场景。
3. 可靠、有序传输
TCP 会帮我们处理很多底层网络问题,例如:
- 数据重传
- 数据顺序
- 流量控制
- 拥塞控制
例如 Android 连续发送:
START_TASK
↓
PAUSE_TASK
↓
RESUME_TASK
TCP 能够保证应用层最终读取到的字节顺序与发送顺序一致。
这对于控制类业务非常重要。
不过这里一定要注意一个词:
字节流。
TCP 保证的是:
可靠、有序的字节流。
但 TCP 并不知道什么叫:
“一条机器人消息”。
这会直接引出 TCP 开发里一个非常重要的问题:
粘包和半包。
这个问题我们后面会单独拿一篇讲。
四、为什么通常是机器人主控做 TCP Server?
服务机器人中很常见的一种架构是:
Linux 主控:TCP Server
Android 控制屏:TCP Client
也就是:
Android
↓ connect()
Linux 主控 TCP Server
为什么通常这样设计?
因为 Linux 主控才是机器人真正长期运行的核心节点。
机器人开机以后,主控通常会一直运行,并负责:
- 导航
- 定位
- 任务
- 底盘
- 硬件
- 状态管理
- 设备通信
Android 控制屏更像是一个接入主控的业务终端。
所以主控启动:
TCP Server
监听一个固定端口,例如:
9000
Android 启动以后:
Socket(host, 9000)
主动连接主控。
整个关系就非常自然:
Linux 主控
提供机器人控制服务
↓
Android
连接机器人控制服务
这和我们平时连接服务器的思维其实非常类似。
五、Android 怎么知道机器人主控的 IP?
这里又会出现一个很现实的问题。
Android 既然是 TCP Client,那么:
它必须知道 TCP Server 在哪里。
也就是至少需要知道:IP Port
例如:
192.168.1.10:9000
在机器人产品中,这个问题通常不会像互联网 App 那么复杂。
因为 Android 控制屏和机器人主控往往属于同一台机器人。
两者之间的网络拓扑通常是固定的。
比如:
Android 控制屏
192.168.10.2
Linux 主控
192.168.10.1
那么 Android 就可以直接连接:
192.168.10.1:9000
所以这种通信环境和:
手机 → Internet → 云服务器
完全不同。
它更像:
两个长期固定存在的设备,在一个内部局域网里进行点对点通信。
这也是原生 TCP 非常适合这种场景的重要原因。
六、TCP 和 WebSocket 到底是什么关系?
很多 Android 开发第一次接触这块都会有一个疑问:
既然 WebSocket 也能长连接、双向通信,为什么不直接使用 WebSocket?
这个问题非常重要。
因为:
WebSocket 底层本身就是建立在 TCP 之上的。
可以简单理解成:
应用协议
WebSocket
↓
TCP
↓
IP
也就是说,WebSocket 并没有绕开 TCP。
它是在 TCP 上面又定义了一层标准化协议。
因此
原生 TCP:自己定义消息协议
WebSocket:在 TCP 之上使用标准 WebSocket 协议
比如原生 TCP 可以自己定义:
- 包头
- 长度
- 命令字
- Sequence
- Data
- Checksum
而 WebSocket 已经帮你定义了一套 Frame 结构。
所以二者真正的区别并不是:
“一个能长连接,一个不能。”
实际上两个都能。
真正的区别是:
你是否需要 WebSocket 这一层应用协议。
七、为什么机器人内部经常直接选择 TCP?
如果场景是:
Android 控制屏
↓
局域网
↓
Linux 机器人主控
而且双方都是自己开发的客户端和服务端,那么使用原生 TCP 往往非常自然。
因为这里并不需要解决浏览器兼容问题。
也不需要经过互联网中的各种 HTTP 基础设施。
系统通常就是:
一个 Android
对应
一个机器人主控
甚至网络环境本身都是固定的。
例如:
Android
192.168.1.2
↓
网线 / Wi-Fi / 内部网络
↓
Linux 主控
192.168.1.1
这种场景非常适合:
TCP + 自定义机器人协议。
八、什么时候 WebSocket 更合适?
这并不是说 WebSocket 不适合机器人。
WebSocket 很适合另外一种场景:
Web 管理后台
↓
互联网
↓
云平台
例如运营人员打开浏览器后台以后,需要实时查看:
- 机器人位置
- 任务状态
- 告警
- 电量
- 在线状态
这时:
Browser
↕
WebSocket
↕
Cloud Server
就非常合理。
- 因为浏览器天然支持 WebSocket。
- 而浏览器不能像普通 Native 程序一样随意创建原生 TCP Socket。
所以经常可以看到这样的组合:
Android 机身屏
↕ TCP
Linux 主控
手机 App
↕ HTTPS / MQTT / WebSocket
云平台
Web 后台
↕ WebSocket
云平台
并不是哪一种协议更高级。
而是:
不同通信场景选择不同的协议。
九、TCP 和 MQTT 又有什么区别?
还有一个很常见的问题:
既然机器人项目已经用了 MQTT,为什么 Android 控制屏和主控之间不直接 MQTT?
因为两者解决的问题不同。
TCP 更适合:点对点
Android ↔ 主控
双方明确知道对方是谁。
MQTT 更适合:
设备 ↔ 云平台
以及:
发布 / 订阅
例如:
Robot A
↓
MQTT Broker
↓
云平台
↓
手机 App
MQTT 中间通常存在: Broker。
而 Android 控制屏和机器人主控就在同一台机器人里。
=============================================================
如果仅仅为了:
Android ↔ 主控
再引入一个 Broker:
Android
↓
Broker
↓
主控
反而增加了系统复杂度。
=============================================================
所以:
机器人内部点对点通信:TCP
机器人连接云平台:MQTT
是一个非常常见的组合。
十、一条机器人 TCP 消息可能长什么样?
既然使用的是原生 TCP,那么应用层通常需要自己设计协议。
一个机器人消息可能类似:
- Header
- Length
- Command
- Sequence
- Data
- Checksum
例如:
AA55 | 0012 | 1001 | 000023 | DATA | CRC
其中:
Header
用于判断一个数据包从哪里开始。
Length
表示整个消息有多长。
Command
表示是什么指令。
例如:
1001 = START_TASK
1002 = STOP_TASK
1003 = GO_TO_POINT
Sequence
表示当前消息编号。
后面可以用于:
- 指令回执
- 超时
- 重试
- 消息匹配
- Data
则是真正的业务数据。
例如:
pointId = 123
这些设计已经不属于 TCP 本身了。
而属于:
机器人应用层通信协议。
这也是机器人通信开发非常有意思的地方。
TCP 只负责:把字节可靠地送过去。
至于:这些字节是什么意思?
需要应用层自己定义。
十一、建立 TCP 连接只是第一步
看到这里,可能会感觉:
Android 创建一个 Socket 不就完了吗?
其实真正的机器人 TCP 开发,难点才刚刚开始。
一个稳定的机器人 TCP 模块至少还要考虑:
- 连接状态
- 心跳保活
- 断线检测
- 自动重连
- 发送队列
- 消息接收
- 线程 / 协程模型
- 粘包
- 半包
- 协议解析
- 指令回执
- 超时
- 重试
- 重复消息
- 生命周期
- 网络切换
- 后台运行
所以:
Socket.connect()
可能只有几行代码。
但是一个真正稳定运行的机器人通信模块,背后其实是一套完整的连接管理系统。
十二、总结
Android 控制屏和机器人主控之间使用 TCP,并不是因为 TCP “比较底层”。
而是因为这个场景天然符合 TCP 的特点:
双方长期在线
↓
局域网点对点通信
↓
需要双向实时收发消息
↓
通信频率较高
↓
连接关系稳定
因此非常适合:
Android
↕
TCP 长连接
↕
Linux 机器人主控
而不同机器人通信场景,又会继续使用不同协议:
Android ↔ Linux 主控
TCP
Linux 主控 ↔ MCU
串口 / CAN
机器人 ↔ 云平台
MQTT / HTTPS
手机 / Web ↔ 云平台
HTTPS / WebSocket
实时音视频
WebRTC / RTC
到了这里,我们只是把:为什么选择 TCP 解释清楚了。
但真正开始写 TCP 代码以后,很快就会遇到另外一个问题:
- 机器人运行过程中网络断了怎么办?
- 连接什么时候算断开?
- 多久发送一次心跳?
- 重连应该一直疯狂重连吗?
- 消息发送的时候连接刚好断了怎么办?
这就是下一篇要解决的问题:
第三篇:《机器人 TCP 长连接如何稳定运行:心跳、掉线检测与自动重连》
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)