Paho MQTT 客户端接入阿里云物联网平台(4)| 学习笔记

简介: 快速学习 Paho MQTT 客户端接入阿里云物联网平台(4)

开发者学堂课程【基于STM32的端到端物联网全栈开发Paho MQTT 客户端接入阿里云物联网平台(4)】学习笔记,与课程紧密联系,让用户快速学习知识。

课程地址:https://developer.aliyun.com/learning/course/574/detail/7940


Paho MQTT 客户端接入阿里云物联网平台(4)


Paho MQTT 客户端接入阿里云物联网平台示例操作

项目例程软件架构:
应用程序:

1.节点端业务程序

2.阿里云 MQTT 连接适配层

中间件:

1.Paho MQTT embedded C

2.mbedTLSHMAC-SHA1

3.网络接口抽象

底层驱动:

1.STM32L4 Cube HAL 硬件抽象层

2.传感器驱动

3.WIFI 模块驱动

image.png

//例程软件中的 Paho MQTT 协议栈向下通过 ST 提供的网络接口抽象层,来调用底层的 wifi 驱动实现网络数据的通信,向上提供 MQTT API 函数给阿里云 MQTT 连接适配层来完成应用程序的功能

//在接下来的课程中将分别介绍代码中实现网络通信的分层结构,发送和接收数据流的传递过程以及 Paho 向下和网络接口的适配以及向上和阿里云 MQTT 连接适配层的实现

各个软件层中主要对应的c文件以及他们之间的调用关系

---------------1.net_wifi 适配  Wifi 驱动

2.net_c2c 适配 C2c 驱动

3.net_eth 适配 Eth 驱动

image.png

//上边的文件通过下边的软件层通过颜色进行了对应

//节点中的业务程序主要在 main.c 业务中实现

//阿里云 MQTT 连接适配层包括 Ali_iotclient.c 文件和 Ali_iot_network_wrapper.c 文件

//Ali_iotclient.c 文件实现了阿里客户端的功能,包括构建 MQTT 连接的参数,与 MQTT 服务器建立连接,订阅发布消息等函数

//Ali_iot_network_wrapper.c 文件中实现了 MQTT 通信网络接口的封装

//MQTTClient.c Paho 协议栈的文件,它提供了众多 API 给上方的 Ali_iotclient.c 中的函数调用

//net.c 是 ST 的网络接口抽象层的文件,MQTTClient.c 通过 Ali_iot_network_wrapper.c 中封装的网络接口函数向下调用 net.c 中对应的函数,再向下调用 wifi 驱动。在本次例程中只使用到了 wifi 驱动,这个软件结构的好处,是可以很方便的移植到比如2G/3G以及以太网这些网络连接方式去,只需要将 net.c 文件部分下的.c 文件进行替换就能完成,上方的文件代码均不会受到影响。

后端配置代码:    

//AliIoT 路径下有三个适配文件:

Ali_iotclient.c

Ali_iot_network_wrapper.cmqtt_msg_handler.c 文件

// Ali_iot_network_wrapper.c 下封装了5个函数,分别是与服务器创建 tcp 连接,与服务器断开 tcp 连接以及向下对网络接口发送和接收数据的两个函数

//mqtt network 这个函数是在向 mqtt 协议栈新建一个网络接口,并且注册相关的数据收发的函数

// Ali_iotclient.c 文件中主要实现阿里客户端的一些功能,比如如何根据三元组信息来构建 mqtt 服务器的地址,如何得到 mqtt 的主题,如何得到 mqtt 连接的用户名,和 mqtt 连接的密码,还有与 mqtt 服务器创建连接向服务器发送消息和订阅主题的函数

//mqtt 连接创建的过程:

main 函数中,前面的初始化操作和 wifi 都已经连接之后,就会开始与阿里云 iot 平台创建 mqtt 的连接,在 main函数中会调用 connect2Aliiothub 函数,此函数是在 Ali_iotclient.c 文件中实现的,此函数首先会根据三元组的信息来构建 mqtt 服务器的地址。得到服务器的地址后先调用 mqtt_connect_network 函数来与服务器建立 tcp 的连接,此函数是在 Ali_iot_network_wrapper.c 文件中实现的。在 mqtt_connect_network 函数中会调用 net.c 文件中所提供的向上的统一的网络接口函数来实现对网络的一些操作,

