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

如何让C++程序等待UDP触发信号时持续采集陀螺仪数据?

需求与问题
  • 需求:Matlab向C程序发送随机触发信号A/B,C程序在触发信号间隙持续采集陀螺仪数据
  • 当前问题:C++的UDP接收使用了阻塞式recvfrom,未收到信号时程序停滞,无法执行陀螺仪采集操作

Matlab 触发信号发送代码

send_trigger_signal = instrfind('Type', 'udp', 'LocalHost', '127.0.0.1','RemoteHost', '192.168.0.100', 'RemotePort', 8888, 'LocalPort', 8844, 'Tag', '');

% Create the udp object if it does not exist otherwise use the object that was found.
 if isempty(send_trigger_signal)
     send_trigger_signal = udp('127.0.0.1', 'RemotePort', 8888, 'LocalPort', 8844);
 else
     fclose(send_trigger_signal);
     send_trigger_signal = send_trigger_signal(1);
 end

 send_trigger_signal.DatagramTerminateMode='off';
 send_trigger_signal.Timeout=0.0001;
 send_trigger_signal.Timerperiod=0.01;
 
% Connect to instrument object, send_trigger_signal.
 fopen(send_trigger_signal);

% Communicating with instrument object, send_trigger_signal.
 on_trigger_command=typecast(swapbytes(uint16([1 1 0 0])),'uint8'); %trigger on
 off_trigger_command=typecast(swapbytes(uint16([0 0 0 0])),'uint8'); %trigger off

while(true) 
 for i=1:1
     fprintf(send_trigger_signal, 'A');
     WaitSecs(5);
end
end
fclose(send_trigger_signal);    

send_trigger_signal=instrfindall;
delete(send_trigger_signal);
instrfindall;

C++ 原UDP接收代码

#include <iostream>
#include <winsock2.h>
using namespace std;

#pragma comment(lib,"ws2_32.lib") // Winsock Library
#pragma warning(disable:4996) 

#define BUFLEN 512
#define PORT 8888

