1. AMQP 和 MQTT
MQTT(Message Queuing Telemetry Transport) 消息隊列遙測傳輸
AMQP(Advanced Message Queuing Protocol) 消息传递协议
1.1 AMQP
amqps就是加密传输,端口号用5671,如果是非加密传输,使用amqp,端口号为5672
特點:
獨立與平台底層消息傳遞協議消費者驅動消息隊列跨語言和平台的互用性5種交換類型 direct,fanout,topic,headers,system面向緩存的,可實現高性能,支持經典的消息隊列支持長週期消息傳遞。支持事務。
1.2 MQTT
計算性能不高的設備不能適應AMQP上的複雜操作,MQTT它是專門為小設備設計的,主要在物聯網中大量使用。
特點:
內存占用低,為小型無聲設備之間通過低帶寬發送短消息而設計不支持長週期存儲和轉發,不允許分段消息(很難發送長消息)支持主題發佈,訂閱,不支持事務簡單的用戶名和密碼,不支持安全連接,消息不透明
2. IPC-CFX
IPC-CFX:IPC Connected Factory Exchange
www.connectedfactoryexchange.com/CFXDemo/sdk…
工廠通信的即插即用的行業標準,設備不太需要關心數據發送到哪裡,來源於哪裡,只需要知道什麼時機發送數據,獲取數據後執行什麼操作
2.1 Publish/Subscribe发布订阅模式
AmqpCFXEndpoint endpoint = new AmqpCFXEndpoint();
endpoint.Open("Vendor1.Model1.Machine34");
// Encode your username and password into the destination Uri
string username = "myusername";
string password = "mypassword";
string hostname = "mycfxbroker.mydomain.com";
// eg. amqps://myusername:mypassword@mycfxbroker.mydomain.com
Uri uri = new Uri(string.Format("amqps://{0}:{1}@{2}", username, password, hostname));
// Target exchange on broker (shown here in RabbitMQ compatible format)
string amqpTarget = "/exchange/myexchange";
endpoint.AddPublishChannel(uri, amqpTarget);
// Source queue on broker (shown here in RabbitMQ compatible format)
string amqpSource = "/queue/myqueue";
endpoint.AddSubscribeChannel(uri, amqpSource);
2.2 Sender 点对点模式
endpoint.ExecuteRequest發佈一個直連的同步請求
string myCFXHandle = "Vendor1.Model1.Machine34";
AmqpCFXEndpoint endpoint = new AmqpCFXEndpoint();
endpoint.Open(myCFXHandle);
string targetEndpointHostname = "machine55.mydomain.com";
string targetCFXHandle = "Vendor2.Model2.Machine55";
string remoteUri = string.Format("amqp://{0}", targetEndpointHostname);
// Set a timeout of 20 seconds. If the target endpoint does not
// respond in this time, the request will time out.
AmqpCFXEndpoint.RequestTimeout = TimeSpan.FromSeconds(20);
// Build a GetEndpointInfomation Request
CFXEnvelope request = CFXEnvelope.FromCFXMessage(new GetEndpointInformationRequest()
{
CFXHandle = targetCFXHandle
});
CFXEnvelope response = endpoint.ExecuteRequest(remoteUri, request);
2.3 Receiver 点对单模式
AmqpCFXEndpoint endpoint;
string myCFXHandle = "Vendor1.Model1.Machine34";
Uri myRequestUri;
public void OpenEndpoint()
{
endpoint = new AmqpCFXEndpoint();
myRequestUri = new Uri(string.Format("amqp://{0}", System.Net.Dns.GetHostName()));
endpoint.OnRequestReceived += Endpoint_OnRequestReceived;
endpoint.Open(myCFXHandle, myRequestUri);
}
private CFXEnvelope Endpoint_OnRequestReceived(CFXEnvelope request)
{
// Process request. Return Result.
if (request.MessageBody is WhoIsThereRequest)
{
CFXEnvelope result = CFXEnvelope.FromCFXMessage(new WhoIsThereResponse()
{ CFXHandle = myCFXHandle, RequestNetworkUri = myRequestUri.ToString(), RequestTargetAddress = "" });
result.Source = myCFXHandle;
result.Target = request.Source;
return result;
}
return null;
}
使用该SDK就是添加发布订阅通道
theEndpoint.AddPublishChannel(new Uri(myPubBroker), myExchange);
theEndpoint.AddSubscribeChannel(new Uri(mySubBroker), myExchange);
theEndpoint.OnRequestReceived += TheEndpoint_OnRequestReceived;
theEndpoint.OnCFXMessageReceived += TheEndpoint_OnCFXMessageReceived;
theEndpoint.OnValidateCertificate += TheEndpoint_OnValidateCertificate;
theEndpoint.OnConnectionEvent += TheEndpoint_OnConnectionEvent;
theEndpoint.OnCFXMessageReceivedFromListener += TheEndpoint_OnCFXMessageReceivedFromListener;
2.4 Source Code
namespace CFX.Transport
{
public class AmqpCFXEndpoint : IDisposable
{
public void AddPublishChannel(AmqpChannelAddress address, string virtualHostName = null, X509Certificate certificate = null)
{
AddPublishChannel(address.Uri, address.Address, virtualHostName, certificate);
}
public void AddPublishChannel(Uri networkAddress, string address, string virtualHostName = null, X509Certificate certificate = null)
{
if (!IsOpen)
{
throw new Exception("The Endpoint must be open before adding or removing channels.");
}
string key = networkAddress.ToString();
AmqpConnection amqpConnection = null;
if (channels.ContainsKey(key))
{
amqpConnection = channels[key];
}
else
{
amqpConnection = new AmqpConnection(networkAddress, this, virtualHostName, certificate);
amqpConnection.OnCFXMessageReceived += Channel_OnCFXMessageReceived;
amqpConnection.OnValidateCertificate += Channel_OnValidateCertificate;
channels[key] = amqpConnection;
}
if (amqpConnection != null)
{
amqpConnection.AddPublishChannel(address);
if (HeartbeatFrequency != TimeSpan.Zero)
{
CFXEnvelope env = new CFXEnvelope(new Heartbeat
{
CFXHandle = CFXHandle,
HeartbeatFrequency = HeartbeatFrequency
});
FillSource(env);
amqpConnection.Publish(env);
}
}
}
public void PublishToChannel(CFXEnvelope env, AmqpChannelAddress address)
{
FillSource(env);
string key = address.Uri.ToString();
AmqpConnection value = null;
if (channels.TryGetValue(key, out value))
{
value.PublishToChannel(env, address.Address);
return;
}
throw new ArgumentException("There is no active publish channel that matches the specified channel address", "address");
}
public void PublishToChannel(CFXMessage msg, AmqpChannelAddress address)
{
CFXEnvelope cFXEnvelope = new CFXEnvelope();
cFXEnvelope.MessageBody = msg;
PublishToChannel(cFXEnvelope, address);
}
public void Publish(CFXEnvelope env)
{
FillSource(env);
foreach (AmqpConnection value in channels.Values)
{
value.Publish(env);
}
}
public void Publish(CFXMessage msg)
{
CFXEnvelope cFXEnvelope = new CFXEnvelope();
cFXEnvelope.MessageBody = msg;
FillSource(cFXEnvelope);
Publish(cFXEnvelope);
}
}
}
2.5 CFXEnvelope
3. Docker Install MQTT
1.安裝命令:
docker pull emqx/emqx
docker run -d --name emqx -p 1883:1883 -p 8083:8083 -p 8883:8883 -p 8084:8084 -p 18083:18083 emqx/emqx
2.各个服务端口说明:各个服务端口说明:
- 1883:MQTT 协议端口
- 8883:MQTT/SSL 端口
- 8083:MQTT/WebSocket 端口
- 8080:HTTP API 端口
- 8084:WSS端口
- 18083:Dashboard 管理控制台端口
访问localhost:18083,默认的用户名是admin/public,=>admin123456
Docker 容器使用压缩包解压安装的方式,软件安装于 /opt/emqx 目录中
3.通配符:
主题通过 / 来区分层级,类似于 URL 路径。
MQTT 主题支持以下两种通配符:+ 和 #。
- +:表示单层通配符,例如 a/+ 匹配 a/x 或 a/y。
- #:表示多层通配符,例如 a/# 匹配 a/x、a/b/c/d。
4.單詞:
- Authentication:身份認證
- Authorization:授權
3.1 MQTTX
每次重啟以後,都要對Connection進行重新連接
3.2 Code
namespace RabbitMQ.Common.Utils
{
public class MqttClient
{
public delegate void MessageHandler(string msg);
public event MessageHandler OnMessage;
public IMqttClient m_MqttClient = null;
public async Task MqttClientCon()
{
try
{
if (m_MqttClient == null)
{
m_MqttClient = new MqttFactory().CreateMqttClient();
var mqttOptions = new MqttClientOptions()
{
ClientId = "Client1122",
ChannelOptions = new MqttClientTcpOptions()
{
//服务主机地址
Server = "localhost",
Port = 1883
},
Credentials = new MqttClientCredentials(userName: "admin", password: Encoding.UTF8.GetBytes("admin123456")) { },
CleanSession = false,
KeepAlivePeriod = TimeSpan.FromSeconds(65535),
Timeout = TimeSpan.FromSeconds(30)
};
CancellationToken cancellationToken = new CancellationToken();
var result = await m_MqttClient.ConnectAsync(mqttOptions, cancellationToken);
if (result.ResultCode == MqttClientConnectResultCode.Success)
{
await m_MqttClient.SubscribeAsync(new MqttTopicFilter() { Topic = "CCC" });
MessageBox.Show("Succeed");
}
else
{
MessageBox.Show("连接失败");
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
m_MqttClient.Dispose();
m_MqttClient = null;
}
}
public async void Send(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken1)
{
var result1 = await m_MqttClient.PublishAsync(applicationMessage, cancellationToken1);
if (result1.ReasonCode == MQTTnet.Client.MqttClientPublishReasonCode.Success)
{
string msg = $"Topic:{applicationMessage.Topic}\n" +
$"Payload:{Encoding.UTF8.GetString(applicationMessage.Payload)}";
MessageBox.Show(msg);
}
else
{
MessageBox.Show("发送失败");
}
}
public async void Dispose()
{
if (m_MqttClient != null)
{
m_MqttClient.Dispose();
m_MqttClient = null;
MessageBox.Show("Disconnected >>Disconnected Server");
}
}
}
}
namespace RabbitMQ.ViewModels.LeftViewModel
{
public class MqttViewModel : BindableBase
{
public DelegateCommand<string> ButtonCommand { get; set; }
MqttClient mqttClient = new MqttClient();
private string sendContent;
public string SendContent
{
get { return sendContent; }
set { sendContent = value; RaisePropertyChanged(); }
}
public MqttViewModel()
{
ButtonCommand = new DelegateCommand<string>(ButtonMethod);
}
private void ButtonMethod(string obj)
{
switch (obj)
{
case "connection":
Connection();
break;
case "published":
Published();
break;
case "close":
Close();
break;
}
}
private void Close()
{
System.Diagnostics.Trace.WriteLine("colse");
}
private void Published()
{
System.Diagnostics.Trace.WriteLine("--- Send Message ---");
byte[] msgBus = Encoding.UTF8.GetBytes(SendContent);
MqttApplicationMessage applicationMessage = new();
CancellationToken cancellationToken1 = new CancellationToken();
applicationMessage.Topic = "CCC";
applicationMessage.Payload = msgBus;
applicationMessage.QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce;
applicationMessage.Retain = false;
mqttClient.Send(applicationMessage, cancellationToken1);
}
private async void Connection()
{
await mqttClient.MqttClientCon();
System.Diagnostics.Trace.WriteLine("===== test connection =====");
}
}
}