c# 版

mqtt 3.1.1 client

http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html

上面为 3.1.1 协议报文

一个外链:

MQTT 3.1.1,值得升级的6个新特性

http://www.blogjava.net/yongboy/archive/2014/12/16/421460.html

socket 客服端,这里我用了 SocketAsyncEventArgs 来实现,

这里感觉要优化的地方还是挺多的,,希望大神指出不足之处

核心实现:

MqttConnection

using System;
using System.IO;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Diagnostics;
using nMqtt.Messages; namespace nMqtt
{
internal sealed class MqttConnection
{
Socket socket;
int m_nConnection = ;
public Action<byte[]> Recv; /// <summary>
/// Socket异步对象池
/// </summary>
SocketAsyncEventArgsPool socketAsynPool; public MqttConnection()
{
const int receiveBufferSize = ;
socketAsynPool = new SocketAsyncEventArgsPool(m_nConnection);
var bufferManager = new BufferManager(receiveBufferSize * m_nConnection, receiveBufferSize);
bufferManager.InitBuffer(); //按照连接数建立读写对象
for (int i = ; i < m_nConnection; i++)
{
var args = new SocketAsyncEventArgs();
args.Completed += IO_Completed;
args.UserToken = new RecvToken();
bufferManager.SetBuffer(args);
socketAsynPool.Push(args);
}
} public void Connect(string server, int port = )
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect(server, port);
socket.ReceiveAsync(socketAsynPool.Pop());
} void IO_Completed(object sender, SocketAsyncEventArgs e)
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Receive:
ProcessRecv(e);
break;
default:
throw new ArgumentException("nError in I/O Completed");
}
} void ProcessRecv(SocketAsyncEventArgs e)
{
Debug.WriteLine("----------------------- ProcessRecv:{0}", e.BytesTransferred);
var token = e.UserToken as RecvToken;
if (e.SocketError == SocketError.Success && e.BytesTransferred > )
{
var buffer = new byte[e.BytesTransferred];
Buffer.BlockCopy(e.Buffer, e.Offset, buffer, , buffer.Length); token.Message.AddRange(buffer); if (token.IsReadComplete)
{
if (Recv != null)
Recv(token.Message.ToArray());
token.Reset();
} socket.ReceiveAsync(e);
}
else
{
token.Reset();
//socketAsynPool.Push(e);
//socket.ReceiveAsync(e);
}
} public void SendMessage(MqttMessage message)
{
Debug.WriteLine("onSend:{0}", message.FixedHeader.MessageType);
using (var stream = new MemoryStream())
{
message.Encode(stream);
stream.Seek(, SeekOrigin.Begin);
Send(stream.ToArray());
}
} void Send(byte[] message)
{
try
{
socket.Send(message, SocketFlags.None);
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
} class RecvToken
{
/// <summary>
/// The read buffer size from the network
/// </summary>
public const int BufferSize = ; /// <summary>
/// The total bytes expected to be read from from the header of content
/// </summary>
public int Count
{
get
{
if (Message != null && Message.Count >= )
{
int offset = ;
byte encodedByte;
var multiplier = ;
var remainingLength = ;
do
{
encodedByte = Message[offset];
remainingLength += encodedByte & 0x7f * multiplier;
multiplier *= 0x80;
} while ((++offset <=) && (encodedByte & 0x80) != ); return remainingLength + offset;
}
return ;
}
} /// <summary>
/// The bytes associated with the message being read.
/// </summary>
public List<byte> Message { get; set; } = new List<byte>(); /// <summary>
/// The buffer the last stream read wrote into.
/// </summary>
public byte[] Buffer; /// <summary>
/// A boolean that indicates whether the message read is complete
/// </summary>
public bool IsReadComplete
{
get { return Message.Count >= Count; }
} public void Reset()
{
Buffer = new byte[BufferSize];
Message.Clear();
}
}
} public enum ConnectionState
{
/// <summary>
/// The MQTT Connection is in the process of disconnecting from the broker.
/// </summary>
Disconnecting, /// <summary>
/// The MQTT Connection is not currently connected to any broker.
/// </summary>
Disconnected, /// <summary>
/// The MQTT Connection is in the process of connecting to the broker.
/// </summary>
Connecting, /// <summary>
/// The MQTT Connection is currently connected to the broker.
/// </summary>
Connected, /// <summary>
/// The MQTT Connection is faulted and no longer communicating with the broker.
/// </summary>
Faulted
} internal sealed class SocketAsyncEventArgsPool
{
private readonly Stack<SocketAsyncEventArgs> pool; internal SocketAsyncEventArgsPool(int capacity)
{
pool = new Stack<SocketAsyncEventArgs>(capacity);
} internal void Push(SocketAsyncEventArgs item)
{
if (item == null)
throw new ArgumentNullException(nameof(item));
lock (pool)
{
pool.Push(item);
}
} internal SocketAsyncEventArgs Pop()
{
lock (pool)
return pool.Pop();
} internal int Count { get { return pool.Count; } }
} internal sealed class BufferManager
{
readonly int m_numBytes;
byte[] m_buffer;
readonly Stack<int> m_freeIndexPool;
int m_currentIndex;
readonly int m_bufferSize; public BufferManager(int totalBytes, int bufferSize)
{
m_numBytes = totalBytes;
m_currentIndex = ;
m_bufferSize = bufferSize;
m_freeIndexPool = new Stack<int>();
} /// <summary>
/// Allocates buffer space used by the buffer pool
/// </summary>
public void InitBuffer()
{
// create one big large buffer and divide that out to each SocketAsyncEventArg object
m_buffer = new byte[m_numBytes];
} /// <summary>
/// Assigns a buffer from the buffer pool to the specified SocketAsyncEventArgs object
/// </summary>
/// <returns>true if the buffer was successfully set, else false</returns>
public bool SetBuffer(SocketAsyncEventArgs args)
{
if (m_freeIndexPool.Count > )
{
args.SetBuffer(m_buffer, m_freeIndexPool.Pop(), m_bufferSize);
}
else
{
if ((m_numBytes - m_bufferSize) < m_currentIndex)
return false;
args.SetBuffer(m_buffer, m_currentIndex, m_bufferSize);
m_currentIndex += m_bufferSize;
}
return true;
} /// <summary>
/// Removes the buffer from a SocketAsyncEventArg object. This frees the buffer back to the
/// buffer pool
/// </summary>
public void FreeBuffer(SocketAsyncEventArgs args)
{
m_freeIndexPool.Push(args.Offset);
args.SetBuffer(null, , );
}
}
}