int receiver(void)
{
    int value = 5;
    system("title UDP Server");

    sockaddr_in server, client;

    // initialise winsock
    WSADATA wsa;
    printf("Initialising Winsock...");
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
    {
        printf("Failed. Error Code: %d", WSAGetLastError());
        exit(0);
    }
    printf("Initialised.\n");

    // create a socket
    SOCKET server_socket;
    if ((server_socket = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
    {
        printf("Could not create socket: %d", WSAGetLastError());
    }
    printf("Socket created.\n");

    // prepare the sockaddr_in structure
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(PORT);

    // bind
    if (bind(server_socket, (sockaddr*)&server, sizeof(server)) == SOCKET_ERROR)
    {
        printf("Bind failed with error code: %d", WSAGetLastError());
        exit(EXIT_FAILURE);
    }
    puts("Bind done.");

    while (true)
    {
        printf("Waiting for data...");
        fflush(stdout);
        char message[BUFLEN] = {};

        // try to receive some data, this is a blocking call
        int message_len;
        int slen = sizeof(sockaddr_in);
        if (message_len = recvfrom(server_socket, message, BUFLEN, 0, (sockaddr*)&client, &slen) == SOCKET_ERROR)
        {
            printf(message);
            printf("recvfrom() failed with error code: %d", WSAGetLastError());
            exit(0);
        }
        if (message[0] == 'A')
        {
            value = 6;
            break;
        }
        if (message[0] == 'B')
        {
            value = 7;
            break;
        }
        // print details of the client/peer and the data received
        printf("Received packet from %s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
        printf("Data: %s\n", message);
        return 0;
    }

    closesocket(server_socket);
    WSACleanup();

    return value;
}

int main()
{
    while (true)
    {

        // Reading some gyro data here

        // Listening UDP
        receiver();

    }
    return 0;
}

解决方案

核心思路是让UDP接收逻辑不阻塞主线程的陀螺仪采集操作,以下是两种可行实现方案:

方案1:非阻塞Socket + 轮询

将UDP Socket设置为非阻塞模式,recvfrom无数据时立即返回,主线程可在轮询间隙持续执行采集逻辑。

修改后的C++代码

#include <iostream>
#include <winsock2.h>
#include <windows.h>
using namespace std;

#pragma comment(lib,"ws2_32.lib")
#pragma warning(disable:4996)

#define BUFLEN 512
#define PORT 8888

int main()
{
    int trigger_value = 5;
    system("title UDP Server");

    sockaddr_in server, client;
    WSADATA wsa;

    // 初始化Winsock
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
    {
        printf("WSAStartup failed. Error Code: %d", WSAGetLastError());
        return 1;
    }

    // 创建Socket并设置为非阻塞模式
    SOCKET server_socket = socket(AF_INET, SOCK_DGRAM, 0);
    if (server_socket == INVALID_SOCKET)
    {
        printf("Could not create socket: %d", WSAGetLastError());
        WSACleanup();
        return 1;
    }

    u_long non_block_mode = 1;
    if (ioctlsocket(server_socket, FIONBIO, &non_block_mode) == SOCKET_ERROR)
    {
        printf("ioctlsocket failed. Error Code: %d", WSAGetLastError());
        closesocket(server_socket);
        WSACleanup();
        return 1;
    }

    // 绑定端口
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(PORT);
    if (bind(server_socket, (sockaddr*)&server, sizeof(server)) == SOCKET_ERROR)
    {
        printf("Bind failed with error code: %d", WSAGetLastError());
        closesocket(server_socket);
        WSACleanup();
        return 1;
    }
    puts("Bind done.");

    int slen = sizeof(sockaddr_in);
    char message[BUFLEN] = {};

    while (true)
    {
        // 1. 持续采集陀螺仪数据
        printf("Collecting gyro data | Current trigger value: %d\n", trigger_value);
        Sleep(100); // 模拟采集耗时,根据实际场景调整

        // 2. 非阻塞检查UDP数据
        int message_len = recvfrom(server_socket, message, BUFLEN, 0, (sockaddr*)&client, &slen);
        if (message_len > 0)
        {
            printf("Received packet from %s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
            printf("Data: %s\n", message);

            // 处理触发信号
            if (message[0] == 'A')
            {
                trigger_value = 6;
                printf("Trigger A received, update value to %d\n", trigger_value);
            }
            else if (message[0] == 'B')
            {
                trigger_value = 7;
                printf("Trigger B received, update value to %d\n", trigger_value);
            }
        }
        else if (WSAGetLastError() != WSAEWOULDBLOCK)
        {
            // 仅处理非"资源暂时不可用"的真实错误
            printf("recvfrom() failed with error code: %d", WSAGetLastError());
            break;
        }
    }

    closesocket(server_socket);
    WSACleanup();
    return 0;
}

方案2:多线程分离接收与采集

创建独立线程处理UDP阻塞接收,主线程专注于陀螺仪采集,通过全局变量传递触发信号状态(复杂场景需加互斥锁保证线程安全)。

修改后的C++代码

#include <iostream>
#include <winsock2.h>
#include <windows.h>
#include <thread>
using namespace std;

#pragma comment(lib,"ws2_32.lib")
#pragma warning(disable:4996)

#define BUFLEN 512
#define PORT 8888

int g_trigger_value = 5;
bool g_running = true;

// UDP接收线程函数
void udp_receiver()
{
    sockaddr_in server, client;
    WSADATA wsa;

    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
    {
        printf("WSAStartup failed. Error Code: %d", WSAGetLastError());
        g_running = false;
        return;
    }

    SOCKET server_socket = socket(AF_INET, SOCK_DGRAM, 0);
    if (server_socket == INVALID_SOCKET)
    {
        printf("Could not create socket: %d", WSAGetLastError());
        WSACleanup();
        g_running = false;
        return;
    }

    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(PORT);
    if (bind(server_socket, (sockaddr*)&server, sizeof(server)) == SOCKET_ERROR)
    {
        printf("Bind failed with error code: %d", WSAGetLastError());
        closesocket(server_socket);
        WSACleanup();
        g_running = false;
        return;
    }
    puts("UDP receiver thread started, waiting for triggers...");

    int slen = sizeof(sockaddr_in);
    char message[BUFLEN] = {};

    while (g_running)
    {
        int message_len = recvfrom(server_socket, message, BUFLEN, 0, (sockaddr*)&client, &slen);
        if (message_len > 0)
        {
            printf("Received trigger from %s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
            // 更新触发值(简单场景全局变量足够,复杂场景建议用std::mutex)
            if (message[0] == 'A')
                g_trigger_value = 6;
            else if (message[0] == 'B')
                g_trigger_value = 7;
        }
        else if (WSAGetLastError() != 0)
        {
            printf("recvfrom() failed with error code: %d", WSAGetLastError());
            break;
        }
    }

    closesocket(server_socket);
    WSACleanup();
}

int main()
{
    // 启动UDP接收线程
    thread receiver_thread(udp_receiver);

    while (g_running)
    {
        // 持续采集陀螺仪数据
        printf("Collecting gyro data | Current trigger value: %d\n", g_trigger_value);
        Sleep(100); // 模拟采集周期
    }

    receiver_thread.join();
    return 0;
}

关键改进说明
  • 原代码中receiver()函数每次调用都重复初始化Winsock和Socket,修改后仅初始化一次,避免资源浪费
  • 方案1通过ioctlsocket将Socket设为非阻塞,无数据时recvfrom返回WSAEWOULDBLOCK,主线程可继续执行采集
  • 方案2分离接收与采集线程,阻塞接收不影响主线程,线程间状态传递简单高效

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 19:05:24