.NET Sockets类实现的MQTT客户端类

本文介绍了如何利用.NET的Sockets类构建MQTT客户端,包括MqttClient.vb、MqttProperties.vb、MqttSubscriptionOptions.vb、MqttMessage.vb和MqttCredential.vb等关键类的详细说明,提供了主要连接属性和方法的初始化,以及简单的调用示例。
摘要由CSDN通过智能技术生成

.NET 使用Sockets类实现的MQTT客户端类

   最近要左物联网消息推送,看来看去还是觉得MQTT使用比较方便,看了相关文档,在手机app端几乎都有现成的类库提供,这个基本没上面问题,服务端也有现成的EMQX可以使用,但是在使用的过程中发现一个问题,就是免费的EMQX服务器不支持消息的存储,那就悲催了,于是想到的时使用一个PC的消息客户端来订阅需要保存的消息,然后吧收到的消息进行存储,这貌似完全可行。网上找了一个消息类库,支持MQTT,可惜的是我用的vs版本比较低,调用不了,而且网上也没找到我目前使用的版本支持的类库,没办法,不想重装最新的VS,那就智能自己些了。花了几天时间基本完成,支持MQTT V3.1、MQTT v3.1.1及最新MQTT v5.0等版本,代码基于VS2013完成。废话不多说,上代码,大部分都做了注释:

1、客户端住类MqttClient.vb

Imports System.Net.Sockets
Imports System.IO
Imports System.Threading
Imports System.Text
Imports System.Net
Imports System.Timers
Imports System.Reflection

