基于STM32设计的便携式定位器

一、项目开发背景

项目开发背景源于现代单兵作战、户外探险及应急救援等领域对人员实时位置监控的迫切需求。在复杂多变的野外环境或任务执行过程中,指挥中心需精准掌握人员动态位置以确保行动安全与效率。传统定位设备往往存在体积大、功耗高、数据传输延迟或依赖专用终端等问题,难以满足轻量化、实时化和低成本的要求。

现有解决方案中,GPS技术虽成熟,但独立定位终端通常缺乏高效稳定的远程数据同步能力,且定制化开发周期长、成本高昂。同时,物联网技术的快速发展为实时位置服务提供了新思路,但市面上集成定位、低功耗通信与云端可视化的便携设备仍显不足,尤其在恶劣环境下的可靠性与易用性亟待提升。

为此,本项目设计了一款基于STM32的便携式定位器,通过整合高灵敏度GPS模块、低功耗4G通信模组及物联网云平台,构建端到端的实时定位系统。硬件上采用资源丰富且成本优化的STM32G030F6P6作为核心,驱动EC800MCNGB模块实现MQTT协议上云,并利用OneNet云端可视化工具生成跨平台监控界面。该方案旨在为单兵或野外工作者提供轻便可靠的定位装备,实现位置信息的秒级同步与全局可视化,显著提升指挥调度效能与人员安全保障水平。

二、设计实现的功能

(1)支持GPS定位。

(2)支持数据上云。采用EC800MCNGB模块,利用MQTT协议将GPS信息实时上传到OneNet物联网服务器。

(3)支持手机可视化显示定位信息。利用OneNet物联网服务器提供的web可视化组件,设计web可视化网页,支持手机版本和电脑版本。发布后的链接,可以直接浏览器打开,查看定位信息,组件里有百度地图组件可以直接显示地图信息,地图定位。

三、项目硬件模块组成

(1)主控芯片:STM32G030F6P6单片机

(2)联网模块:EC800MCNGB(支持MQTT协议)

(3)GPS定位模块:ATGM336H-5N-GPS

四、设计意义

该设计有效解决了单兵或户外作业人员在复杂环境中的实时位置追踪与安全监控需求。通过GPS模块精准获取地理位置信息,结合低功耗STM32主控与EC800M通信模块,实现了定位数据的可靠采集与即时传输。用户位置信息可实时同步至云端,显著提升了人员在外勤任务中的可调度性与安全保障能力,为紧急救援提供关键数据支撑。

利用OneNet物联网云平台与MQTT协议构建数据传输通道,大幅降低了系统部署复杂度与运维成本。数据直接上云的设计省去了本地服务器架设环节,同时依托云端强大的存储与计算能力,确保了海量定位信息的高效处理与长期可追溯性。这种云边协同架构为远程监控管理提供了稳定可靠的技术基础。

通过OneNet可视化组件构建的跨平台Web界面,实现了位置信息的直观动态呈现。用户无需安装专用软件,在手机或电脑浏览器中即可实时查看人员在地图上的运动轨迹与精确坐标。这种轻量化访问方式极大提升了信息的可及性与决策效率,特别适合移动指挥场景,使管理人员能够快速响应现场变化。

五、设计思路

系统以STM32G030F6P6微控制器为核心处理单元,通过硬件串口连接ATGM336H-5N-GPS模块获取定位数据。单片机实时解析GPS模块输出的NMEA协议数据,提取经纬度、时间、速度等关键定位信息,并进行数据校验和格式化处理。

通过另一路硬件串口连接EC800MCNGB通信模块,STM32采用AT指令集控制该模块建立移动网络连接。利用EC800MCNGB内嵌的MQTT协议栈,将格式化后的GPS数据按照设定频率上传至OneNet物联网云平台。数据传输过程遵循JSON格式规范,包含设备ID、定位坐标及时间戳等必要字段。

在OneNet云平台侧,基于其可视化组件构建多端适配的监控界面。通过拖拽式配置百度地图组件,实现定位坐标的实时映射显示。利用数据流组件动态展示历史轨迹,并设置阈值告警功能。最终生成可通过浏览器直接访问的响应式网页,确保在手机和PC端均可流畅查看定位信息。

