老外做的中国汉字网站如何用网络推广自己的公司
- 作者: 五速梦信息网
- 时间: 2026年04月20日 10:33
当前位置: 首页 > news >正文
老外做的中国汉字网站,如何用网络推广自己的公司,安卓sdk下载,能赚钱的网站怎么做目录 前言一、WIFI简介二、NTP协议2.1 NTP简介2.2 NTP实现 三、HTTP协议3.1 HTTP协议简介3.2 HTTP服务器 四、MQTT协议4.1 MQTT协议简介4.1.1 MQTT通信模型4.1.2 MQTT协议实现原理4.1.3 MQTT 控制报文 4.2 移植MQTT协议 前言 本文主要介绍一下物联网协议如NTP协议、HTTP协议和M… 目录 前言一、WIFI简介二、NTP协议2.1 NTP简介2.2 NTP实现 三、HTTP协议3.1 HTTP协议简介3.2 HTTP服务器 四、MQTT协议4.1 MQTT协议简介4.1.1 MQTT通信模型4.1.2 MQTT协议实现原理4.1.3 MQTT 控制报文 4.2 移植MQTT协议 前言 本文主要介绍一下物联网协议如NTP协议、HTTP协议和MQTT协议的接口使用 一、WIFI简介 在了解WIFI之前需要了解一下TCP/IP协议和lwIP协议参考以下链接https://blog.csdn.net/weixin_44567668/article/details/139619797 首先lwIP协议是一种专为嵌入式系统设计的轻量级TCP/IP协议栈。lwIP与 TCP/IP体系结构的对应关系 回到WIFI市面上很多都是以太网或者WIFI透传模块他们有的使用SPI接口有的使用UART串口来与MCU进行通讯如ESP8266。但实际上有部分芯片是内嵌WIFI模组的MCU如ESP32、W601等等。这里以ESP32-S3为例其内嵌WiFi MAC内核只需了解它扮演 TCP/IP协议的网络接口层角色即可。如下图所示 到这里对WIFI已经有基本的了解我这里再扩充一下WLAN设备。WLAN 框架是RT-Thread开发的一套用于管理WIFI的中间件。对下连接具体的WIFI驱动控制 WIFI 的连接断开扫描等操作。对上承载不同的应用为应用提供 WIFI 控制事件数据导流等操作为上层应用提供统一的 WIFI 控制接口。WIFI 框架层次图
APP为应用层是基于WLAN框架的具体应用如后面的HTTP、MQTTWLAN protocol为协议层其中上面讲的lwIP协议就处于这一层
二、NTP协议
2.1 NTP简介 NTPNetwork Time Protocol网络时间协议基于UDP是用来使计算机时间同步化的一种协议它可以使计算机对其服务器或时钟源如石英钟GPS 等等做同步化它可以提供高精准度的时间校正LAN 上与标准间差小于 1 毫秒WAN 上几十毫秒且可介由加密确认的方式来防止恶毒的协议攻击。时间按 NTP 服务器的等级传播。按照离外部 UTC 源的远近把所有服务器归入不同的 Stratum层中。 NTP数据报文格式如下图所示 NTP数据报文格式的各个字段的作用如下表所示 从上表可知NTP 报文的字段非常多这些字段并不是每一个都必须设置的可以根据项目的需要来构建 NTP 请求报文。
2.2 NTP实现 由上可以知道获取 NTP 实时时间步骤了 ① 以 UDP 协议连接阿里云 NTP 服务器 ② 发送 NTP 报文到阿里云 NTP 服务器 ③ 获取阿里云 NTP服务器返回的数据取第 40 位到 43 位的十六进制数值。 ④ 把 40 位到 43 位的十六进制数值转成十进制 ⑤ 把十进制数值减去1900-1970 的时间差2208988800 秒 ⑥ 数值转成年月日时分秒 lwip_demo.h头文件 主要创建两个结构体一个用来获取参数一个用来显示时间
#define NTP_DEMO_RX_BUFSIZE 2000 /* 定义udp最大接收数据长度 /
#define NTP_DEMO_PORT 123 / 定义udp连接的本地端口号 /typedef struct _NPTformat
{char version; / 版本号 /char leap; / 时钟同步 /char mode; / 模式 /char stratum; / 系统时钟的层数 /char poll; / 更新间隔 /signed char precision; / 精密度 /unsigned int rootdelay; / 本地到主参考时钟源的往返时间 /unsigned int rootdisp; / 统时钟相对于主参考时钟的最大误差 /char refid; / 参考识别码 /unsigned long long reftime;/ 参考时间 /unsigned long long org; / 开始的时间戳 /unsigned long long rec; / 收到的时间戳 /unsigned long long xmt; / 传输时间戳 */
} NPTformat;typedef struct _DateTime /此结构体定义了NTP时间同步的相关变量/
{int year; /* 年 /int month; / 月 /int day; / 天 /int hour; / 时 /int minute; / 分 /int second; / 秒 /
} DateTime;#define SECS_PERDAY 86400UL / 一天中的几秒钟 60*60*24 /
#define UTC_ADJ_HRS 8 / SEOUL : GMT8东八区北京 /
#define EPOCH 1900 / NTP 起始年 */
#define HOST_NAME ntp1.aliyun.com /*阿里云NTP服务器域名 /lwip_demo.c源文件
#define NTP_TIMESTAMP_DELTA 2208988800UL
const char g_days[12] {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
NPTformat g_ntpformat; / NT数据包结构体 /
DateTime g_nowdate; / 时间结构体 /
uint8_t g_ntp_message[48]; / 发送数据包的缓存区 /
uint8_t g_ntp_demo_recvbuf[NTP_DEMO_RX_BUFSIZE]; / NTP接收数据缓冲区 */uint8_t g_lwip_time_buf[100];/*brief 计算日期时间*param secondsUTC 世界标准时间*retval 无
/
void lwip_calc_date_time(unsigned long long time)
{unsigned int Pass4year;int hours_per_year;if (time 0){time 0;}g_nowdate.second (int)(time % 60); / 取秒时间 /time / 60;g_nowdate.minute (int)(time % 60); / 取分钟时间 /time / 60;g_nowdate.hour (int)(time % 24); / 小时数 */Pass4year ((unsigned int)time / (1461L * 24L));/* 取过去多少个四年每四年有 1461*24 小时 /g_nowdate.year (Pass4year 2) 1970; / 计算年份 */time % 1461 * 24; /* 四年中剩下的小时数 /for (;;) / 校正闰年影响的年份计算一年中剩下的小时数 */{hours_per_year 365 * 24; /* 一年的小时数 /if ((g_nowdate.year 3) 0) / 判断闰年 /{hours_per_year 24; / 是闰年一年则多24小时即一天 /}if (time hours_per_year){break;}g_nowdate.year;time - hours_per_year;}time / 24; / 一年中剩下的天数 /time; / 假定为闰年 /if ((g_nowdate.year 3) 0) / 校正闰年的误差计算月份日期 /{if (time 60){time–;}else{if (time 60){g_nowdate.month 1;g_nowdate.day 29;return ;}}}for (g_nowdate.month 0; g_days[g_nowdate.month] time; g_nowdate.month) / 计算月日 */{time - g_days[g_nowdate.month];}g_nowdate.day (int)(time);return;}/*brief 从NTP服务器获取时间*param buf存放缓存*param idx定义存放数据起始位置*retval 无
*/
void lwip_get_seconds_from_ntp_server(uint8_t buf, uint16_t idx)
{unsigned long long atk_seconds 0; uint8_t i 0;for (i 0; i 4; i) / 获取40~43位的数据 /{atk_seconds (atk_seconds 8) | buf[idx i]; / 把40~43位转成16进制再转成十进制 /}atk_seconds - NTP_TIMESTAMP_DELTA;/ 减去减去1900-1970的时间差2208988800秒 /lwip_calc_date_time(atk_seconds); / 由UTC时间计算日期 */
}/*brief 初始化NTP Client信息*param 无*retval 无
/
void lwip_ntp_client_init(void)
{uint8_t flag;g_ntpformat.leap 0; / leap indicator /g_ntpformat.version 3; / version number /g_ntpformat.mode 3; / mode /g_ntpformat.stratum 0; / stratum /g_ntpformat.poll 0; / poll interval /g_ntpformat.precision 0; / precision /g_ntpformat.rootdelay 0; / root delay /g_ntpformat.rootdisp 0; / root dispersion /g_ntpformat.refid 0; / reference ID /g_ntpformat.reftime 0; / reference time /g_ntpformat.org 0; / origin timestamp /g_ntpformat.rec 0; / receive timestamp /g_ntpformat.xmt 0; / transmit timestamp /flag (g_ntpformat.version 3) g_ntpformat.mode; / one byte Flag */memcpy(g_ntp_message, (void const *)(flag), 1);
}/* brief lwip_demo程序入口* param 无* retval 无
*/
void lwip_demo(void)
{err_t err;static struct netconn *udpconn;static struct netbuf *recvbuf;static struct netbuf *sentbuf;ip_addr_t destipaddr;uint32_t data_len 0;struct pbuf q;lwip_ntp_client_init();/ 第一步创建udp控制块 /udpconn netconn_new(NETCONN_UDP);/ 定义接收超时时间 /udpconn-recv_timeout 10;if (udpconn ! NULL) / 判断创建控制块释放成功 /{/ 第二步绑定控制块、本地IP和端口 /err netconn_bind(udpconn, IP_ADDR_ANY, NTP_DEMO_PORT);/ 域名解析 */netconn_gethostbyname((char )(HOST_NAME), (destipaddr));/ 第三步连接或者建立对话框 /netconn_connect(udpconn, destipaddr, NTP_DEMO_PORT); / 连接到远端主机 /if (err ERR_OK) / 绑定完成 */{while (1){sentbuf netbuf_new();netbuf_alloc(sentbuf, 48);memcpy(sentbuf-p-payload, (void )g_ntp_message, sizeof(g_ntp_message));err netconn_send(udpconn, sentbuf); / 将sentbuf中的数据发送出去 /if (err ! ERR_OK){printf(发送失败\r\n);netbuf_delete(sentbuf); / 删除buf /}netbuf_delete(sentbuf); / 删除buf // 第五步接收数据 /netconn_recv(udpconn, recvbuf);vTaskDelay(1000); / 延时1s /if (recvbuf ! NULL) / 接收到数据 */{memset(g_ntp_demo_recvbuf, 0, NTP_DEMO_RX_BUFSIZE); /*数据接收缓冲区清零 */for (q recvbuf-p; q ! NULL; q q-next) /*遍历完整个pbuf链表 /{/ 判断要拷贝到UDP_DEMO_RX_BUFSIZE中的数据是否大于UDP_DEMO_RX_BUFSIZE的剩余空间如果大于 // 的话就只拷贝UDP_DEMO_RX_BUFSIZE中剩余长度的数据否则的话就拷贝所有的数据 /if (q-len (NTP_DEMO_RX_BUFSIZE - data_len)) memcpy(g_ntp_demo_recvbuf data_len, q-payload, (NTP_DEMO_RX_BUFSIZE - data_len)); / 拷贝数据 /else memcpy(g_ntp_demo_recvbuf data_len, q-payload, q-len);data_len q-len;if (data_len NTP_DEMO_RX_BUFSIZE) break; / 超出TCP客户端接收数组,跳出 /}data_len 0; / 复制完成后data_len要清零 /lwip_get_seconds_from_ntp_server(g_ntp_demo_recvbuf,40); / 从NTP服务器获取时间 /printf(北京时间%02d-%02d-%02d %02d:%02d:%02d\r\n, g_nowdate.year, g_nowdate.month 1,g_nowdate.day,g_nowdate.hour 8,g_nowdate.minute,g_nowdate.second);sprintf((char)g_lwip_time_buf,BJ time:%02d-%02d-%02d %02d:%02d:%02d, g_nowdate.year, g_nowdate.month 1,g_nowdate.day,g_nowdate.hour 8,g_nowdate.minute,g_nowdate.second);lcd_show_string(5, 170, lcddev.width, 16, 16, (char)g_lwip_time_buf, RED);netbuf_delete(recvbuf); / 删除buf /}else vTaskDelay(5); / 延时5ms */}}else printf(NTP绑定失败\r\n);}else printf(NTP连接创建失败\r\n);
}在此文件下定义了四个函数这些函数的作用如下表所示
函数描述lwip_demo()实现UDP连接使用NETCONN接口lwip_ntp_client_init()构建NTP请求报文lwip_get_seconds_from_ntp_server()获取NTP服务器的数据lwip_calc_date_time()计算日期时间
三、HTTP协议
3.1 HTTP协议简介 HTTPHypertext Transfer Protocol协议即超文本传输协议是用于从万维网WWW:World Wide Web 服务器传输超文本到本地浏览器的传送协议。HTTP 协议是基于TCP/IP 协议的网络应用层协议。默认端口为80端口。HTTP 协议是一种请求/响应式的协议。一个客户端与服务器建立连接之后发送一个请求给服务器。服务器接收到请求之后通过接收到的信息判断响应方式并且给予客户端相应的响应完成整个 HTTP数据交互流程。 HTTP定义了与服务器交互的不同方法其最基本的方法是 GET、PORT 和 HEAD。如下图所示。
GET从服务端获取数据。PORT向服务器传送数据。HEAD检测一个对象是否存在。 互联网通过URL来定位URL全称是 Uniform Resource Locator是互联网上用来标识某一处资源的绝对地址大部分 URL 都会遵循 URL 的语法一个 URL 的组成有多个不同的组件一个 URL的通用格式如下
scheme://user:passwordhost:port/path;params?query#fragHTTP 报文是由 3 个部分组成分别是对报文进行描述的“起始行”包含属性的“首部”以及可选的“数据主体”对于请求报文与应答报文只有“起始行”的格式是不一样的。起始行和首部就是由行分隔的 ASCII 文本组成每行都以由两个字符组成的行终止序列作为结束其中包括一个回车符ASCII 码 13和一个换行符ASCII 码 10 这个行终止序列可以写做 CRLF。
HTTP请求报文
method request-URL version //起始行 headers //首部entity-body //数据主体# HTTP应答报文 version status reason-phrase //起始行 headers //首部entity-body //数据主体下面就对这两种 HTTP 报文的各个部分简单描述一下 方法methodHTTP 请求报文的起始行以方法作为开始方法用来告知服务器要做些什么常见的方法有 GET、POST、HEAD 等比如“GET /forum.php HTTP/1.1” 使用的就是 GET 方法。请求 URLrequest-URL指定了所请求的资源。版本version指定报文所使用的 HTTP 协议版本其中指定了主要版本号 指定了次要版本号它们都是整数其格式如下 HTTP/major.minor状态码status这是在 HTTP 应答报文中使用的状态码是在每条响应报文的起始行中返回的一个数字码描述了请求过程中所发送的情况比如成功、失败等不同的状态码有不同的含义具体见表格 原因短语reason-phrase这其实是给我们看的原因短语因为数字是不够直观它只是状态码的一个文本形式表达而已。首部headerHTTP 报文可以有 0 个、1 个或者多个首部HTTP 首部字段向请求和响应报文中添加了一些附加信息从本质上来说它们是一个名字值对每个首部都包含一个名字紧跟着一个冒号“:”然后是一个可选的空格接着是一个值最后以 CRLF 结束比如“Host: www.firebbs.cn”就是一个首部。数据主体entity-body这部分包含一个由任意数据组成的数据块其实这与我们前面所讲的报文数据区域是一样的用于携带数据HTTP 报文可以承载很多类型的数字数据图片、视频、音频、HTML 文档、软件应用程 序等。 3.2 HTTP服务器 HTTP协议可以应用在客户端也可以在服务器端在客户端可以用来获取服务器数据比如从服务器下载固件进行升级。也可以用在服务器端那样我们可以做一个简单网页来访问控制单片机。同上一个例程一样新建一个任务调用函数lwip_demo() /* HTTP报头总是以响应码开头(例如HTTP/1.1 200 OK)和一个内容类型以便客户端知道接下来是什么然后是一个空行: / / 浏览器响应数据类型为文本数据 / static const char http_html_hdr[] HTTP/1.1 200 OK\r\nContent-type: text/html\r\n\r\n; static const char http_index_html[] !DOCTYPE html\html\head\title Webserver实验 /title\meta http-equivContent-Type contenttext/html; charsetGB2312/\/head\body\h1http server/h1\div classlabel \labelLED State:/label\/div\div classcheckboxes\input typecheckbox nameled1 value1 /打开nbsp;nbsp;nbsp;input typecheckbox nameled1 value2 /关闭\/div\br\br\div classlabel\labelBEEP State:/label\/div\div classcheckboxes\input typecheckbox nameled1 value1 /打开nbsp;nbsp;nbsp;input typecheckbox nameled1 value2 /关闭\/div\br\br\input typesubmit classsendbtn value发送\br\/body\/html;/** brief 寻找指定字符位置* param buf 缓冲区指针* param name 寻找字符* retval 返回字符的地址*/ char *lwip_data_locate(char *buf, char *name) {char *p;p strstr((char )buf, name);if (p NULL){return NULL;}p strlen(name);return p; }/** brief 服务HTTP线程中接受的一个HTTP连接* param conn netconn控制块* retval 无*/ static void lwip_server_netconn_serve(struct netconn *conn) {struct netbuf *inbuf;char *buf;u16_t buflen;err_t err;char ptemp;/ 从端口读取数据如果那里还没有数据则阻塞。我们假设请求(我们关心的部分)在一个netbuf中 /err netconn_recv(conn, inbuf);if (err ERR_OK){netbuf_data(inbuf, (void **)buf, buflen);/ 这是一个HTTP GET命令吗?只检查前5个字符因为GET还有其他格式我们保持简单)/if (buflen 5 buf[0] G buf[1] E buf[2] T buf[3] buf[4] / ){start_html:/ 发送HTML标题从大小中减去1因为我们没有在字符串中发送\0NETCONN_NOCOPY:我们的数据是常量静态的所以不需要复制它 /netconn_write(conn, http_html_hdr, sizeof(http_html_hdr) - 1, NETCONN_NOCOPY);/ 发送我们的HTML页面 */netconn_write(conn, http_index_html, sizeof(http_index_html) - 1, NETCONN_NOCOPY);}else if(buflen8buf[0]Pbuf[1]Obuf[2]Sbuf[3]T){ptemp lwip_data_locate((char *)buf, led1);if (ptemp ! NULL){if (ptemp 1) / 查看led1的值。为1则灯亮为2则灭此值与HTML网页中设置有关 /{LED0(0); / 点亮LED1 /}else{LED0(1); / 熄灭LED1 */}}ptemp lwip_data_locate((char )buf, beep); / 查看beep的值。为3则灯亮为4则灭此值与HTML网页中设置有关 */if (ptemp ! NULL ){if (ptemp 3){/ 打开蜂鸣器 /}else{/ 关闭蜂鸣器 /}}goto start_html;}}/ 关闭连接(服务器在HTTP中关闭) /netconn_close(conn);/ 删除缓冲区(netconn_recv给我们所有权所以我们必须确保释放缓冲区) */netbuf_delete(inbuf); }/**
- brief lwip_demo程序入口
- param 无
- retval 无
*/
void lwip_demo(void)
{struct netconn *conn, newconn;err_t err;/ 创建一个新的TCP连接句柄 // 使用默认IP地址绑定到端口80 (HTTP) /conn netconn_new(NETCONN_TCP);netconn_bind(conn, IP_ADDR_ANY, 80);/ 将连接置于侦听状态 */netconn_listen(conn);do{err netconn_accept(conn, newconn);if (err ERR_OK){lwip_server_netconn_serve(newconn);//调用HTTP服务器子程序netconn_delete(newconn);}}while (err ERR_OK);netconn_close(conn);netconn_delete(conn);
}lwip_demo()建立 TCP 连接lwip_data_locate()寻找指定字符位置lwip_server_netconn_serve()服务 HTTP 线程中接受的一个HTTP连接主要分为三步 当浏览器输入IP地址并且回车确认时程序调用函数netconn_write把网页数据发送到浏览器当中当网页发送一个PORT命令时程序调用函数lwip_data_locate判断触发源判断完成之后根据触发源来执行相应的动作程序执行goto语句重新发送网页字符串到网页当中这个步骤相当于更新网页网页样式如下 其中网页格式为HTML具体语法可参考
HTML5超文本标记语言https://blog.csdn.net/weixin_44567668/article/details/125626370CSS3层叠样式表https://blog.csdn.net/weixin_44567668/article/details/132521477
四、MQTT协议
4.1 MQTT协议简介 MQTTMessage Queuing Telemetry Transport消息队列遥测传输协议是一个基于客户端-服务器的消息发布/订阅publish/subscribe传输协议该协议构建于 TCP/IP 协议上由 IBM 在 1999 年发布。
4.1.1 MQTT通信模型 实现 MQTT 协议需要客户端和服务器端 MQTT 协议中有三种身份发布者Publish、代理Broker服务器、订阅者Subscribe。其中消息的发布者和订阅者都是客户端消息代理是服务器消息发布者可以同时是订阅者如下图所示 MQTT 传输的消息分为主题Topic和消息的内容payload两部分
Topic可以理解为消息的类型订阅者订阅Subscribe后就会收到该主题的消息内容payload。Payload可以理解为消息的内容是指订阅者具体要使用的内容。
4.1.2 MQTT协议实现原理 要在客户端与代理服务端建立一个 TCP 连接建立连接的过程是由客户端主动发起的代理服务一直是处于指定端口的监听状态当监听到有客户端要接入的时候就会立刻去处理。客户端在发起连接请求时携带客户端 ID、账号、密码、心跳间隔时间等数据。代理服务收到后检查自己的连接权限配置中是否允许该账号密码连接如果允许则建立会话标识并保存绑定客户端 ID 与会话并记录心跳间隔时间判断是否掉线和启动遗嘱时用和遗嘱消息等然后回发连接成功确认消息给客户端客户端收到连接成功的确认消息后进入下一步通常是开始订阅主题如果不需要订阅则跳过。如下图所示 客户端将需要订阅的主题经过 SUBSCRIBE 报文发送给代理服务代理服务则将这个主题记录到该客户端 ID 下以后有这个主题发布就会发送给该客户端然后回复确认消息SUBACK 报文客户端接到 SUBACK 报文后知道已经订阅成功则处于等待监听代理服务推送的消息也可以继续订阅其他主题或发布主题如下图所示 当某一客户端发布一个主题到代理服务后代理服务先回复该客户端收到主题的确认消息该客户端收到确认后就可以继续自己的逻辑了。但这时主题消息还没有发给订阅了这个主题的客户端代理要根据质量级别QoS来决定怎样处理这个主题。所以这里充分体现了是MQTT 协议是异步通信模式不是立即端到端反应的如下图所示
如果发布和订阅时的质量级别 QoS 都是至多一次那代理服务则检查当前订阅这个主题的客户端是否在线在线则转发一次收到与否不再做任何处理。这种质量对系统压力最小。如果发布和订阅时的质量级别 QoS 都是至少一次那要保证代理服务和订阅的客户端都有成功收到才可以否则会尝试补充发送具体机制后面讨论。这也可能会出现同一主题多次重复发送的情况。这种质量对系统压力较大。如果发布和订阅时的质量级别 QoS 都是只有一次那要保证代理服务和订阅的客户端都有成功收到并只收到一次不会重复发送具体机制后面讨论。这种质量对系统压力最大。 4.1.3 MQTT 控制报文 固定报头 MQTT 协议工作在 TCP 协议之上因为客户端和服务器都是应用层那么必然需要一种协议在两者之间进行通信那么随之而来的就是 MQTT 控制报文 MQTT 控制报文有3个部分组成分别是固定报头fixed header、可变报头variable header、有效荷载数据区域 payload。固定报头所有的 MQTT 控制报文都包含可变报头与有效载荷是部分 MQTT 控制报文包含。固定报头占据两字节的空间具体见图 固定报头的第一个字节分为控制报文的类型4bit以及控制报文类型的标志位控制类型共有 14 种其中0与15被系统保留出来其他的类型具体见表格 固定报头的 bit0-bit3 为标志位依照报文类型有不同的含义事实上除了 PUBLISH类型报文以外其他报文的标志位均为系统保留PUBLISH 报文的第一字节 bit3 是控制报文的重复分发标志DUPbit1-bit2 是服务质量等级bit0 是 PUBLISH 报文的保留标志用于标识 PUBLISH 是否保留当客户端发送一个 PUBLISH 消息到服务器如果保留标识位置 1那么服务器应该保留这条消息当一个新的订阅者订阅这个主题的时候最后保留的主题消息应被发送到新订阅的用户。 固定报头的第二个字节开始是剩余长度字段是用于记录剩余报文长度的表示当前的消息剩余的字节数包括可变报头和有效载荷区域如果存在但剩余长度不包括用于编码剩余长度字段本身的字节数。 剩余长度字段使用一个变长度编码方案对小于 128 的值它使用单字节编码而对于更大的数值则按下面的方式处理每个字节的低 7 位用于编码数据长度最高位bit7用于标识剩余长度字段是否有更多的字节且按照大端模式进行编码因此每个字节可以编码 128 个数值和一个延续位剩余长度字段最大可拥有 4 个字节。 可变报头 可变报头并不是所有的 MQTT 报文都带有的比如 PINGREQ 心跳请求与 PINGRESP心跳响应报文就没有可变报头只有某些报文才拥有可变报头它在固定报头和有效负载之间可变报头的内容会根据报文类型的不同而有所不同但可变报头的报文标识符Packet Identifier字段存在于在多个类型的报文里而有一些报文又没有报文标识符字段具体见表格 报文标识符结构具体见图 因为对于不同的报文可变报头是不一样的下面就简单讲解几个报文的可变报头
CONNECT 在一个会话中客户端只能发送一次 CONNECT 报文它是客户端用于请求连接服务器的报文常称之为连接报文如果客户端发送多次连接报文那么服务端必须将客户端发送的第二个 CONNECT 报文当作协议违规处理并断开客户端的连接。CONNECT 报文的可变报头包含四个字段协议名Protocol Name、协议级别Protocol Level、连接标志Connect Flags以及保持连接Keep Alive字段。协议名是 MQTT 的 UTF-8 编码的字符串其中还包含用于记录协议名长度的两字节字段 MSB 与 LSB。 在协议名之后的是协议级别MQTT 协议使用 8 位的无符号值表示协议的修订版本对于 MQTT3.1 版的协议协议级别字段的值是 3(0x03)而对于 MQTT3.1.1 版的协议协议级别字段的值是 4(0x04)。如果服务器发现连接报文中的协议级别字段是不支持的协议级别服务端必须给发送一个返回码为 0x01不支持的协议级别的 CONNACK 响应连接报文然后终止客户端的连接请求。连接标志字段涉及的内容比较多它在协议级别之后使用一个字节表示但分成很多个标志位具体见图 bit0是 MQTT 保留的标志位在连接过程中服务器会检测连接标志的 bit0 是否为 0如果不为 0 则服务器任务这个连接报文是不合法的会终止连接请求。bit1是清除会话标志 Clean Session如果清除会话标志设置为 1那么客户端不会收到旧的应用消息清除会话标志设置为 0 的客户端在重新连接后会收到所有在它连接断开期间其他发布者发布的 QoS1 和 QoS2 级别的消息。因此要确保不丢失连接断开期间的消息需要使用 QoS1 或 QoS2 级别同时将清除会话标志设置为 0。bit2是遗嘱标志 Will Flag如果该位被设置为 1表示如果客户端与服务器建立了会话遗嘱消息Will Message将必须被存储在服务器中当这个客户端断开连接的时候遗嘱消息将被发送到订阅这个会话主题的所有订阅者这个消息是很有用的我们可以知道这个设备的状况它是否已经掉线了以备启动备用方案当然想要不发送遗嘱消息也是可以的只需要让服务器端收到 DISCONNECT 报文时删除这个遗嘱消息即可。bit3-bit4用于指定发布遗嘱消息时使用的服务质量等级与其他消息的服务质量是一样的遗嘱 QoS 的值可以等于 0(0x00)1(0x01)2(0x02)当然使用遗嘱消息的前提是遗嘱标志位为 1。bit5表示遗嘱保留标志位当客户端意外断开连接时如果 Will Retain 置一那么服务器必须将遗嘱消息当作保留消息发布反之则无需保留。bit6是密码标志位 Password Flag如果密码标志被设置为 0有效载荷中不能包含密码字段反之则必须包含密码字段。bit7是用户名标志位 User Name Flag如果用户名标志被设置为 0有效载荷中不能包 含用户名字段反之则必须包含用户名字段。 总的来说整个 CONNECT 报文可变报头的内容如下
CONNACK 它是由连接确认标志字段Connect Acknowledge Flags与连接返回码字段 Connect Return code组成各占用 1 个字节。它的第 1 个字节是 连接确认标志字段bit1-bit7 是保留位且必须设置为 0 bit0 是当前会话Session Present标志位。它的第 2 个字节是返回码字段如果服务器收到一个 CONNECT 报文但出于某些原因无法处理它服务器会返回一个包含返回码的 CONNACK 报文。如果服务器返回了一个返回码字段是非 0 的 CONNACK 报文那么它必须关闭网络连接返回码描述具体见表格 如果服务端收到清理会话CleanSession标志为 1 的连接除了将 CONNACK报文中的返回码设置为 0 之外还必须将 CONNACK 报文中的当前会话设置Session Present标志为 0。那么总的来说CONNACK 报文的可变报头部分内容具体见图
4.2 移植MQTT协议
相关文章
-
老外做的中国汉字网站开发公司交的农民工工资保证金可以退还吗
老外做的中国汉字网站开发公司交的农民工工资保证金可以退还吗
- 技术栈
- 2026年04月20日
-
老师用什么网站做pptthinkphp 网站源码
老师用什么网站做pptthinkphp 网站源码
- 技术栈
- 2026年04月20日
-
老师问我做网站用到什么创新技术做龙之向导网站有用吗
老师问我做网站用到什么创新技术做龙之向导网站有用吗
- 技术栈
- 2026年04月20日
-
老鹰网营销型网站建设销售网站怎么做的
老鹰网营销型网站建设销售网站怎么做的
- 技术栈
- 2026年04月20日
-
乐陵seo优化seo网站推广多少钱
乐陵seo优化seo网站推广多少钱
- 技术栈
- 2026年04月20日
-
乐陵森seosem推广
乐陵森seosem推广
- 技术栈
- 2026年04月20日