Public Class MqttClient
    ''' <summary>MQTT默认端口号</summary>
    Public Const PORT_DEFAULT As Integer = 1883
    Public Enum MqttQoS As Byte
        QoS_0 = 0
        QoS_1 = 1
        QoS_2 = 2
    End Enum
    ''' <summary>
    ''' MQTT控制报文类型枚举
    ''' </summary>
    ''' <remarks></remarks>
    Public Enum ControlType As Byte
        ''' <summary>客户端请求连接服务器(客户端到服务端)</summary>
        CONTROL_TYPE_CONNCET = 1
        ''' <summary>连接确认(服务端到客户端)</summary>
        CONTROL_TYPE_CONNACK = 2
        ''' <summary>发布消息(两个方向都允许)</summary>
        CONTROL_TYPE_PUBLISH = 3
        ''' <summary>发布确认,响应QoS等级为1的PUBLISH包(两个方向都允许)</summary>
        CONTROL_TYPE_PUBACK = 4
        ''' <summary>发布接收(有保证的交付第1部分)(两个方向都允许)</summary>
        CONTROL_TYPE_PUBREC = 5
        ''' <summary>发布释放(有保证的交付第2部分)(两个方向都允许)</summary>
        CONTROL_TYPE_PUBREL = 6
        ''' <summary>发布完成(有保证的交付第3部分)(两个方向都允许)</summary>
        CONTROL_TYPE_PUBCOMP = 7
        ''' <summary>客户端订阅请求(客户端到服务端)</summary>
        CONTROL_TYPE_SUBSCRIBE = 8
        ''' <summary>订阅确认(服务端到客户端)</summary>
        CONTROL_TYPE_SUBACK = 9
        ''' <summary>客户端取消订阅请求(客户端到服务端)</summary>
        CONTROL_TYPE_UNSUBSCRIBE = 10
        ''' <summary>取消订阅确认(服务端到客户端)</summary>
        CONTROL_TYPE_UNSUBACK = 11
        ''' <summary>心跳请求(客户端到服务端)</summary>
        CONTROL_TYPE_PINGREQ = 12
        ''' <summary>心跳回复(服务端到客户端)</summary>
        CONTROL_TYPE_PINGRESP = 13
        ''' <summary>断开连接(两个方向都允许)</summary>
        CONTROL_TYPE_DISCONNECT = 14
        ''' <summary>认证信息交换(两个方向都允许)</summary>
        CONTROL_TYPE_AUTH = 15
    End Enum
    ''' <summary>
    ''' 连接原因码
    ''' </summary>
    ''' <remarks></remarks>
    Public Enum ReasonCode As Byte
        ''' <summary>已接受连接 </summary>
        REASON_CODE_CONNECTION_ACCEPTED = &H0
    ''' <summary>服务器不支持客户端请求的MQTT协议版本</summary>
        REASON_CODE_UNACCEPTABLE_PROTOCOL_VER = &H1
    ''' <summary>客户端标识符是正确的UTF-8,但服务器不允许</summary>
        REASON_CODE_ID_ENTIFIER_REJECTED = &H2
    ''' <summary>已建立网络连接,但MQTT服务不可用</summary>
        REASON_CODE_SERVER_UNAVAILABLE = &H3
    ''' <summary>用户名或密码中的数据格式不正确</summary>
        REASON_CODE_BAD_USER_OR_PWD = &H4
    ''' <summary>客户端未被授权连接</summary>
        REASON_CODE_NOT_AUTBORIZED = &H5
    ''' <summary>服务端不愿透露的错误,或者没有适用的原因码</summary>
        REASON_CODE_UNSPECIFIED_ERROR = &H80
    ''' <summary>CONNECT报文内容不能被正确的解析</summary>
        REASON_CODE_INVALID_MESSAGE = &H81
    ''' <summary>CONNECT报文内容不符合本规范</summary>
        REASON_CODE_PROTOCOL_ERROR = &H82
    ''' <summary>CONNECT有效,但不被服务端所接受</summary>
        REASON_CODE_VALIDITY_NOT_ACCEPTED = &H83
    ''' <summary>服务端不支持客户端所请求的MQTT协议版本</summary>
        REASON_CODE_PROTOCOL_VER_NOT_SUPPORTED = &H84
    ''' <summary>客户标识符有效,但未被服务端所接受</summary>
        REASON_CODE_INVALID_IDENTIFIER = &H85
    ''' <summary>客户端指定的用户名密码未被服务端所接受</summary>
        REASON_CODE_USER_OR_PWD_ERROR = &H86
    ''' <summary>客户端未被授权连接</summary>
        REASON_CODE_UNAUTHORIZED = &H87
    ''' <summary>MQTT服务端不可用</summary>
        REASON_CODE_SERVER_NOT_AVAILABLE = &H88
    ''' <summary>服务端正忙,请重试</summary>
        REASON_CODE_SERVER_BUSY = &H89
    ''' <summary>客户端被禁止,请联系服务端管理员</summary>
        REASON_CODE_PROHIBIT = &H8A
    ''' <summary>认证方法未被支持,或者不匹配当前使用的认证方法</summary>
        REASON_CODE_INVALID_AUTHENTICATION_METHOD = &H8C
    ''' <summary>遗嘱主题格式正确,但未被服务端所接受</summary>
        REASON_CODE_INVALID_SUBJECT_NAME = &H90
    ''' <summary>CONNECT报文超过最大允许长度</summary>
        REASON_CODE_MESSAGE_TOO_LONG = &H95
    ''' <summary>已超出实现限制或管理限制</summary>
        REASON_CODE_QUOTA_EXCEEDED = &H97
    ''' <summary>遗嘱载荷数据与载荷格式指示符不匹配</summary>
        REASON_CODE_INVALID_LOAD_FORMAT = &H99
    ''' <summary>遗嘱保留标志被设置为1,但服务端不支持保留消息</summary>
        REASON_CODE_RESERVATION_NOT_SUPPORTED = &H9A
    ''' <summary>服务端不支持遗嘱中设置的QoS等级</summary>
        REASON_CODE_UNSUPPORTED_QOS_LEVEL = &H9B
    ''' <summary>客户端应该临时使用其他服务端</summary>
        REASON_CODE_TEMP_USE_OTHER_SERVER = &H9C
    ''' <summary>客户端应该永久使用其他服务端</summary>
        REASON_CODE_PERMANENT_USE_OTHER_SERVER = &H9D
    ''' <summary>超出了所能接受的连接速率限制</summary>
        REASON_CODE_EXCEEDING_CONNECT_SPEED_LIMIT = &H9F
    End Enum

    ''' <summary>TCP客户端套接字 </summary>
    Private tcpSocket As Socket
    ''' <summary>用于心跳检测定时计数器</summary>
    Private timeCount As Integer
    ''' <summary>发送心跳包后服务器应答标志</summary>
    Private isPingResp As Boolean
    ''' <summary>存放心跳包发送无应答次数(3次无应答视为离线)</summary>
    Private keepNotRespCount As Integer
    ''' <summary>定义标识符,用以表示TCP连接是否建立 </summary>
    Private isConnected As Boolean = False
    ''' <summary>标志是否执行了断开MQTT连接方法</summary>>
    Private isMqttDisconnect As Boolean = False
    ''' <summary>MQTT连接选项(存放连接必须的一些参数)</summary>
    Private mConnectOptions As MqttConnectOptions
    ''' <summary>接收信息线程</summary>
    Private readThread As Thread
    ''' <summary>装载该类的窗体类(用于实现线程内托管操作主线程UI)</summary>
    Private mForm As Form
    ''' <summary>执行心跳包发送用定时器</summary>
    Private WithEvents timerKeepLive As Timers.Timer
    '**************************事件接托管********************************'
#Region "Delegates 事件顶级托管声明"
    ''' <summary>
    ''' 发布接收
    ''' </summary>
    ''' <param name="topic">主题名称</param>
    ''' <param name="message">发布的消息</param>
    ''' <remarks></remarks>
    Public Delegate Sub PublishReceive(ByVal topic As String, ByVal message As MqttMessage)
    ''' <summary>
    ''' 接收错误
    ''' </summary>
    ''' <param name="controlType">触发错误的控制类型</param>
    ''' <param name="errCode">错误代码</param>
    ''' <param name="errMessage">错误描述消息</param>
    ''' <remarks></remarks>
    Public Delegate Sub ErrorReceive(ByVal controlType As ControlType, ByVal errCode As Byte, ByVal errMessage As String)
    ''' <summary>
    ''' 连接成功
    ''' </summary>
    ''' <param name="sessionPresent">
    ''' 如果服务端接受了一个CleanSession设置为1的连接,服务端必须将CONNACK包中的Session Present设置为0,并且CONNACK包的返回码也设置为0。
    ''' 如果服务端接受了一个CleanSession设置为0的连接,Session Present的值取决于服务端是否已经存储了客户端Id对应的绘画状态。
    ''' 如果服务端已经存储了会话状态,CONNACK包中的Session Present必须设置为1。
    ''' 如果服务端没有存储会话状态,CONNACK包的Session Present必须设置为0。
    ''' 另外CONNACK包中的返回码必须设为0。
    ''' </param>
    ''' <remarks></remarks>
    Public Delegate Sub ConnectSuccess(ByVal sessionPresent As Boolean)
    ''' <summary>
    ''' 发布进度
    ''' </summary>
    ''' <param name="controlType">控制类型</param>
    ''' <param name="Identifier">识别id</param>
    ''' <remarks></remarks>
    Public Delegate Sub PublishProcess(ByVal controlType As ControlType, ByVal Identifier As UShort)
    ''' <summary>
    ''' 主题订阅主题主题
    ''' </summary>
    ''' <param name="isSubscribe">是订阅主题。true 是订阅主题,false 取消主题订阅</param>
    ''' <param name="Identifier">唯一标识id</param>
    ''' <remarks></remarks>
    Public Delegate Sub SubscribeState(ByVal isSubscribe As Boolean, ByVal Identifier As UShort)
    ''' <summary>
    ''' 断开连接
    ''' </summary>
    ''' <param name="onlyMqttConnect">true 是MQTT连接断开;false TCP连接断开</param>
    ''' <remarks></remarks>
    Public Delegate Sub DisConnect(ByVal onlyMqttConnect As Boolean)
