返回首页

HTTP Client 学习笔记

本笔记学习 Zephyr 的 HTTP Client API,用于 HTTP 请求。


示例路径

zephyr/samples/net/sockets/http_client/

核心概念

HTTP 协议

方法说明
GET获取资源
POST提交数据
PUT更新资源
DELETE删除资源

HTTP Client 工作流程

创建 Socket → 连接服务器 → 发送请求 → 接收响应 → 关闭连接

核心 API

头文件

#include <zephyr/net/socket.h>
#include <zephyr/net/http/client.h>

HTTP 请求结构

struct http_request {
    enum http_method method;      // HTTP 方法
    const char *url;              // URL 路径
    const char *host;             // 主机名
    const char *protocol;         // 协议版本 (HTTP/1.1)
    const char *payload;          // 请求体
    size_t payload_len;           // 请求体长度
    http_response_cb_t response;  // 响应回调
    uint8_t *recv_buf;            // 接收缓冲区
    size_t recv_buf_len;          // 缓冲区大小
};

HTTP 方法枚举

enum http_method {
    HTTP_GET,
    HTTP_HEAD,
    HTTP_POST,
    HTTP_PUT,
    HTTP_DELETE,
    ...
};

创建 Socket 连接

#include <zephyr/net/socket.h>

int setup_socket(sa_family_t family, const char *server, int port)
{
    struct sockaddr_in addr;
    int sock;
    
    // 创建 TCP Socket
    sock = socket(family, SOCK_STREAM, IPPROTO_TCP);
    if (sock < 0) {
        return -1;
    }
    
    // 设置服务器地址
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    inet_pton(family, server, &addr.sin_addr);
    
    // 连接服务器
    if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        close(sock);
        return -1;
    }
    
    return sock;
}

HTTP GET 请求

static int response_cb(struct http_response *rsp,
                       enum http_final_call final_data,
                       void *user_data)
{
    if (final_data == HTTP_DATA_FINAL) {
        printk("Response status: %s\n", rsp->http_status);
        printk("Content length: %zd bytes\n", rsp->data_len);
    }
    return 0;
}

void http_get_example(void)
{
    struct http_request req = {0};
    uint8_t recv_buf[512];
    int sock;
    
    sock = setup_socket(AF_INET, "192.168.1.100", 80);
    if (sock < 0) {
        return;
    }
    
    req.method = HTTP_GET;
    req.url = "/";
    req.host = "192.168.1.100";
    req.protocol = "HTTP/1.1";
    req.response = response_cb;
    req.recv_buf = recv_buf;
    req.recv_buf_len = sizeof(recv_buf);
    
    int ret = http_client_req(sock, &req, 3000, NULL);
    
    close(sock);
}

HTTP POST 请求

void http_post_example(void)
{
    struct http_request req = {0};
    uint8_t recv_buf[512];
    const char *payload = "{\"temp\": 25.5}";
    int sock;
    
    sock = setup_socket(AF_INET, "192.168.1.100", 80);
    if (sock < 0) {
        return;
    }
    
    req.method = HTTP_POST;
    req.url = "/api/data";
    req.host = "192.168.1.100";
    req.protocol = "HTTP/1.1";
    req.payload = payload;
    req.payload_len = strlen(payload);
    req.response = response_cb;
    req.recv_buf = recv_buf;
    req.recv_buf_len = sizeof(recv_buf);
    
    int ret = http_client_req(sock, &req, 3000, NULL);
    
    close(sock);
}

HTTPS (TLS) 支持

#include <zephyr/net/tls_credentials.h>

sec_tag_t sec_tag_list[] = {
    CA_CERTIFICATE_TAG,
};

// 创建 TLS Socket
sock = socket(family, SOCK_STREAM, IPPROTO_TLS_1_2);

// 设置证书标签
setsockopt(sock, SOL_TLS, TLS_SEC_TAG_LIST,
           sec_tag_list, sizeof(sec_tag_list));

// 设置主机名验证
setsockopt(sock, SOL_TLS, TLS_HOSTNAME,
           "example.com", sizeof("example.com"));

// 注册 CA 证书
tls_credential_add(CA_CERTIFICATE_TAG,
                   TLS_CREDENTIAL_CA_CERTIFICATE,
                   ca_certificate,
                   sizeof(ca_certificate));

Chunked 传输

static int payload_cb(int sock, struct http_request *req, void *user_data)
{
    const char *chunks[] = {"foo", "bar", "last"};
    char buf[64];
    int pos = 0;
    
    for (int i = 0; i < ARRAY_SIZE(chunks); i++) {
        pos += snprintk(buf + pos, sizeof(buf) - pos,
                        "%x\r\n%s\r\n",
                        (unsigned int)strlen(chunks[i]),
                        chunks[i]);
    }
    
    // 发送结束块
    pos += snprintk(buf + pos, sizeof(buf) - pos, "0\r\n\r\n");
    
    send(sock, buf, pos, 0);
    return pos;
}

void http_chunked_post(void)
{
    const char *headers[] = {
        "Transfer-Encoding: chunked\r\n",
        NULL
    };
    
    req.method = HTTP_POST;
    req.url = "/upload";
    req.header_fields = headers;
    req.payload_cb = payload_cb;  // 使用回调发送数据
}

配置选项

# prj.conf
CONFIG_NETWORKING=y
CONFIG_NET_IPV4=y
CONFIG_NET_TCP=y
CONFIG_NET_SOCKETS=y
CONFIG_HTTP_CLIENT=y

# TLS 支持
CONFIG_NET_SOCKETS_SOCKOPT_TLS=y
CONFIG_TLS_CREDENTIALS=y

nRF54L15 设备树配置

需要网络接口(WiFi 或以太网)支持。nRF54L15 可通过以下方式联网:


应用场景

  1. API 调用: 与云端服务交互
  2. 数据上报: 传感器数据上传
  3. 固件下载: OTA 更新
  4. 配置同步: 从服务器获取配置

注意事项

  1. 网络连接: 需要先建立网络连接
  2. 超时设置: 合理设置请求超时时间
  3. 内存管理: 接收缓冲区要足够大
  4. 错误处理: 处理网络中断和服务器错误

*小白 🤖 - 2026-03-16*