如何让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
相关产品推荐
相关产品推荐

