M2Mqtt断网重连

在使用M2Mqtt.dll这个控件时候,对于断网后,按照网上的说法,是处于broken状态,而且这个状态基本是不会主动改变的,在连接后,还是不能重新收发数据,而且这个控件当中,Ping检索是使用的1分钟检索一次,再次内部处理操作时,往往会超过5分钟左右,对 于现实开发的项目来说,非常不实用,方便。而且也查找了一些网上说的断网重连的方式,感觉要么是不用能,要么是缺少关键性的东西,所以,经过自己研究以后,修改了一下原代码并且加了外部处理,解决了断网重连的问题,下面分享一下,希望能够帮助到后续的使用人员。

首先,打开M2Mqtt这个项目,打开MqttClient这个类,修改KeepAliveThread()这个线程方法,将this.keepAliveEvent.WaitOne(wait);改为this.keepAliveEvent.WaitOne(1000);,这是要求等待时间从原来的60000改为1000,也就是1分钟改为1秒钟。修改注释掉if (delta >= this.keepAlivePeriod)这个语句,包括块else里的内容,一起注释掉,这个方法下面,加上this.Ping();这个是保证在不能保证时间是否超时之前,强行去执行Ping校验,通过校验结果判断是否关闭Mqtt。

第二,在外部调用时,在M2Mqtt连接成功时,开启一个线程,循环监控当前连接状态,如果连接断开,重新连接,这个时候,一定要记住当前连接的Clientid、user、password、订阅主题和发布主题等信息,以免不能够及时连接后,接收后续的消息。因为在mqtt判断ping连接断开后,mqtt内部会自动断开连接,所以这个时候,我们不用手动去断开连接,但重连时,mqtt会自动搜索当前的clientid,所以这个时间有些长,而且第一次时会抛出错误,第二次连接时,才会成功,这个具体机制没有研究。

下面分享一下代码,可以做一个参考:

MqttClient.cs内修改代码:

/// <summary>
        /// Thread for handling keep alive message
        /// </summary>
        private void KeepAliveThread()
        {
            int delta = 0;
            int wait = this.keepAlivePeriod;
            
            // create event to signal that current thread is end
            this.keepAliveEventEnd = new AutoResetEvent(false);

            while (this.isRunning)
            {
#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
                // waiting...
                this.keepAliveEvent.WaitOne(wait, false);
#else
                // waiting...
                this.keepAliveEvent.WaitOne(1000);//wait);
#endif

                if (this.isRunning)
                {
                    delta = Environment.TickCount - this.lastCommTime;

                    // if timeout exceeded ...
//                    if (delta >= this.keepAlivePeriod)
//                    {
//#if BROKER
//                        // client must close connection
//                        this.OnConnectionClosing();
//#else
//                        // ... send keep alive
//                        this.Ping();
//                        wait = this.keepAlivePeriod;
//#endif
//                    }
//                    else
//                    {
//                        // update waiting time
//                        wait = this.keepAlivePeriod - delta;
//                    }
                    this.Ping();
                    wait = this.keepAlivePeriod;
                }
            }

            // signal thread end
            this.keepAliveEventEnd.Set();
        }

外部调用代码:

mqttClient = new MqttClient(Address);

mqttClient.MqttMsgPublishReceived += mqttClient_MqttMsgPublishReceived;//发布主题后接收消息事件

string userName = UserName;
string passWord = PassWord;

try
                    {
                        string clientid = Guid.NewGuid().ToString();
                        byte res = mqttClient.Connect(clientid, userName, passWord);
                        MqttClientCheckedStatusEventArgs e = new MqttClientCheckedStatusEventArgs();
                        e.ClientId = clientid;
                        e.PassWord = passWord;
                        e.UserName = userName;
                        e.IsConnected = mqttClient.IsConnected;
                        e.Client = mqttClient;
                        if (res == 1)
                            throw new Exception("MQTT, 服务端不支持客户端请求的MQTT协议级别");
                        else if (res == 2)
                            throw new Exception("MQTT, 客户端标识符是正确的UTF-8编码,但服务端不允许使用");
                        else if (res == 3)
                            throw new Exception("MQTT, 网络连接已建立,但MQTT服务不可用");
                        else if (res == 4)
                            throw new Exception("MQTT, 无效的用户名或密码");
                        else if (res == 5)
                            throw new Exception("MQTT, 客户端未被授权连接到此服务器");
                        else if (res != 0)
                            throw new Exception("MQTT, 未知错误");
                        else
                        {
                            //"MQTT, 连接已被服务端接受"
                            string[] topics = ((List<string>)ma.SubscribeList).ToArray();
                            byte[] qoslevel = new byte[topics.Length];// { MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE };
                            e.Topics = topics;
                            e.Qoslevels = qoslevel;
                            for (int j = 0; j < qoslevel.Length; j++)
                            {
                                qoslevel[j] = MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE;
                            }
                            if (mqttClient.IsConnected)
                            {
                                mqttClient.Subscribe(topics, qoslevel);
                                //FileLibrary.LogInfo.WriteLog("订阅主题成功");
                            }
                            else
                            {
                                throw new Exception("与服务器未建立连接");
                            }
                        }
                        Tx.StartThread(new ParameterizedThreadStart(CheckedStatus), e);
                        break;
                    }
                    catch (Exception ex)
                    {
                        try
                        {
                            mqttClient.Disconnect();
                        }
                        catch { }
                        throw new Exception(ex.Message);
                    }

 