其中 RecvToken

            public int Count
{
get
{
if (Message != null && Message.Count >= )
{
int offset = ;
byte encodedByte;
var multiplier = ;
var remainingLength = ;
do
{
encodedByte = Message[offset];
remainingLength += encodedByte & 0x7f * multiplier;
multiplier *= 0x80;
} while ((++offset <=) && (encodedByte & 0x80) != ); return remainingLength + offset;
}
return ;
}
}

此为报文长度,这里需多注意

例示:

using System;
using System.Text;
using nMqtt.Messages; namespace nMqtt.Test
{
class Program
{
static void Main(string[] args)
{
var client = new MqttClient("server", "clientId");
var state = client.Connect("username");
if (state == ConnectionState.Connected)
{
client.MessageReceived += OnMessageReceived;
client.Subscribe("a/b", Qos.AtLeastOnce);
}
Console.ReadKey();
} static void OnMessageReceived(string topic, byte[] data)
{
Console.WriteLine("-------------------");
Console.WriteLine("topic:{0}", topic);
Console.WriteLine("data:{0}", Encoding.UTF8.GetString(data));
}
}
}

最后:

代码托管至 https://github.com/dtxlink/nMqtt

c#版 mqtt 3.1.1 client 实现的更多相关文章

  1. 16-网页,网站,微信公众号基础入门(网页版MQTT,页面控件位置调整入门)

    https://www.cnblogs.com/yangfengwu/p/11200767.html 说一下,只要你java学的很好,那么几乎所有的语言都不在话下了 来看一下样式设置 运行 在左上角感 ...

  2. 14-网页,网站,微信公众号基础入门(网页版MQTT,小试牛刀)

    https://www.cnblogs.com/yangfengwu/p/11192639.html 抱歉哈...最近由于做板子,,教程的进度落下了... 这些天总共做了还几块板子 首先对当前这个教程 ...

  3. DotNetty 版 mqtt 开源客户端 (MqttFx)

    一.DotNetty背景介绍 某天发现 dotnet  是个好东西,就找了个项目来练练手.于是有了本文的 Mqtt 客户端   (github:  MqttFx ) DotNetty是微软的Azure ...

  4. 15-网页,网站,微信公众号基础入门(网页版MQTT,做自己的MQTT调试助手)

    https://www.cnblogs.com/yangfengwu/p/11198572.html 说一下哈,,如果教程哪里看不明白...就去自己百度补充哪一部分,,学习不是死记硬背,需要会学习,永 ...

  5. C#版 Winform界面 Socket编程 Client客户端

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  6. mqtt client python example

    This is a simple example showing how to use the [Paho MQTT Python client](https://eclipse.org/paho/c ...

  7. MQTT 协议学习:000-有关概念入门

    背景 从本章开始,在没有特殊说明的情况下,文章中的MQTT版本均为 3.1.1. MQTT 协议是物联网中常见的协议之一,"轻量级物联网消息推送协议",MQTT同HTTP属于第七层 ...

  8. MQTT 协议是个啥?这篇文章告诉你!

    文章首发于我的公众号「程序员cxuan」,欢迎大家关注呀- 说到做到! 之前有位读者给我留言说想要了解一下什么是 MQTT 协议,顺便还把我夸了一把,有点不好意思啦. 那么读者的要求必须要满足啊,所以 ...

  9. Oracle 精简绿色版客户端的配置

    在项目开发中常常用到Oracle.但Oracle 客户端体积很大.安装后,主要用的就1个功能:TNS配置服务名,偶尔用到SqlPlus.在开发过程中,大量使用Navicate和PL/SQL Devel ...

随机推荐

  1. Perl中文件读取操作

    Perl中文件读取操作 http://blog.csdn.net/yangxuan12580/article/details/51506216

  2. 1.Firedac开门篇

    firedac是Delphi开发跨平台的数据库应用程序的通用数据访问组件,同样适用于C++ Builder和FreePascal.firedac可以高速直接访问: 1.InterBase 2.SQLi ...

  3. 1000: 恶意IP 课程作业

    1000: 恶意IP Time Limit: 1 Sec  Memory Limit: 16 MB Description Water同学最近好不容易学会了用Tornado建起一个个人的Website ...

  4. Scrapy爬虫:抓取大量斗图网站最新表情图片

      一:目标 第一次使用Scrapy框架遇到很多坑,坚持去搜索,修改代码就可以解决问题.这次爬取的是一个斗图网站的最新表情图片www.doutula.com/photo/list,练习使用Scrapy ...

  5. http-server:一个简单的零配置命令行的http服务器

    首先简介一下http-server: http-server是一个简单的零配置命令行http服务器,他对于生产使用来说足够强大,他是简单和可删节足以用于测试,足够简单易用,而且可用于本地开发 1.首先 ...

  6. Unique Binary Search Trees I&&II(II思路很棒)——动态规划(II没理解)

      Given n, how many structurally unique BST's (binary search trees) that store values 1...n? For exa ...

  7. MVC - 12.HtmlHelper

    1.动态生成URL @Url.Action("Index3","Stu3") @Url.RouteUrl("Default2", new { ...

  8. [水煮 ASP.NET Web API2 方法论](1-2)在 WebForm 应用程序中添加 ASP.NET Web API

    问题 怎么样将 Asp.Net Web Api 加入到 Asp.Net Web From 应用程序中 解决方案 在 Visual Studio 2013 中,创建新的 Web From,可以直接在&q ...

  9. 递归遍历JSON树

    递归遍历JSON树 前几天有个人问我,json串的层级无限深,但在json串中的key是已知的,在json串中的value,有些事Object,有些是Array,如何把这些层级无限深的key所对应的v ...

  10. Web测试中容易被忽略的Charset问题

    今天继续进行一个更综合的脚本制作,录制设置.进行录制.脚本修改,一切都轻车熟路,进行得很顺利.经过近一个小时的对比和修改,OK,脚本大功告成,终于可以小试牛刀了,嘿嘿.    运行,replay lo ...