You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用libcurl向本地HTTPS服务器请求时出现CURLE_RECV_ERROR(错误码56)

CURLE_RECV_ERROR(56):libcurl(mbedTLS)单片机无法接收本地Python HTTPS服务器响应

问题背景

  • 搭载libcurl(以mbedTLS为SSL库)的单片机向本地Python HTTPS测试服务器发起请求时,返回CURLE_RECV_ERROR(错误码56)
  • 服务器可正常响应其他客户端,设备也能正常访问公网HTTPS服务器,已排除代理、防火墙问题
  • 已添加服务器自签名证书,且关闭了CURLOPT_SSL_VERIFYHOST和CURLOPT_SSL_VERIFYPEER校验
  • 服务器日志显示已返回200响应,但客户端libcurl日志仅记录发送GET请求后直接关闭连接,未收到响应内容

Python HTTPS服务器代码

import http.server
import ssl
import logging
from io import BytesIO

# Configure logging
logging.basicConfig(level=logging.INFO)

class HTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
    def do_GET(self):
        original_wfile = self.wfile
        self.wfile = BytesIO()
        super().do_GET()
        self.send_captured_response(original_wfile)

    def send_captured_response(self, original_wfile):
        response_body = self.wfile.getvalue()
        logging.info(f"Response body:\n{response_body.decode('utf-8')}")

        self.wfile = original_wfile
        self.wfile.write(response_body)

def create_server(server_class=http.server.HTTPServer, handler_class=HTTPRequestHandler, port=443):
    server_address = ('192.168.0.107', port)
    httpd = server_class(server_address, handler_class)
    # Set up SSL context
    context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    context.load_cert_chain('Path/To/certificate.pem', 'Path/To/private.key') 
    httpd.socket = context.wrap_socket(httpd.socket, server_side=True)
    return httpd

if __name__ == "__main__":
    httpd = create_server()
    logging.info("Serving HTTPS on port 443...")
    httpd.serve_forever()

服务器日志

INFO:root:Serving HTTPS on port 443...
192.168.0.201 - - [17/Apr/2024] "GET / HTTP/1.1" 200 -
INFO:root:Response body:
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.11.5        
Date: Thu, 17 Apr 2024 
Content-type: text/html
Content-Length: 941
Last-Modified: Thu, 17 Apr 
<!DOCTYPE html>
<html lang="en">
<head>
The rest is simply an HTML content ...

客户端C++代码

#include <iostream>
#include <curl/curl.h>

int main() {
    CURL* curl;
    CURLcode res;

    curl_global_init(CURL_GLOBAL_DEFAULT);

    curl = curl_easy_init();
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, "https://192.168.0.107:443");
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // Disables host name verification
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // Disables Peer's certificate verfication
        
        res = curl_easy_perform(curl);
        
        std::cout << "Curl Response Response Code: " << res << std::endl;
        
        curl_easy_cleanup(curl);
    }
    curl_global_cleanup();
    return 0;
}

客户端libcurl日志

* timeout on name lookup is not supported
*   Trying 192.168.0.107:443...
* Connected to 192.168.0.107 (192.168.0.107) port 443
* mbedTLS: Connecting to 192.168.0.107:443
* mbedTLS: Handshake complete, cipher is TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256
* SSL connected
* using HTTP/1.x
> GET / HTTP/1.1
Host: 192.168.0.107
Accept: */*

* Closing connection
Curl Code: 56

问题排查与解决

核心原因

  1. 服务器响应处理逻辑异常:自定义的HTTPRequestHandler中,替换self.wfile为BytesIO后调用父类do_GET(),父类方法会自动完成响应发送流程,后续手动写回original_wfile的操作会导致响应数据写入不完整,触发TCP连接异常关闭。
  2. 响应头格式错误:服务器日志显示Date和Last-Modified字段格式不完整,会导致libcurl解析响应头失败,主动关闭连接。

修复步骤

  1. 简化服务器响应日志逻辑:去掉拦截wfile的操作,直接在父类响应方法中添加日志:
class HTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
    def send_response(self, code, message=None):
        super().send_response(code, message)
        logging.info(f"Sent {code} response for path {self.path}")
  1. 确保响应头格式正确:依赖父类SimpleHTTPRequestHandler自动生成规范的Date、Last-Modified等响应头,不要手动修改破坏流程。

  2. 客户端添加响应回调:给libcurl设置CURLOPT_WRITEFUNCTION,确保框架能正确接收响应数据:

size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) {
    return size * nmemb; // 仅返回接收长度,也可在此处理响应内容
}

int main() {
    // ... 原有初始化代码 ...
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, "https://192.168.0.107:443");
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); // 添加回调
        
        res = curl_easy_perform(curl);
        // ... 原有后续代码 ...
    }
}
  1. 验证mbedTLS加密套件支持:确认单片机上的mbedTLS版本支持协商的TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256加密套件,无数据包校验过度严格的配置。

内容的提问来源于stack exchange,提问作者Bornak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 06:25:03