/// <summary>
        /// 检查状态改变事件
        /// </summary>
        /// <param name="sender"></param>
        private void CheckedStatus(object sender)
        {
            MqttClientCheckedStatusEventArgs e = sender as MqttClientCheckedStatusEventArgs;
            while (SourceLibrary.CommonParame.IsStart)
            {
                if (!e.Client.IsConnected)
                {
                    try
                    {
                        byte res = e.Client.Connect(e.ClientId, e.UserName, e.PassWord);
                        if (res == 0)
                        {
                            e.Client.Subscribe(e.Topics, e.Qoslevels);
                        }
                    }
                    catch(Exception ex) { }
                }
                if (MqttChangedStatusEvent != null)
                    MqttChangedStatusEvent.Invoke(this, e);
                Thread.Sleep(500);
            }
        }

 

需要的事件:

public class MqttReceivedMessageEventArgs : EventArgs
    {
        /// <summary>
        /// 收到的消息
        /// </summary>
        public string Message
        { get; set; }
        /// <summary>
        /// 返回的主题
        /// </summary>
        public string Topic
        { get; set; }
    }

    public class MqttClientCheckedStatusEventArgs : EventArgs
    {
        /// <summary>
        /// 连接状态
        /// </summary>
        public bool IsConnected
        { get; set; }
        /// <summary>
        /// 连接的用户名
        /// </summary>
        public string UserName
        { get; set; }
        /// <summary>
        /// 连接的密码
        /// </summary>
        public string PassWord
        { get; set; }
        /// <summary>
        /// 连接的客户端ID
        /// </summary>
        public string ClientId
        { get; set; }
        /// <summary>
        /// 连接的客户端
        /// </summary>
        public MqttClient Client
        { get; set; }
        /// <summary>
        /// 订阅主题
        /// </summary>
        public string[] Topics
        { get; set; }
        /// <summary>
        /// QOS_LEVEL_AT_MOST_ONCE
        /// </summary>
        public byte[] Qoslevels
        { get; set; }
    }

需要的委托:

/// <summary>
    /// 收到MQTT主题返回消息事件
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public delegate void MqttReveivedMessageEvent(object sender, MqttReceivedMessageEventArgs e);
    /// <summary>
    /// 检测MQTT状态
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    delegate void MqttCheckedClientStatusEvent(object sender, MqttClientCheckedStatusEventArgs e);

需要的事件:

/// <summary>
        /// 收到订阅主题返回消息事件
        /// </summary>
        public event MqttReveivedMessageEvent MqttReveivedMessageComTemplate;
        /// <summary>
        /// Mqtt连接状态改变事件
        /// </summary>
        public event MqttCheckedClientStatusEvent MqttChangedStatusEvent;