#End Region
#Region "事件句柄声明"
    ''' <summary>
    ''' 当新数据到达时出现
    ''' </summary>
    ''' <remarks>当新数据到达时出现</remarks>
    Public Event publishReceiveArrival As PublishReceive
    ''' <summary>
    ''' 当接收到错误消息时
    ''' </summary>
    ''' <remarks>当新错误数据到达时出现</remarks>
    Public Event errorReceiveArrival As ErrorReceive
    ''' <summary>
    ''' 当与MQTT服务端连接成功
    ''' </summary>
    ''' <remarks>MQTT服务端应答确认连接时</remarks>
    Public Event connectMqttSuccess As ConnectSuccess
    ''' <summary>
    ''' 发布主题消息过程
    ''' </summary>
    ''' <remarks>发布完成或发布中</remarks>
    Public Event publishTopicProcess As PublishProcess
    ''' <summary>
    ''' 完成主题订阅或取消订阅
    ''' </summary>
    ''' <remarks>收到订阅完成或退定完成时</remarks>
    Public Event subscribeTopicState As SubscribeState
    ''' <summary>
    ''' 断开连接
    ''' </summary>
    ''' <remarks>收到MQTT断开消息或TCP断开时</remarks>
    Public Event disConnectSuccess As DisConnect
#End Region
#Region "安全事件托管"
    Protected Sub OnPublishReceive(ByVal topic As String, ByVal msg As MqttMessage)
        ' Console.WriteLine("主题名称:" + topic + " 主题消息:" + msg.getPayloadString)
        RaiseEvent publishReceiveArrival(topic, msg)  '触发事件
    End Sub
    Protected Sub OnErrorReceive(ByVal controlType As ControlType, ByVal errCode As Byte, ByVal errMessage As String)
        RaiseEvent errorReceiveArrival(controlType, errCode, errMessage)
    End Sub
    Protected Sub OnConnectSuccess(ByVal sessionPresent As Boolean)
        RaiseEvent connectMqttSuccess(sessionPresent)
    End Sub
    Protected Sub OnPublishProcess(ByVal controlType As ControlType, ByVal Identifier As UShort)
        RaiseEvent publishTopicProcess(controlType, Identifier)
    End Sub
    Protected Sub OnSubscribeState(ByVal isSubscribe As Boolean, ByVal Identifier As UShort)
        RaiseEvent subscribeTopicState(isSubscribe, Identifier)
    End Sub
    Protected Sub OnDisConnect(ByVal onlyMqttConnect As Boolean)
        RaiseEvent disConnectSuccess(onlyMqttConnect)
    End Sub
