在使用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;