MQTT/mosquitto大文件传输遇4MB限制:能否传150MB文件?
我的目标是通过Mosquitto传输约150MB的文件,目前暂不考虑传输效率,仅需确认该传输是否可行。
MQTT规范提到消息payload上限为256MB,但我仅能成功传输最大4095KB的文件。发布端基于C语言Mosquitto库实现,动态分配存储二进制数据的vector。发布程序未报错,但当文件大小超过4065KB时,Broker端无发布通知,mosquitto_sub也无法接收消息,仅显示发布客户端成功断开连接:
1711360340: New connection from 127.0.0.1:50002 on port 1883. 1711360340: New client connected from 127.0.0.1:50002 as auto-A51C5B5B-F445-5952-5EA5-49B12F2E614D (p2, c1, k5). 1711360340: No will message specified. 1711360340: Sending CONNACK to auto-A51C5B5B-F445-5952-5EA5-49B12F2E614D (0, 0) 1711360340: Client auto-A51C5B5B-F445-5952-5EA5-49B12F2E614D disconnected: Success.
补充代码
发布端实现代码:
#include <cstdio> #include <mosquitto.h> #include <iostream> #include <fstream> #include <vector> #include <memory> struct mosquitto *mosq; void publishImage(const char *topic, const char *image_path) { std::ifstream fs(image_path, std::ios::in | std::ios::binary | std::ios::app); std::unique_ptr<std::vector<uint8_t>> v_buf; void *msgPayload = nullptr; int msgPayloadLen; if (fs.good()) { v_buf.reset(new std::vector<uint8_t>((std::istreambuf_iterator<char>(fs)), (std::istreambuf_iterator<char>()))); fs.close(); msgPayload = static_cast<void*>(v_buf.get()->data()); msgPayloadLen = v_buf.get()->size(); } if(msgPayload == nullptr) { fprintf(stderr, "image could not be copied to memory\n"); return; } int rc = mosquitto_publish(mosq, NULL, topic, msgPayloadLen, msgPayload, 1, false); if (rc != MOSQ_ERR_SUCCESS) { fprintf(stderr, "mosquitto publish error: %s\n", mosquitto_strerror(rc)); } return; }; int main(int argc, const char* argv[]) { if (argc < 2) { fprintf(stderr, "Not enough input arguments!\nUsage: imageUpdater <topic> <path_to_image>\n"); return 1; } const char *topic = argv[1]; const char *pathToImage = argv[2]; mosquitto_lib_init(); mosq = mosquitto_new(NULL, true, NULL); if (mosq == NULL) { fprintf(stderr, "Error: Out of memory.\n"); return 1; } int rc = mosquitto_connect(mosq, "127.0.0.1", 1883, 5); if (rc != MOSQ_ERR_SUCCESS) { mosquitto_destroy(mosq); fprintf(stderr, "Error: %s\n", mosquitto_strerror(rc)); return 1; } else { printf("Connected!\n"); } rc = mosquitto_loop_start(mosq); if (rc != MOSQ_ERR_SUCCESS) { mosquitto_destroy(mosq); fprintf(stderr, "Error: %s\n", mosquitto_strerror(rc)); return 1; } publishImage(topic, pathToImage); mosquitto_lib_cleanup(); return 0; }
补充日志:成功发布小文件的情况
1711376922: New client connected from 127.0.0.1:38426 as auto-33E25100-81EB-0583-E540-C73A5D29E267 (p2, c1, k5). 1711376922: No will message specified. 1711376922: Sending CONNACK to auto-33E25100-81EB-0583-E540-C73A5D29E267 (0, 0) 1711376922: Received PUBLISH from auto-33E25100-81EB-0583-E540-C73A5D29E267 (d0, q1, r0, m1, 'fota/update', ... (4192256 bytes)) 1711376922: Sending PUBACK to auto-33E25100-81EB-0583-E540-C73A5D29E267 (m1, rc0) 1711376922: Client auto-33E25100-81EB-0583-E540-C73A5D29E267 closed its connection.
额外测试发现:使用QoS0替代QoS1可传输更大文件,但可靠性极低,10MB文件尝试5次才成功;QoS1多次尝试后也能传输10MB文件,但QoS0对4000KB左右文件的可靠性不如QoS1。
现询问:通过Mosquitto传输此类大文件是否可行?若可行,实现的最佳实践是什么?
1. 可行性结论
通过Mosquitto传输150MB文件完全可行,当前传输失败是因为客户端实现逻辑和Broker默认配置的限制,而非MQTT或Mosquitto本身的能力问题。
2. 当前代码的核心问题
代码在调用publishImage后立即执行mosquitto_lib_cleanup()并退出程序,但MQTT QoS1的消息需要等待Broker返回PUBACK确认后才算发布完成。大文件的网络传输+Broker处理需要更长时间,此时客户端直接退出会导致消息传输中断,Broker会直接断开连接,自然不会处理该消息。
3. 快速修复方案
3.1 等待消息确认完成
可以通过回调函数监听PUBACK,确保消息发送完成后再退出:
void on_publish(struct mosquitto *mosq, void *obj, int mid) { printf("消息ID %d 发布成功\n", mid); // 收到确认后标记可以退出 *(bool*)obj = true; } int main(int argc, const char* argv[]) { // ... 原有代码省略 ... bool publish_done = false; // 设置发布回调 mosquitto_publish_callback_set(mosq, on_publish); // 将标记变量绑定到客户端实例 mosquitto_user_data_set(mosq, &publish_done); publishImage(topic, pathToImage); // 循环等待直到收到发布确认 while(!publish_done) { mosquitto_loop(mosq, 100, 1); } mosquitto_lib_cleanup(); return 0; }
3.2 调整Mosquitto Broker配置
修改mosquitto.conf文件,调整以下参数后重启Broker:
message_size_limit 268435456:设置为256MB(匹配MQTT规范上限)max_inflight_messages 10:提升QoS1/2的消息处理队列容量max_queued_messages 100:增大Broker的消息队列,避免大消息阻塞
4. 大文件传输最佳实践
虽然单次传输大文件可行,但更推荐以下方案提升稳定性:
- 文件分片传输:将150MB文件拆分为多个小分片(比如每片1MB),按顺序发布,订阅端接收后重组。这种方式能避免单次大消息的内存压力,还支持断点续传。
- 添加分片标识:每个分片消息中包含总片数、当前片序号、文件唯一ID,方便订阅端校验和重组。
- 内存优化:避免一次性加载整个大文件到内存,分块读取文件并发布,降低客户端内存占用。
- 使用QoS1:依靠PUBACK机制保证每个分片的可靠传输,避免丢包。
5. 验证步骤
- 修改Mosquitto配置并重启服务
- 修复客户端代码,确保等待消息确认后退出
- 测试传输150MB文件,确认Broker和订阅端能正常接收
内容的提问来源于stack exchange,提问作者Antoni

