[3]ESP32连接MQTT服务端

news2024/11/18 21:43:14

MQTT库:PubSubClient 

连接MQTT服务端

#include <Arduino.h>
#include <WiFi.h>
#include <PubSubClient.h>

const char *ssid = "613专属";
const char *password = "613613613";
const char *mqttServer = "test.ranye-iot.net"; // MQTT服务器地址

WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

void connectMQTTServer()
{
  // 根据ESP32的MAC地址生成客户端ID(避免与其它esp32的客户端ID重名)
  String clientId = "esp32-" + WiFi.macAddress();

  // 连接MQTT服务器
  if (mqttClient.connect(clientId.c_str()))
  {
    Serial.println("MQTT Server Connected.");
    Serial.println("Server Address: ");
    Serial.println(mqttServer);
    Serial.println("ClientId:");
    Serial.println(clientId);
  }
  else
  {
    Serial.print("MQTT Server Connect Failed. Client State:");
    Serial.println(mqttClient.state());  // returnCode--连接返回码
    delay(3000);
  }
}

void connectWifi()
{

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) // 等待WiFi连接,成功连接后输出成功信息
  {
    delay(1000);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi Connected!");
  Serial.println("");
}

void setup()
{
  Serial.begin(115200);
  WiFi.mode(WIFI_STA); // 设置ESP32工作模式

  connectWifi();

  mqttClient.setServer(mqttServer, 1883); // 设置MQTT服务器和端口号

  connectMQTTServer(); // 连接MQTT服务器
}

void loop()
{
  if (mqttClient.connected())
  {                    // 如果开发板成功连接服务器
    mqttClient.loop(); // 保持客户端心跳,在连接状态下不要使用delay()
  }
  else
  {                      // 如果开发板未能成功连接服务器
    connectMQTTServer(); // 则尝试连接服务器
  }
}

发布、订阅和取消订阅

MQTT报文:

PUBLISH--发布信息

SUBSCRIBE--订阅主题

SUBACK--订阅主题

UNSUBSCRIBE--取消订阅

PUBLISH--发布信息

 MQTT客户端一旦连接到服务端,便可以发布信息,每条发布的MQTT消息必须包含一个主题,MQTT服务器可以通过主题确定将信息转发给那些服务器。

PUBLISH报文:

  1.  topicName--主题名:用于识别此信息应发布到哪一个主题
  2. QoS(Quality of Service)--服务质量等级:QoS有三个级别:0、1和2。QoS决定MQTT通讯有什么样的服务保证。
  3. packetId--报文标识符:报文标识符的内容与QoS级别有密不可分的关系。只有QoS级别大于0时,报文标识符才是非零数值。如果QoS等于0,报文标识符为0。
  4. retainFlag--保留标志:在默认情况下,当客户端订阅了某一主题后,并不会马上接收到该主题的信息。只有在客户端订阅该主题后,服务端接收到该主题的新信息时,服务端才会将最新接收到的该主题信息推送给客户端。但是在有些情况下,我们需要客户端在订阅了某一主题后马上接收到一条该主题的信息。这时候就需要用到保留标志这一信息
  5. payload--有效载荷:有效載荷是我们希望通过MQTT所发送的实际内容。我们可以使用MQTT协议发送文本,图像等格式的内容。这些内容都是通过有效載荷所发送的。
  6. dupFlag--重发标志:当MQTT报文的接收方没有及时确认收到报文时,发送方会重复发送MQTT报文。在重复发送MQTT报文时,发送方会将此“重发标志”设置为true。请注意,重发标志只在QoS级别大于0时使用

SUBSCRIBE--订阅主题

当客户端连接到服务端后,除了可以发布信息,也可以接收信息,客户端要想订阅主题,首先要向服务端发送主题订阅请求。一个SUBSCRIBE报文可以用于订阅一个或者多个主题。

SUBACK--订阅确认

服务端接收到客户端的订阅报文后,会向客户端发送SUBACK报文确认订阅。SUBACK报文包含有“订阅返回码和“报文标识符这两个信息。

返回码Return Code Response
0订阅成功 – QoS 0
1订阅成功- QoS 1
2订阅成功- QoS 2
128订阅失败

UNSUBSCRIBE--取消订阅

 当客户端要取消订阅某主题时,可通过向服务端发送UNSUBSCRIBE – 取消订阅报文来实现。

