网络编程(socket).WinSocket_recvfrom出错,GetLastError()为10054
1、在写 我的Qt598(vs2017)x64版本的 shadowsocks程序时遇到的。
具体问题情况 大概是这样:QUdpSocket(假设是sktA) connect接收函数,sktA侦听 端口9999,然后 sktA向本机端口9989发数据,然后现象是 接收函数中 QUdpSocket::hasPendingDatagrams()返回true,QUdpSocket::pendingDatagramSize()有反应,但是返回的数值是0,此时 sktA再向本机9989发送数据,接收函数 接收函数 再也没有反映了...
ZC:我之前的 猜测:sktA侦听,但是 它侦听到了 不该它侦听的端口的信息,导致了错误,然后我就朝着这个方向 查了好多资料... ∵ 确实是 接收函数 有反映了... 然后我查到了 有人说他只能接收一次数据 后面的数据接收不到的情况,他的原因是 之前的 数据没有处理,导致数据堵在那里了。 我觉得和我的现象很像,sktA接收到了 不该它接收的端口的信息,于是 就堵了 接收函数再也没反映了...(现在想想 还一种情况:向 9989 发送数据 没反应之后,再向 9999发数据的话 接收函数 还会有反应吗?? 测试了一下,没反应了,就像是 接收函数 挂掉了,或者说 QUdpSocket挂掉了?)
ZC:后来 没辙了,就尝试 VC(vs2017+Win10x64) 会不会也有此种情况,发现 确实 recvfrom会报错 GetLastError()返回 10054,从这里 找到了 真正的原因:由于 9989端口的数据 不可达,于是返回了 不可达的ICMP数据,然后 QUdpSocket错误的以为是 接收到了数据??(为啥此种情况 QUdpSocket connect的异常输出函数没反应??)然后 接收函数 就不工作了???
2、
3、在 Qt中设置之后,上面的现象 也没有了
3.1、Qt中 这样设置:
#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR, 12) BOOL bNewBehavior = FALSE; DWORD dwBytesReturned = 0; WSAIoctl(p->socketDescriptor(), SIO_UDP_CONNRESET, &bNewBehavior, sizeof bNewBehavior, NULL, 0, &dwBytesReturned, NULL, NULL);
ZC: 原始套接字的获取:QUdpSocket::socketDescriptor()
4、
5、资料:
5.1、Windows UDP socket recvfrom返回10054错误的解决办法 - 朝雾之归乡 - 博客园.html(https://www.cnblogs.com/cnpirate/p/4059137.html)
现象: 在Windows 7系统上,A使用UDP socket,调用sendto函数向一个目标地址B发送数据,但是目标地址B没有接收数据,如果A此时立即调用recvfrom试图接收目标地址B发回的数据的话,recvfrom会立即返回-1,WSAGetLastError()返回10045。 原因: 上述现象是Windows socket的一个bug,当UDP Socket在某次发送后收到一个不可到达的ICMP包时,这个错误将在下一个接收中返回,所以上面的套接字在下一次的接收中返回了SOCKET_ERROR,错误是10045。 解决办法: 使用WSAIoctl设置UDP socket的工作模式,让其忽略这个错误。具体做法如下: 复制代码
#include <Winsock2.h> #include <Mstcpip.h> #include <stdio.h> #pragma comment(lib, "ws2_32.lib") #define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR, 12) BOOL bNewBehavior = FALSE; DWORD dwBytesReturned = 0; WSAIoctl(iSock, SIO_UDP_CONNRESET, &bNewBehavior, sizeof bNewBehavior, NULL, 0, &dwBytesReturned, NULL, NULL);
复制代码 SIO_UDP_CONNREST选项:Controls whether UDP PORT_UNREACHABLE messages are reported. Set to TRUE to enable reporting. Set to FALSE to disable reporting. 备注: setsockopt是修改套接口的属性,只是该套接口在工作的过程中需要用到的一些参数;WSAIoctl则是修改套接口的工作模式,更多的定义了这个套接口要以怎样的形式进行工作,有本质的区别。 参考文献: [1]. http://blog.csdn.net/wpullo/article/details/5905616 [2]. http://msdn.microsoft.com/zh-cn/ms741621 [3]. http://blog.sina.com.cn/s/blog_536e955201009xqp.html [4]. http://blog.csdn.net/threewall/article/details/5089817
5.2、System Error Codes (9000-11999) (Windows).html(https://technet.microsoft.com/zh-cn/ms681391(v=vs.80))
WSAECONNRESET 10054 (0x2746) An existing connection was forcibly closed by the remote host.
5.3、System Error Codes (Windows).html(https://technet.microsoft.com/zh-cn/ms681381(v=vs.80))
6、
7、我的 VC(vs2017)测试代码:
#include <stdio.h> #include <winsock2.h> #pragma comment(lib,"ws2_32.lib") #define BUF_SIZE 1024 #define PORT_ 9411 int main_server(void) { WSADATA wsd; int iRet = 0; // 初始化套接字动态库 if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0) { printf("WSAStartup failed:%d!\n", WSAGetLastError()); return -1; } SOCKET socketSrv = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); SOCKADDR_IN addrSrv; SOCKADDR_IN addrClient; char strRecv[BUF_SIZE] = { 0 }, strSend[BUF_SIZE] = "udp server send"; int len = sizeof(SOCKADDR); // 设置服务器地址 ZeroMemory(strRecv, BUF_SIZE); addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //addrSrv.sin_addr.S_un.S_addr = inet_addr("192.168.2.102"); addrSrv.sin_family = AF_INET; addrSrv.sin_port = htons(PORT_); // 绑定套接字 iRet = bind(socketSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); if (SOCKET_ERROR == iRet) { printf("bind failed%d!\n", WSAGetLastError()); closesocket(socketSrv); WSACleanup(); } { //#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR, 12) // BOOL bNewBehavior = FALSE; // DWORD dwBytesReturned = 0; // WSAIoctl(socketSrv, SIO_UDP_CONNRESET, &bNewBehavior, sizeof bNewBehavior, NULL, 0, &dwBytesReturned, NULL, NULL); SOCKADDR_IN servAddr; servAddr.sin_family = AF_INET; //servAddr.sin_addr.S_un.S_addr = inet_addr("192.168.0.104"); servAddr.sin_addr.S_un.S_addr = inet_addr("192.168.2.109");// _WINSOCK_DEPRECATED_NO_WARNINGS servAddr.sin_port = htons(9418); // 向服务器发送数据 int nServAddLen = sizeof(servAddr); iRet = sendto(socketSrv, strSend, BUF_SIZE, 0, (sockaddr *)&servAddr, nServAddLen); if (iRet == SOCKET_ERROR) { printf("sendto() failed:%d\n", WSAGetLastError()); closesocket(socketSrv); WSACleanup(); return -1; } } // 从客户端接收数据 printf("udp server start...(port:%d)\n", ntohs(addrSrv.sin_port)); while (TRUE) { iRet = recvfrom(socketSrv, strRecv, BUF_SIZE, 0, (SOCKADDR*)&addrClient, &len); if (SOCKET_ERROR == iRet) { int iErr = ::GetLastError(); printf("recvfrom failed ! (%d)\n", iErr); closesocket(socketSrv); WSACleanup(); return -1; } printf("Recv From Client:%s\n", strRecv); // 向客户端发送数据 // sendto(socketSrv, strSend, sizeof(strSend), 0, (SOCKADDR*)&addrClient, len); } closesocket(socketSrv); WSACleanup(); return 0; }
8、
9、