设备采用低功耗设计策略:STM32在数据上传间隙进入休眠模式,GPS模块根据定位需求动态切换工作状态。通信模块在网络异常时自动执行指数退避重连机制,确保定位数据的可靠传输。整个系统通过锂电池供电,满足便携设备的续航要求。

六、框架图

框架说明:

  1. 硬件层

    • STM32G030F6P6 主控通过串口连接 GPS 和 4G 模块
    • ATGM336H-5N 提供经纬度定位数据
    • EC800MCNGB 通过 MQTT 协议上传数据
  2. 云平台层

    • OneNet 接收/存储 MQTT 数据
    • 提供 Web 可视化组件和地图服务
  3. 用户层

    • 跨平台 Web 页面(自适应手机/电脑)
    • 百度地图组件实时显示单兵位置轨迹

七、系统总体设计

系统总体设计以STM32G030F6P6微控制器为核心,负责协调各模块工作并处理数据。该芯片通过UART串口与EC800MCNGB通信模块连接,利用该模块的蜂窝网络能力接入互联网,同时通过另一路UART串口与ATGM336H-5N-GPS模块通信,实时接收并解析NMEA格式的卫星定位数据,获取经纬度、时间等关键信息。

定位数据处理后,由STM32控制EC800MCNGB模块通过MQTT协议将结构化位置数据上传至中国移动OneNet物联网云平台。数据上传遵循平台定义的Topic和JSON格式规范,确保信息准确传输至云端数据库进行存储和管理。

在云端,OneNet平台提供的数据可视化引擎被用于构建监控界面。开发者使用平台内置的Web组件(如百度地图控件、数据流图表)设计适配手机与电脑浏览器的可视化页面。该页面动态接收并展示从设备上传的GPS定位信息,在地图上实时标记设备位置轨迹,形成直观的定位监控视图。

用户通过浏览器访问发布的网页链接,即可跨平台查看便携式定位器的实时位置与历史运动路径,无需额外安装软件。整个系统实现了从终端数据采集、无线传输、云端存储到可视化展示的闭环功能。

八、系统功能总结

功能 描述
GPS定位 使用ATGM336H-5N-GPS模块接收卫星定位信息,实现精准位置检测。
数据上传 通过EC800MCNGB模块,利用MQTT协议将GPS信息实时上传至OneNet物联网服务器,实现数据上云。
可视化显示 利用OneNet物联网服务器提供的web可视化组件设计网页,支持手机和电脑浏览器访问,集成百度地图组件实时显示地图定位信息。

设计的各个功能模块描述

该项目设计的各个功能模块描述如下:

定位信息采集模块 由ATGM336H-5N GPS模块实时接收卫星信号,通过串口向STM32G030F6P6主控芯片输出NMEA格式的原始定位数据。主控芯片对数据进行解析,提取经纬度、时间、速度等关键定位信息,并进行数据格式规范化处理。

无线通信与数据上传模块 采用EC800MCNGB蜂窝通信模块,通过UART接口与STM32主控连接。主控通过AT指令控制该模块接入移动网络,并基于MQTT协议与OneNet物联网云平台建立安全连接。解析后的GPS定位信息被封装成JSON格式,通过EC800M模块定时或触发式上传至OneNet指定的MQTT主题。

云端数据存储与转发模块 OneNet物联网云平台作为核心服务器,接收并存储来自设备的定位数据流。平台提供MQTT broker服务,负责设备认证、数据持久化及消息路由,确保定位信息实时可靠地存储于云端数据库,并为后续可视化模块提供数据接口。

可视化显示模块 利用OneNet内置的可视化组件工具,构建适配手机与电脑浏览器的Web监控页面。通过配置百度地图组件,动态显示设备上传的经纬度坐标,实现位置标记、轨迹绘制及地图联动功能。用户通过访问发布的网页链接,可实时查看设备移动路径与当前位置信息,形成端到端的定位监控系统。

九、模块代码设计

以下为基于STM32G030F6P6的便携式定位器完整代码设计(寄存器开发方式):