ESP32发布MQTT消息

 注意:

// 建立发布主题。主题名称以Taichi-Maker-为前缀,后面添加设备的MAC地址。
  // 这么做是为确保不同用户进行MQTT信息发布时,ESP8266客户端名称各不相同,
  String topicString = "Taichi-Maker-Pub-" + WiFi.macAddress(); // 订阅主题
  char publishTopic[topicString.length() + 1];                  // 把字符串变量转换为字符数组,因为publish只能接收字符数组类型的数据
                                                                // 字符串数组后面多了个结束符\0,所以长度要+1
  strcpy(publishTopic, topicString.c_str());// .c_str()的作用是把字符串转换为char类型
#include <Arduino.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <Ticker.h>

const char *ssid = "613专属";
const char *password = "613613613";
const char *mqttServer = "test.ranye-iot.net"; // MQTT服务器地址

Ticker ticker;
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
int count; // Ticker计数用变量

void connectMQTTServer()
{
  // 根据ESP32的MAC地址生成客户端ID(避免与其它esp32的客户端ID重名)
  String clientId = "esp32-" + WiFi.macAddress();

  // 连接MQTT服务器
  if (mqttClient.connect(clientId.c_str()))
  {
    Serial.println("MQTT Server Connected.");
    Serial.println("Server Address: ");
    Serial.println(mqttServer);
    Serial.println("ClientId:");
    Serial.println(clientId);
  }
  else
  {
    Serial.print("MQTT Server Connect Failed. Client State:");
    Serial.println(mqttClient.state()); // returnCode--连接返回码
    delay(3000);
  }
}

void connectWifi()
{

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) // 等待WiFi连接,成功连接后输出成功信息
  {
    delay(1000);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi Connected!");
  Serial.println("");
}
void tickerCount()
{
  count++;
}

// 发布信息
void pubMQTTmsg()
{
  static int value; // 客户端发布信息用数字,静态变量的作用域相当于全局变量

  // 建立发布主题。主题名称以Taichi-Maker-为前缀,后面添加设备的MAC地址。
  // 这么做是为确保不同用户进行MQTT信息发布时,ESP8266客户端名称各不相同,
  String topicString = "Taichi-Maker-Pub-" + WiFi.macAddress(); // 订阅主题
  char publishTopic[topicString.length() + 1];                  // 把字符串变量转换为字符数组,因为publish只能接收字符数组类型的数据
                                                                // 字符串数组后面多了个结束符\0,所以长度要+1
  strcpy(publishTopic, topicString.c_str()); // .c_str()的作用是把字符串转换为char类型

  // 建立发布信息。信息内容以Hello World为起始,后面添加发布次数。
  String messageString = "Hello World " + String(value++);
  char publishMsg[messageString.length() + 1];
  strcpy(publishMsg, messageString.c_str());

  // 实现ESP8266向主题发布信息
  if (mqttClient.publish(publishTopic, publishMsg))
  {
    Serial.println("Publish Topic:");
    Serial.println(publishTopic);
    Serial.println("Publish message:");
    Serial.println(publishMsg);
  }
  else
  {
    Serial.println("Message Publish Failed.");
  }
}
void setup()
{
  Serial.begin(115200);
  WiFi.mode(WIFI_STA); // 设置ESP32工作模式

  connectWifi();

  mqttClient.setServer(mqttServer, 1883); // 设置MQTT服务器和端口号

  connectMQTTServer(); // 连接MQTT服务器

  ticker.(1, tickerCount); // 每隔1s执行一次tickerCount函数
}

void loop()
{
  if (mqttClient.connected())
  { // 如果开发板成功连接服务器
    // 每隔3秒钟发布一次信息
    if (count >= 3)
    {
      pubMQTTmsg();
      count = 0;
    }
    // 保持心跳
    mqttClient.loop();
  }
  else
  {                      // 如果开发板未能成功连接服务器
    connectMQTTServer(); // 则尝试连接服务器
  }
}

MQTT主题进阶

1.主题基本形式;2.主题分级;3.主题通配符;4.主题应用注意事项

1.主题基本形式

主题基本形式:主题的最基本形式就是一个字符串,以下是几个主题示例:

  • myTopic
  • motorSpeed
  • MotorSpeed
  • current time

