一个基于 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);

    }

相关推荐
追风赶月、4 小时前
【QT】控件一(QWidget、Button、Label)
开发语言·qt
十秒耿直拆包选手6 小时前
Qt:Qt桌面程序正常退出注意事项
c++·qt
姆路6 小时前
Qt背景平铺
开发语言·qt
范纹杉想快点毕业7 小时前
初探Qt信号与槽机制
java·c语言·开发语言·c++·qt·visualstudio·visual studio
我言秋日胜春朝★12 小时前
【Linux网络编程】基于udp套接字实现的网络通信
linux·网络·udp
uyeonashi1 天前
【QT系统相关】QT文件
开发语言·c++·qt·学习
菜鸟康1 天前
C++实现分布式网络通信框架RPC(3)--rpc调用端
分布式·网络协议·rpc
Wyn_1 天前
【QT】QTableView自定义样式:仅显示行间隔、隐藏列间隔、表头样式、表格样式、单行选中等
qt·qtableview
٩( 'ω' )و2601 天前
qt信号与槽--01
开发语言·qt
傻傻虎虎1 天前
【QT】自动更新库QSimpleUpdater使用实例封装
开发语言·qt