【IPC-CFX】两种模式简介

936 阅读4分钟

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…

工廠通信的即插即用的行業標準,設備不太需要關心數據發送到哪裡,來源於哪裡,只需要知道什麼時機發送數據,獲取數據後執行什麼操作

图片.png

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

图片.png

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 目录中

图片.png

3.通配符:

主题通过 / 来区分层级,类似于 URL 路径。

MQTT 主题支持以下两种通配符:+ 和 #。

  • +:表示单层通配符,例如 a/+ 匹配 a/x 或 a/y。
  • #:表示多层通配符,例如 a/# 匹配 a/x、a/b/c/d。

4.單詞:

  • Authentication:身份認證
  • Authorization:授權

3.1 MQTTX

mqttx.app/zh/download…

每次重啟以後,都要對Connection進行重新連接

图片.png

3.2 Code

图片.png

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 =====");
        }
    }
}