MFC C开发UDP Winsock聊天程序recvfrom/sendto返回-1问题
问题根因汇总
你的代码存在多处逻辑错误,直接导致sendto()/recvfrom()返回SOCKET_ERROR,按影响优先级排序:
1. 端口占用冲突(核心触发点)
- 你给服务端、客户端绑定了完全相同的端口(代码注释标注为30112)。如果服务端和客户端运行在同一台机器上,先启动的程序会独占该端口,后启动的程序
bind()会直接失败,后续所有套接字操作都会返回-1。 - UDP双向通信不需要两端绑定相同端口:服务端绑定固定监听端口即可,客户端不需要手动绑定固定端口;如果一定要手动绑定,必须选择和服务端不同的、未被占用的端口。
2. 客户端bind()逻辑完全错误
- 客户端
bind()传入的地址是服务端IP(inet_addr(SERVERIP)),而非本地网卡地址。bind()的作用是给套接字分配本地端点地址,传入远端服务端IP时,系统会判定你要绑定不属于本机的IP,直接返回绑定失败。 - 客户端如果要手动执行bind,地址必须填
htonl(INADDR_ANY),端口选择本地未被占用的端口,不能填服务端的IP和端口。
3. 目标地址初始化错误
- 服务端代码提前把
client_addr初始化为INADDR_ANY+服务端端口,属于无效发送目标。正确逻辑是:服务端不需要提前初始化client_addr,第一次收到客户端消息时,recvfrom()会自动把客户端的真实IP、端口填充到client_addr结构中,之后用这个自动填充的地址调用sendto()才能正确向客户端发消息。使用提前写死的无效地址发消息必然报错。 - 客户端的
FromServer地址结构没有做ZeroMemory初始化,内存中是随机垃圾值,调用recvfrom()时传入未初始化的地址结构会触发参数错误。
4. 套接字提前关闭错误
- 客户端发送按钮的逻辑中,调用完
sendto()不管成功失败,直接执行closesocket(dlg->clnt_sock)关闭套接字。第一次点击发送就会销毁后台接收线程正在使用的套接字,之后后台线程调用recvfrom()会直接返回错误,后续再点击发送时因为套接字已经关闭,sendto()也必然报错。
5. Winsock资源释放语法错误
- 线程末尾写的
WSACleanup;只是函数名,没有加括号根本不会执行,正确写法是WSACleanup();。这个错误会导致Winsock资源泄漏,多次启动退出程序后可能出现套接字创建失败的问题。
6. 字符串拷贝缓冲区溢出风险
- MFC默认使用Unicode编码时,
CString存储的是宽字符,strcpy(Text, strText)会直接拷贝失败甚至内存越界。宽字符环境下需要用wcstombs做编码转换,或者直接用TCHAR类型缓冲区匹配MFC字符集设置,否则发送内容会是乱码,严重时直接触发内存访问错误导致套接字调用失败。
修复步骤
- 删掉客户端手动
bind()的代码,UDP客户端不需要手动绑定,内核会自动分配空闲本地端口和合适的网卡地址,完全满足双向通信需求。 - 服务端删掉提前初始化
client_addr的代码,保持结构为空,等第一次recvfrom()成功返回后,client_addr中就存储了客户端的正确地址,后续发消息直接使用该地址即可。 - 删掉客户端发送按钮里的
closesocket(dlg->clnt_sock)代码,套接字关闭操作要放在接收线程退出时统一执行,不能发一次消息就关闭套接字。 - 客户端的
FromServer结构使用前必须用ZeroMemory清零,避免垃圾值导致参数错误。 - 修正
WSACleanup写法,补上括号改为合法函数调用。 - 处理Unicode编码下的字符串转换问题,不要直接用
strcpy把CString拷贝到char缓冲区。 - 每次套接字接口返回
SOCKET_ERROR时,立刻调用WSAGetLastError()获取具体错误码,根据错误码可以快速定位问题,不要只弹错误提示框就直接break。常见错误码对应原因:WSAEACCES/WSAEADDRINUSE:端口被占用WSAEFAULT:传入的地址参数无效、内存不可访问WSAENOTSOCK:传入的套接字句柄无效、已被关闭WSAEADDRNOTAVAIL:bind时传入了不属于本机的IP地址
内容的提问来源于stack exchange,提问作者LieuRabbit
相关产品推荐
相关产品推荐