<think>好的,用户需要关于C语言实现MQTT协议在络中连接的解决方案或示例代码。首先,我需要回顾一下MQTT的基本知识,尤其是断开连的机制。根据引用[1],MQTT是一种轻量级协议,常用于物联设备通信,而断开连是保证可靠性的关键。用户可能已经了解Java的实现,现在需要C语言的方案。 接下来,我得确定用户的具体需求。他们可能在使用C语言开发嵌入式或资源受限的设备,需要高效的线连机制。引用[3]提到MQTT三元组(客户端ID、用户名、密码),这在连接时是必须的,所以代码中需要包含这些信息的处理。引用[4]提供了具体的连接参数示例,比如服务器地址、端口、客户端ID等,这些可以作为代码示例的参考。 然后,我需要考虑C语言中常用的MQTT库。常见的如Paho MQTT C库(Eclipse Paho)或者mosquitto库。这里选择Paho的嵌入式客户端库,因为更适合资源受限的环境,并且支持异步通信,适合处理络中的情况。 接下来是连机制的实现。根据引用[2]的流程图,连应该在检测断开后触发。在代码中,需要设置回调函数来检测连接丢失,然后启动连。需要处理指数退避策略,避免频繁连导致服务器压力过大。同时,需要确保在连时新订阅主题,否则可能无法接收消息。 然后,需要处理络状态检测。可能的方法包括心跳机制、定时发送PINGREQ,或者监听络事件。在C语言中,可以使用select或poll等系统调用来监控socket连接状态。一旦检测断开,立即尝试连。 代码示例部分需要包含初始化MQTT客户端、设置回调函数、连接服务器、订阅主题,以及处理断开事件。要注意错误处理和资源释放,避免内存泄漏。例如,在连接失败时,等待一段时间再试,并使用随机化的连间隔来防止多个设备同时连导致的服务器负载激增。 另外,用户可能需要了解如何维护会话状态,确保连后不会丢失之前的订阅信息。MQTT协议中,Clean Session标志的设置会影响这一点。如果设为0,服务器会保留会话,这样连后无需新订阅。但需要考虑服务器是否支持。 最后,要验证示例代码的正确性,可能需要测试断开和恢复的情况,确保自动连功能正常工作。同时,提醒用户在实际应用中添加日志记录,方便调试和监控连接状态。 总结一下,回答应包括选择适合的C语言MQTT库、连机制的具体实现步骤、代码示例、以及注意事项如指数退避和会话管理。需要引用提供的资料,特别是关于MQTT三元组和连接参数的部分,确保信息准确可靠。</think>### C语言MQTT连实现方案 #### 一、实现原理 MQTT协议通过心跳机制(Keep Alive)和持久会话(Clean Session)实现络中检测。C语言实现需依赖MQTT客户端库(如Eclipse Paho MQTT C库),通过以下核心机制实现连: 1. **连接状态回调**:注册`onConnectionLost`回调函数捕获连事件[^1] 2. **指数退避策略**:连间隔按$2^n$递增(如1s, 2s, 4s...)避免络风暴 3. **络状态检测**:通过TCP层心跳或应用层`MQTTAsync_isConnected()`检测连接状态 #### 二、代码实现(基于Paho MQTT C库) ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <MQTTClient.h> #define ADDRESS "tcp://122.112.225.194:1883" // 服务器地址[^4] #define CLIENTID "AAA" // 客户端ID #define USERNAME "test1" // 认证信息[^3] #define PASSWORD "12345678" #define TOPIC "BBB/#" // 订阅主题[^4] #define QOS 1 #define TIMEOUT 10000L volatile int reconnect_attempts = 0; void connection_lost(void *context, char *cause) { printf("连接丢失,原因: %s\n", cause); reconnect_attempts = 0; // 连计数器 } void reconnect(MQTTClient client) { int rc; while(1) { rc = MQTTClient_connect(client, NULL); if(rc != MQTTCLIENT_SUCCESS) { int delay = 1 << reconnect_attempts; // 指数退避 if(delay > 60) delay = 60; // 最大60秒 printf("连失败,%d秒后试...\n", delay); sleep(delay); reconnect_attempts++; } else { printf("连成功!\n"); MQTTClient_subscribe(client, TOPIC, QOS); // 新订阅[^2] break; } } } int main(int argc, char* argv[]) { MQTTClient client; MQTTClient_create(&client, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL); MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer; conn_opts.keepAliveInterval = 20; conn_opts.cleansession = 0; // 保留会话[^3] conn_opts.username = USERNAME; conn_opts.password = PASSWORD; MQTTClient_setCallbacks(client, NULL, connection_lost, NULL, NULL); while(1) { int rc = MQTTClient_connect(client, &conn_opts); if(rc != MQTTCLIENT_SUCCESS) { reconnect(client); } // 业务逻辑处理 sleep(1); } MQTTClient_disconnect(client, 10000); MQTTClient_destroy(&client); return 0; } ``` #### 三、关键优化点 1. **会话保持**:设置`cleansession=0`保留订阅信息,避免连后复订阅 2. **双检测**:结合TCP层RST包检测和应用层心跳超时 3. **资源释放**:连前调用`MQTTClient_disconnect()`释放socket资源 4. **主题恢复**:连成功后需新调用`MQTTClient_subscribe()`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值