使用主题时需要注意以下几点:

  1. 主题是区分大小写的,比如motorSpeed和MotorSpeed是两个完全不同的主题
  2. 主题可以使用空格,例如current time
  3. 大部分MQTT服务端是不支持中文主题的,所以应使用英文字符串或ASCII字符来作为MQTT主题。

2.主题分级

为了更好的对主题进行管理和分类,我们可以对主题进行分级处理,MQTT主题各个级别之间可以使用"/"来分隔,例:Tyler-1/motor/1/speed

在以上示例中一共有四级主题,分别是第1级 Tyler-1、第2级motor、第三级1、第4级speed。主题的每一级至少需要一个字符。

我们再来看几个分级主题的示例:

home/sensor/kitchen/temperature
home/sensor/kitchen/brightness
home/sensor/bedroom/temperature
home/sensor/bedroom/brightness

3.主题通配符

当客户端订阅主题时,可以使用通配符同时订阅多个主题,通配符只能在订阅主题时使用,两种通配符:单级通配符和多级通配符

(1)单级通配符:+,单级通配符可以代替一个主题级别

例如:home/sensor/+/temperature

当客户端订阅了以上主题后,它将会收到以下主题的信息内容:

home/sensor/kitchen/temperature
home/sensor/bedroom/temperature

在home后面的级别中,由于客户端订阅的主题使用了+ 单级通配符,因此无论home级别后面的内容是什么,客户端都能收到这些主题的信息。

(2)多级通配符:#可以涵盖任意数量的主题级别。如下示例所示, 多级通配符必须是主题中的最后一个字符

home/sensor/#

当客户端订阅了以上含有”#”的主题后,可以收到以下主题的信息。

home/sensor/kitchen/temperature
home/sensor/bedroom/brightness
home/sensor/data

 多级通配符可以代替多级主题信息,因此无论”home/sensor”后面有一级还是多级主题,都可以被订阅了”home/sensor/#”的客户端接收到。

4.主题应用注意事项

– 以$开始的主题

以$开始的主题是MQTT服务端系统保留的特殊主题,我们不能随意订阅或者向其发布信息。例如:

 – 不要用 “/” 作为主题开头

MQTT允许使用“/”作为主题的开头,例如/home/sensor/data。但是这将这么做毫无意义,而且会额外产生一个没有用处的主题级别。

– 主题中不要使用空格

MQTT协议允许我们在主题中使用空格,但是阅读和调试含有空格的主题会显得异常困难。

– 保持主题简洁明了

– 主题中尽量使用ASCII字符

-在主题中嵌入客户端ID

在主题中嵌入发布消息的客户端ID,这一操作可以为开发和管理MQTT信息提供便利。通过主题中的客户端ID内容,我们可以很容易的了解该主题信息是由哪一台设备所发布的。

ESP32订阅MQTT主题

学习内容:1.订阅单个主题;2.订阅多个主题;3.使用单级通配符订阅主题;4.使用多级通配符订阅主题

1.订阅单个主题

执行点灯操作

#include <Arduino.h>
#include <WiFi.h>
#include <PubSubClient.h>

#define LED1 1 // LED测试引脚

const char *ssid = "613专属";
const char *password = "613613613";
const char *mqttServer = "test.ranye-iot.net"; // MQTT服务器地址

WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

// 订阅指定主题
void subscribeTopic()
{
  String topicString = "Taichi-Maker-Sub-" + WiFi.macAddress();
  char subTopic[topicString.length() + 1];
  strcpy(subTopic, topicString.c_str());

  // 通过串口监视器输出是否成功订阅主题以及订阅的主题名称
  if (mqttClient.subscribe(subTopic))
  {
    Serial.println("Subscrib Topic:");
    Serial.println(subTopic);
  }
  else
  {
    Serial.print("Subscribe Fail...");
  }
}

void connectMQTTServer()
{
  // 根据ESP32的MAC地址生成客户端ID(避免与其它esp32的客户端ID重名)
  String clientId = "esp32-" + WiFi.macAddress();

  // 连接MQTT服务器
  if (mqttClient.connect(clientId.c_str()))
  {
    Serial.println("MQTT Server Connected.");
    Serial.println("Server Address: ");
    Serial.println(mqttServer);
    Serial.println("ClientId:");
    Serial.println(clientId);
    subscribeTopic(); // 订阅指定主题
  }
  else
  {
    Serial.print("MQTT Server Connect Failed. Client State:");
    Serial.println(mqttClient.state()); // returnCode--连接返回码
    delay(3000);
  }
}