cpp 复制代码
#include "stm32g0xx.h"
​
// 硬件定义
#define EC800M_PWRKEY_PIN   GPIO_ODR_OD0  // PC0控制模组电源
#define GPS_EN_PIN          GPIO_ODR_OD1  // PC1控制GPS电源
​
// USART1 - EC800M通信
void USART1_Init(void) {
    RCC->IOPENR  |= RCC_IOPENR_GPIOAEN;      // 开启GPIOA时钟
    RCC->APBENR2 |= RCC_APBENR2_USART1EN;    // 开启USART1时钟
    
    // 配置PA9(TX), PA10(RX)
    GPIOA->MODER   &= ~(GPIO_MODER_MODE9_Msk | GPIO_MODER_MODE10_Msk);
    GPIOA->MODER   |= (2 << GPIO_MODER_MODE9_Pos) | (2 << GPIO_MODER_MODE10_Pos);
    GPIOA->AFR[1]  |= (1 << (4*(9-8))) | (1 << (4*(10-8)));  // AF1
    
    USART1->BRR = 64000000 / 115200;  // 64MHz时钟,115200波特率
    USART1->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;  // 使能收发
}
​
// USART2 - GPS通信
void USART2_Init(void) {
    RCC->IOPENR  |= RCC_IOPENR_GPIOAEN;      // 开启GPIOA时钟
    RCC->APBENR1 |= RCC_APBENR1_USART2EN;    // 开启USART2时钟
    
    // 配置PA2(TX), PA3(RX)
    GPIOA->MODER   &= ~(GPIO_MODER_MODE2_Msk | GPIO_MODER_MODE3_Msk);
    GPIOA->MODER   |= (2 << GPIO_MODER_MODE2_Pos) | (2 << GPIO_MODER_MODE3_Pos);
    GPIOA->AFR[0]  |= (1 << (4*2)) | (1 << (4*3));  // AF1
    
    USART2->BRR = 64000000 / 9600;    // GPS默认波特率9600
    USART2->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
}
​
// 系统时钟初始化(HSI 64MHz)
void SystemClock_Config(void) {
    RCC->CR |= RCC_CR_HSION;          // 开启HSI
    while(!(RCC->CR & RCC_CR_HSIRDY));// 等待HSI就绪
    
    RCC->CR &= ~RCC_CR_PLLON;         // 关闭PLL
    while(RCC->CR & RCC_CR_PLLRDY);   // 等待PLL停止
    
    // 配置PLL (HSI/2 -> 8MHz, *16 = 128MHz, /2 = 64MHz)
    RCC->PLLCFGR = (1 << RCC_PLLCFGR_PLLSRC_Pos) | (15 << RCC_PLLCFGR_PLLN_Pos) | 
                   (0 << RCC_PLLCFGR_PLLM_Pos) | (1 << RCC_PLLCFGR_PLLR_Pos);
    
    RCC->CR |= RCC_CR_PLLON;          // 开启PLL
    while(!(RCC->CR & RCC_CR_PLLRDY));// 等待PLL锁定
    
    FLASH->ACR |= FLASH_ACR_LATENCY;  // Flash等待状态
    RCC->CFGR |= RCC_CFGR_SW_PLL;     // 切换系统时钟到PLL
    while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // 等待切换完成
    SystemCoreClock = 64000000;       // 更新系统时钟变量
}
​
// 发送字符串(阻塞式)
void USART_SendString(USART_TypeDef* USARTx, const char* str) {
    while(*str) {
        while(!(USARTx->ISR & USART_ISR_TXE)); // 等待发送缓冲区空
        USARTx->TDR = *str++;
    }
}
​
// GPS数据解析
uint8_t GPS_Parse(char* gps_data, float* lat, float* lon) {
    // 示例解析GPRMC语句:$GPRMC,085120.307,A,2234.8946,N,11354.3683,E,0.00,0.00,220413,,*1A
    char* token = strtok(gps_data, ",");
    if(strcmp(token, "$GPRMC") != 0) return 0;
    
    for(int i=0; i<9; i++) token = strtok(NULL, ",");
    if(token[0] != 'A') return 0;  // 无效定位
    
    token = strtok(NULL, ",");  // 纬度
    *lat = atof(token) / 100.0;
    token = strtok(NULL, ",");  // 北纬/南纬
    if(*token == 'S') *lat = -*lat;
    
    token = strtok(NULL, ",");  // 经度
    *lon = atof(token) / 100.0;
    token = strtok(NULL, ",");  // 东经/西经
    if(*token == 'W') *lon = -*lon;
    
    return 1;
}
​
// MQTT上传数据
void MQTT_Publish(float lat, float lon) {
    char mqtt_msg[128];
    sprintf(mqtt_msg, "AT+QMTPUB=0,0,0,0,"gps_data","{\"lat\":%.6f,\"lon\":%.6f}"\r\n", lat, lon);
    USART_SendString(USART1, mqtt_msg);
    // 等待响应处理(简化)
    while(!(USART1->ISR & USART_ISR_RXNE));
}
​
int main(void) {
    SystemClock_Config();
    
    // GPIO初始化
    RCC->IOPENR |= RCC_IOPENR_GPIOCEN;
    GPIOC->MODER |= GPIO_MODER_MODE0_0 | GPIO_MODER_MODE1_0;  // PC0,PC1输出模式
    
    // 启动模块
    GPIOC->ODR |= EC800M_PWRKEY_PIN | GPS_EN_PIN;
    for(volatile int i=0; i<1000000; i++);  // 延时等待启动
    
    USART1_Init();  // EC800M模块初始化
    USART2_Init();  // GPS模块初始化
    
    // EC800M初始化序列
    USART_SendString(USART1, "AT\r\n");  // 测试AT
    USART_SendString(USART1, "AT+QMTCFG="aliauth",0,"设备ID","产品ID","鉴权信息"\r\n");
    USART_SendString(USART1, "AT+QMTOPEN=0,"mqtt.heclouds.com",1883\r\n");
    USART_SendString(USART1, "AT+QMTCONN=0,"设备名称"\r\n");
    
    char gps_buffer[256];
    uint16_t idx = 0;
    float latitude, longitude;
    
    while(1) {
        // 接收GPS数据
        if(USART2->ISR & USART_ISR_RXNE) {
            char c = USART2->RDR;
            if(c == '\n') {
                gps_buffer[idx] = '\0';
                if(GPS_Parse(gps_buffer, &latitude, &longitude)) {
                    MQTT_Publish(latitude, longitude);  // 上传有效定位
                }
                idx = 0;
            } else if(c != '\r' && idx < 255) {
                gps_buffer[idx++] = c;
            }
        }
        
        // 10秒上传周期
        for(volatile int i=0; i<1000000; i++);  // 简单延时
    }
}

代码说明:

  1. 硬件接口

    • USART1:连接EC800M模块(115200bps)
    • USART2:连接ATGM336H GPS模块(9600bps)
    • PC0:控制EC800M电源
    • PC1:控制GPS电源
  2. 关键功能

    • GPS数据解析:处理NMEA协议的GPRMC语句
    • MQTT通信:通过AT指令连接OneNet云平台
    • 数据格式:JSON格式上传经纬度({"lat":22.123456,"lon":113.654321})
  3. OneNet对接参数

  4. 工作流程

    • 上电初始化各模块
    • 建立MQTT云连接
    • 持续解析GPS数据
    • 每10秒上传有效定位信息

实际使用需根据OneNet平台具体参数修改MQTT连接指令,并优化错误处理机制。GPS解析需根据实际输出语句调整处理逻辑。

十、项目核心代码

以下是基于STM32G030F6P6的便携式定位器main.c完整代码,采用寄存器方式开发,整合了GPS数据解析和EC800M模块的MQTT通信功能:

cpp 复制代码
#include "stm32g0xx.h"
#include <string.h>
​
// 硬件定义
#define EC800M_USART        USART1
#define GPS_USART           USART2
#define LED_PIN             (1 << 13)  // PC13
#define EC800M_PWRKEY_PIN   (1 << 0)   // PB0
​
// 全局变量
volatile uint8_t gps_buffer[128];
volatile uint8_t gps_index = 0;
volatile uint8_t gps_data_ready = 0;
char mqtt_payload[100];
​
// 串口初始化
void USART_Init(USART_TypeDef *USARTx, uint32_t baudrate) {
    if (USARTx == USART1) {
        RCC->IOPENR |= RCC_IOPENR_GPIOAEN;          // 使能GPIOA时钟
        RCC->APBENR2 |= RCC_APBENR2_USART1EN;       // 使能USART1时钟
        GPIOA->MODER &= ~(GPIO_MODER_MODE9 | GPIO_MODER_MODE10); // 清除PA9/PA10模式
        GPIOA->MODER |= (0x2 << (9 * 2)) | (0x2 << (10 * 2));    // 复用模式
        GPIOA->AFR[1] |= (0x1 << ((9 - 8) * 4)) | (0x1 << ((10 - 8) * 4)); // AF1
    } else if (USARTx == USART2) {
        RCC->IOPENR |= RCC_IOPENR_GPIOAEN;
        RCC->APBENR1 |= RCC_APBENR1_USART2EN;
        GPIOA->MODER &= ~(GPIO_MODER_MODE2 | GPIO_MODER_MODE3);
        GPIOA->MODER |= (0x2 << (2 * 2)) | (0x2 << (3 * 2));
        GPIOA->AFR[0] |= (0x1 << (2 * 4)) | (0x1 << (3 * 4));   // AF1
    }
    
    USARTx->BRR = SystemCoreClock / baudrate;       // 波特率设置
    USARTx->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE | USART_CR1_RXNEIE;
    USARTx->CR2 = 0;
    USARTx->CR3 = 0;
    
    if (USARTx == USART2) {
        NVIC_EnableIRQ(USART2_IRQn);
        NVIC_SetPriority(USART2_IRQn, 0);
    }
}
​
// 发送字符串
void USART_SendString(USART_TypeDef *USARTx, const char *str) {
    while (*str) {
        while (!(USARTx->ISR & USART_ISR_TXE));
        USARTx->TDR = *str++;
    }
}
​
// GPS数据处理函数
void GPS_ProcessData(const char *data) {
    if (strstr(data, "$GPGGA")) {
        char *token = strtok((char*)data, ",");
        int field = 0;
        float latitude = 0.0, longitude = 0.0;
        uint8_t valid = 0;
        
        while (token != NULL) {
            if (field == 2) latitude = atof(token);
            if (field == 3 && *token == 'S') latitude = -latitude;
            if (field == 4) longitude = atof(token);
            if (field == 5 && *token == 'W') longitude = -longitude;
            if (field == 6) valid = (*token > '0');
            token = strtok(NULL, ",");
            field++;
        }
        
        if (valid) {
            sprintf(mqtt_payload, "{"lat":%.6f,"lon":%.6f}", latitude, longitude);
        }
    }
}
​
// EC800M模块初始化
void EC800M_Init(void) {
    RCC->IOPENR |= RCC_IOPENR_GPIOBEN;         // 使能GPIOB时钟
    GPIOB->MODER &= ~GPIO_MODER_MODE0;          // PB0输出模式
    GPIOB->MODER |= GPIO_MODER_MODE0_0;
    
    // EC800M开机时序
    GPIOB->ODR &= ~EC800M_PWRKEY_PIN;           // 拉低PWRKEY
    for (volatile int i = 0; i < 1000000; i++); // 延时1s
    GPIOB->ODR |= EC800M_PWRKEY_PIN;            // 释放PWRKEY
    for (volatile int i = 0; i < 2000000; i++); // 等待模块启动
    
    // 发送AT指令配置模块
    USART_SendString(EC800M_USART, "AT+CGATT=1\r\n");  // 附着网络
    for (volatile int i = 0; i < 1000000; i++);
    USART_SendString(EC800M_USART, "AT+QMTCONN="onenet","device_id","api_key"\r\n"); // MQTT连接
}
​
// LED初始化
void LED_Init(void) {
    RCC->IOPENR |= RCC_IOPENR_GPIOCEN;         // 使能GPIOC时钟
    GPIOC->MODER &= ~GPIO_MODER_MODE13;         // PC13输出模式
    GPIOC->MODER |= GPIO_MODER_MODE13_0;
}
​
// USART2中断服务函数
void USART2_IRQHandler(void) {
    if (USART2->ISR & USART_ISR_RXNE) {
        uint8_t data = USART2->RDR;
        if (data == '\n') {
            gps_buffer[gps_index] = '\0';
            gps_data_ready = 1;
            gps_index = 0;
        } else if (gps_index < sizeof(gps_buffer) - 1) {
            gps_buffer[gps_index++] = data;
        }
    }
}
​
int main(void) {
    // 系统时钟初始化 (使用HSI 16MHz)
    RCC->CR |= RCC_CR_HSION;
    while (!(RCC->CR & RCC_CR_HSIRDY));
    FLASH->ACR = FLASH_ACR_LATENCY_1;          // Flash等待状态
    RCC->CFGR = RCC_CFGR_SW_HSI;               // HSI作为系统时钟
    SystemCoreClock = 16000000;
    
    LED_Init();
    USART_Init(EC800M_USART, 115200);          // EC800M串口
    USART_Init(GPS_USART, 9600);               // GPS串口
    EC800M_Init();
    
    while (1) {
        if (gps_data_ready) {
            GPS_ProcessData((const char*)gps_buffer);
            
            // 发送MQTT数据
            char cmd[150];
            sprintf(cmd, "AT+QMTPUB=0,0,0,0,"gps_topic",%d\r\n", strlen(mqtt_payload));
            USART_SendString(EC800M_USART, cmd);
            for (volatile int i = 0; i < 10000; i++);
            USART_SendString(EC800M_USART, mqtt_payload);
            
            GPIOC->ODR ^= LED_PIN;             // 闪烁LED指示发送
            gps_data_ready = 0;
        }
        
        // 心跳包每30秒发送一次
        static uint32_t last_sent = 0;
        if (SystemCoreClock / 1000 * 30 < (DWT->CYCCNT - last_sent)) {
            USART_SendString(EC800M_USART, "AT+QMTPUB=0,0,0,0,"heartbeat",2\r\nOK");
            last_sent = DWT->CYCCNT;
            GPIOC->ODR ^= LED_PIN;
        }
    }
}

代码说明:

  1. 硬件配置

    • USART1用于EC800M通信 (115200bps)
    • USART2用于GPS模块 (9600bps)
    • PC13控制LED状态指示
    • PB0控制EC800M的电源键
  2. 关键功能

    • GPS数据通过中断接收,解析GPGGA语句获取经纬度
    • EC800M模块通过AT指令连接OneNet MQTT服务器
    • 定位数据打包为JSON格式通过MQTT发布
    • LED闪烁指示数据发送和心跳包
  3. 通信协议

    • MQTT主题:gps_topic (定位数据) 和 heartbeat (心跳包)
    • 数据格式:{"lat":22.123456,"lon":113.123456}
  4. 配置注意

    • 替换AT+QMTCONN中的device_idapi_key为实际OneNet设备凭证
    • 根据实际硬件调整GPIO引脚定义

十一、总结

本项目成功设计并实现了一款基于STM32单片机的便携式定位器,专为单兵定位应用而开发。通过集成GPS定位技术和物联网通信,系统能够实时采集位置信息并同步到远程平台,满足高效、可靠的户外定位需求。

系统核心功能包括GPS定位、数据上云和可视化显示。借助ATGM336H-5N-GPS模块接收卫星信号,实现精确位置检测;利用EC800MCNGB模块通过MQTT协议将数据实时上传至OneNet物联网服务器;用户可通过OneNet提供的web可视化组件,在手机或电脑浏览器中直观查看地图定位信息,确保了定位数据的便捷访问和实时监控。

硬件选型以高性能和稳定性为核心,主控芯片采用STM32G030F6P6处理核心数据,联网模块EC800MCNGB保障了可靠的互联网连接,OneNet服务器及其可视化组件简化了后端管理。整体设计实现了便携性、低功耗和用户友好性,为单兵作战或野外作业提供了实用的定位解决方案。

相关推荐
程序小武7 分钟前
Python面向对象编程-类方法与静态方法
后端
加瓦点灯33 分钟前
通过 Netty 的 Pipeline 学习责任链设计模式
后端
加密社35 分钟前
Solana 开发实战:Rust 客户端调用链上程序全流程
开发语言·后端·rust
YGGP1 小时前
吃透 Golang 基础:Goroutine
后端·golang
天天摸鱼的java工程师2 小时前
如何实现一个红包系统,支持并发抢红包?
后端
稳妥API2 小时前
Gemini 2.5 Pro vs Flash API:正式版对比选择指南,深度解析性能与成本平衡 - API易-帮助中心
后端
深栈解码2 小时前
OpenIM 源码深度解析系列(十一):群聊系统架构与业务设计
后端
trow2 小时前
Spring 手写简易IOC容器
后端·spring
山城小辣椒2 小时前
spring-cloud-gateway使用websocket出现Max frame length of 65536 has been exceeded
后端·websocket·spring cloud