当前位置: 首页 > news >正文

ESP-idf注册双服务器配置

在开发ESP服务器时遇到一个问题,如果开启了mjpeg视频流,再开发其它功能会被阻塞,例如想在网页中新增一个按钮,点击一下可以在串口打印文字,这个会被阻塞住。

(索引):21 POST https://192.168.5.100/test-button net::ERR_TIMED_OUT
(索引):30 网络错误: TypeError: Failed to fetchat testButton ((索引):21:9)at HTMLButtonElement.onclick ((索引):17:36)

MJPEG 流(multipart/x-mixed-replace)不是一次性响应,而是 HTTP 长连接。浏览器发 GET /stream,ESP32 一直不关闭连接,持续推送 JPEG 帧。在 ESP-IDF 的 esp_http_server 里,每一个客户端请求对应一条 TCP socket。这个 socket 不释放,直到浏览器主动断开或者 ESP 出错才会释放。结果就会造成只要页面开着,/stream 连接就是“永久占用”,导致页面中的其它服务始终排不上队,连接超时。

一个比较好的方法是创建两个服务器。

注意,在ESP-idf使用中,esp_http_serveresp_https_server是两个不一样的服务器配置包,当前服务器使用https服务器,因此应当主要使用esp_https_server。但注意,这两个包并不是完全并列的,esp_https_server 基于 esp_http_server 实现,在 HTTP 服务器之上增加 TLS/SSL 能力,HTTP 服务器的大部分文档与 API 也适用于 HTTPS 服务器

conf.port_secure/port_insecuretransport_mode、证书与密钥字段、会话票据、ALPN、证书选择钩子、TLS 握手超时等,都是 esp_https_server 特有或扩展的配置项;而 httpd_config_t.httpd 则承载通用的 HTTP 层配置(任务与资源等)

