目录
错误记录
程序
解决
初学网络,理解浅薄,记录此错误待日后深入!
问题:将recvfrom函数置于一路线程中,sendto函数置于主线程中,当单独运行下面程序时recvfrom()函数出现10054错误。
网上找到的原因:上述现象是Windows socket的一个bug,当UDP Socket在某次发送后收到一个不可到达的ICMP包时,这个错误将在下一个接收中返回,所以上面的套接字在下一次的接收中返回了SOCKET_ERROR,错误是10045。
#define _CRT_SECURE_NO_WARNINGS 1
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include
#include
//#include
#include
#pragma comment(lib, "ws2_32.lib")
#define IP "127.0.0.1"
#define SPORT 17799
#define PPORT 7799
SOCKET udpSock;
HANDLE hThread;
int udp_init(const char* ip, int port)
{
//打开网络库
WORD wdVersion = MAKEWORD(2, 2);
WSADATA wsaData;
int nRes = WSAStartup(wdVersion, &wsaData);
if (0 != nRes)
{
printf("WSAStartup fail\n");
return -1;
}
//版本校验
if (2 != HIBYTE(wsaData.wVersion) || 2 != LOBYTE(wsaData.wVersion))
{
printf("Version Error\n");
WSACleanup();
return -1;
}
//套接字
udpSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (INVALID_SOCKET == udpSock)
{
printf("socket fail\n");
WSACleanup();
return -1;
}
//本机地址结构体
struct sockaddr_in selfMng;
selfMng.sin_family = AF_INET;
selfMng.sin_port = htons(SPORT);
selfMng.sin_addr.S_un.S_addr = inet_addr(IP);
int clBind = bind(udpSock, (struct sockaddr*)&selfMng, sizeof(selfMng));
if (SOCKET_ERROR == clBind)
{
int a = WSAGetLastError();
printf("bind fail\n");
closesocket(udpSock);
WSACleanup();
return -1;
}
printf("udp init ok\n");
return 0;
}
DWORD WINAPI UDPRecv(LPVOID lpparam)
{
struct sockaddr_in peerMng;
int nLen = sizeof(peerMng);
while (1)
{
char buff[548] = { 0 };
int recvData = recvfrom(udpSock, buff, 548, 0, (struct sockaddr*)&peerMng, &nLen);
if (SOCKET_ERROR == recvData)
{
int a = WSAGetLastError();
//if (10054 == a) continue;
printf("recvfrom fail\n");
return -1;
}
else if (0 < recvData)
{
printf("%s\n", buff);
}
}
}
BOOL WINAPI CtrFun(DWORD dwType)
{
switch (dwType)
{
case CTRL_CLOSE_EVENT:
CloseHandle(hThread);
closesocket(udpSock);
WSACleanup();
break;
}
return TRUE;
}
int main()
{
//强制退出关闭
SetConsoleCtrlHandler(CtrFun, TRUE);
//网络库、套接字、本机地址初始化
udp_init(IP, SPORT);
//创建线程,接收消息
DWORD ThreadID;
hThread = CreateThread(NULL, 0, UDPRecv, 0, 0, &ThreadID);
if (NULL == hThread)
{
printf("创建线程失败\n");
}
//发送
struct sockaddr_in peerMng;
peerMng.sin_family = AF_INET;
peerMng.sin_port = htons(PPORT);
peerMng.sin_addr.S_un.S_addr = inet_addr(IP);
while (1)
{
int sendData = sendto(udpSock, "hehe", strlen("hehe"), 0, (struct sockaddr *)&peerMng, sizeof(peerMng));
if (SOCKET_ERROR == sendData)
{
return -1;
}
}
WaitForSingleObject(hThread, INFINITE);
//关闭
CloseHandle(hThread);
closesocket(udpSock);
WSACleanup();
return 0;
}
直接忽略这个错误:if (10054 == a) continue;
int recvData = recvfrom(udpSock, buff, 548, 0, (struct sockaddr*)&peerMng, &nLen);
if (SOCKET_ERROR == recvData)
{
int a = WSAGetLastError();
if (10054 == a) continue;
printf("recvfrom fail\n");
return -1;
}
else if (0 < recvData)
{
printf("%s\n", buff);
}
上述操作后,启动对端程序,可以正常通信。但是不知道如此操作是否准确,是否对程序的后续运行有影响!
网上查询的解决方案:
#include
#include
#include
#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);
将上述代码放置在sendto函数前就行,测试有效!