Winsock UDP通信中添加bind代码后sendto调用失败(错误码10038)求助
问题分析与解决方案
首先,错误码10038对应的是WSAENOTSOCK,意思是你试图在一个非有效套接字句柄上执行socket操作。结合你的代码和现象,问题出在两个核心点上:
1. 错误的Bind目标
你现在把socket绑定到了远程设备的IP地址(172.10.1.2)和端口1600,这完全违背了bind函数的设计初衷:
bind的作用是将UDP套接字关联到本机的IP地址和端口,用来指定这个socket从哪个本地端口发送/接收数据;- 远程目标的地址应该只在
sendto时指定,而不是绑定。
当你尝试绑定到一个不属于本机的IP地址时,即使bind返回成功,这个socket实际上已经处于异常状态,后续调用sendto自然会报错。
2. 不完整的错误处理
你的代码中,当bind失败时,虽然调用了closesocket(my_socket),但没有终止程序,后续代码依然会使用这个已经被关闭的无效套接字,这也会触发10038错误。
修正后的代码
下面是调整后的完整代码,我标注了关键修改点:
#ifndef UNICODE #define UNICODE #endif #define WIN32_LEAN_AND_MEAN #include <winsock2.h> #include <Ws2tcpip.h> #include <stdio.h> #pragma comment(lib, "Ws2_32.lib") int main() { int iResult; WSADATA wsaData; SOCKET my_socket = INVALID_SOCKET; // 新增:用于绑定的本地地址结构体 struct sockaddr_in local_addr; // 用于指定远程目标的地址结构体 struct sockaddr_in remote_addr; unsigned short LocalPort = 0; // 0表示让系统自动分配本地端口 unsigned short RemotePort = 1600; char SendBuf[1024] = "Look Here"; int BufLen = (int)strlen(SendBuf) + 1; // 优化:只发送实际长度的内容,不用全发1024字节 // 初始化两个地址结构体 memset(&local_addr, 0, sizeof(local_addr)); memset(&remote_addr, 0, sizeof(remote_addr)); //---------------------- // Initialize Winsock iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); if (iResult != NO_ERROR) { wprintf(L"WSAStartup failed with error: %d\n", iResult); return 1; } //--------------------------------------------- // Create a UDP socket my_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (my_socket == INVALID_SOCKET) { wprintf(L"socket failed with error: %ld\n", WSAGetLastError()); WSACleanup(); return 1; } //--------------------------------------------- // 配置本地绑定地址:绑定到本机所有网卡(INADDR_ANY),端口由系统自动分配 local_addr.sin_family = AF_INET; local_addr.sin_port = htons(LocalPort); local_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 绑定到本地地址 iResult = bind(my_socket, (SOCKADDR*)&local_addr, sizeof(local_addr)); if (iResult == SOCKET_ERROR) { wprintf(L"bind failed with error %u\n", WSAGetLastError()); closesocket(my_socket); WSACleanup(); // 这里要加上Winsock清理 return 1; // 绑定失败直接退出,避免后续使用无效socket } wprintf(L"bind returned success\n"); //--------------------------------------------- // 配置远程目标地址 remote_addr.sin_family = AF_INET; remote_addr.sin_port = htons(RemotePort); inet_pton(AF_INET, "172.10.1.2", &remote_addr.sin_addr); //--------------------------------------------- // 发送数据到远程设备 wprintf(L"Sending a datagram to the receiver...\n"); iResult = sendto(my_socket, SendBuf, BufLen, 0, (SOCKADDR*)&remote_addr, sizeof(remote_addr)); if (iResult == SOCKET_ERROR) { wprintf(L"sendto failed with error: %d\n", WSAGetLastError()); closesocket(my_socket); WSACleanup(); return 1; } wprintf(L"Sent %d bytes successfully\n", iResult); //--------------------------------------------- // 关闭套接字 wprintf(L"Finished sending. Closing socket.\n"); iResult = closesocket(my_socket); if (iResult == SOCKET_ERROR) { wprintf(L"closesocket failed with error: %d\n", WSAGetLastError()); WSACleanup(); return 1; } //--------------------------------------------- // 清理退出 wprintf(L"Exiting.\n"); WSACleanup(); return 0; }
关键修改说明
- 分离了本地绑定地址和远程目标地址两个结构体,彻底避免地址混淆;
bind时使用INADDR_ANY,表示让socket监听本机所有可用网卡的端口,端口设为0则由系统自动分配(如果你需要固定本地端口,把LocalPort改成你想要的数值即可);- 修复了
bind失败后的错误处理:添加WSACleanup并直接返回,防止后续代码使用无效套接字; - 优化了发送缓冲区长度:只发送实际内容的长度,减少不必要的数据传输。
额外注意事项
- 如果你不需要指定本地端口,其实UDP客户端完全可以不用调用
bind——系统会在第一次调用sendto时自动为socket分配一个随机本地端口,这也是你之前移除bind后能正常发送的原因; - 确保你的笔记本和目标设备在同一个网络段,或者路由可达,否则即使代码正确,也可能出现无法发送的情况。
内容的提问来源于stack exchange,提问作者pekoms
相关产品推荐
相关产品推荐

