我的目标是能够通过
mosquitto
发送大约 150MB 的文件。这种转移的效率问题目前并不重要,只是看看这样的转移是否可能。
MQTT 规范提到消息有效负载限制为 256MB,但我只能发送最大为 4095KB 的文件,仅此而已。发布器是用 C mosquitto 库实现的,动态分配带有二进制数据的向量。发布者程序不会出现任何错误,但是在客户端发布数据后,如果发送的文件大于4065KB,则代理端不会出现发布通知,
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.
真的可以通过
mosquitto
发送那些大文件吗?如果是这样,实施方面的最佳实践是什么?
编辑:附加文件源代码
#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;
}
EDIT2:成功发布的日志输出(小文件)
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文件。对于 4000KB 左右的文件,QoS0 的可靠性不如 QoS1。
这里正确的解决方案可能是在
mosquitto_loop_stop()
之前使用
mosquitto_lib_cleanup()
正如文档所解释的,这应该等待未完成的消息发送后再退出。
这是线程客户端界面的一部分。调用一次以停止先前使用 mosquitto_loop_start 创建的网络线程。此调用将阻塞,直到网络线程完成。
文档:https://mosquitto.org/api/files/mosquitto-h.html#mosquitto_loop_stop
在
mosquitto_lib_cleanup
之前添加超时是解决方案。似乎在大型有效负载场景中,网络堆栈无法在清理调用之前处理整个有效负载。可能非阻塞循环(mosquitto 循环是用 mosquitto_start
启动的)根本没有等待包通过管道传输到网络中。