首先它会先调用 net_sock_create 来创建一个 socket,在此函数中,他会根据你所使用的不同网络接口来调用对应不同的函数来创建一个 socketsocket 创建成功后,它会将对应的 socket 的操作的函数注册到 sock 结构体中。上层的应用程序来通过 sock 这个结构体来做对应的操作时,实际调用的就是它已经注册的函数。

比如要通过 sock 来进行发送数据,接收输入,那么它调用的就是 net_sock_send_tcp_wifi net_sock_sen_recv_wifi 两个函数,这些函数的实现是在 net.tcp.wifi.c 文件中。mqtt_connect_network 函数首先会创建一个 sock,然后在创建 sock 的过程中,已经注册好一系列对 socket 操作的函数,接收数据,发送数据,断开连接等函数都已经注册成功,然后在对 socket 进行一些配置,最后通过 socket 与服务器建立 tcp 的连接。

#include "main.h"

#include "Ali_iot_network_wrapper.h"

int mqtt_connect_network(letwork* n, const char * host_addressint port)

{

int mqtt_socket_send(Network *network,unsigmed char *bufint len,int timeout){

{

int mqtt_socket_recv(Network *network,unsigmed char *bufint len,int timeout)

{

int mqtt_socket_disconnect(Network *network)

{

//@sz, nev a MQTT netvork interface vith WIFI module

//must called before baidu_connect_netvork_tls/baidu_connect_network

int mqtt_network _new (Network *network)

{

network->my socket = 0;

network->mqttread = mqtt_socket_recv;

network->mqttwrite = mqtt_socket_send;

network->disconnect = mqtt_socket_disconnect;

return SUCCESS;

}

如何构建服务器地址:
extern int PrepareMqttPayload (char * PayloadBuffer,int PayloadSize) ;

extern void Parameters_message_handler(MessageData * data) ;

extern void Service_message_handler(MessageData * data) ;

void calpassword(void) ;

int get_mgtt_server_addr(char* host_addrchar* region_id,char* product_key )

{

//$( YourProductKey}.iot-as-mqtt.$ { YourRegionId}.aliyuncs.com:1883

uint32_t return_len=0;

return_len = snprintf(host_addr,NQTT_CLIENT_INFO_S12E,"4s.iot-a38-mgtt.i8.aliyuncs.com" ,product_key ,regic

n_id) ;

if (return_len >= MQTT_CLIENT_INFO_SIZE)

{

mag_info ("no enough space for host address\n") ;

return -1;

}

msg_info ("MQIT server address is :is\n",host_addr);

return 0;

}

int build_mgtt_topic(void)

{                              

MQTT连接过程:

//WIFI SSID/passov rd config and initializationl

// and config device certificate

ret = initPlatform() ;

if (ret! =0)

{

mag_info ("wifi initial failed! \n");

while(1) ;

// connect to Ali Iot platformret = connect2Aliiothub () :;

if (ret!=0)l

mag_info ("MQIT connection failed! \n");

while(1) ;

}

// subscribe to topicsdeviceSubscribe () ;

/ *USER CODE END 2* /

/*Infinite loop * /

/* USER CODE BEGIN WHILE*/while (1)

{

//MQTT reconnection check

if (rebuildMQTTConnection ( )==0){

doMqttYield( );

/*USER CODE END WHILE*/

int net_sock_create_tcp_wifi(net_hnd_t nethndnet_ockhnd_t * sockhndnet_proto_t proto)

int rc = NET_ERR;

net_ctxt_t *ctxt = (net_ctxt_t *) nethnd;

net_sock_ctxt_t *sock = NULL;

sock = net_malloc (sizeof (net_sock_ctxt_t) );

if (sock == NULL)

mag_error ("net_sock_create allocation failed.in");

rc = NET_ERR;

else

memset (sock, 0, sizeof (net_sock _ctxt_t));sock->net = ctxt;

sock->next = ctxt->sock_list;

sock->methods.create = (net_sock_create_tcp_wifi) ;

sock->methods.open= (net_sock_open_tcp_wifi);

sock->methodg.recv= (net_sock_recv_tcp_wifi);

sock->methoda.send= (net_sock_send_tcp_wifi);

sock->methods.close= (net_sock_close_tcp_wifi) ;

sock->methods.destroy=(net_sock_destroy_tcp_wifi) ;

sock->proto= proto;

sock->blocking= NET_DEFAULT_BLOCKING;

sock->read_timeout= NET_DEFAULT_BLOCKING_READ_TIMEOUT;sock->write_timeout= NET_DEEAULT_BLOCKING_WRITE_TIMEOUT;

ctxt->sock_list= sock; /* Insert at the head of the list */

*sockhnd = (net_sockhnd_t) sock;

rc = NET_OK;

}

return rc;      

网络分层及数据流:

代码中实现网络通信的分层结构,发送和接收数据流的传送过程

//这里所说的网络分层并不是指 tcp 的网络分层,而是指代码中实现网络接口调用程序的时候通过网络接口抽象层,也就是下图中的蓝色部分,将底层实际的网络接口,绿色的 WIFI 驱动部分,与上层的应用程序,黄色的部分独立开,尽量保证底层网络接口的变化不会对上层应用程序产生影响。WIFI 模块驱动也分为了三层,在 emw3080_io.c 文件中是最低层跟外设打交道的部分,包括初始化引脚,从窗口读取和发送数据。

emw3080.c 文件中是对 at 指令的实现。wifi.c wifi 底层驱动和 wifi 网络层面抽象层的接口。从 tcp ip 网络分层的角度,在本例程中的代码仅仅实现了应用层的 mqtt 协议,传输层和网络层都是在 wifi 模块中实现的,从串口传给wifi 模块以及从 wifi 模块接收的都是已经封装完毕的 mqtt 数据包。传输层 tcp 和网络层封装都是由 wifi 模块完成的。

//图中的箭头是接收和发送数据传递的过程

//绿色箭头指发送和接收网络数据,例如发送温湿度信息,接收云端下发的温度阈值。紫色的箭头是 mcu 控制 wifi 模块的 at 指令以及返回值,比如连接 wifi 热点,查询固件版本等等。对于这类指令,只需要将执行的结果返回到上层的应用程序不需要将收到的具体数据向应用层传递。

//图中粉色的变量是数据在传递过程中保存的位置

//应用数据的发送的过程:比如程序要将检测到的温湿度的值从 wifi 发送到云端,应用程序首先会调用 Paho mqtt publish 函数来发送温湿度信息,在 Paho 协议栈中它会将数据封装成数据包的格式并拷贝到 mqtt_write_buf 中,然后通过 mqtt_socket_send 函数向网络接口发送。

通过网络抽象层的函数 net_sock_send net_sock_send_tcp_wifi 来调用 wifi 模块的驱动最后通过EMW3080_IO_Send 函数发送到串口,通过串口发送到 wifi 模块。

//对于云端下发的数据:wifi 模块通过串口将包含温湿度阈值 mqtt 数据包发送,数据首先会保存到 Uart Ring Buffer中,Paho 协议栈中会通过 mqtt 的函数不断的查询是否有新的数据收到,mqtt yarn 的函数会调用 mqtt_socket_recv再通过网络抽象层的函数来调用 WIFI 模块的驱动函数,查询对应的 Socket Buffer 中是否有未读出的数据,如果有就将数据拷贝到 MQTT_read.buf 中,如果则会通过 pullSocktData 函数将串口中 Ring Buffer 新的数据读取到 Socket Buffer 中再拷贝到 MQTT_read_bufMQTT_read_buf 中的数据在 Paho 协议栈中进行分析,再将真正的负载数据,就是温度阈值取出并提供给上层应用程序。

//除了应用数据流的接收,还有连接wifi热点,查询固件版本的 ak 指令的发送和返回值的接收,这部分数据的传递主要在 WIFI 模块驱动这层完成,就是紫色箭头指示的部分,这部分 ak 指令的发送主要在 net_if_int 函数中触发,程序初始化 wifi 模块时调用了 net_interface_ination,然后再此函数中完成 wifi 模块的初始化,wifi 固件版本的查询,以及 wifi 热点连接的工作。

以连接 wifi 热点为例,分析紫色部分传递数据的操作:

连接 wifi 热点是通过调用 WIFI_Connect 函数完成,在这个函数中会调用 emw3080中的函数,在此函数中会进行连接 wifi 热点的 at 指令的组装,然后保存在 Atcmd_buf 中通过 runAtCmd 函数,将数据通过串口发送到 wifi 模块,同时它继续在 runAtCmd 中等待模块发回的at指令返回值,在等待返回值的过程中,通过调用 EMW3080_IO_Receive函数查询串口的 Ring Buffer 中是否收到 wifi 模块的返回值。

问:串口的 Ring Buffer 中既有需要传递到应用层的数据,又有只需要在 WIFI 模块驱动层进行数据处理的数据,程序是如何区分两类数据的呢?

答:wifi 模块在发来的每一个数据都有一定的标识,用来说明这一帧数据的类型,比如 wifi 模块发送从云端收到的温度阈值数据包时,会加上 cip 英文数据的头,从这个标识就可以区分不同的数据包,然后进行数据处理。

网络数据产生/消耗层:Paho

image.png

网络接口:

image.png

WIFI 模块驱动:

image.png

设备端向云端发送数据的过程:

int devicest,atusPub (void)

{

int ret;

MQTTMessage MQTT_mag;

ret =0;

MQTT_msg.qos = Q0S1;  //QoS1 ;

MQTT_msg.dup = 0;//The DUP flag MST be set to 1 by the Client or Server vhen it attemptsto re-deliver a PUBLISH Packet

// The DuP flag MUST be set to 0 for all Qos 0 messages

MQTT_msg.retained = 1; //the Server MJsT store 757 the application Message and its Qos

MQTT_mag.payload = payload_buf;

// TODO:prepare pub payload,@sz

payload_buf [0]= GetTemperaturevalue ( ) ;

payload_buf [1]=GetHumvalue () ;

payload_buf[2]= GetTempratureThreshold( ;

MQTT_mag.payloadlen = 3;

if ( (ret=MQITPublish(zClient,temp_hum_topic, &MQTT_mag)) != 0){

mag_error ("Failed to publish data. td " ,ret) ;

mag_info(": temprature = %d,humidity = %d\n\n",GetTemperatureValue () ,cetHumValue());

}

数据接收的过程:

/**

@brief send data over the vifi connection.

*@paramBuffer: the buffer to send

*@paramLength: the Buffer's data size.

*@retval Returns EMW3080_OK on success and EM3080_ERROR othervise.

*/

EMW3080_StatusTypeDef EMNN3080_SendData(uint8_t socket,uint8_t* Bufferuint32_t Lengthuint32_t Timeout)

EMw3080_statusTypeDef Ret = EMw3080_OK;

if(Buffer != NULL)

uint32_t tickstart;

{

/ *Construct thecommand * /

memset(Atcnd, '1o",MAX_AT_CMD_SIZE);

sprintf( (char * )AtCmd,"AT+CIPSEND=%lu,$lusc", socket,Length,'r');

/ *Tne command doesn't have a return command

until the data is actually sent. Thus ve check here whether

ve got the '>' prompt or not. */

Ret = runAtCmd (AtCmdstrlen ((char *)AtCmd)(uint8_t*)AT_SEND_PROMPT_STRINGTimeout) ;

/ * Return Error */

if (Ret != EMw3080_oK){

return EMN3080_ERROR;

}

/ send the data */

Ret = runAtCmd (Buffer,Length,(uint8_t*)AT_OK_STRINGTimeout) ;

return Ret;

}

驱动和应用层数组定义:

1.Buffer 名称:MQTT_write_buf

默认大小:MQTT_BUFFER_SIZE512字节)
定义所在位置:Ali_iotclient.c
注释:

image.png

2.Buffer 名称:MQTT_read_buf

默认大小:MQTT_BUFFER_SIZE512字节)
定义所在位置:Ali_iotclient.c
注释:

image.png

3.Buffer 名称:AyCmd

默认大小:MAX_AT_SIZE(256字节)
定义所在位置:emw3080.c
注释:

image.png

4.Buffer 名称:RxBuffer

默认大小:MAX_RX_SIZE(1500字节)

定义所在位置:emw3080.c

5.Buffer 名称:WIFIRxBuffer

默认大小: RING_BUFFER_SIZE(1024字节)

定义所在位置:emw3080_io.h

6.Buffer 名称:”sock buffer”

默认大小:MAX_SOCKET_SIZE(512字节)定义所在位置:emw3080.h

WIFI_OpenClientConnection函数中分配内存

Paho MQTT客户端对下(网络连接)的适配

1.  网络接口的适配

typedef struct Network Network;

struct Network

net_sockhnd_t my_socket;

int (*mqttread) (Network*, unsigmed char* , int,int) ;

int (*mqttwrite)(Network* , unsigmed char* , int,int) ;

int (*disconnect)(Network*) ;

};

2.  Timer 的适配

struct Timer {

uint32_t init_tick;

uint32_t timeout_ms;

};

typedef struct Timer Timer;

void TimerCountdownMS(Timer* timerunsigmed int timeout_ms);

void TimerCountdown (Timer* timerunsigmed int timeout);

int TimerLeftMS(Timer* timer);

char TimerIsExpired(Timer* timer);

void TimerInit(Timer* timer) ;

3.  returnCode 枚举与 ST HAL 库的不兼容

Paho MQTT Client 的调用

1.MQTTClientlnit

初始化 MQTT 客户端

2.MQTTConnect

与服务器建立 MQTT 连接

3.MQTTSubscribe

向服务器订阅消息主题,并注册收到消息后的回调函数

4.MQTTPublish

向服务器发布某个主题的消息

5.MQTTYield

根据应用调整周期调用的间隔

Paho MQTT 客户端对上(阿里云 loT)的适配

与阿里云 MQTT 服务器连接需要的参数

1.用户名/密码

2.MQTT ClientID

3.保活时间

4.Cleansession

5.MQTT 服务器域名

typedef struct{

{

/** The eyecatcher for this structure. must be MQTC.*/

char struct_id[4];

/**The version number of this structure.Must be 0 */

int struct_version;

/**version of MQTT to be used. 3 = 3.1 4= 3.1.1

*/

unsigmed char MQITVersion;

MQTTString clientID;

unsigmed short keepAliveInterval;

unsigmed char cleansession;

unsigmed char willElag;

MQTTPacket_wil10ptions will;

MQTTString username;

MQITString password;

}MQTTPacket_connectData;  

构建 MQTT 服务器域名:

MQTT 服务器域名:
${YourProductKey}.iot-as-mqtt.${YourRegionld}.aliyuncs.com:1883

image.png

举例:
1.
服务器域名:

a1b05UeAQ6M.iot-as-mqtt.shanghai-cn.aliyuncs.com

2.端口:1883    

构建 MQTT ClientID

MQTT ClientID: clientld+"[Isecuremode=3,signmethod=hmacsha1,timestamp=132323232

clientld:客户端自己定义的ID号,可以使用 MAC 地址
3:
安全模式,可以选2TLS 直连)和3TCP 直连)

hmacsha1:签名算法支持:

hmacmd5

hmacsha1

hmacsha256
132323232:当前的时间戳。可以通过 HAL_GetTick 获取当前时间戳。

注意:
如果 clientld“b0f8933b9467”',签名算法选择 hmacsha1,当前时间戳为24081

MQTTClientID 为:“b0f8933b9467|securemode=3,signmethod=hmacsha1,timestamp=24081|'    

构建 MQTT 用户名:MQTT 用户名:DeviceName+&+ProductKey

举例:MQTT 用户名就是“smartthermometer&a1b05UeAQ6M”  

构建 MQTT 密码:

MQTT 密码=sign_hmac(DeviceSecret,content)

image.png

Mbedtls:
Demo IAR
工程      

Mbedtls 协议包              

MQTT 主题与消息负载格式:

1.功能:设置温度阈值

MQTT 主题:${productKey}/${deviceName}/tempThresholdSet
操作权限(设备):订阅

消息方向:下行

负载格式:一个字节:温度阈值。直接二进制传输,例如:0x1E 代表30

2.功能:解除警报

MQTT 主题:${productKey}/${deviceName}/clearAlarm
操作权限(设备):订阅

消息方向:下行

负载格式:一个字节,固定位0x01

3.功能:高温报警

MQTT 主题:S{productKey}/${deviceName}/tempAlarm
操作权限(设备):发布

消息方向:上行

负载格式:一个字节,固定位0x01

4.功能:上报属性

MQTT 主题:S{productKey}/${deviceName}/tempHumUpload
操作权限(设备):发布

消息方向:上行

负载格式:  Byte1:温度值

Byte2:湿度值

Byte3:温度阈值

MQTT 订阅消息的回调函数:

image.png

Demo 参数输入:
需要保存在 MCU 闪存中的信息:

1.WIFI 配网参数(串口输入)

2.阿里云 loT 平台三元组信息(串口输入)

3.温度报警阈值(云端下发)      

Demo 参数存储:

1.MCU 用户闪存容量2M,页大小4K(双 bank 下)

2.取末尾32K 作为用户参数存储区

Sensor 数据的读取(1

Sensor 数据的读取(2

项目例程内存占用:

总内存占用(使用 IAR v8.32.3,最高优化等级)

1.Flash52924字节

2.Ram:10874字节(包括4KB 堆栈)

主要模块内存占用:

image.png

相关文章
|
8天前
|
消息中间件 DataWorks 物联网
MQTT问题之接入阿里云物联网平台如何解决
MQTT接入是指将设备或应用通过MQTT协议接入到消息服务器,以实现数据的发布和订阅;本合集着眼于MQTT接入的流程、配置指导以及常见接入问题的解决方法,帮助用户实现稳定可靠的消息交换。
41 1
|
8天前
|
JSON 物联网 开发工具
MQTT协议问题之搭建物联网空调的服务器如何解决
MQTT协议是一个轻量级的消息传输协议,设计用于物联网(IoT)环境中设备间的通信;本合集将详细阐述MQTT协议的基本原理、特性以及各种实际应用场景,供用户学习和参考。
34 1
|
8天前
|
消息中间件 网络协议 物联网
MQTT协议问题之阿里云物联网服务器断开如何解决
MQTT协议是一个轻量级的消息传输协议,设计用于物联网(IoT)环境中设备间的通信;本合集将详细阐述MQTT协议的基本原理、特性以及各种实际应用场景,供用户学习和参考。
45 1
|
26天前
|
存储 安全 物联网
安防摄像头IPC如何快速接入阿里云Link Visual视频服务(阿里云生活物联网)
Link Visual是生活物联网平台针对视频产品推出的增值服务,提供视频数据上云、存储、转发、AI计算等能力。 大白话就是:通过阿里云的Link Visual视频服务,可以让你的IPC摄像头设备完成上云功能,并快速实现如下功能介绍中的功能。其中可以享受阿里云P2P协议支持,帮助企业节省流量服务器流量带宽。
152 7
|
1月前
|
传感器 负载均衡 网络协议
物联网协议之MQTT
物联网协议之MQTT
64 0
|
2月前
|
网络协议 物联网 网络性能优化
物联网网络协议-MQTT协议的使用
物联网网络协议-MQTT协议的使用
|
16小时前
|
弹性计算 Ubuntu Linux
阿里云【幻兽帕鲁/Palworld】服务器一键搭建教程(最全)
《幻兽帕鲁》主机如何搭建?现在的阿里云有多方便?按官方说法,玩家只需要点三次鼠标,最快只要10秒,就能自动安装好《幻兽帕鲁》主机。阿里云提供了一种简单、快速的方法,让新手小白也能0基础10秒搭建幻兽帕鲁游戏联机主机!
|
1天前
|
弹性计算 应用服务中间件 数据中心
阿里云香港服务器24元1个月、288元一年,补货!
阿里云香港服务器24元1个月、288元一年,补货!阿里云香港服务器中国香港数据中心网络线路类型BGP多线精品,中国电信CN2高速网络高质量、大规格BGP带宽,运营商精品公网直连中国内地,时延更低,优化海外回中国内地流量的公网线路,可以提高国际业务访问质量
|
1天前
|
弹性计算 大数据 测试技术
惊呼!阿里云云服务器ECS优惠价99元一年,不限新老用户!
惊呼!阿里云云服务器ECS优惠价99元一年,不限新老用户!云服务器ECS经济型e实例2核2G、3M固定带宽99元一年、ECS u1实例2核4G、5M固定带宽、80G ESSD Entry盘优惠价格199元一年,轻量应用服务器2核2G3M带宽轻量服务器一年61元、2核4G4M带宽轻量服务器一年165元12个月、2核4G服务器30元3个月
115 0
|
2天前
|
弹性计算 数据安全/隐私保护
2024年雾锁王国(Enshrouded)自建个人专属16人联机服务器教程(阿里云)
雾锁王国服务器可以在Steam中直接使用官方给出的工具来搭建,也可以自己买一个服务器,然后自己配置自己调试。阿里云提供服务器一键部署的功能,适合小白玩家使用。雾锁王国(Enshrouded)作为一款热门多人在线游戏,为了给玩家提供稳定、流畅的联机体验,阿里云提供了高效便捷的快速部署解决方案,本文将为大家分享阿里云一键部署雾锁王国联机服务器详细教程。

相关产品

  • 物联网平台