对于我们尤其要注意的,两个服务器的监听端口也必须不同(HTTP 用 httpd_config_t.server_port;而我们的HTTPS 用 httpd_ssl_config_t.port_secureport_insecure

如何在一个程序里启动两个https服务器

可以肯定的是,一个程序里可以启动两个https服务器,但必须配置好conf.port_secureconf.httpd.ctrl_port,前者用于配置监听端口,后者是服务器内部的“控制通道”UDP 端口。

/*** @brief 启动 MJPEG 专用 HTTPS 服务器* @return httpd_handle_t 服务器句柄*/static httpd_handle_t start_mjpeg_webserver(void)
{httpd_handle_t server = NULL;ESP_LOGI(TAG, "Starting MJPEG webserver");/* MJPEG 专用 HTTPS 服务器配置 */httpd_ssl_config_t conf = HTTPD_SSL_CONFIG_DEFAULT();/* 仅启用 HTTPS,并改端口为 8443 */conf.transport_mode = HTTPD_SSL_TRANSPORT_SECURE;  // 只跑 TLSconf.port_secure    = 8443;                        // HTTPS 监听端口conf.httpd.ctrl_port = 32769;/* HTTPD 内部参数(仍在 conf.httpd.* 下设置) */conf.httpd.max_open_sockets = 3;     // 流用 1~2 条,3 足够conf.httpd.lru_purge_enable = true;  // 满员时踢最旧连接conf.httpd.task_priority    = 5;conf.httpd.stack_size       = 8192;/* 加载服务器证书 */extern const unsigned char servercert_start[] asm("_binary_servercert_pem_start");extern const unsigned char servercert_end[]   asm("_binary_servercert_pem_end");conf.servercert = servercert_start;conf.servercert_len = servercert_end - servercert_start;/* 加载私钥 */extern const unsigned char prvtkey_pem_start[] asm("_binary_prvtkey_pem_start");extern const unsigned char prvtkey_pem_end[]   asm("_binary_prvtkey_pem_end");conf.prvtkey_pem = prvtkey_pem_start;conf.prvtkey_len = prvtkey_pem_end - prvtkey_pem_start;/* 启动服务器 */esp_err_t ret = httpd_ssl_start(&server, &conf);if (ESP_OK != ret) {ESP_LOGE(TAG, "Error starting MJPEG server!");return NULL;}/* 注册 MJPEG URI 处理器 */ESP_LOGI(TAG, "Registering MJPEG URI handlers");httpd_register_uri_handler(server, &mjpeg_stream);return server;
}/*** @brief 停止 MJPEG 专用 HTTPS 服务器* @param server 服务器句柄* @return esp_err_t 停止结果*/
static esp_err_t stop_mjpeg_webserver(httpd_handle_t server)
{return httpd_ssl_stop(server);
}/*** @brief 启动 HTTPS 服务器* @return httpd_handle_t 服务器句柄*/
static httpd_handle_t start_webserver(void)
{httpd_handle_t server = NULL;ESP_LOGI(TAG, "Starting main HTTPS server");/* HTTPS 服务器配置 */httpd_ssl_config_t conf = HTTPD_SSL_CONFIG_DEFAULT();conf.port_secure = 443;conf.httpd.ctrl_port = 32768;conf.httpd.lru_purge_enable = true;      // 启用最近最少使用清理机制/* 加载服务器证书 */extern const unsigned char servercert_start[] asm("_binary_servercert_pem_start");extern const unsigned char servercert_end[]   asm("_binary_servercert_pem_end");conf.servercert = servercert_start;conf.servercert_len = servercert_end - servercert_start;/* 加载私钥 */extern const unsigned char prvtkey_pem_start[] asm("_binary_prvtkey_pem_start");extern const unsigned char prvtkey_pem_end[]   asm("_binary_prvtkey_pem_end");conf.prvtkey_pem = prvtkey_pem_start;conf.prvtkey_len = prvtkey_pem_end - prvtkey_pem_start;/* 启动服务器 */esp_err_t ret = httpd_ssl_start(&server, &conf);if (ESP_OK != ret) {ESP_LOGE(TAG, "Error starting server!");return NULL;}/* 注册 URI 处理器 */ESP_LOGI(TAG, "Registering URI handlers");httpd_register_uri_handler(server, &root);httpd_register_uri_handler(server, &test_button);return server;
}/*** @brief 停止 HTTPS 服务器* @param server 服务器句柄* @return esp_err_t 停止结果*/
static esp_err_t stop_webserver(httpd_handle_t server)
{return httpd_ssl_stop(server);
}

参考上面的代码,我们启用了两个服务器,一个是start_mjpeg_webserver一个是start_webserver,分别用于视频流和主页面。

二者设置关键在这里:

/* 仅启用 HTTPS,并改端口为 8443 */
conf.transport_mode = HTTPD_SSL_TRANSPORT_SECURE;  // 只跑 TLS
conf.port_secure    = 8443;                        // HTTPS 监听端口
conf.httpd.ctrl_port = 32769;

必须设置不同的conf.port_secureconf.httpd.ctrl_port,否则先启动的服务器会挤占通道资源,导致后面的服务器无法启动。

服务器注册与启动顺序

  1. 准备证书与 HTTPS 配置
    对于https服务器需要配置密钥,参考启用WiFi和http server,对于需要开启https服务器的项目来说,推荐在main程序下新建certs文件夹,用来专门存在密钥对。推荐层级结构如下:

    	esp32_https_server/├── main/│   ├── main.c              # 主程序(包含所有功能)│   └── certs/              # SSL证书目录│       ├── servercert.pem  # 服务器证书│       └── prvtkey.pem     # 服务器私钥├── CMakeLists.txt          # 构建配置└── sdkconfig               # 项目配置
    

    在certs目录下依次执行如下两条命令(需要先下载好openssl软件):

    openssl genrsa -out prvtkey.pem 2048openssl req -new -x509 -key prvtkey.pem -out servercert.pem -days 365
    

    执行第二个语句时需要填写一些内容:

    Country Name (2 letter code):输入 CN
    State or Province Name:输入你的省份,比如 Beijing
    City Name:输入你的城市
    Organization Name:输入你的组织名称
    Organizational Unit Name:可以输入你的部门
    Common Name:这个很重要,输入你的ESP32设备的IP地址或域名
    Email Address:输入邮箱地址
    

    如果有内容填就如实填,如果没有其实无所谓。需要稍微考虑一下的是Common Name这个选项,实测随便 填一个名字就可以。
    在这里插入图片描述

    生成并嵌入证书/私钥,填入 httpd_ssl_config_tservercert/servercert_lenprvtkey_pem/prvtkey_len;按需设置 transport_modeport_secure 等。注意,要把需要文件在main目录层级下的CMakeLists.txt文件中引入,否则无法正确配置:

    idf_component_register(SRCS "main.c"INCLUDE_DIRS "."EMBED_TXTFILES "certs/servercert.pem" "certs/prvtkey.pem")
    
  2. 启动 HTTPS 服务器
    在编写的服务器函数里调用 httpd_ssl_start(&handle, &config) 创建服务器实例并分配资源,成功后获得 handle。例如上述代码中在每一个服务器注册函数的后半部分使用:

    /* 启动服务器 */
    esp_err_t ret = httpd_ssl_start(&server, &conf);
    if (ESP_OK != ret) {ESP_LOGE(TAG, "Error starting server!");return NULL;
    }
    
  3. 定义 URL 路由与处理函数
    路由用 httpd_uri_t 描述,包含 uri、method、handler 和可选 user_ctx;handler 内用 httpd_req_recv/httpd_resp_send 等处理请求。当前工程定义了三个路由,分别是页面根目录、视频流处理和一个测试按钮:

    /* URI 路由配置 */
    static const httpd_uri_t root = {
    .uri       = "/",
    .method    = HTTP_GET,
    .handler   = root_get_handler
    };static const httpd_uri_t mjpeg_stream = {.uri       = "/stream",.method    = HTTP_GET,.handler   = mjpeg_stream_handler
    };static const httpd_uri_t test_button = {.uri       = "/test-button",.method    = HTTP_POST,.handler   = test_button_handler
    };
    
  4. 注册路由到服务器
    httpd_register_uri_handler(handle, &uri) 进行绑定;若同 URI+方法已存在会返回 ESP_ERR_HTTPD_HANDLER_EXISTS

  5. (可选)启用高级功能
    长连接、WebSocket(需 CONFIG_HTTPD_WS_SUPPORT)等按需启用,API 与 HTTP 服务器相同(少数底层传输覆盖 API 例外)。

  6. 停止服务器
    调用 httpd_ssl_stop(handle)关闭 HTTPS 服务器并释放资源。本工程单独设计一个stop函数用来停止服务器,例如:

    /**
    * @brief 停止 MJPEG 专用 HTTPS 服务器* @param server 服务器句柄
    * @return esp_err_t 停止结果*/
    static esp_err_t stop_mjpeg_webserver(httpd_handle_t server)
    {return httpd_ssl_stop(server);
    }
    

以上配置完成,在app_main中依次启用服务器:

/* ========== HTTPS 服务器启动 ========== */
// 启动主服务器(端口443)
main_server = start_webserver();
if (main_server == NULL) {ESP_LOGE(TAG, "Failed to start main HTTPS server!");return;
}// 启动MJPEG专用服务器(端口8443)
mjpeg_server = start_mjpeg_webserver();
if (mjpeg_server == NULL) {ESP_LOGE(TAG, "Failed to start MJPEG HTTPS server!");// 主服务器仍可继续运行} else {ESP_LOGI(TAG, "MJPEG server started successfully on port 8443");
}
http://www.xdnf.cn/news/1472275.html

相关文章:

  • SemiSAM+:在基础模型时代重新思考半监督医学图像分割|文献速递-深度学习人工智能医疗图像
  • 笔记:现代操作系统:原理与实现(2)
  • CLIP学习
  • 【C++】Vector完全指南:动态数组高效使用
  • Transformer核心—自注意力机制
  • 大批项目经理被迫上前线,酸爽
  • 图片在vue2中引用的方式和优缺点
  • 【数字孪生核心技术】什么是倾斜摄影?
  • 遇到 Git 提示大文件无法上传确实让人头疼
  • SVT-AV1编码器中实现WPP依赖管理核心调度
  • 门控MLP(Qwen3MLP)与稀疏混合专家(Qwen3MoeSparseMoeBlock)模块解析
  • 【开题答辩全过程】以 基于JSP的宠物医院管理系统设计为例,包含答辩的问题和答案
  • LTV-1008-TP1-G 电子元器件 LiteOn光宝 发光二极管 核心解析
  • 字符串(2)
  • 一文读懂 RAG 与 KAG:原理、工程落地与开源实战
  • scrypt 密钥派生算法(RFC7914)技术解析及源码示例
  • 流固耦合|08-1外部数据导入
  • 基于Django+Vue3+YOLO的智能气象检测系统
  • 【Python - 类库 - requests】(02)使用“requests“发起GET请求的详细教程
  • Markdown Editor开发文档(附下载地址)
  • AmbiSSL
  • 深度学习模型在边缘计算设备上的部署
  • python库 Py2exe 的详细使用(将 Python 脚本变为Windows独立软件包)
  • 孙宇晨被质疑“砸盘”川普家族项目WLFI,惨遭拉黑锁仓?自称无辜展开“维权”!
  • Process Explorer 学习笔记(第三章3.3.1):DLL和句柄
  • C++中的Reactor和Proactor模型进行系统性解析
  • 2025经管领域十大高含金量证书推荐
  • Redis 集群:连锁银行的 “多网点智能协作系统”
  • 2025 IT行业含金量超高的8大证书推荐
  • PAT 1096 Consecutive Factors