Windows下C语言多线程实现UDP通信程序recvfrom()函数出现10054错误:远程主机强迫关闭了一个现有的连接

目录

错误记录

程序

解决

错误记录

        初学网络,理解浅薄,记录此错误待日后深入!

        问题:将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函数前就行,测试有效!

你可能感兴趣的:(udp,windows,c语言,网络,网络协议)