基于Winsock2与DLL的MATLAB UDP读取无数据问题排查
MATLAB调用UDP读取DLL返回全零问题排查与解决
问题描述
我正在尝试将MATLAB与基于Winsock2库的UDP数据读取C代码对接,把C代码编译为DLL后通过MATLAB的loadlibrary函数加载使用。C代码单独运行时能正常读取UDP数据,但通过MATLAB调用时返回结果全为零,已确认问题出在MATLAB端,附上相关代码文件,求问题原因及解决办法。
C代码
#include <stdio.h> #include <stdlib.h> #include <winsock2.h> #include <windows.h> // Include for Sleep function #include "udpfunc2.h" #define BUFLEN 48 SOCKET s; // Function to initialize UDP socket int init_udp_socket() { WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("WSAStartup failed\n"); return -1; } return 0; } // Function to create and bind UDP socket int create_udp_socket() { if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) { printf("Socket creation failed\n"); return -1; } return 0; } // Function to bind UDP socket to specified IP address and port int bind_udp_socket(const char *ip_address, int port) { struct sockaddr_in server; server.sin_family = AF_INET; server.sin_addr.s_addr = inet_addr(ip_address); server.sin_port = htons(port); if (bind(s, (struct sockaddr *)&server, sizeof(server)) == SOCKET_ERROR) { printf("Bind failed\n"); return -1; } return 0; } // Function to set socket to non-blocking mode int set_socket_nonblocking() { unsigned long mode = 1; // 1 to enable non-blocking mode if (ioctlsocket(s, FIONBIO, &mode) != 0) { printf("Failed to set socket to non-blocking mode\n"); return -1; } return 0; } // Function to continuously read data through UDP as 12 single values (non-blocking) void udp_read_continuous(float *data) { struct sockaddr_in si_other; int slen = sizeof(si_other); int recv_len; //zero out the memory buffer memset(data,0,12 * sizeof(float)); recv_len = recvfrom(s, (char *)data, BUFLEN, 0, (struct sockaddr *)&si_other, &slen); // Convert data from big-endian to host byte order // Assuming each single value is a 32-bit float for (int i = 0; i < 12; i++) { unsigned int val; memcpy(&val , &data[i], sizeof(float)); val = ntohl(val); data[i] = *((float*)&val); } // Print received data printf("Received data: "); for (int i = 0; i < 12; i++) { printf("%.6f ", data[i]); } printf("\n"); } // Function to close UDP socket void close_udp_socket() { closesocket(s); WSACleanup(); }
头文件
#ifndef UDP_FUNC2_H #define UDP_FUNC2_H #include <winsock2.h> #ifdef __cplusplus extern "C" { #endif // Function prototypes int init_udp_socket(); int create_udp_socket(); int bind_udp_socket(const char *ip_address, int port); int set_socket_nonblocking(); void udp_read_continuous(float *data); void close_udp_socket(); #ifdef __cplusplus } #endif #endif // UDP_FUNC2_H
MATLAB脚本
clc; clear; if libisloaded('udpfunc2') unloadlibrary('udpfunc2') end loadlibrary('udpfunc2.dll', 'udpfunc2.h'); % Define UDP parameters ip_address = '127.0.0.1'; % Local IP address localPort = 44444; % Example port number % Initialize UDP socket status = calllib('udpfunc2', 'init_udp_socket'); if status == 0 disp('UDP socket initialized successfully'); else disp('Failed to initialize UDP socket'); return; end % Open UDP socket status = calllib('udpfunc2', 'create_udp_socket'); if status == -1 disp('Failed to Open UDP Socket'); end % Bind UDP Socket to Local Port and IP address status = calllib('udpfunc2', 'bind_udp_socket', ip_address, localPort); if status == 0 disp('UDP socket binded successfully'); else disp('Failed to bind socket'); return; end % Set Socket to Non-Blocking status = calllib('udpfunc2', 'set_socket_nonblocking'); if status == 0 disp('UDP socket set to Non-Blocking successfully'); else disp('Failed to set socket to Non-Blocking'); return; end % Create a MATLAB struct to hold the 12 single values dataPacket = zeros(1, 12, 'single'); % Read continuous UDP data while true calllib('udpfunc2', 'udp_read_continuous', dataPacket); fprintf('%.6f\n', dataPacket(12)); end % Close UDP socket calllib('udpfunc2', 'close_udp_socket'); disp('UDP socket closed'); % Unload the DLL unloadlibrary('udpfunc2');
问题原因
- MATLAB数组传递方式错误:MATLAB调用
calllib时,直接传递数组会默认按值传递,即创建数组副本传给C函数。C函数修改的是副本内存,原MATLAB数组dataPacket不会被更新,因此MATLAB端始终读到初始的零值。 - 非阻塞Socket错误处理缺失:C代码中
recvfrom在非阻塞模式下无数据时会返回SOCKET_ERROR,但代码未处理该情况,直接对清零后的缓冲区进行字节序转换并返回,导致MATLAB持续收到零值。
解决办法
1. 修改MATLAB传递方式为指针传递
使用libpointer创建指向MATLAB数组的指针,让C函数直接修改原数组内存:
clc; clear; if libisloaded('udpfunc2') unloadlibrary('udpfunc2') end loadlibrary('udpfunc2.dll', 'udpfunc2.h'); % Define UDP parameters ip_address = '127.0.0.1'; % Local IP address localPort = 44444; % Example port number % Initialize UDP socket status = calllib('udpfunc2', 'init_udp_socket'); if status == 0 disp('UDP socket initialized successfully'); else disp('Failed to initialize UDP socket'); return; end % Open UDP socket status = calllib('udpfunc2', 'create_udp_socket'); if status == -1 disp('Failed to Open UDP Socket'); end % Bind UDP Socket to Local Port and IP address status = calllib('udpfunc2', 'bind_udp_socket', ip_address, localPort); if status == 0 disp('UDP socket binded successfully'); else disp('Failed to bind socket'); return; end % Set Socket to Non-Blocking status = calllib('udpfunc2', 'set_socket_nonblocking'); if status == 0 disp('UDP socket set to Non-Blocking successfully'); else disp('Failed to set socket to Non-Blocking'); return; end % Create array and corresponding pointer dataPacket = zeros(1, 12, 'single'); p_data = libpointer('singlePtr', dataPacket); % Read continuous UDP data while true calllib('udpfunc2', 'udp_read_continuous', p_data); % Synchronize pointer value back to MATLAB array dataPacket = p_data.Value; fprintf('%.6f\n', dataPacket(12)); % Add small delay to reduce CPU usage pause(0.01); end % Close UDP socket calllib('udpfunc2', 'close_udp_socket'); disp('UDP socket closed'); % Unload the DLL unloadlibrary('udpfunc2');
2. 完善C代码的非阻塞错误处理
在udp_read_continuous函数中,处理recvfrom的返回值,仅当成功接收完整数据时才进行字节序转换:
void udp_read_continuous(float *data) { struct sockaddr_in si_other; int slen = sizeof(si_other); int recv_len; // zero out the memory buffer memset(data,0,12 * sizeof(float)); recv_len = recvfrom(s, (char *)data, BUFLEN, 0, (struct sockaddr *)&si_other, &slen); // Handle non-blocking socket no data case if (recv_len == SOCKET_ERROR) { int err = WSAGetLastError(); if (err != WSAEWOULDBLOCK) { printf("recvfrom failed with error: %d\n", err); } // Return with zeroed buffer when no data return; } // Only process if complete data is received if (recv_len == BUFLEN) { // Convert data from big-endian to host byte order for (int i = 0; i < 12; i++) { unsigned int val; memcpy(&val , &data[i], sizeof(float)); val = ntohl(val); data[i] = *((float*)&val); } } else { printf("Received incomplete data: %d bytes expected %d\n", recv_len, BUFLEN); return; } // Print received data printf("Received data: "); for (int i = 0; i < 12; i++) { printf("%.6f ", data[i]); } printf("\n"); }
3. 额外注意事项
- 确保编译DLL的编译器与MATLAB位数一致(32位/64位),避免加载失败或调用异常。
- 使用
mex -setup配置MATLAB兼容的C编译器,确保loadlibrary能正确解析头文件。 - 测试时可使用UDP工具(如NetAssist)向
127.0.0.1:44444发送48字节的大端浮点数据,验证接收逻辑。
内容的提问来源于stack exchange,提问作者Tamim Akhtar
相关产品推荐
相关产品推荐

