一个基于 ESP-IDF 的 RPC over UDP 示例

创建了一个基于 ESP-IDF 的 RPC over UDP 示例,包括服务端与客户端的完整代码。您可以查看并根据需求调整方法名、参数解析和网络配置等

/*

  • ESP32 RPC over UDP Example (Detailed Version)
  • Description:
  • This example demonstrates a simple Remote Procedure Call (RPC) mechanism
  • over UDP. An ESP32 device acts as both server and client in separate RTOS tasks.
    • The server listens on a UDP port and handles incoming requests of the form:
复制代码
    <method>:<arg1>,<arg2>\n
复制代码
  Supported methods:
复制代码
    add - return sum of two integers
复制代码
    sub - return difference between two integers (arg1 - arg2)
    • The client periodically sends RPC commands to the server and logs the responses.
  • Key Components:
    • FreeRTOS tasks: udp_server_task, udp_client_task
    • BSD sockets: socket(), bind(), recvfrom(), sendto()
    • Simple parsing via sscanf() and strcmp()
  • Configuration:
    • SERVER_PORT: UDP port on which server listens
    • CLIENT_PORT: UDP port bound by client to receive responses
    • CONFIG_EXAMPLE_SERVER_IP: set to server IP in menuconfig
  • ESP-IDF Requirements:
    • esp_netif (network interface)
    • Wi-Fi or Ethernet connectivity established via example_connect()
    • NVS flash initialization for Wi-Fi credentials
      */

#include <string.h>

#include <stdio.h>

#include <errno.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <freertos/FreeRTOS.h>

#include <freertos/task.h>

#include <esp_system.h>

#include <esp_log.h>

#include <nvs_flash.h>

#include <esp_netif.h>

#include <esp_event.h>

#include <esp_wifi.h>

#include <protocol_examples_common.h> // For example_connect()

// UDP port definitions

#define SERVER_PORT 3333 // Port on which the RPC server listens

#define CLIENT_PORT 4444 // Local port for RPC client to receive responses

#define BUF_SIZE 128 // Maximum buffer size for UDP packets

static const char *TAG = "rpc_udp"; // Logging tag