void connectWifi()
{

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) // 等待WiFi连接,成功连接后输出成功信息
  {
    delay(1000);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi Connected!");
  Serial.println("");
}



// 收到信息后的回调函数
// 参数1:主题;参数2:主题内容;信息长度
void receiveCallback(char *topic, byte *payload, unsigned int length)
{
  Serial.print("Message Received [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++)
  {
    Serial.print((char)payload[i]); // 接收到的信息内容,传递过来的数据格式为数组
  }
  Serial.println("");
  Serial.print("Message Length(Bytes) ");
  Serial.println(length);

  if ((char)payload[0] == '1')
  {                                 // 如果收到的信息以“1”为开始
    digitalWrite(LED1, HIGH); // 则点亮LED。
    Serial.println("LED ON");
  }
  else
  {
    digitalWrite(LED1, LOW); // 否则熄灭LED。
    Serial.println("LED OFF");
  }
}

void setup()
{
  Serial.begin(115200);
  pinMode(LED1, OUTPUT);

  WiFi.mode(WIFI_STA); // 设置ESP32工作模式

  connectWifi();

  mqttClient.setServer(mqttServer, 1883); // 设置MQTT服务器和端口号

  mqttClient.setCallback(receiveCallback); // 设置MQTT订阅回调函数
  connectMQTTServer();                     // 连接MQTT服务器
}

void loop()
{
  if (mqttClient.connected())
  {
    mqttClient.loop();
  }
  else
    connectMQTTServer();
}

2.订阅多个主题

// 订阅指定主题
void subscribeTopic(){
 
  // 建立订阅主题1。主题名称以Taichi-Maker-Sub为前缀,后面添加设备的MAC地址。
  // 这么做是为确保不同设备使用同一个MQTT服务器测试消息订阅时,所订阅的主题名称不同
  String topicString = "Taichi-Maker-Sub-" + WiFi.macAddress();
  char subTopic[topicString.length() + 1];  
  strcpy(subTopic, topicString.c_str());
  
  // 建立订阅主题2
  String topicString2 = "Taichi-Maker-Sub2-" + WiFi.macAddress();
  char subTopic2[topicString2.length() + 1];  
  strcpy(subTopic2, topicString2.c_str());
  
  // 通过串口监视器输出是否成功订阅主题1以及订阅的主题1名称
  if(mqttClient.subscribe(subTopic)){
    Serial.println("Subscrib Topic:");
    Serial.println(subTopic);
  } else {
    Serial.print("Subscribe Fail...");
  }  
 
  // 通过串口监视器输出是否成功订阅主题2以及订阅的主题2名称
  if(mqttClient.subscribe(subTopic2)){
    Serial.println("Subscrib Topic:");
    Serial.println(subTopic2);
  } else {
    Serial.print("Subscribe Fail...");
  }    
}

3.使用单级通配符订阅主题

// 订阅指定主题
void subscribeTopic(){
 
  // 建立订阅主题。主题名称以Taichi-Maker-Sub为前缀,后面添加设备的MAC地址。
  // 这么做是为确保不同设备使用同一个MQTT服务器测试消息订阅时,所订阅的主题名称不同
  String topicString = "Taichi-Maker-Sub-" + WiFi.macAddress()+"/+/data";
  char subTopic[topicString.length() + 1];  
  strcpy(subTopic, topicString.c_str());
  
  // 通过串口监视器输出是否成功订阅主题以及订阅的主题名称
  if(mqttClient.subscribe(subTopic)){
    Serial.println("Subscrib Topic:");
    Serial.println(subTopic);
  } else {
    Serial.print("Subscribe Fail...");
  }  
}

4.使用多级通配符订阅主题

// 订阅指定主题
void subscribeTopic(){
 
  // 建立订阅主题。主题名称以Taichi-Maker-Sub为前缀,后面添加设备的MAC地址。
  // 这么做是为确保不同设备使用同一个MQTT服务器测试消息订阅时,所订阅的主题名称不同
  String topicString = "Taichi-Maker-Sub-" + WiFi.macAddress()+"/sensor/#";
  char subTopic[topicString.length() + 1];  
  strcpy(subTopic, topicString.c_str());
  
  // 通过串口监视器输出是否成功订阅主题以及订阅的主题名称
  if(mqttClient.subscribe(subTopic)){
    Serial.println("Subscrib Topic:");
    Serial.println(subTopic);
  } else {
    Serial.print("Subscribe Fail...");
  }  
}

【重要】ESP32同时订阅和发布MQTT信息

#include <Arduino.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <Ticker.h>

#define LED1 1 // LED测试引脚
int count;     // Ticker计数用变量

const char *ssid = "613专属";
const char *password = "613613613";
const char *mqttServer = "test.ranye-iot.net"; // MQTT服务器地址

WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

// 订阅指定主题
void subscribeTopic()
{
  String topicString = "Taichi-Maker-Sub-" + WiFi.macAddress();
  char subTopic[topicString.length() + 1];
  strcpy(subTopic, topicString.c_str());

  // 通过串口监视器输出是否成功订阅主题以及订阅的主题名称
  if (mqttClient.subscribe(subTopic))
  {
    Serial.println("Subscrib Topic:");
    Serial.println(subTopic);
  }
  else
  {
    Serial.print("Subscribe Fail...");
  }
}

void connectMQTTServer()
{
  // 根据ESP32的MAC地址生成客户端ID(避免与其它esp32的客户端ID重名)
  String clientId = "esp32-" + WiFi.macAddress();

  // 连接MQTT服务器
  if (mqttClient.connect(clientId.c_str()))
  {
    Serial.println("MQTT Server Connected.");
    Serial.println("Server Address: ");
    Serial.println(mqttServer);
    Serial.println("ClientId:");
    Serial.println(clientId);
    subscribeTopic(); // 订阅指定主题
  }
  else
  {
    Serial.print("MQTT Server Connect Failed. Client State:");
    Serial.println(mqttClient.state()); // returnCode--连接返回码
    delay(3000);
  }
}

void connectWifi()
{

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) // 等待WiFi连接,成功连接后输出成功信息
  {
    delay(1000);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi Connected!");
  Serial.println("");
}

// 收到信息后的回调函数
// 参数1:主题;参数2:主题内容;信息长度
void receiveCallback(char *topic, byte *payload, unsigned int length)
{
  Serial.print("Message Received [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++)
  {
    Serial.print((char)payload[i]); // 接收到的信息内容,传递过来的数据格式为数组
  }
  Serial.println("");
  Serial.print("Message Length(Bytes) ");
  Serial.println(length);

  if ((char)payload[0] == '1')
  {                           // 如果收到的信息以“1”为开始
    digitalWrite(LED1, HIGH); // 则点亮LED。
    Serial.println("LED ON");
  }
  else
  {
    digitalWrite(LED1, LOW); // 否则熄灭LED。
    Serial.println("LED OFF");
  }
}

void setup()
{
  Serial.begin(115200);
  pinMode(LED1, OUTPUT);
  // Ticker定时对象
  ticker.attach(1, tickerCount);

  WiFi.mode(WIFI_STA); // 设置ESP32工作模式

  connectWifi();

  mqttClient.setServer(mqttServer, 1883); // 设置MQTT服务器和端口号

  mqttClient.setCallback(receiveCallback); // 设置MQTT订阅回调函数
  connectMQTTServer();                     // 连接MQTT服务器
}
void tickerCount()
{
  count++;
}
// 发布信息
void pubMQTTmsg()
{
  // 建立发布主题。主题名称以Taichi-Maker-为前缀,后面添加设备的MAC地址。
  // 这么做是为确保不同用户进行MQTT信息发布时,ESP8266客户端名称各不相同,
  String topicString = "Taichi-Maker-Pub-" + WiFi.macAddress();
  char publishTopic[topicString.length() + 1];
  strcpy(publishTopic, topicString.c_str());

  // 定时向服务器主题发布当前D3引脚状态
  String messageString;
  if (digitalRead(D3))
  {
    messageString = "on";
  }
  else
  {
    messageString = "off";
  }
  char publishMsg[messageString.length() + 1];
  strcpy(publishMsg, messageString.c_str());

  // 实现ESP8266向主题发布信息
  if (mqttClient.publish(publishTopic, publishMsg))
  {
    Serial.println("Publish Topic:");
    Serial.println(publishTopic);
    Serial.println("Publish message:");
    Serial.println(publishMsg);
  }
  else
  {
    Serial.println("Message Publish Failed.");
  }
}
void loop()
{
  if (mqttClient.connected())
  { // 每隔3秒钟发布一次信息
    if (count >= 3)
    {
      pubMQTTmsg();
      count = 0;
    }
    mqttClient.loop();
  }
  else
    connectMQTTServer();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/125125.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CDGA|持续投入开展数据治理工作可以从这四大方向着手

数字化转型趋势下&#xff0c;外部监管以及内部数据使用都对数据治理提出更高效、更准确、更完备、更合规的要求&#xff0c;企业如何抓住新形势下的要求&#xff0c;开展自身数据治理工作&#xff1f; 纵观数据治理的发展历程&#xff0c;剖析数据治理的建设路径&#xff0c;持…

3. 中断向量是( )。 ————计算机组成原理

中断向量是&#xff08; &#xff09; A.子程序入口地址 B.中断向量表的首地址 C.终端服务程序入口地址 D.终端服务入口地址的地址 答案&#xff1a; C 知识点&#xff1a; 终端的概念&#xff1a; 1 机器出现了一些紧急事务&#xff0c;CPU不得不停下当前正在执行的程序&…

SQL经典练习:电脑商店

表结构 本文使用的表结构如下&#xff1a; 以下是创建表的语句&#xff1a; -- 厂商表 CREATE TABLE Manufacturers (Code INTEGER NOT NULL PRIMARY KEY, -- 编号&#xff0c;主键Name VARCHAR(255) NOT NULL, -- 名称 );-- 产品表 CREATE TABLE Products (Code INTEGER NO…

深蓝学院-多传感器融合定位课程-第10章-基于图优化的建图方法

专栏文章: 深蓝学院-多传感器融合定位课程-第1章-概述_goldqiu的博客-CSDN博客 深蓝学院-多传感器融合定位课程-第2章-3D激光里程计I_goldqiu的博客-CSDN博客 深蓝学院-多传感器融合定位课程-第3章-3D激光里程计II_goldqiu的博客-CSDN博客 深蓝学院-多传感器融合定位课程-第…

Spring动态数据源的简单理解

连接数据库时&#xff0c;需要url&#xff0c;userName&#xff0c;和password&#xff0c;怎么做到动态呢&#xff0c;那就是在使用时&#xff0c;根据你的设置&#xff0c;去连接不同的url&#xff0c;userName&#xff0c;和password&#xff0c;实现数据源的切换。 1.先写…

【JavaScript】JS飞机大战网页简易版

文章目录一、效果演示设计思路二、鼠标版飞机大战代码展示1.HTML结构代码2.CSS样式代码3.JavaScript代码js.js文件plane.js文件三、键盘版飞机大战代码展示1.HTML结构代码2.CSS样式代码3.JavaScript代码四、代码资源分享一、效果演示 利用html&#xff0c;css&#xff0c;js制…

php进程管理

PHP-FPM 先来了解一些名词概念&#xff1a; CGI是Common Gateway Interface&#xff08;通用网管协议&#xff09;&#xff0c;用于让交互程序和Web服务器通信的协议。它负责处理URL的请求&#xff0c;启动一个进程&#xff0c;将客户端发送的数据作为输入&#xff0c;由Web服…

首发!飞凌嵌入式i.MX9系列核心板重磅发布

来源&#xff1a;飞凌嵌入式官网www.forlinx.com为了让更多设备实现高能效、高安全性和智能化升级&#xff0c;NXP推出了全新的i.MX 93x系列处理器。作为NXP的重要合作伙伴&#xff0c;飞凌嵌入式在i.MX 9352的α阶段便进行该款处理器的产品研发工作。今天&#xff0c;飞凌嵌入…

HNU编译原理实验三cminus_compiler-2022-fall

前言&#xff1a;实验难度再次提升&#xff0c;不过会一个就可以做其他部分&#xff0c;很多都是相同的&#xff0c;个人认为更难的还是配置环境部分&#xff0c;真的会折磨死人 lab3 实验报告实验要求 第一部分: 了解LLVM IR。通过clang生成的.ll&#xff0c;了解LLVM IR与c代…

Windows10下CLion配置说明

Windows10下CLion配置说明 CLion 是 C/C的 IDE&#xff0c;可以配置多种编译环境&#xff0c;本文以配置MinGW编译环境为例。 安装 CLion 的安装可直接到官网下载 ZIP,文件解压后直接运行即可。我在安装过程中没有遇到困难&#xff0c;网上关于安装的教程很多&#xff0c;如…

TCP连接积压导致服务假死问题

目录 一、事故表现 二、事故问题分析 三、测试环境重现 四、解决方案 一、事故表现 2022-01-14日凌晨00:00开始&#xff0c;TCP_alloc&#xff1a;已分配TCP连接&#xff0c;一直未释放。导致未释放的 TCP连接一直积压。最终服务LOGISTICS-DS-ES-COMMON-SERVICE假死&#x…

jenkins下载安装

目录 1.下载安装 2.初始化配置 3.新建项目 3.1.安装插件 3.2.配置项目 3.2.1.选择项目类型 3.2.1.源码管理 3.3.3.配置maven 1.下载安装 安装清单&#xff1a; JDK 1.8Maven 3.6.3gitJenkins 2.334 环境&#xff1a; centos 7 Jenkins是JAVA编写的&#xff0c;需要JDK环…

年终报告:通过3个维度,回看2022全球电商市场的“多样性”

回顾年中&#xff0c;各国由于经济政策的调整导致贸易壁垒的层层叠加&#xff0c;也有非常复杂多变的因素在发酵&#xff0c;今年下半年突然爆发的俄乌两国的冲突使得原来就脆弱的全球经济再次遭受动荡&#xff0c;这一系列的动荡因素直接导致了今年全球经济的整体不景气&#…

鼠标拖拽菜单栏控制宽度大小及flex实现经典左右两栏布局

目录 1. 实现的效果如下图所示&#xff1a; 2. 思路 3.代码 3.1 js核心代码简单理解版&#xff1a; 3.2 实际应用-react版 4. 使用flex实现左右两栏式经典布局 4.1 图示&#xff1a; 4.2 代码实例&#xff1a; 1. 实现的效果如下图所示&#xff1a; 2. 思路 1. 使用定…

欧科云链接受北京电视台采访以创新科技助力《反电信网络诈骗法》

近日&#xff0c;欧科云链作为创新科技企业的代表&#xff0c;就《反电信网络诈骗法》实施的相关问题接受了来自北京电视台的采访。 编辑&#xff5c;小O 出品&#xff5c;欧科云链 近年来&#xff0c;随着数字技术的快速发展&#xff0c;越来越多的交易都转移到线上&#xff0…

softnms源码解读(python)

前言 想写这篇文章的原因是最近碰见了一个比较棘手的事情&#xff0c;如果想把一个目标检测模型及其相关的后处理移到嵌入式设备上&#xff0c;不能用c的opencv库&#xff0c;也就不能用cv2.dnn.nms这个函数来进行nms的后处理&#xff0c;需要用c实现&#xff0c;那就必须了解…

重磅综述|Nat Rev Gastroenterol Hepatol:人类胃肠道中的产甲烷古菌

期刊&#xff1a;Nat Rev Gastroenterol Hepatol 影响因子&#xff1a;73.082 发表时间&#xff1a;2022年9月 一、摘要 人类微生物群与人类健康和疾病密切相关。除了细菌、病毒和真核生物外&#xff0c;人类胃肠道中许多古菌与甲烷的产生有关&#xff0c;临床上可…

PMP一般要提前多久备考?

一般是1-3个月吧&#xff0c;PMP考试通过率至少有60%&#xff0c;报培训班可以有80%以上&#xff0c;不用备考太长时间&#xff0c;但时间太短也无法把项目管理的知识学完&#xff0c;1-3个月是最佳备考时间。 &#xff08;字数很多&#xff0c;都是干货&#xff0c;资料在文末…

写给Java程序员的GRPC入门系列(1)

点击上方GRPC专栏看系列 文章目录Abstract前置依赖本文初始状态创建MAVEN module修改依赖测试下一步Abstract 网上有很多GRPC的例子&#xff0c;但是却没有能够写给普通Java开发人员手把手入门少走弯路的教程。 本教程保证按照步骤一步步来你就可以完成GRPC从0到1的构建。 源码…

一文读懂机器学习常用算法的基本概念和适用场景

引用一句英国统计学家George E. P. Box的名言&#xff1a;All models are wrong, but some are useful. 没有哪一种算法能够适用所有情况&#xff0c;只有针对某一种问题更有用的算法。 机器学习算法不会要求一个问题被 100%求解&#xff0c;取而代之的是把问题转化为最优化的…