CH395+EMQX实现MQTT应用(Windows系统)
- MQTT协议
1.MQTT简介
MQTT是一种基于 发布/订阅 模式的轻量级消息协议,工作在TCP/IP协议族上。其最大的优点是用极少量的代码和有限的宽带为设备间提供实时可靠的消息服务。在物联网(IOT)和机器与机器(M2M)等方面有较广泛的应用。
2.MQTT特性
2.1 发布/订阅模式,提供一对多的消息发布,方便消息在传感器间传递,解耦Client/Server的模式,不在需要预先知道对端的存在,不用同时运行
2.2 提供服务质量管理(QoS):
①Qos0(至多一次):发送方发送的消息,接收方最多能收到一次。这一级别会发生消息丢失或者重复,消息的发布依赖于底层TCP/IP网络。这一级别可用于如传感器数据上发,丢掉一次无所谓的情况,因为之后会二次发送。
②Qos1(至少一次):发送方发送的消息,接收方最少能收到一次,因此消息可能会发生重复。
③Qos2(只有一次):确保消息只到达一次。这一级别在网络中开销最高,适用于一些对数据严格的场景。
2.3 MQTT有1字节固定报头,2字节心跳报文,可以最小化传输开销,有效减少网络流量。因此非常时候应用在物联网,传感器数据采集和嵌入式设备信息收集等领域。
3.MQTT实现方式
3.1 使用MQTT协议通讯时,有三种身份:发布者(publish)、代理(Broker/服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息的代理是服务器,消息的发布者同时可以是订阅者。
3.2 MQTT传输的消息分为主题(Topic)和负载(Payload)两部分:
主题:即消息的类型,订阅者订阅某主题后,就会收到该主题的消息内容
负载:即消息的具体内容

3.3 MQTT客户端:是指一个使用MQTT协议的应用程序或设备,客户端可以:发布其他客户端可能会订阅的信息;订阅其他客户端发布的信息
MQTT服务器:位于消息发布者和订阅者之间,服务器可以:接收客户端的连接;接收客户端发布的信息;处理客户端的订阅和退订;向订阅者转发消息。
4.MQTT数据包结构
4.1 MQTT协议中,一个MQTT数据包由固定头、可变头、消息体三部分组成。
固定头(Fixed header):存在所有MQTT数据包中,表示数据包类型。固定头是不虚的,所有MQTT数据包中必须包含固定头。
可变头(Variable header):部分MQTT数据包有,由数据包类型所决定。
消息体(Payload):存在于部分MQTT数据包中,表示客户端收到的具体内容,也是由数据包类型决定。
4.2 固定头
固定头包含两部分(如下图),首字节为第一字节,剩余消息报文长度是指当前数据包中剩余内容长度的字节数。
|
bit |
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ||
| Byte1 | MQTT数据包类型 | 数据包类型标识 | ||||||||
| Byte2 | 剩余长度 | |||||||||
- 其中,Byte1中的MQTT数据包类型和标识:
| 数据包类型(说明) | 7-4bit值 | 数据方向 | bit3 | bit2 | bit1 | bit0 |
| 保留 | 0000 | |||||
| CONNECT (客户端连接服务器) | 0001 | Client--->Server | 0 | 0 | 0 | 0 |
| CONNACK (连接确认) | 0010 | Server--->Client | 0 | 0 | 0 | 0 |
| PUBLISH (发布消息) | 0011 | Client<-->Server | DUP | Qos | Qos | RETAIN |
| PUBACK (发布确认Qos1) | 0100 | Client<-->Server | 0 | 0 | 0 | 0 |
| PUBREC (消息已接收 Qos2一阶段) | 0101 | Client<-->Server | 0 | 0 | 0 | 0 |
| PUBREL (消息释放 Qos2二阶段) | 0110 | Client<-->Server | 0 | 0 | 1 | 0 |
| PUBCOMP (发布结束 Qos2三阶段) | 0111 | Client<-->Server | 0 | 0 | 0 | 0 |
| SUBSCRIBE(客户端请求订阅) | 1000 | Client--->Server | 0 | 0 | 1 | 0 |
| SUBACK (服务端确认订阅) | 1001 | Server--->Client | 0 | 0 | 0 | 0 |
| UNSUBACRIBE(客户端取消订阅) | 1010 | Client--->Server | 0 | 0 | 1 | 0 |
- 如果bit3 DUP位为1,表示数据包是重复的消息;如果为0则表示该数据包是第一次发布
- 如果 bit1 和 bit2都为0,则表示Qos0;如果 bit1 为1,则表示Qos1;如果 bit2 为1,则表示Qos2;如果 bit1和 bit2都置1,则表示非法消息,会关闭当前连接。
4.3 可变头和消息体的相关报文解析可以查看官方文档
MQTT官方文档:MQTT Version 3.1.1 (oasis-open.org)
- EMQX具体操作步骤
1.EMQX下载: https://packages.emqx.net/emqx-ce/v4.3.22/emqx-windows-4.3.22.zip
2.下载完成后会生成一个emqx文件夹

3.右击开始
图标,选择Windows PowerShell(管理员)
4.①使用cd命令进入eqmx文件夹下的bin;
②进入bin文件后使用 .\emqx install ,出现成功则表示安装完成。
③使用 .\emqx console,会出现一个弹窗,点击确认即可,之后会打初始化一系列使用到的端口,如果最终成功,会出现EMQX is running now!

5.打开浏览器输入localhost:18083即可进入EMQX登录界面,初始账号为admin,密码为public。
6.当我们用395成功连接上EMQX服务器后,在客户端列表中能看到我们设备的信息,点击Client ID下的 admin1,能详细的看到我们设备的ip、端口等信息。


7.在监视器界面能看到具体数据量,在主题界面能看到设备所发布的主题。


- 相关代码说明
1.MQTT驱动文件

2. CH395.c
1 /* CH395相关定义 */
2 const UINT8 CH395IPAddr[4] = {192,168,1,100}; /* CH395IP地址 */
3 const UINT8 CH395GWIPAddr[4] = {192,168,1,1}; /* CH395网关 */
4 const UINT8 CH395IPMask[4] = {255,255,255,0}; /* CH395子网掩码 */
5
6 /* socket 相关定义*/
7 const UINT8 Socket0DesIP[4] = {192,168,1,24}; /* Socket 0目的IP地址 */
8 const UINT16 Socket0DesPort = 1883; /* Socket 0目的端口 */
9 const UINT16 Socket0SourPort = 4000; /* Socket 0源端口 */
10
11 u8 publishValid = 0;
12 u16 timeCnt = 0;
13 char *username = "user2"; //Device name, unique for each device, available "/" for classification
14 char *password = "user2"; //Server login password
15 char *sub_topic = "topic_wch"; //subscribed session name
16 char *pub_topic = "topic_wch"; //Published session name
17 int pub_qos = 0; //Publish quality of service
18 int sub_qos = 0; //Subscription quality of service
19 char *payload = "WCHNET MQTT"; //Publish content
20 //有效负载
21 u8 con_flag = 0; //Connect MQTT server flag
22 u8 pub_flag = 0; //Publish session message flag/
23 u8 sub_flag = 0; //Subscription session flag
24 u8 tout_flag = 0; //time-out flag
25 u16 packetid = 5; //package id
程序使用时,需要
①将Socket0DesIP改为相应电脑的本机IP
②username为用户名称,sub_topic为订阅的主题名,pub_topic为发布消息的主题名,pub_qos为发布消息的服务质量,sub_qos为订阅消息的服务质量,payload为负载。
1 /*********************************************************************
2 * @fn TIM2_Init
3 *
4 * @brief Initializes TIM2.
5 *
6 * @return none
7 */
8 void TIM2_Init( void )
9 {
10 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure={0};
11
12 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
13
14 TIM_TimeBaseStructure.TIM_Period = SystemCoreClock / 1000000 - 1;
15 TIM_TimeBaseStructure.TIM_Prescaler = WCHNETTIMERPERIOD * 1000 - 1;
16 TIM_TimeBaseStructure.TIM_ClockDivision = 0;
17 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
18 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
19 TIM_ITConfig(TIM2, TIM_IT_Update ,ENABLE);
20
21 TIM_Cmd(TIM2, ENABLE);
22 TIM_ClearITPendingBit(TIM2, TIM_IT_Update );
23 NVIC_EnableIRQ(TIM2_IRQn);
24 }
25
26 void TIM2_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
27 void TIM2_IRQHandler(void)
28 {
29 if(!publishValid){ //Set the publishValid flag every 20s
30 timeCnt += 10;
31 if(timeCnt > 20000){
32 publishValid = 1;
33 timeCnt = 0;
34 }
35 }
36 // WCHNET_TimeIsr(WCHNETTIMERPERIOD);
37 TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
38 }
定时器设置,20秒给MQTT服务器上传一包数据,数据内容即为 WCHNET MQTT。
1 /**********************************************************************************
2 * Function Name : InitCH395InfParam
3 * Description : 初始化CH395Inf参数
4 * Input : None
5 * Output : None
6 * Return : None
7 **********************************************************************************/
8 void InitCH395InfParam(void)
9 {
10 memset(&CH395Inf,0,sizeof(CH395Inf)); /* 将CH395Inf全部清零*/
11 memcpy(CH395Inf.IPAddr,CH395IPAddr,sizeof(CH395IPAddr)); /* 将IP地址写入CH395Inf中 */
12 memcpy(CH395Inf.GWIPAddr,CH395GWIPAddr,sizeof(CH395GWIPAddr)); /* 将网关IP地址写入CH395Inf中 */
13 memcpy(CH395Inf.MASKAddr,CH395IPMask,sizeof(CH395IPMask)); /* 将子网掩码写入CH395Inf中 */
14 }
15
16
17 /**********************************************************************************
18 * Function Name : InitSocketParam
19 * Description : 初始化socket
20 * Input : None
21 * Output : None
22 * Return : None
23 **********************************************************************************/
24 void InitSocketParam(void)
25 {
26 memset(&SockInf,0,sizeof(SockInf)); /* 将SockInf[0]全部清零*/
27 memcpy(SockInf.IPAddr,Socket0DesIP,sizeof(Socket0DesIP)); /* 将目的IP地址写入 */
28 SockInf.DesPort = Socket0DesPort; /* 目的端口 */
29 SockInf.SourPort = Socket0SourPort; /* 源端口 */
30 SockInf.ProtoType = PROTO_TYPE_TCP; /* TCP模式 */
31 SockInf.TcpMode = TCP_CLIENT_MODE;
32 }
33 /**********************************************************************************
34 * Function Name : CH395SocketInitOpen
35 * Description : 配置CH395 socket 参数,初始化并打开socket
36 * Input : None
37 * Output : None
38 * Return : None
39 **********************************************************************************/
40 void CH395SocketInitOpen(void)
41 {
42 UINT8 i;
43
44 /* socket 0为TCP 客户端模式 */
45 CH395SetSocketDesIP(0,SockInf.IPAddr); /* 设置socket 0目标IP地址 */
46 CH395SetSocketProtType(0,SockInf.ProtoType); /* 设置socket 0协议类型 */
47 CH395SetSocketDesPort(0,SockInf.DesPort); /* 设置socket 0目的端口 */
48 CH395SetSocketSourPort(0,SockInf.SourPort); /* 设置socket 0源端口 */
49
50 i = CH395OpenSocket(0); /* 打开socket 0 */
51 mStopIfError(i); /* 检查是否成功 */
52
53 i = CH395TCPConnect(0); /* TCP连接 */
54 mStopIfError(i); /* 检查是否成功 */ /* 检查是否成功 */
55
56 }
因为MQTT协议是基于TCP/IP协议,所以初始化395后,需要设置一个socket为tcp模式。
1 /*********************************************************************
2 * @fn MQTT_Connect
3 *
4 * @brief Establish MQTT connection.
5 *
6 * @param username - user name.
7 * password - password
8 *
9 * @return none
10 */
11 void MQTT_Connect(char *username, char *password)
12 {
13 MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
14 u32 len;
15 u8 buf[200];
16
17 data.clientID.cstring = "admin1";
18 data.keepAliveInterval = 2000;
19 data.cleansession = 1;
20 data.username.cstring = username;
21 data.password.cstring = password;
22
23 len = MQTTSerialize_connect(buf,sizeof(buf),&data);//
24 Transport_SendPacket(buf,len);
25 }
MQTT连接函数
17行 设置设备id,
18行 为心跳间隔,
19行 设置Clean Session标志,当设置为0时,表示创建一个持久对话,在客户端断开连接时,会话仍会保持并保存离线消息,直到会话超时注销。
当设置成1时,表示创建一个新的临时对话,当客户端断开时,会话自动注销。
(此部分为报文类型CONNECT时,可变头中的相关内容,详细报文内容可以查阅官方手册)
1 /*********************************************************************
2 * @fn MQTT_Subscribe
3 *
4 * @brief MQTT subscribes to a topic.
5 *
6 * @param topic - Topic name to subscribe to.
7 * req_qos - quality of service
8 *
9 * @return none
10 */
11 void MQTT_Subscribe( char *topic,int req_qos)
12 {
13 MQTTString topicString = MQTTString_initializer;
14 u32 len;
15 u32 msgid = 1;//
16 u8 buf[200];
17
18 topicString.cstring = topic;
19 len = MQTTSerialize_subscribe(buf,sizeof(buf),0,msgid,1,&topicString,&req_qos);//
20 Transport_SendPacket(buf,len);//
21 }
22 /*********************************************************************
23 * @fn MQTT_Unsubscribe
24 *
25 * @brief MQTT unsubscribe from a topic.
26 *
27 * @param topic - Topic name to unsubscribe to.
28 *
29 * @return none
30 */
31 void MQTT_Unsubscribe(char *topic)
32 {
33 MQTTString topicString = MQTTString_initializer;
34 u32 len;
35 u32 msgid = 1;
36 u8 buf[200];
37
38 topicString.cstring = topic;
39 len = MQTTSerialize_unsubscribe(buf,sizeof(buf),0,msgid,1,&topicString);
40 Transport_SendPacket(buf,len);
41 }
42 /*********************************************************************
43 * @fn MQTT_Publish
44 *
45 * @brief MQTT publishes a topic.
46 *
47 * @param topic - Published topic name.
48 * qos - quality of service
49 * payload - data buff
50 *
51 * @return none
52 */
53 void MQTT_Publish(char *topic, int qos, char *payload)
54 {
55 MQTTString topicString = MQTTString_initializer;
56 u32 payloadlen;
57 u32 len;
58 u8 buf[1024];
59
60 topicString.cstring = topic;
61 payloadlen = strlen(payload);
62 len = MQTTSerialize_publish(buf,sizeof(buf),0,qos,0,packetid++,topicString,payload,payloadlen);
63 Transport_SendPacket(buf,len);
64 }
上面三个函数分别为订阅主题,取订主题,发布消息的功能函数,直接调用即可。
1 /**********************************************************************************
2 * Function Name : CH395SocketInterrupt
3 * Description : CH395 socket 中断,在全局中断中被调用
4 * Input : sockindex
5 * Output : None
6 * Return : None
7 **********************************************************************************/
8 void CH395SocketInterrupt(UINT8 sockindex)
9 {
10 int qos, payloadlen;
11 MQTTString topicName;
12 UINT8 sock_int_socket;
13 UINT8 i;
14 UINT32 len;
15 // UINT16 tmp;
16 unsigned short packetid;
17 unsigned char retained, dup;
18 unsigned char *payload;
19
20 sock_int_socket = CH395GetSocketInt(sockindex); /* 获取socket 的中断状态 */
21 if(sock_int_socket & SINT_STAT_SENBUF_FREE) /* 发送缓冲区空闲,可以继续写入要发送的数据 */
22 {
23 }
24 if(sock_int_socket & SINT_STAT_SEND_OK) /* 发送完成中断 */
25 {
26 }
27 if(sock_int_socket & SINT_STAT_RECV) /* 接收中断 */
28 {
29 len = CH395GetRecvLength(sockindex); /* 获取当前缓冲区内数据长度 */
30 #if CH395_DEBUG
31 printf("receive len = %d\n",len);
32 #endif
33 if(len == 0)return;
34 if(len > 512)len = 512; /* MyBuffer缓冲区长度为512 */
35 CH395GetRecvData(sockindex,len,MyBuffer); /* 读取数据 */
36 // for(tmp =0; tmp < len; tmp++) /* 将所有数据按位取反 */
37 // {
38 // MyBuffer[tmp] = ~MyBuffer[tmp];
39 // }
40 switch(MyBuffer[0] >>4)//判断数据包类型
41 {
42 case CONNACK:
43 printf("CONNACK\r\n");
44 con_flag = 1;
45 MQTT_Subscribe(sub_topic, sub_qos);
46 break;
47 case PUBLISH:
48 MQTTDeserialize_publish(&dup,&qos,&retained,&packetid,&topicName,
49 &payload,&payloadlen,MyBuffer,len);
50 msgDeal(payload, payloadlen);
51 break;
52
53 case SUBACK:
54 sub_flag = 1;
55 printf("SUBACK\r\n");
56 break;
57
58 default:
59
60 break;
61 }
62 memset(MyBuffer, 0 ,sizeof(MyBuffer));
63 // CH395SendData(sockindex,MyBuffer,len);
64
65 }
66
67 /*
68 **产生断开连接中断和超时中断时,CH395默认配置是内部主动关闭,用户不需要自己关闭该Socket,如果想配置成不主动关闭Socket需要配置
69 **SOCK_CTRL_FLAG_SOCKET_CLOSE标志位(默认为0),如果该标志为1,CH395内部不对Socket进行关闭处理,用户在连接中断和超时中断时调用
70 **CH395CloseSocket函数对Socket进行关闭,如果不关闭则该Socket一直为连接的状态(事实上已经断开),就不能再去连接了。
71 */
72 if(sock_int_socket & SINT_STAT_CONNECT) /* 连接中断,仅在TCP模式下有效*/
73 {
74 // CH395SetKeepLive(sockindex,1); /*打开KEEPALIVE保活定时器*/
75 // CH395SetTTLNum(sockindex,40); /*设置TTL*/
76 MQTT_Connect(username, password);
77 printf("TCP Connect Success\r\n");
78 }
79
80 if(sock_int_socket & SINT_STAT_DISCONNECT) /* 断开中断,仅在TCP模式下有效 */
81 {
82 con_flag = 0;
83 printf("TCP Disconnect\r\n");
84 }
85 if(sock_int_socket & SINT_STAT_TIM_OUT) /* 超时中断 */
86 {
87 printf("time out \n");
88 con_flag = 0;
89 // i = CH395CloseSocket(sockindex);
90 // mStopIfError(i);
91 }
92 }
在socket中断的接收中断中,判断服务器发给395的数据包的类型,具体协议包类型可以看MQTT协议中的类型表格,在连接中断中发送连接函数,在断开中断和超时中断中将连接标志位置0。
1 while(1)
2 {
3 // WCHNET_MainTask();
4 if(CH395_INT_WIRE==RESET) /* 当中断引脚电平变化,进入全局中断 */
5 {
6 CH395GlobalInterrupt();
7 }
8 if (publishValid == 1) {
9 publishValid = 0;
10
11 if(con_flag)
12 {
13
14 MQTT_Publish(pub_topic, pub_qos, payload);
15 }
16 // if(con_flag) MQTT_Pingreq(); //heartbeat packet
17 }
18 }
在main函数的while循环中,当 publishValid标志为1,则表示20s间隔时间已到,在此条件下如果连接标志 con_flag为1,则上传消息。
完整工程链接:
https://files.cnblogs.com/files/blogs/808422/EXAM_mqtt.zip?t=1702258990&download=true
CH395+EMQX实现MQTT应用(Windows系统)的更多相关文章
- windows系统 安装 mysql.fx
windows系统 安装 mqtt.fx 软件官网:http://mqttfx.jfx4ee.org/ 软件下载:http://www.jensd.de/apps/mqttfx/1.1.0/
- DOS下windows系统查看wifi密码
DOS下windows系统查看wifi密码 首先,按win+R键,win键如下 弹出框中输入cmd 在弹出界面输入 netsh wlan show profiles 你可以看到你链接过的所有wifi名 ...
- windows系统下fis3安装教程
注意:在安装fis3前必须安装node和npm,详情请见官网http://nodejs.org node版本要求 0.8.x,0.10.x, 0.12.x,4.x,6.x,不在此列表中的版本不予支持. ...
- Windows系统上的.Net版本和.NETFramework的C#版本
前言 注:本文内容摘自维基百科,用于在墙内时当作笔记看. WinForm 需要.Net最低版本 2.0 WPF需要的.Net最低版本 3.0 (Win7及之上版本自带) C#版本 版本 语言规格 日期 ...
- windows系统快捷操作の高级篇
上次介绍了windows系统上几个比较好用的软件和系统快捷键,虽然有些很方便,但是毕竟还是太少了,而且无法自定义专属于自己的快捷键.所以我写了这么一篇教程,主要介绍两个神器:windows平台上的au ...
- windows系统快捷操作の进阶篇
上次介绍了windows系统上一些自带的常用快捷键,有些确实很方便,也满足了我们的一部分需求.但是我们追求效率的步伐怎会止步于此?这一次我将会进一步介绍windows上提升效率的方法. 一:运行 打开 ...
- windows系统快捷操作の基础篇
从网上汇总了一些windows系统上常用的快捷键,结合自己的使用经验,将平时使用电脑时最常用的快捷键记录在此.注意这里罗列的是平时最常用到的,其他的一般来说不怎么用到的并不在此列,如果想要完整列表,请 ...
- UEFI+GPT模式下的Windows系统中分区结构和默认分区大小及硬盘整数分区研究
内容摘要:本文主要讨论和分析在UEFI+GPT模式下的Windows系统(主要是最新的Win10X64)中默认的分区结构和默认的分区大小,硬盘整数分区.4K对齐.起始扇区.恢复分区.ESP分区.MSR ...
- 如何用python在Windows系统下,生成UNIX格式文件
平时测试工作中,少不了制造测试数据.最近一个项目,我就需要制造一批可在UNIX下正确读取的文件.为确保这批文件能从FTP下载成功,开发叮嘱我:“文件中凡是遇到换行,换行符必须是UNIX下的LF,而不是 ...
- 关于DOS与cmd(windows系统)
dos是计算机的最初期的操作系统,对电脑操作必须输入各种dos命令窗口,可以理解成运行计算机机器内部语言,知道编程吗?其实早期dos命令操作系统就是运行计算机内部的编程命令,因此操作人员都必须具有一定 ...
随机推荐
- C# 异步执行操作
为了方便测试异步,先加个计时 计时相关(可以直接跳过该部分) //开始计时 Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); // 停 ...
- jdk17下netty导致堆内存疯涨原因排查
背景: 介绍 天网风控灵玑系统是基于内存计算实现的高吞吐低延迟在线计算服务,提供滑动或滚动窗口内的count.distinctCout.max.min.avg.sum.std及区间分布类的在线统计计算 ...
- 《Docker到Kubernetes容器运维实战》简介
#好书推荐##好书奇遇季#<Docker到Kubernetes容器运维实战>已经出版.本书帮助读者系统掌握Docker与K8s运维技能. 本书内容 本书分两部分系统介绍Docker与K ...
- 自定义注解实现数据序列化时进行数据脱敏(基于springboot默认jackjson)、消息转换器HttpMessageConverter
消息转换器 HttpMessageConverter 消息转化器的作用 将请求报文转化为Java对象 将Java对象转化为响应报文 消息转换器接口 public interface HttpMessa ...
- 🖖少年,该升级 Vue3 了!
你好,我是 Kagol. 前言 根据 Vue 官网文档的说明,Vue2 的终止支持时间是 2023 年 12 月 31 日,这意味着从明年开始: Vue2 将不再更新和升级新版本,不再增加新特性,不再 ...
- .NET应用如何防止被反编译
前言 前段时间分享了两篇关于.NET反编译相关的文章,然后文章留言区就有小伙伴提问:如何防止被反编译?因此本篇文章我们就来讲讲.NET应用如何防止被反编译..NET反编译相关的文章可以看如下文章: 4 ...
- stat函数详解
Linux系统函数之文件系统管理 stat函数 作用:获取文件信息 include <sys/types.h> #include <sys/stat.h> #include & ...
- RedisStack部署/持久化/安全/与C#项目集成
前言 Redis可好用了,速度快,支持的数据类型又多,最主要的是现在可以用来向量搜索了. 本文记录一下官方提供的 redis-stack 部署和配置过程. 关于 redis-stack redis-s ...
- jmeter不用工具获取随机值的几种方法
第一种:直接获取 "vcContent": "${__time(yyyyMMddHHmmss)}${__RandomString(8,QWERTYUIOPASDFGHJK ...
- 俄罗斯版IDM安装与破解以及解决B站视频网站不弹出下载浮窗
IDM 全称 Internet Download Manager,是一款非常优秀的多线程下载和视频嗅探工具,不仅可以显著提高文件下载速度,配合IDM浏览器扩展插件,还可以嗅探并下载YouTube.知乎 ...