物联网项目做多了,你会发现一个很有意思的现象:十个项目里,八个在协议选型上纠结过。尤其是刚入行的兄弟,面对 MQTT、TCP、HTTP 这三个词,往往是一脸懵——不都是传数据吗,有啥区别?随便选一个能跑通不就行了?
还真不行。我见过太多项目,前期为了图省事直接上 HTTP 轮询,结果设备一上量,服务器 CPU 直接拉满;也见过该用 MQTT 的场景硬套 TCP 长连接,自己造了一套心跳、重连、消息去重的轮子,最后维护成本高到想哭。协议选型这件事,选对了是事半功倍,选错了就是给自己挖坑,而且这个坑往往要到项目中期才会暴露出来,那时候改造成本已经很高了。
这篇内容我打算把这三个协议掰开揉碎了讲清楚。不是那种教科书式的罗列概念,而是从实际项目出发,告诉你每个协议到底适合什么场景、为什么适合、选型时该看哪些指标、踩过哪些坑。不管你是刚接触物联网开发的新手,还是正在做架构选型的老手,应该都能从中找到对自己有用的东西。核心关键词就三个:MQTT、TCP、HTTP,外加一个选型方法论。
1. 三个协议到底在解决什么问题
1.1 先搞清楚它们不在一个层面上
很多人纠结 MQTT、TCP、HTTP 选哪个,其实这个问题的问法本身就有点问题。因为它们并不完全在一个层面上。TCP 是传输层协议,HTTP 和 MQTT 都是应用层协议,而且 HTTP 底层通常就是跑在 TCP 上的,MQTT 底层也依赖 TCP。所以严格来说,不是“三选一”的关系,而是“你需不需要在 TCP 之上再套一层应用层协议,以及套哪一层”的问题。
打个比方。TCP 就像是一条公路,它保证货物能从 A 点运到 B 点,不丢、不乱序。但这条公路上跑什么车、车上装什么货、货物怎么编号,TCP 不管。HTTP 和 MQTT 就像是两种不同的物流体系:HTTP 是“快递上门”,你每次要寄东西都得重新填单子、建立连接、等对方签收;MQTT 是“订阅报纸”,你提前跟邮局说好我要哪类报纸,之后报纸到了直接投递给你,不用每次重新打招呼。
理解了这个层次关系,后面的选型逻辑就顺了。如果你只是两个设备之间点对点传数据,量不大,自己定义个简单的报文格式跑在 TCP 上完全够用。但如果你要面对成百上千台设备、要处理设备上下线、要保证消息可靠投递,那自己造轮子的成本就太高了,这时候就该考虑 MQTT 或 HTTP。
1.2 各自的核心特征一句话概括
我用最直白的话把三个协议的核心特征说一下,方便你快速建立印象。
TCP 的特点是可靠、面向连接、字节流。它保证数据按顺序到达,丢了会重传,但它是字节流而不是消息流,也就是说你发两次数据,对方可能一次收到,也可能分三次收到,边界需要你自己处理。这是很多人第一次用 TCP 时容易踩的坑。
HTTP 的特点是请求-响应、无状态、文本化。客户端不发请求,服务器就不会主动推数据。每次请求都要带上完整的头部信息,冗余比较大。但它的好处是生态极其成熟,调试方便,几乎任何语言、任何平台都支持。
MQTT 的特点是发布-订阅、长连接、轻量级。设备和服务端保持一条长连接,通过主题(Topic)来收发消息,支持一对多、多对多的通信模式。头部开销极小,最小报文只有 2 个字节。特别适合网络带宽有限、设备资源紧张的场景。
1.3 为什么选型这么重要
选型的重要性体现在三个维度:成本、可靠性和可维护性。
成本方面,HTTP 每次请求都要建立连接(虽然 HTTP/1.1 支持 Keep-Alive,但本质上还是请求-响应模式),在设备数量大、上报频率高的场景下,连接建立和断开的开销会迅速累积。MQTT 的长连接模式在这方面的开销就小得多。
可靠性方面,MQTT 内置了 QoS(服务质量)等级机制,分 0、1、2 三档,分别对应“最多一次”“至少一次”“恰好一次”。HTTP 本身没有这种机制,需要应用层自己保证。TCP 虽然保证传输可靠,但连接断了之后的重连、消息补发都需要自己实现。
可维护性方面,HTTP 的调试工具最丰富,抓包、Postman、curl 都能用。MQTT 需要专门的客户端工具,比如 MQTTX 之类的。TCP 最底层,调试起来最麻烦,往往需要自己写测试工具。
提示:选型不是选“最好的”,而是选“最合适的”。一个只有 10 台设备、每分钟上报一次数据的内部系统,用 HTTP 完全没问题,没必要为了“先进”硬上 MQTT。
2. MQTT 协议深度拆解与实操要点
2.1 发布订阅模型到底怎么运转
MQTT 的核心是发布-订阅模型,这里面有三个角色:发布者(Publisher)、订阅者(Subscriber)和代理服务器(Broker)。发布者把消息发到某个主题上,Broker 负责把消息转发给所有订阅了这个主题的客户端。发布者和订阅者互相不知道对方的存在,完全解耦。
这个模型的好处非常明显。第一是解耦,设备只管往主题发数据,不用关心谁在消费。第二是扩展性,新增一个消费者只需要订阅对应主题就行,不用改发布者的代码。第三是天然支持一对多,一条消息可以同时推给多个订阅者。
主题的设计是 MQTT 使用中的关键。主题是分层级的,用斜杠分隔,比如home/livingroom/temperature。支持通配符,+匹配单层,#匹配多层。home/+/temperature能匹配home/livingroom/temperature和home/bedroom/temperature,但匹配不了home/livingroom/sensor1/temperature。home/#则能匹配home下面所有层级的主题。
主题设计有几个经验:层级不要太深,一般三到五层就够了;不要用#订阅所有主题,性能会很差;主题名要有意义,方便后期排查问题。
2.2 QoS 等级怎么选才不浪费
QoS 是 MQTT 最核心的机制之一,也是选型时最容易被忽视的地方。三个等级的区别如下:
| QoS 等级 | 语义 | 消息可能重复 | 消息可能丢失 | 开销 |
|---|---|---|---|---|
| 0 | 最多一次 | 否 | 是 | 最低 |
| 1 | 至少一次 | 是 | 否 | 中等 |
| 2 | 恰好一次 | 否 | 否 | 最高 |
QoS 0 就是发出去不管了,适合那些丢一两条无所谓的场景,比如周期性上报的温度数据,丢一个点不影响整体趋势。QoS 1 保证消息至少到达一次,但可能重复,适合大多数业务场景,比如设备状态上报,重复了做幂等处理就行。QoS 2 保证恰好一次,开销最大,需要四次握手,适合那些绝对不能重复也不能丢的场景,比如计费、开关控制指令。
实际项目中,我见过太多人无脑用 QoS 2,觉得最安全。结果设备一多,Broker 的 CPU 直接飙高。其实大部分场景 QoS 1 就够了,配合应用层的幂等处理,效果一样好,开销小很多。
2.3 连接保活与断线重连的坑
MQTT 的长连接需要保活机制。客户端在连接时会设置一个 Keep Alive 时间,单位是秒。如果在这个时间内没有发送任何报文,客户端需要发一个 PINGREQ 心跳包,Broker 回复 PINGRESP。如果 Broker 在 1.5 倍的 Keep Alive 时间内没收到任何报文,就会认为客户端离线,触发遗嘱消息。
Keep Alive 的设置是个权衡。设太短,心跳包太频繁,浪费带宽和电量;设太长,设备异常离线后,服务端要很久才能感知到。一般建议设 60 秒左右,移动网络场景可以适当放宽到 120 秒。
断线重连是另一个大坑。MQTT 客户端断线后,需要自己实现重连逻辑。重连时要注意几点:重连要有退避策略,不能一直高频重试,否则会把 Broker 打挂;重连成功后要重新订阅之前的主题;如果用了 Clean Session 为 false 的会话,Broker 会保留离线期间的消息,重连后会自动推送。
注意:Clean Session 设置为 false 时,Broker 会为客户端保存会话状态,包括订阅关系和未确认的消息。但这也意味着 Broker 需要为每个离线客户端维护状态,设备数量大时对 Broker 内存压力不小。要根据实际情况权衡。
2.4 一个完整的 MQTT 接入示例
下面用 Python 的 paho-mqtt 库演示一个完整的设备接入流程,包含连接、订阅、发布和断线重连。
import paho.mqtt.client as mqtt import time import json BROKER = "broker.example.com" PORT = 1883 CLIENT_ID = "device_001" TOPIC_REPORT = "device/001/report" TOPIC_CMD = "device/001/cmd" def on_connect(client, userdata, flags, rc): if rc == 0: print("连接成功") client.subscribe(TOPIC_CMD, qos=1) else: print(f"连接失败,返回码:{rc}") def on_message(client, userdata, msg): print(f"收到指令:{msg.topic} -> {msg.payload.decode()}") payload = json.loads(msg.payload.decode()) if payload.get("action") == "reboot": print("执行重启操作") def on_disconnect(client, userdata, rc): print(f"连接断开,返回码:{rc}") while True: try: client.reconnect() break except Exception as e: print(f"重连失败:{e},5秒后重试") time.sleep(5) client = mqtt.Client(client_id=CLIENT_ID, clean_session=False) client.on_connect = on_connect client.on_message = on_message client.on_disconnect = on_disconnect client.connect(BROKER, PORT, keepalive=60) client.loop_start() while True: data = {"temp": 25.3, "humidity": 60, "ts": int(time.time())} client.publish(TOPIC_REPORT, json.dumps(data), qos=1) time.sleep(10)这段代码里有几个关键点值得说明。clean_session=False让 Broker 保留会话,断线重连后能收到离线消息。loop_start()启动一个后台线程处理网络循环,不阻塞主线程。重连逻辑放在on_disconnect回调里,用循环加延时的方式实现退避。
2.5 MQTT 的适用边界在哪里
MQTT 不是万能的。它适合设备数量多、网络不稳定、需要服务端主动推送的场景。但它也有明显的短板。
第一,MQTT 不适合传输大文件。虽然协议本身没有限制报文大小,但 Broker 通常有最大报文限制,而且大报文会阻塞连接,影响其他消息的实时性。传文件建议用 HTTP 或专门的方案。
第二,MQTT 的请求-响应模式支持得不好。虽然可以用两个主题模拟请求和响应,但用起来比较别扭。如果你的业务大量是“客户端问、服务端答”的模式,HTTP 可能更自然。
第三,MQTT 的调试相对麻烦。没有 HTTP 那么直观的工具链,排查问题需要专门的客户端和 Broker 日志。
3. TCP 直连的适用场景与自建协议实践
3.1 什么时候该考虑裸 TCP
裸 TCP 听起来很底层,但在某些场景下反而是最优解。什么场景?我总结了几类。
第一类是私有协议、极致性能的场景。比如某些工业设备,通信协议是厂商自定义的二进制格式,报文很短,频率很高,用 HTTP 或 MQTT 的头部开销都显得浪费。这时候直接在 TCP 上跑自定义协议最合适。
第二类是点对点、固定连接的场景。比如两个服务之间需要长期保持一条通道,传输的数据格式自己完全可控,不需要发布订阅、不需要一对多,那裸 TCP 就够了。
第三类是对延迟极度敏感的场景。TCP 没有应用层的额外握手和头部开销,理论上延迟最低。当然这个优势在大多数场景下并不明显,因为网络本身的延迟往往才是大头。
3.2 自定义协议设计的核心要素
如果你决定用裸 TCP,那自定义协议的设计就是核心工作。一个完整的自定义协议通常包含这几个部分:魔数、版本号、消息类型、消息长度、消息体、校验码。
魔数用来快速识别报文边界,防止解析错乱。版本号方便协议升级。消息类型区分不同的业务指令。消息长度告诉接收方要读多少字节。校验码保证数据完整性。
这里有个关键问题:TCP 是字节流,没有消息边界。你发两次,对方可能一次收到,也可能分三次收到。所以必须在协议里明确长度字段,接收方根据长度来切分消息。这是新手最容易踩的坑,没有之一。
一个典型的报文结构长这样:
+--------+--------+--------+--------+--------+--------+ | 魔数(2) | 版本(1) | 类型(1) | 长度(4) | 消息体(N) | 校验(2) | +--------+--------+--------+--------+--------+--------+魔数固定为 0xEB90 之类的值,长度字段表示消息体的字节数,接收方先读固定长度的头部,解析出长度,再读对应长度的消息体。
3.3 粘包拆包的处理方案
粘包和拆包是 TCP 编程绕不开的问题。粘包是指发送方发的多条消息被接收方一次读出来了,拆包是指一条消息被分多次读出来。根本原因就是 TCP 是字节流,没有消息边界。
处理方案主要有三种。第一种是固定长度,每条消息长度固定,接收方按固定长度读。简单但浪费带宽,适合消息长度固定的场景。第二种是分隔符,用特殊字符分隔消息,比如换行符。简单但消息体里不能出现分隔符,需要转义。第三种是长度字段,在头部放一个长度字段,接收方先读头部再读消息体。最通用,推荐使用。
实际项目中,长度字段方案用得最多。实现时要注意,读头部和读消息体要分开处理,因为一次 read 调用可能只读到半个头部。通常的做法是维护一个缓冲区,不断从 socket 读数据追加到缓冲区,然后尝试从缓冲区解析完整消息,解析出来就消费掉,剩下的留着下次继续。
3.4 心跳与重连的自建方案
裸 TCP 没有内置的心跳机制。连接断了,如果不主动检测,可能很久都发现不了。所以需要自己实现心跳。
心跳的实现方式有两种。一种是应用层心跳,客户端定时发一个心跳包,服务端收到后回复。如果连续几个心跳没收到回复,就认为连接断了。另一种是 TCP Keep-Alive,利用操作系统层面的保活机制。但 TCP Keep-Alive 的默认时间很长(通常两小时),而且需要修改系统参数,不太灵活。所以应用层心跳更常用。
重连策略也要设计好。不能一断就疯狂重连,要有退避。常见的做法是指数退避,第一次等 1 秒,第二次等 2 秒,第三次等 4 秒,以此类推,设一个上限比如 60 秒。这样既能快速恢复,又不会在服务端故障时造成雪崩。
3.5 裸 TCP 的维护成本
裸 TCP 最大的问题不是开发,而是维护。你造了一套协议,就要维护协议的文档、测试工具、兼容性处理。设备端升级了协议版本,服务端要兼容老版本。出了问题,没有现成的工具排查,得自己写日志、抓包分析。
所以我的建议是,除非有明确的性能或场景需求,否则优先考虑成熟的应用层协议。裸 TCP 适合那些对性能有极致要求、或者协议已经固定的场景。如果只是图省事不想引入 MQTT,那这个“省事”往往是假象,后期的维护成本会加倍还回来。
4. HTTP 在物联网中的角色与优化策略
4.1 HTTP 为什么在物联网中依然有一席之地
HTTP 在物联网领域经常被唱衰,说它太重、不适合物联网。但实际情况是,HTTP 在很多场景下依然是最优解。
第一,设备能力强的场景。比如智能摄像头、智能音箱这类设备,本身有不错的处理能力和内存,跑 HTTP 完全没压力。而且这些设备往往需要和云端做复杂的交互,HTTP 的请求-响应模式更自然。
第二,低频上报场景。比如每天上报一次数据的表计类设备,用 HTTP 完全够用,没必要为了省那点开销引入 MQTT。
第三,与现有系统集成。很多企业的后端系统本身就是 HTTP 接口,设备直接调 HTTP 接口,省去了中间层的转换。
第四,调试和运维方便。HTTP 的工具链太成熟了,curl、Postman、浏览器都能用,排查问题非常方便。
4.2 轮询模式的代价与优化
HTTP 在物联网中最常见的用法是轮询,设备定时向服务端发请求拉取指令或上报数据。轮询的问题在于,如果轮询频率高,服务端压力大;如果频率低,指令下发不及时。
优化轮询有几个思路。第一是长轮询,客户端发请求后,服务端不立即返回,而是 hold 住连接,等到有数据或者超时才返回。这样既减少了无效请求,又保证了实时性。第二是自适应轮询,根据业务情况动态调整轮询间隔,比如白天频繁、夜间稀疏。第三是合并请求,把多个小请求合并成一个大请求,减少连接建立的开销。
4.3 HTTP/2 与 HTTP/3 带来的改变
HTTP/2 引入了多路复用,一条连接上可以并行处理多个请求,解决了 HTTP/1.1 的队头阻塞问题。对于需要频繁请求的设备来说,HTTP/2 能显著减少连接建立的开销。HTTP/2 还支持头部压缩,减少了冗余的头部传输。
HTTP/3 基于 QUIC 协议,用 UDP 替代 TCP,进一步减少了连接建立的延迟,而且在网络切换时能保持连接不中断。对于移动网络下的设备,HTTP/3 的体验提升很明显。
不过要注意,HTTP/2 和 HTTP/3 的普及度还不如 HTTP/1.1,设备端和服务端的支持情况需要确认。而且 HTTP/2 的多路复用虽然好,但在丢包率高的网络下,TCP 层的队头阻塞依然存在,这时候 HTTP/3 的优势才真正体现出来。
4.4 HTTP 接口设计的实操建议
如果决定用 HTTP,接口设计有几个建议。
第一,用 RESTful 风格,资源用名词,操作用 HTTP 方法。比如上报数据用 POST /devices/{id}/data,获取指令用 GET /devices/{id}/commands。
第二,控制单次请求的数据量。不要一次传几百条数据,分批传,避免请求超时。
第三,做好幂等设计。网络抖动可能导致请求重发,服务端要能处理重复请求。可以用请求 ID 做去重。
第四,合理设置超时和重试。超时时间不能太长,否则设备会卡住;重试要有次数限制和退避策略。
第五,压缩传输数据。如果数据量大,开启 gzip 压缩能显著减少传输量。
4.5 HTTP 与 MQTT 的混合架构
实际项目中,HTTP 和 MQTT 并不是非此即彼的关系,很多项目是混合使用的。常见的模式是:设备用 MQTT 上报实时数据,用 HTTP 做配置管理、固件升级、历史数据查询。
这种混合架构的好处是各取所长。MQTT 负责实时性要求高的数据通道,HTTP 负责请求-响应式的业务操作。设备端需要同时支持两种协议,但现在的物联网模组和 SDK 通常都支持,成本增加有限。
5. 选型决策框架与实战对照
5.1 一张表看清选型维度
选型时要从多个维度综合评估,我整理了一个对照表:
| 维度 | MQTT | TCP 直连 | HTTP |
|---|---|---|---|
| 通信模式 | 发布订阅 | 点对点 | 请求响应 |
| 实时性 | 高 | 高 | 低(轮询) |
| 头部开销 | 极小(2字节起) | 自定义 | 较大 |
| 设备数量支持 | 海量 | 中等 | 中等 |
| 服务端推送 | 原生支持 | 需自建 | 不支持 |
| 断线重连 | 需实现 | 需实现 | 无需 |
| 调试难度 | 中等 | 高 | 低 |
| 生态成熟度 | 高 | 低 | 极高 |
| 适合场景 | 海量设备、实时推送 | 私有协议、极致性能 | 低频、强交互 |
5.2 按场景快速决策
如果你不想看那么多分析,就想快速知道选哪个,可以按下面的场景对号入座。
选 MQTT 的场景:设备数量超过 100 台;需要服务端主动下发指令;网络不稳定,需要断线重连和消息补发;设备资源有限,需要省流量省电;需要一对多的消息分发。
选 TCP 直连的场景:通信协议是厂商固定的二进制协议;对延迟和吞吐有极致要求;两个服务之间点对点通信;不需要发布订阅和一对多。
选 HTTP 的场景:设备数量少、上报频率低;设备能力强,不差那点开销;需要和现有 HTTP 系统集成;开发调试优先,追求快速上线。
5.3 混合选型的实战案例
举个实际项目的例子。某环境监测项目,有 500 台传感器设备,每分钟上报一次温湿度数据,同时需要支持服务端下发校准指令。
最初的方案是全部用 HTTP 轮询,设备每分钟发一次请求上报数据,同时拉取指令。上线后发现两个问题:一是服务端 QPS 在整点时刻飙升,因为设备都是整点上报;二是指令下发延迟高,最长要等一分钟。
后来改成混合方案:数据上报用 MQTT,QoS 1,设备通过 MQTT 主题上报数据;指令下发也用 MQTT,服务端往设备订阅的主题发指令,实时到达;配置管理、固件升级、历史数据查询用 HTTP。改造后,服务端压力下降明显,指令延迟从分钟级降到秒级。
这个案例说明,选型不是一次性的,随着业务发展,可能需要调整。关键是理解每个协议的特性,知道什么场景用什么。
5.4 选型时容易忽略的隐性成本
选型时大家往往关注显性指标,比如性能、开销,但隐性成本同样重要。
团队技术栈。如果团队没人用过 MQTT,引入 MQTT 意味着学习成本和踩坑成本。如果团队 HTTP 很熟,用 HTTP 可能整体效率更高。
运维复杂度。MQTT 需要维护 Broker,Broker 的高可用、监控、扩容都是额外工作。HTTP 服务端的运维相对成熟,方案多。
设备端限制。有些低端模组只支持 TCP 或 HTTP,不支持 MQTT。选型前要确认设备端的能力。
长期演进。协议选型要考虑未来三五年的业务发展。如果设备数量会从几百涨到几万,那从一开始就该考虑 MQTT 这类可扩展的方案。
6. 常见问题排查与避坑经验
6.1 MQTT 常见问题速查
| 问题现象 | 可能原因 | 排查方向 |
|---|---|---|
| 连接频繁断开 | Keep Alive 设置过短 | 检查网络质量,适当增大 Keep Alive |
| 消息丢失 | QoS 设为 0 | 提升 QoS 等级,检查 Broker 配置 |
| 消息重复 | QoS 设为 1 | 应用层做幂等处理 |
| 订阅收不到消息 | 主题不匹配 | 检查主题层级和通配符 |
| Broker CPU 高 | QoS 2 用太多 | 降级到 QoS 1,优化主题设计 |
| 内存持续增长 | Clean Session 为 false | 检查会话清理策略 |
6.2 TCP 粘包问题的排查思路
粘包问题的表现是:接收方解析出的消息错乱,或者解析到一半卡住。排查时先确认协议设计是否有长度字段,如果没有,那基本就是粘包问题。
排查步骤:第一,在接收方打印每次 read 到的字节数和内容,看是否和预期一致。第二,检查解析逻辑是否正确处理了半包情况。第三,用固定长度的测试数据验证,排除业务数据的干扰。
解决粘包的核心就是:维护缓冲区,按长度字段切分,不完整就等待下次数据。
6.3 HTTP 轮询导致服务端压力大的优化
如果发现服务端压力大,先看请求量。如果是轮询导致的,几个优化方向:增大轮询间隔,用长轮询替代短轮询,引入 MQTT 分流实时数据,用 CDN 或缓存减轻后端压力。
还有一个容易被忽视的点:设备的轮询时间要加随机抖动。如果所有设备都在整点轮询,会造成瞬间峰值。让每个设备的轮询时间随机偏移几秒,能把峰值摊平。
6.4 协议选型的几个反直觉经验
最后分享几个我在实际项目中总结的反直觉经验。
第一,不是越新越好。MQTT 5.0 功能很强,但很多 Broker 和客户端对 5.0 的支持还不完善,用 3.1.1 反而更稳。
第二,不是越省越好。有人为了省流量,把 QoS 设成 0,结果关键数据丢了,排查半天。该花的开销要花。
第三,不是越统一越好。有的团队追求技术栈统一,所有场景都用一种协议,结果在某些场景下很别扭。混合使用往往是最优解。
第四,测试环境跑通不代表生产没问题。测试环境设备少、网络好,很多问题暴露不出来。上线前一定要做压力测试和弱网测试。
提示:弱网测试可以用网络模拟工具,模拟高延迟、丢包、断网的场景,验证设备的重连和消息补发逻辑。这一步很多团队会跳过,但往往是生产事故的根源。
6.5 一个真实的踩坑记录
说一个我亲身经历的坑。某项目用 MQTT,设备端设置了 Clean Session 为 false,想着断线后能收到离线消息。结果上线一段时间后,Broker 内存持续增长,最后 OOM 了。
排查发现,有些设备上线后很快就离线了,而且再也没上线。这些设备的会话一直保留在 Broker 上,订阅关系和未确认消息越积越多。Broker 默认的会话过期时间很长,导致内存被慢慢吃光。
解决办法是设置合理的会话过期时间,对于长时间离线的设备,主动清理会话。MQTT 5.0 支持设置 Session Expiry Interval,3.1.1 则需要 Broker 端配置。这个坑告诉我们,任何机制都有代价,用之前要想清楚边界。
协议选型这件事,说到底是对业务场景的理解。你得清楚你的设备有多少、网络怎么样、实时性要求多高、团队技术栈是什么。把这些想清楚了,选型自然就清晰了。我个人的习惯是,先按最简方案来,HTTP 能搞定就用 HTTP,搞不定再上 MQTT,实在有特殊需求才考虑裸 TCP。这样能避免过度设计,也能让项目快速跑起来。