最近在做物联网相关的内容,本打算用socket去做,但对于硬件来说,需要一个体量小、精简、低带宽和不稳定的网络环境中提供可靠的网络服务、方便快捷,于是我在这方面优先选择了MQTT协议。
由于是做本地内网传输, Unity项目作为服务器,这还不是想怎么玩就怎么玩么。
简而言之、就是玩儿~
这块百度一搜都有了,我简单点说,划几个重点。
1.对于没做过服务器开发的人员来说,开发是真的简单。
2.防火墙穿透。
3.应用广泛,几乎现在随便找一家大型的硬件、互联网企业,都可以找到MQTT的身影,例如Facebook、alibaba、baidu等等。
4.无法做负载均衡。
5.同一局域网,有设备A和设备B分别建立本地服务器,在同一主题的情况下,客户端C连接设备A并监听,那么设备B能监听到是谁发送的,需要注意消息的安全性。
在unity上使用C# 版本的 下载链接:https://github.com/chkr1011/MQTTnethttps://github.com/chkr1011/MQTTnet
运行,找到MQTTnet项目,生成它!
我们就能在bin中找到这个dll
你们要啥子版本就从里面拿,这里我们使用net452/MQTTnet.dll
然后,老规矩塞到unity的plugins里面。
using MQTTnet;
using MQTTnet.Diagnostics.Logger;
using MQTTnet.Protocol;
using MQTTnet.Server;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
namespace MQTT
{
public class MqttServer
{
private IMqttServer m_MqttServer = null;
private List m_MqttClientObject = new List();
public Action MessageCallback = null;
public bool IsConnect
{
get
{
if (m_MqttServer == null)
return false;
return m_MqttServer.IsStarted;
}
}
public MqttServer()
{
m_MqttClientObject.Clear();
}
public void OpenMqttServer(int port = 3883)
{
var server_logger = new MqttNetEventLogger("server log");
server_logger.LogMessagePublished += (sender, e) =>
{
//Debug.Log(e.LogMessage.ToString());
};
m_MqttServer = new MqttFactory().CreateMqttServer(server_logger);
var optionbuilder = new MqttServerOptionsBuilder();
//默认端口是1883,这里可以自己设置
optionbuilder.WithDefaultEndpointPort(port);
optionbuilder.WithConnectionValidator(WithConnectionValidator);
m_MqttServer.StartedHandler = new MqttServerStartedHandlerDelegate(StartedHandler);
m_MqttServer.StoppedHandler = new MqttServerStoppedHandlerDelegate(StoppedHandler);
m_MqttServer.UseClientConnectedHandler(UseClientConnectedHandler);
m_MqttServer.UseClientDisconnectedHandler(UseClientDisconnectedHandler);
m_MqttServer.UseApplicationMessageReceivedHandler(UseApplicationMessageReceivedHandler);
m_MqttServer.ClientSubscribedTopicHandler = new MqttServerClientSubscribedTopicHandlerDelegate(ClientSubscribedTopicHandler);
m_MqttServer.ClientUnsubscribedTopicHandler = new MqttServerClientUnsubscribedTopicHandlerDelegate(ClientUnsubscribedTopicHandler);
m_MqttServer.StartAsync(optionbuilder.Build());
}
public void Disconnect()
{
m_MqttServer.StopAsync();
}
public async Task ConnectClientCount()
{
if (m_MqttServer == null) return 0;
var msgs = await m_MqttServer.GetClientStatusAsync();
return msgs.Count;
}
private void WithConnectionValidator(MqttConnectionValidatorContext context)
{
//这里可以校验连接client的username、password 也可以选择不校验
if (string.IsNullOrWhiteSpace(context.Username) || string.IsNullOrWhiteSpace(context.Password))
{
Debug.Log($"用户:{context.ClientId}登录失败,用户信息为空");
context.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
context.ReasonString = "登录失败,用户名或密码为空";
return;
}
if (context.Username.Equals("admin") && context.Password.Equals("123456"))
{
m_MqttClientObject.Add(new MqttClientObject() { ClientID = context.ClientId, UserName = context.Username, PassWord = context.Password });
context.ReasonCode = MqttConnectReasonCode.Success;
context.ReasonString = "登录成功";
}
}
private void StartedHandler(EventArgs eventArgs)
{
Debug.Log("服务: started!");
Loom.RunAsync(() =>
{
Loom.QueueOnMainThread(() =>
{
MessageCallback?.Invoke("Start Server", "服务: started!");
});
});
}
private void StoppedHandler(EventArgs eventArgs)
{
Debug.Log("服务: stopped!");
}
///
/// 客户端连接消息
///
///
private void UseClientConnectedHandler(MqttServerClientConnectedEventArgs eventArgs)
{
Debug.Log($"服务:client Connected ClientId:{eventArgs.ClientId}");
Loom.RunAsync(() => { Loom.QueueOnMainThread(() => { MessageCallback?.Invoke(eventArgs.ClientId, "客户端已连接"); }); });
}
///
/// 客户端断开消息
///
///
private void UseClientDisconnectedHandler(MqttServerClientDisconnectedEventArgs eventArgs)
{
Debug.Log($"服务:client Disconnected ClientId:{eventArgs.ClientId} Type:{eventArgs.DisconnectType}");
for (int i = 0; i < m_MqttClientObject.Count; i++)
{
if (m_MqttClientObject[i].ClientID.Equals(eventArgs.ClientId))
{
m_MqttClientObject.RemoveAt(i);
break;
}
}
Loom.RunAsync(() => { Loom.QueueOnMainThread(() => { MessageCallback?.Invoke(eventArgs.ClientId, "客户端已断开"); }); });
}
///
/// 接收到的应用程序消息处理
///
///
private void UseApplicationMessageReceivedHandler(MqttApplicationMessageReceivedEventArgs eventArgs)
{
if (string.IsNullOrEmpty(eventArgs.ClientId)) return;
var msg = eventArgs.ApplicationMessage;
var topic = msg.Topic;
var payload = msg.ConvertPayloadToString();
Debug.Log($"服务:ClientId:'{eventArgs.ClientId}' Topic:'{topic}' Payload:'{payload}'");
Loom.RunAsync(() =>
{
Loom.QueueOnMainThread(() =>
{
MessageCallback?.Invoke(eventArgs.ClientId, payload);
});
});
}
///
/// 客户端订阅的主题
///
///
private void ClientSubscribedTopicHandler(MqttServerClientSubscribedTopicEventArgs eventArgs)
{
Debug.Log($"服务: topic subscribed ClientId:{eventArgs.ClientId} Topic:{eventArgs.TopicFilter}");
}
///
/// 客户端取消订阅的主题
///
///
private void ClientUnsubscribedTopicHandler(MqttServerClientUnsubscribedTopicEventArgs eventArgs)
{
Debug.Log($"服务: topic ubsubscribed ClientId:{eventArgs.ClientId} Topic:{eventArgs.TopicFilter}");
}
///
/// 推送所有的主题是clinetId的客户端
///
///
public void SendMessage(string message)
{
for (int i = 0; i < m_MqttClientObject.Count; i++)
{
m_MqttServer.PublishAsync(new MqttApplicationMessage
{
Topic = m_MqttClientObject[i].ClientID,
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce,
Retain = false,
Payload = Encoding.UTF8.GetBytes(message),
}, CancellationToken.None);
}
}
///
/// 推送监听某主题的客户端
///
///
///
public void SendMessage(string topic, string message)
{
m_MqttServer.PublishAsync(new MqttApplicationMessage
{
Topic = topic,
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce,
Retain = false,
Payload = Encoding.UTF8.GetBytes(message),
}, CancellationToken.None);
}
}
}
注意:
1.log我去掉了
2.WithConnectionValidator可以校验连接client的信息
3.如果回调中包含 赋值Unity主线程的代码,需要使用Loom配合的
4.PublishAsync, 服务器topic传递的是客户端监听的topic,
例如:
客户端
m_MqttClient.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("Data").Build());
服务器端
m_MqttServer.PublishAsync(new MqttApplicationMessage
{
Topic = "Data",
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce,
Retain = false,
Payload = Encoding.UTF8.GetBytes(message),
}, CancellationToken.None);
这样服务器推送的消息,这个客户端才能收到,否则是没有客户端能接到这个推送
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
using MQTTnet.Client.Options;
using MQTTnet.Client.Receiving;
using MQTTnet.Protocol;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using UnityEngine;
namespace MQTT
{
public class MqttClient
{
private IMqttClient m_MqttClient;
private string m_ClientID;
public Action MessageCallback = null;
public Action ConnectCallback = null;
public Action DisconnectCallback = null;
///
/// 推送下来的消息
///
public string Message
{
get;
private set;
}
public bool IsConnect
{
get
{
if (m_MqttClient == null) return false;
return m_MqttClient.IsConnected;
}
}
public string ClientId
{
get
{
return m_ClientID;
}
}
public MqttClient(string clientID, string ip = "127.0.0.1", int port = 3883)
{
m_ClientID = clientID;
var options = new MqttClientOptionsBuilder()
.WithClientId(m_ClientID)
.WithTcpServer(ip, port)
.WithCredentials("admin", "123456")
.Build();
m_MqttClient = new MqttFactory().CreateMqttClient();
m_MqttClient.ConnectedHandler = new MqttClientConnectedHandlerDelegate(MqttClient_Connected);
m_MqttClient.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(MqttClient_Disconnected);
//m_MqttClient.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(MqttClient_Recevied);
m_MqttClient.UseApplicationMessageReceivedHandler(MqttClient_Recevied);
m_MqttClient.ConnectAsync(options);
}
public void Disconnect()
{
if (m_MqttClient != null)
m_MqttClient.DisconnectAsync();
}
private void MqttClient_Connected(MqttClientConnectedEventArgs eventArgs)
{
Debug.Log($"mqtt client '{m_ClientID}' started!");
//关联服务端订阅, 用于接受服务端推送信息
if (m_MqttClient != null)
m_MqttClient.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic(m_ClientID).Build());
Loom.RunAsync(() => { Loom.QueueOnMainThread(() => { ConnectCallback?.Invoke(this); }); });
}
private void MqttClient_Disconnected(MqttClientDisconnectedEventArgs eventArgs)
{
Debug.Log($"mqtt client '{m_ClientID}' stopped!");
Loom.RunAsync(() => { Loom.QueueOnMainThread(() => { DisconnectCallback?.Invoke(this); }); });
}
private void MqttClient_Recevied(MqttApplicationMessageReceivedEventArgs eventArgs)
{
if (eventArgs.ApplicationMessage.Topic.Equals("Client")) return;
Message = Encoding.UTF8.GetString(eventArgs.ApplicationMessage.Payload);
Debug.Log($"mqtt client '{eventArgs.ClientId}' Topic:{eventArgs.ApplicationMessage.Topic} Recevied: '{Message}'");
Loom.RunAsync(() => { Loom.QueueOnMainThread(() => { MessageCallback?.Invoke(m_ClientID, Message); }); });
}
///
/// 客户端推送信息
///
///
///
public void SendMessage(string message)
{
if (m_MqttClient == null) return;
m_MqttClient.PublishAsync(new MqttApplicationMessage
{
Topic = "Client",
QualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce,
Retain = false,
Payload = Encoding.UTF8.GetBytes(message),
}, CancellationToken.None);
}
}
}
有兴趣的小伙伴可以关注一波
o(* ̄▽ ̄*)ブ