MQTT手写协议
一、连接
Wireshark抓包工具
socket.Connect("127.0.0.1", 10613);//连接WLAN

socket.Connect("127.0.0.1", 1883);//连接自己的桌面应用程序 Adapter for loopback

连接成功


换一个协议版本0x06
都没有响应,直接被踢了, 没有返回错误信息,不支持的协议版本
端口过滤 tcp.port==1883
如果不返回固定头部32就不是响应报文
这样判断,这个库不太行
用户名密码还是有判断的
协议版本没有处理响应
二、订阅
//订阅主题多个,用逗号分隔,支持中文
List<string> topic = new List<string>
{
"车间/设备","zys"
};
Subscription(topic);


有几个topic,再加上4个字节,就是最终响应的字节数
三、心跳
死循环,要异步处理,新线程处理
100秒,稍微等个90秒

区别就是请求类型,请求12,响应13
// See https://aka.ms/new-console-template for more information using System.Net.Sockets; using System.Text; class Program { static Socket socket; static void Main(string[] args) { Console.WriteLine("Hello, World!"); Connection(); //订阅主题多个,用逗号分隔,支持中文 List<string> topic = new List<string> { "车间/设备","zys" }; Subscription(topic); Console.ReadKey(); } static void Connection() { //MQTT不支持udp socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//Socket支持UDP socket.Connect("127.0.0.1", 1883); List<byte> headBytes = new List<byte>() { 1<<4//连接请求消息类型 }; string protocolName = "MQTT"; byte[] protocolNameBytes = Encoding.UTF8.GetBytes(protocolName);//协议名称,固定为MQTT,对应服务端,UTF8和ASCII编码对英文字母来说都可以,英文字母占1个字节,中文占2个字节 英文字母是最低支持,汉字ASCII是没有的,不可以使用UTF8Encoding //大小端,高位放在低字节 List<byte> bodyBytes = new List<byte>(); bodyBytes.Add((byte)(protocolNameBytes.Length / 256 % 256)); bodyBytes.Add((byte)(protocolNameBytes.Length % 256)); bodyBytes.AddRange(protocolNameBytes); bodyBytes.Add(0x04);//协议级别,固定为4,对应MQTT 3.1.1 //QaS byte flagByte = 0; //用户名 flagByte |= 128; //1000 0000 128 //密码 //0100 0000 64 flagByte |= 64; //1100 0000 变成 //CleanSession //0000 0010 2 flagByte |= 2; //1100 0010 变成 bodyBytes.Add(flagByte); int seconds = 100;//保持连接时间,单位秒,最大65535,对应MQTT 3.1.1 bodyBytes.Add((byte)(seconds / 256 % 256)); bodyBytes.Add((byte)(seconds % 256)); //客户端ID string clientId = "Z001"; byte[] clientIdBytes = Encoding.UTF8.GetBytes(clientId); bodyBytes.Add((byte)(clientIdBytes.Length / 256 % 256)); bodyBytes.Add((byte)(clientIdBytes.Length % 256)); bodyBytes.AddRange(clientIdBytes); //用户名 string userName = "admin"; byte[] userNameBytes = Encoding.UTF8.GetBytes(userName); bodyBytes.Add((byte)(userNameBytes.Length / 256 % 256)); bodyBytes.Add((byte)(userNameBytes.Length % 256)); bodyBytes.AddRange(userNameBytes); //密码 string password = "123456";// apolo默认密码:password byte[] passwordBytes = Encoding.UTF8.GetBytes(password); bodyBytes.Add((byte)(passwordBytes.Length / 256 % 256)); bodyBytes.Add((byte)(passwordBytes.Length % 256)); bodyBytes.AddRange(passwordBytes); headBytes.Add((byte)bodyBytes.Count); headBytes.AddRange(bodyBytes); socket.Send(headBytes.ToArray()); byte[] respBytes = new byte[4]; int recvLen = socket.Receive(respBytes, 0, 4, SocketFlags.None); string respStr = Encoding.UTF8.GetString(respBytes, 0, recvLen); Console.WriteLine(respStr); //判断是否连接正常 //开始心跳 Task.Run(async () => { byte[] pingBytes = new byte[2] { 12 << 4, 0 }; while (true) { await Task.Delay(10000); socket.Send(pingBytes); } }); } static Random random = new Random(); static void Subscription(List<string> topics) { List<byte> headerBytes = new List<byte>(); byte msgType = 8 << 4; // 1000 0000 headerBytes.Add((byte)(msgType | 2)); List<byte> bodyBytes = new List<byte>(); int pi = random.Next(0, 1000); // Package Identifier bodyBytes.Add((byte)(pi / 256 % 256)); bodyBytes.Add((byte)(pi % 256)); foreach (var item in topics) { byte[] itemBytes = Encoding.UTF8.GetBytes(item); bodyBytes.Add((byte)(itemBytes.Length / 256 % 256)); bodyBytes.Add((byte)(itemBytes.Length % 256)); bodyBytes.AddRange(itemBytes); bodyBytes.Add(0x00); // QoS 0 } headerBytes.Add((byte)bodyBytes.Count); headerBytes.AddRange(bodyBytes); socket.Send(headerBytes.ToArray()); } }
浙公网安备 33010602011771号