#End Region
    '*******************构造函数**************’
    ''' <summary>
    ''' 构造函数
    ''' </summary>
    ''' <param name="form">加载这个类的窗体类</param>
    ''' <remarks></remarks>
    Public Sub New(ByVal form As Form)
        Me.timerKeepLive = New Timers.Timer(1000)   '初始化并设置事件触发时间间隔
        Me.mForm = form
    End Sub
    ''' <summary>
    ''' 构造函数
    ''' </summary>
    '''  <param name="form">加载这个类的窗体类</param>
    ''' <param name="onnectOptions">连接选项</param>
    ''' <remarks></remarks>
    Public Sub New(ByVal form As Form, ByVal onnectOptions As MqttConnectOptions)
        mConnectOptions = onnectOptions
        Me.timerKeepLive = New Timers.Timer(1000)   '初始化并设置事件触发时间间隔
        Me.mForm = form
    End Sub
    '****************类方法*********************’
    Protected Overrides Sub Finalize()
        timerKeepLive.Stop()
        timerKeepLive.Close()
        disConnectSock()
        tcpSocket = Nothing
        MyBase.Finalize()
    End Sub
    '****************************事件************************************’
    ''' <summary>
    ''' 定时器触发事件
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub timerKeepLive_Elapsed(sender As Object, e As ElapsedEventArgs) Handles timerKeepLive.Elapsed
        If timeCount < mConnectOptions.keepAliveInterval Then
            timeCount += 1
        Else
            If isMqttDisconnect Then
                isMqttDisconnect = False
                disConnectSock()
                keepNotRespCount = 0
            Else
                If Not isPingResp Then
                    keepNotRespCount += 1
                Else
                    keepNotRespCount = 0
                End If
                If keepNotRespCount < 3 Or isPingResp Then
                    isPingResp = False
                    mqttPingReq() '发送心跳包
                Else
                    disConnectSock() '超出心跳应答次数,断开连接
                    keepNotRespCount = 0
                End If
            End If
            timeCount = 0  '清零重新计数
        End If
    End Sub
    '**********************公有方法*********************************’
    ''' <summary>
    ''' 设置连接选项
    ''' </summary>
    ''' <param name="connectOptions"></param>
    ''' <remarks></remarks>
    Public Sub setConnectOptions(ByVal connectOptions As MqttConnectOptions)
        mConnectOptions = connectOptions
    End Sub
    ''' <summary>
    ''' 连接选项
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property connectOptions() As MqttConnectOptions
        Get
            Return mConnectOptions
        End Get
        Set(value As MqttConnectOptions)
            mConnectOptions = value
        End Set
    End Property
    ''' <summary>
    ''' 连接MQTT服务器(默认端口1883)
    ''' </summary>
    ''' <param name="addr">服务器IP地址</param>
    ''' <param name="clientId">客户端唯一标识符</param>
    ''' <remarks></remarks>
    Public Function ConnectServer(ByVal addr As String, ByVal clientId As String) As Boolean
        Return ConnectServer(addr, PORT_DEFAULT, clientId)
    End Function
    ''' <summary>
    ''' 连接到MQTT服务器
    ''' </summary>
    ''' <param name="addr">服务器IP地址</param>
    ''' <param name="port">tcp端口号</param>
    ''' <param name="clientId">客户端唯一标识符</param>
    ''' <remarks></remarks>
    Public Function ConnectServer(ByVal addr As String, ByVal port As Integer, ByVal clientId As String) As Boolean
        If IsNothing(mConnectOptions) Then
            mConnectOptions = New MqttConnectOptions()
        End If
        mConnectOptions.ClientId = clientId
        mConnectOptions.IpAddress = addr
        mConnectOptions.Port = port
        Return ConnectServer()
    End Function
    ''' <summary>
    ''' 连接MQTT服务器
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function ConnectServer() As Boolean
        If mConnectOptions Is Nothing Then
            Return isConnected
        End If
        Try
            If Not isConnected Then
                Dim remoteAddr As New IPEndPoint(Net.IPAddress.Parse(mConnectOptions.IpAddress), IIf(mConnectOptions.Port > 0, mConnectOptions.Port, PORT_DEFAULT))
                tcpSocket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)  '创建套接字对象
                tcpSocket.Connect(remoteAddr)   '连接到指定的地址
                isConnected = True    '将标志置为连接状态
                readThread = New Thread(AddressOf reciveMsgCallback)  '创建接收线程
                readThread.Start()     '启用接收线程
                timerKeepLive.Start()   '打开定时器
                If connectMqtt() <> -1 Then
                    timeCount = 0
                Else
                    disConnectSock()
                End If
            End If
        Catch ex As Exception
            isConnected = False
        End Try
        Return isConnected
    End Function
    ''' <summary>
    ''' 断开MQTT连接
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function ConnectClose(Optional ByVal causeCode As Byte = 0) As Integer
        If isConnected Then
            Dim p() As Byte
            If mConnectOptions.MqttVersion < MqttConnectOptions.Version.MQTT_VERSION_5_0 Then
                p = {ControlType.CONTROL_TYPE_DISCONNECT << 4, 1, causeCode}
            Else
                p = {ControlType.CONTROL_TYPE_DISCONNECT << 4, 2, causeCode, 0}
            End If
            timeCount = 0  '清零重新计数
            isMqttDisconnect = True
            Return tcpSocket.Send(p)
        Else
            disConnectSock()
            Return 1
        End If
    End Function
    ''' <summary>
    ''' 订阅主题
    ''' </summary>
    ''' <param name="topic">主题名称</param>
    ''' <param name="QoS">服务端发送给客户端应用消息的最大等级</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function subscribe(ByVal topic As String, Optional ByVal QoS As MqttQoS = MqttQoS.QoS_0) As IntPtr
        Return subscribe(topic, QoS, 0)
    End Function
    ''' <summary>
    ''' 订阅主题
    ''' </summary>
    ''' <param name="topic">主题名称</param>
    ''' <param name="subscribeOptions">订阅选项</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function subscribe(ByVal topic As String, ByVal subscribeOptions As MqttSubscriptionOptions) As IntPtr
        Return subscribe(New String() {topic}, New MqttSubscriptionOptions() {subscribeOptions}, 0)
    End Function
    ''' <summary>
    ''' 订阅主题
    ''' </summary>
    ''' <param name="topic">主题名称</param>
    ''' <param name="QoS">服务端发送给客户端应用消息的最大等级</param>
    ''' <param name="packetIdentifier">包唯一id,默认使用1-65535之间的随机整数</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function subscribe(ByVal topic As String, Optional ByVal QoS As MqttQoS = MqttQoS.QoS_0, _
                              Optional ByVal packetIdentifier As UShort = 0, Optional ByVal properties As MqttProperties = Nothing) As IntPtr
        Dim returnval As IntPtr = -1
        If Not IsNothing(topic) Then
            If topic.Length > 0 Then
                returnval = subscribe(New String() {topic}, New MqttQoS() {QoS}, packetIdentifier, properties)
            End If
        End If
        Return returnval
    End Function
    ''' <summary>
    ''' 订阅主题
    ''' </summary>
    ''' <param name="topics">主题名称数组</param>
    ''' <param name="QoS">服务端发送给客户端应用消息的最大等级数组</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function subscribe(ByVal topics() As String, ByVal QoS() As MqttQoS, Optional ByVal properties As MqttProperties = Nothing) As IntPtr
        Return subscribe(topics, QoS, 0, properties)
    End Function
    ''' <summary>
    ''' 订阅主题
    ''' </summary>
    ''' <param name="topics">主题名称数组</param>
    ''' <param name="QoS">服务端发送给客户端应用消息的最大等级数组</param>
    ''' <param name="packetIdentifier">包唯一id,默认使用1-65535之间的随机整数</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function subscribe(ByVal topics() As String, ByVal QoS() As MqttQoS, Optional ByVal packetIdentifier As UShort = 0, _
                              Optional ByVal properties As MqttProperties = Nothing) As IntPtr
        Dim subscribeOptions(QoS.Length - 1) As MqttSubscriptionOptions
        For i = 0 To QoS.Length - 1
            subscribeOptions(i) = New MqttSubscriptionOptions(QoS(i))
        Next
        Return subscribe(topics, subscribeOptions, packetIdentifier, properties)
    End Function
    ''' <summary>
    ''' 订阅主题
    ''' </summary>
    ''' <param name="topics">主题名称数组</param>
    ''' <param name="subscribeOptions">订阅选项,在该选项中除qos选项在私有版本有效外,其余选项仅5.0及以上有效</param>
    ''' <param name="packetIdentifier">包唯一标识符</param>
    ''' <param name="properties">属性</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function subscribe(ByVal topics() As String, ByVal subscribeOptions() As MqttSubscriptionOptions, Optional ByVal packetIdentifier As UShort = 0, _
                              Optional ByVal properties As MqttProperties = Nothing) As IntPtr
        If isConnected Then
            Dim topicBuff() As Byte = Nothing   '订阅主题字节数组
            Dim propertiesBuff() As Byte = Nothing '属性字节数组
            Dim propertiesLengthBuff() As Byte = Nothing  '属性长度字节数组
            Dim pos As Integer = 0
            Dim remainingLen As Int32 = 0  '剩余长度(固定头)
            Dim minLen As Integer = IIf(topics.Length < subscribeOptions.Length, topics.Length, subscribeOptions.Length) - 1   '获取主题与QoS两个数组的最小一个最大下标
            For i = 0 To minLen
                Dim tempTopic() As Byte = System.Text.Encoding.GetEncoding("UTF-8").GetBytes(topics(i))
                Dim tempTopicLen() As Byte = IByte(CShort(tempTopic.Length))
                '订阅选项
                Dim tempOptions As Byte
                If mConnectOptions.mqttVersion < MqttConnectOptions.Version.MQTT_VERSION_5_0 Then
                    tempOptions = subscribeOptions(i).QoS
                Else
                    tempOptions = (subscribeOptions(i).RetainHandling << 4) Or (subscribeOptions(i).getRetainAsPublished() << 3) Or _
                                  (subscribeOptions(i).getNoLocal() << 2) Or subscribeOptions(i).QoS
                End If
                ReDim Preserve topicBuff(pos + tempTopicLen.Length + tempTopic.Length)  '动态定义主题数组下标(不改变已有内容)
                tempTopicLen.CopyTo(topicBuff, pos)    '主题长度
                pos += tempTopicLen.Length
                tempTopic.CopyTo(topicBuff, pos)    '主题
                pos += tempTopic.Length
                topicBuff(pos) = tempOptions    '订阅选项
                pos += 1
            Next
            If packetIdentifier = 0 Then
                packetIdentifier = CInt(Math.Ceiling(Rnd() * 65535)) + 1   '生成1-65535之间的随机ID
            End If
            Dim packetIds() As Byte = IByte(packetIdentifier)   '可变包部分带有包唯一标识
            remainingLen = topicBuff.Length + packetIds.Length   '获取可变头与载荷的字节长度
            If connectOptions.mqttVersion > MqttConnectOptions.Version.MQTT_VERSION_3_1_1 Then
                If IsNothing(properties) Then
                    ReDim propertiesLengthBuff(0)
                    remainingLen += propertiesLengthBuff.Length
                Else
                    If properties.SubscriptionIdentifier > 0 Then
                        ' Dim userBuff() As Byte = properties.getUserProperty()
                        Dim IDbuff() As Byte = properties.getSubscriptionIdentifier()
                        ReDim propertiesBuff(IDbuff.Length - 1)
                        IDbuff.CopyTo(propertiesBuff, 0)
                        ' userBuff.CopyTo(propertiesBuff, IDbuff.Length)
                        ' propertiesBuff = properties.getSubscriptionIdentifier()
                        propertiesLengthBuff = VariableLengthByte(CUInt(propertiesBuff.Length))
                        remainingLen += propertiesLengthBuff.Length + propertiesBuff.Length
                    Else
                        ReDim propertiesLengthBuff(0)
                        remainingLen += propertiesLengthBuff.Length
                    End If
                End If
            End If
            Dim fHead() As Byte = getFixedHead(ControlType.CONTROL_TYPE_SUBSCRIBE, remainingLen, False, 1, False)  '获取固定头字节
            Dim subscribeBuff(remainingLen + fHead.Length - 1) As Byte   '定义主题字节最大下标
            pos = 0    '将位置计数器清零
            fHead.CopyTo(subscribeBuff, pos)
            pos += fHead.Length
            packetIds.CopyTo(subscribeBuff, pos)
            pos += packetIds.Length
            If mConnectOptions.getMqttVersion() > MqttConnectOptions.Version.MQTT_VERSION_3_1_1 Then
                propertiesLengthBuff.CopyTo(subscribeBuff, pos)
                pos += propertiesLengthBuff.Length
                If Not IsNothing(propertiesBuff) Then
                    propertiesBuff.CopyTo(subscribeBuff, pos)
                    pos += propertiesBuff.Length
                End If
            End If
            topicBuff.CopyTo(subscribeBuff, pos)
            pos += topicBuff.Length
            Return tcpSocket.Send(subscribeBuff)
        Else
            Return -1
        End If
    End Function
    ''' <summary>
    ''' 退订主题
    ''' </summary>
    ''' <param name="topicName">主题名</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function unSubscribe(ByVal topicName As String) As Integer
        Return unSubscribe(topicName, 0)
    End Function
    ''' <summary>
    ''' 退订主题
    ''' </summary>
    ''' <param name="topicName">主题名</param>
    ''' <param name="packetIdentifier">包唯一id,默认使用1-65535之间的随机整数</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function unSubscribe(ByVal topicName As String, Optional ByVal packetIdentifier As UShort = 0) As IntPtr
        Dim returnval As IntPtr = -1
        If Not IsNothing(topicName) Then
            If topicName.Length > 0 Then
                returnval = unSubscribe(New String() {topicName}, packetIdentifier)
            End If
        End If
        Return returnval

    End Function
    ''' <summary>
    ''' 退订主题
    ''' </summary>
    ''' <param name="topicNames">主题列表</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function unSubscribe(ByVal topicNames() As String) As IntPtr
        Return unSubscribe(topicNames, 0)
    End Function
    ''' <summary>
    ''' 退订主题
    ''' </summary>
    ''' <param name="topicNames">主题列表</param>
    ''' <param name="packetIdentifier">包唯一id,默认使用1-65535之间的随机整数</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function unSubscribe(ByVal topicNames() As String, Optional ByVal packetIdentifier As UShort = 0) As IntPtr
        If isConnected Then
            Dim remainingLen As Int32 = 0
            If packetIdentifier = 0 Then
                packetIdentifier = CInt(Math.Ceiling(Rnd() * 65535)) + 1   '生成1-65535之间的随机ID
            End If
            Dim packetIds() As Byte = IByte(packetIdentifier)   '可变包部分带有包唯一标识
            remainingLen += packetIds.Length
            Dim propertiesLengthBuff() As Byte = Nothing
            If connectOptions.mqttVersion > MqttConnectOptions.Version.MQTT_VERSION_3_1_1 Then
                ReDim propertiesLengthBuff(0)
                remainingLen += propertiesLengthBuff.Length
            End If
            Dim topics() As Byte = Nothing
            Dim pos As Integer = 0
            For i = 0 To topicNames.Length - 1
                Dim tempTopic() As Byte = System.Text.Encoding.GetEncoding("UTF-8").GetBytes(topicNames(i))
                Dim tempTopicLen() As Byte = IByte(CShort(tempTopic.Length))
                ReDim Preserve topics(pos + tempTopicLen.Length + tempTopic.Length - 1)  '动态定义主题数组下标(不改变已有内容)
                tempTopicLen.CopyTo(topics, pos)
                pos += tempTopicLen.Length
                tempTopic.CopyTo(topics, pos)
                pos += tempTopic.Length
            Next
            remainingLen += topics.Length
            Dim fHead() As Byte = getFixedHead(ControlType.CONTROL_TYPE_UNSUBSCRIBE, remainingLen, False, 1, False)
            Dim subscribeBuff(remainingLen + fHead.Length - 1) As Byte
            pos = 0
            fHead.CopyTo(subscribeBuff, pos)
            pos += fHead.Length
            packetIds.CopyTo(subscribeBuff, pos)
            pos += packetIds.Length
            If connectOptions.mqttVersion > MqttConnectOptions.Version.MQTT_VERSION_3_1_1 Then
                propertiesLengthBuff.CopyTo(subscribeBuff, pos)
                pos += propertiesLengthBuff.Length
            End If
            topics.CopyTo(subscribeBuff, pos)
            pos += topics.Length
            Return tcpSocket.Send(subscribeBuff)
        Else
            Return -1
        End If
    End Function
    ''' <summary>
    ''' 发布主题消息
    ''' </summary>
    ''' <param name="topicName">话题名称</param>
    ''' <param name="payload">发布的消息内容</param>
    ''' <param name="DUP">重复传送</param>
    ''' <param name="QoS">服务质量。为0 时接收端无需应答</param>
    ''' <param name="retain">
    ''' 存储话题标志
    ''' 如果RETAIN标识被设置为1,在一个从客户端发送到服务端的PUBLISH包中,服务端必须存储应用消息和QoS,以便可以发送给之后订阅这个话题的订阅者[MQTT-3.3.1-5]。
    ''' 当一个新的订阅发生,最后一个保留的消息,如果有的话,而且匹配订阅话题,必须发送给订阅者[MQTT-3.3.1-6]。
    ''' 如果服务端收到一个QoS 0并且RETAIN标识设置为1的消息,它必须清空之前为这个话题保存的所有消息。
    ''' 服务端应该存储新的QoS 0的消息作为这个话题新的保留消息,但是也可以选择在任何时候清空保留消息——如果这样做了,那这个话题就没有保留消息了[MQTT-3.3.1-7]。
    ''' <param name="properties">属性(适用于MQTT 5.0及以上版本)</param>
    ''' </param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function publish(ByVal topicName As String, ByVal payload As String, Optional ByVal DUP As Boolean = False, Optional ByVal QoS As MqttQoS = MqttQoS.QoS_0, _
                            Optional ByVal retain As Boolean = False, Optional ByVal properties As MqttProperties = Nothing) As IntPtr
        Dim payloadData() As Byte = System.Text.Encoding.GetEncoding("UTF-8").GetBytes(payload)
        Return publish(topicName, payloadData, DUP, QoS, retain, properties) 'tcpSocket.Send(publishBuff)
    End Function
    ''' <summary>
    ''' 发布主题消息
    ''' </summary>
    ''' <param name="topicName">话题名称</param>
    ''' <param name="payload">发布的消息内容</param>
    ''' <param name="DUP">重复传送</param>
    ''' <param name="QoS">服务质量。为0 时接收端无需应答</param>
    ''' <param name="retain">
    ''' 存储话题标志
    ''' 如果RETAIN标识被设置为1,在一个从客户端发送到服务端的PUBLISH包中,服务端必须存储应用消息和QoS,以便可以发送给之后订阅这个话题的订阅者[MQTT-3.3.1-5]。
    ''' 当一个新的订阅发生,最后一个保留的消息,如果有的话,而且匹配订阅话题,必须发送给订阅者[MQTT-3.3.1-6]。
    ''' 如果服务端收到一个QoS 0并且RETAIN标识设置为1的消息,它必须清空之前为这个话题保存的所有消息。
    ''' 服务端应该存储新的QoS 0的消息作为这个话题新的保留消息,但是也可以选择在任何时候清空保留消息——如果这样做了,那这个话题就没有保留消息了[MQTT-3.3.1-7]。
    ''' <param name="properties">属性(适用于MQTT 5.0及以上版本)</param>
    ''' </param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function publish(ByVal topicName As String, payload() As Byte, Optional ByVal DUP As Boolean = False, Optional ByVal QoS As MqttQoS = MqttQoS.QoS_0, _
                            Optional ByVal retain As Boolean = False, Optional ByVal properties As MqttProperties = Nothing) As IntPtr
        Return publish(topicName, payload, DUP, QoS, retain, 0, properties) 'tcpSocket.Send(publishBuff)
    End Function
    ''' <summary>
    ''' 发布主题消息
    ''' </summary>
    ''' <param name="topicName">话题名称</param>
    ''' <param name="payload">有效载荷</param>
    ''' <param name="DUP">重复传送标志</param>
    ''' <param name="QoS">服务质量。为0 时接收端无需应答</param>
    ''' <param name="retain">
    ''' 存储话题标志
    ''' 如果RETAIN标识被设置为1,在一个从客户端发送到服务端的PUBLISH包中,服务端必须存储应用消息和QoS,以便可以发送给之后订阅这个话题的订阅者[MQTT-3.3.1-5]。
    ''' 当一个新的订阅发生,最后一个保留的消息,如果有的话,而且匹配订阅话题,必须发送给订阅者[MQTT-3.3.1-6]。
    ''' 如果服务端收到一个QoS 0并且RETAIN标识设置为1的消息,它必须清空之前为这个话题保存的所有消息。
    ''' 服务端应该存储新的QoS 0的消息作为这个话题新的保留消息,但是也可以选择在任何时候清空保留消息——如果这样做了,那这个话题就没有保留消息了[MQTT-3.3.1-7]。
    ''' </param>
    ''' <param name="packetIdentifier">包唯一id,仅QoS》0时需要,可以不设置,不设置默认产生1-65535的随机ID</param>
    ''' <param name="properties">属性(适用于MQTT 5.0及以上版本)</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function publish(ByVal topicName As String, ByVal payload() As Byte, Optional ByVal DUP As Boolean = False, Optional ByVal QoS As MqttQoS = MqttQoS.QoS_0, _
                            Optional ByVal retain As Boolean = False, Optional ByVal packetIdentifier As UShort = 0, Optional ByVal properties As MqttProperties = Nothing) As IntPtr

        Dim message As New MqttMessage()

        message.setPayload(payload)
        message.duplicate = DUP
        message.QoS = QoS
        message.retained = retain
        message.messageId = packetIdentifier
        message.setProperies(properties)
        publish(topicName, message)
    End Function
    ''' <summary>
    ''' 发布主题消息
    ''' </summary>
    ''' <param name="topicName">传递给的主题名称,例如“finance/stock/ibm”</param>
    ''' <param name="payload">要用作有效负载</param>
    ''' <param name="QoS">传递消息的服务质量。有效值为0、1或2</param>
    ''' <param name="retain"> 服务器是否应保留此消息</param>
    ''' <param name="properties">属性(适用于MQTT 5.0及以上版本)</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function publish(ByVal topicName As String, ByVal payload As String, Optional ByVal QoS As MqttQoS = MqttQoS.QoS_0, _
                            Optional ByVal retain As Boolean = False, Optional ByVal properties As MqttProperties = Nothing) As IntPtr
        Return publish(topicName, payload, , QoS, retain, properties)
    End Function
    ''' <summary>
    ''' 发布主题消息
    ''' </summary>
    ''' <param name="topicName">传递给的主题名称,例如“finance/stock/ibm”</param>
    ''' <param name="payload">要用作有效负载</param>
    ''' <param name="QoS">传递消息的服务质量。有效值为0、1或2</param>
    ''' <param name="retain"> 服务器是否应保留此消息</param>
    ''' <param name="properties">属性(适用于MQTT 5.0及以上版本)</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function publish(ByVal topicName As String, ByVal payload() As Byte, Optional ByVal QoS As MqttQoS = MqttQoS.QoS_0, _
                            Optional ByVal retain As Boolean = False, Optional ByVal properties As MqttProperties = Nothing) As IntPtr
        Return publish(topicName, payload, False, QoS, retain, properties)
    End Function
    ''' <summary>
    ''' 发布主题消息
    ''' </summary>
    ''' <param name="topicName">传递给的主题名称,例如“finance/stock/ibm”</param>
    ''' <param name="message">要传递的消息</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function publish(ByVal topicName As String, ByVal message As MqttMessage) As IntPtr
        If isConnected And Not IsNothing(message) Then
            Dim topics() As Byte = System.Text.Encoding.GetEncoding("UTF-8").GetBytes(topicName)  '主题名称
            Dim topicLen() As Byte = IByte(CShort(topics.Length))    '主题名称长度
            Dim packetIds() As Byte = Nothing    '包唯一标识符数据
            Dim packetIdLen As Byte = 0          '包标识符长度
            Dim propertiesBuff() As Byte = Nothing  '属性数据包
            Dim propertiesLengthBuff() As Byte = Nothing  '属性长度包
            'Dim VariableLengthByte(CUInt(propertiesBuff.Length))
            Dim remainingLen As Int32 = 0
            If message.QoS > 0 Then   '只有当QoS等于1或2时才使用包id
                If message.messageId = 0 Then
                    message.messageId = CInt(Math.Ceiling(Rnd(
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值