/**

  • @brief Simple RPC handler function.
  • Parses the incoming request string and executes the requested method.
  • @param req The null-terminated request string, e.g. "add:2,3".
  • @param resp Buffer to write the null-terminated response.
  • @param resp_max_len Maximum length of the response buffer.
  • @return Number of bytes written into resp (excluding null terminator),
复制代码
      or negative on error.

/
static int rpc_handler(const char
req, char* resp, size_t resp_max_len)

{

char method[16]; // Buffer for method name ("add" or "sub")

int arg1 = 0;

int arg2 = 0;

复制代码
// Use sscanf to extract method and two integer arguments.
// Format: <method>:<arg1>,<arg2>
int parsed = sscanf(req, "%15[^:]:%d,%d", method, &arg1, &arg2);
if (parsed != 3) {
    // Parsing failed: invalid format
    return snprintf(resp, resp_max_len, "error: invalid format");
}

// Dispatch based on method name
if (strcmp(method, "add") == 0) {
    int result = arg1 + arg2;
    return snprintf(resp, resp_max_len, "%d", result);
} else if (strcmp(method, "sub") == 0) {
    int result = arg1 - arg2;
    return snprintf(resp, resp_max_len, "%d", result);
}

// Unknown method
return snprintf(resp, resp_max_len, "error: unknown method");

}

/**

  • @brief UDP RPC server task.

  • Creates a UDP socket, binds to SERVER_PORT, and waits for incoming requests.

  • For each request, calls rpc_handler() and sends the response back to the client.
    /
    static void udp_server_task(void
    arg)

    {

    char rx_buffer[BUF_SIZE]; // Buffer for incoming request

    char addr_str[INET_ADDRSTRLEN]; // Human-readable client IP

    struct sockaddr_in server_addr, client_addr;

    socklen_t addr_len = sizeof(client_addr);

    // Create UDP socket

    int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);

    if (sock < 0) {

    ESP_LOGE(TAG, "Failed to create socket: errno %d", errno);

    vTaskDelete(NULL);

    return;

    }

    // Prepare server address struct

    server_addr.sin_family = AF_INET;

    server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // Listen on all interfaces

    server_addr.sin_port = htons(SERVER_PORT);

    // Bind socket to the specified port

    if (bind(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {

    ESP_LOGE(TAG, "Socket bind failed: errno %d", errno);

    close(sock);

    vTaskDelete(NULL);

    return;

    }

    ESP_LOGI(TAG, "UDP server listening on port %d", SERVER_PORT);

    while (1) {

    // Receive a UDP packet

    int len = recvfrom(sock, rx_buffer, BUF_SIZE - 1, 0,

    (struct sockaddr*)&client_addr, &addr_len);

    if (len < 0) {

    ESP_LOGE(TAG, "recvfrom error: errno %d", errno);

    break;

    }

    复制代码
     // Null-terminate and log request
     rx_buffer[len] = '\0';
     inet_ntoa_r(client_addr.sin_addr, addr_str, sizeof(addr_str));
     ESP_LOGI(TAG, "Received %d bytes from %s:%d", len,
              addr_str, ntohs(client_addr.sin_port));
     ESP_LOGI(TAG, "Request: %s", rx_buffer);
    
     // Process the RPC command
     char response[BUF_SIZE];
     int resp_len = rpc_handler(rx_buffer, response, sizeof(response));
    
     // Send response back to the client
     int err = sendto(sock, response, resp_len, 0,
                      (struct sockaddr*)&client_addr, addr_len);
     if (err < 0) {
         ESP_LOGE(TAG, "sendto error: errno %d", errno);
         break;
     }

    }

    // Clean up socket and task

    close(sock);

    vTaskDelete(NULL);

    }

/**

  • @brief UDP RPC client task (example usage).

  • Sends predefined RPC commands to the server and logs the responses.
    /
    static void udp_client_task(void
    arg)

    {

    char rx_buffer[BUF_SIZE]; // Buffer for incoming response

    struct sockaddr_in dest_addr;

    dest_addr.sin_family = AF_INET;

    dest_addr.sin_addr.s_addr = inet_addr(CONFIG_EXAMPLE_SERVER_IP);

    dest_addr.sin_port = htons(SERVER_PORT);

    // Create UDP socket

    int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);

    if (sock < 0) {

    ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);

    vTaskDelete(NULL);

    return;

    }

    // Bind client socket to receive responses on CLIENT_PORT

    struct sockaddr_in client_addr = {

    .sin_family = AF_INET,

    .sin_addr.s_addr = htonl(INADDR_ANY),

    .sin_port = htons(CLIENT_PORT)

    };

    if (bind(sock, (struct sockaddr*)&client_addr, sizeof(client_addr)) < 0) {

    ESP_LOGE(TAG, "Client bind failed: errno %d", errno);

    close(sock);

    vTaskDelete(NULL);

    return;

    }

    // Example RPC commands

    const char* rpc_cmds[] = { "add:5,3", "sub:10,4" };

    size_t cmd_count = sizeof(rpc_cmds) / sizeof(rpc_cmds[0]);

    for (size_t i = 0; i < cmd_count; i++) {

    // Send RPC command

    ESP_LOGI(TAG, "Sending RPC command: %s", rpc_cmds[i]);

    sendto(sock, rpc_cmds[i], strlen(rpc_cmds[i]), 0,

    (struct sockaddr*)&dest_addr, sizeof(dest_addr));

    复制代码
     // Wait for response (blocking)
     int len = recvfrom(sock, rx_buffer, BUF_SIZE - 1, 0, NULL, NULL);
     if (len < 0) {
         ESP_LOGE(TAG, "recvfrom failed: errno %d", errno);
         break;
     }
     rx_buffer[len] = '\0';  // Null-terminate
     ESP_LOGI(TAG, "RPC response: %s", rx_buffer);
    
     // Delay before next command
     vTaskDelay(pdMS_TO_TICKS(2000));

    }

    // Clean up

    close(sock);

    vTaskDelete(NULL);

    }

/**

  • @brief Main application entry point.

  • Initializes NVS, network interfaces, connects to Wi-Fi/Ethernet,

  • and launches the UDP server and client tasks.

    */

    void app_main(void)

    {

    // 1. Initialize NVS for Wi-Fi credentials storage

    esp_err_t ret = nvs_flash_init();

    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {

    ESP_ERROR_CHECK(nvs_flash_erase());

    ret = nvs_flash_init();

    }

    ESP_ERROR_CHECK(ret);

    // 2. Initialize TCP/IP network interface

    ESP_ERROR_CHECK(esp_netif_init());

    // 3. Create default event loop

    ESP_ERROR_CHECK(esp_event_loop_create_default());

    // 4. Connect to configured Wi-Fi or Ethernet network

    // This call blocks until an IP address is obtained.

    example_connect();

    // 5. Start the RPC server and client tasks

    xTaskCreate(udp_server_task, "udp_server_task", 4096, NULL, 5, NULL);

    xTaskCreate(udp_client_task, "udp_client_task", 4096, NULL, 5, NULL);

    }

相关推荐
走过冬季1 小时前
TCP与UDP区别及应用场景详解
网络协议·tcp/ip·udp
__BMGT()2 小时前
C++ QT生成GIF,处理原始图像RGBA数据,窗口生成简单的动画
开发语言·c++·qt
wirepuller_king2 小时前
QT软件开发环境及简单图形的绘制-图形学(实验一)-[成信]
开发语言·qt
君的名字4 小时前
怎么判断一个Android APP使用了Qt 这个跨端框架
android·开发语言·qt
教练、我想打篮球5 小时前
03 基于 java udp 做一个dns服务器 和 一个dns代理服务器
网络协议·udp·dns·dig·nslookup
潇-xiao8 小时前
Qt window frame + windowTitle + windowIcon属性(3)
c++·笔记·qt
青春:一叶知秋9 小时前
【Qt开发】Qt核心属性
开发语言·qt
苏克贝塔11 小时前
使用 Qt QGraphicsView/QGraphicsScene 绘制色轮
开发语言·qt
2501_9159090612 小时前
一次复杂接口故障的抓包全过程:四款工具协同作战实录(含 Charles)
websocket·网络协议·tcp/ip·网络安全·https·udp