Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Windows UDP sockets: recvfrom() fails with error 10054


Hello everyone.
I'm trying to use Windows sockets to send and receive UDP packets (in C++).
It worked well until three days ago, when the program stopped behaving properly.
To summarize the situation:

  • When calling WSAPoll() on my socket, it always returns my socket updated with EVERY revents possible (corresponding to every events I gave the pollfd), even if there is no server launched.
  • When calling recvfrom() and no server is launched, it returns SOCKET_ERROR with error code 10054(*).
  • When calling recvfrom() and a server is launched, it works properly - blocks until it receives something.
  • The behavior is the same whether I try to connect to localhost or to a distant host.

(*) I investigated this error. In UDP, it means that there is an ICMP problem. ("On a UDP-datagram socket this error indicates a previous send operation resulted in an ICMP Port Unreachable message.").
I indeed call sendto() before recvfrom(), so the problem's not here.
I tried to put down my firewall to see if it changed anything, but it didn't. I also tried to put down every network flowing through my PC. In this state I managed to get the program to work for a few minutes, but when I enabled the networks it stopped working again. I tried to repeat the process but it would not work anymore.
I tried compiling with both visual studio (2015) and MinGW.
I tried on another computer too (under Windows 7, mine has Windows 8.1), to no avail.

Here is a simple test file which does not work on my computer.

#undef _WIN32_WINNT
#define _WIN32_WINNT 0x501
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <vector>
#include <iostream>

int main() {
  int clientSock;
  char buf[100];
  int serverPort;

  /* Initializing WSA */
  WSADATA wsaData;
  WSAStartup(MAKEWORD(2, 2), &wsaData);

  /* I create my socket */
  struct addrinfo specs;
  struct addrinfo *addr = new addrinfo;
  ZeroMemory(&specs, sizeof(specs));
  specs.ai_family = AF_INET;
  specs.ai_socktype = SOCK_DGRAM;
  specs.ai_flags = 0;
  getaddrinfo("127.0.0.1", "2324", &specs, &addr);

  clientSock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);

  /* I get the server's address */
  struct sockaddr_in serverAddr;
  serverAddr.sin_family = AF_INET;
  serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
  serverAddr.sin_port = htons(2324);
  int len = sizeof(struct sockaddr);

  /* I'll poll & recvfrom on my socket */
  std::vector<pollfd> fds;
  pollfd fd;
  fd.fd = clientSock;
  fd.events = POLLRDNORM;
  fd.revents = -1;
  fds.push_back(fd);

  while(1) {
    memset(buf,0,sizeof(buf));
    printf("\nClient--->: ");
    gets(buf);
    /* It's UDP, so it doesn't matter if there is someone to receive the packet */
    sendto(clientSock, buf, strlen(buf), 0, (sockaddr*)&serverAddr ,len);

    memset(buf,0,sizeof(buf));
    int ret;
    /* Always returns "1" */
    if ((ret = WSAPoll(fds.data(), 1, 0)) > 0) {
      std::cout << ret;
      /* Always returns "-1" */
      std::cout << recvfrom(clientSock,buf,sizeof(buf),0, (sockaddr*)&serverAddr,&len) << std::endl;
      printf("\n--->From the server: ");
      printf("%s",buf);
    }
  }

  closesocket(clientSock);
  WSACleanup();

  return 0;
}

Two questions:

  1. Why does WSAPoll() always returns an updated socket, even if there wasn't any interaction with it ?
  2. Why does recvfrom() return this error and how can I fix it ? I suppose it comes from my computer. I tried allowing ICMP through my firewall but it didn't change anything, maybe I did something wrong ?

Edit: I fixed my main program (not shown here because it is way too large) by just ignoring any "error 10054" I received. Now it works the same way it does on Unix.
Still, it is not really a solution (ignoring an error code... meh) and if anyone knows why I get the "ICMP Port Unreachable" error when calling sendto(), I'd be glad to hear about it.

like image 467
Heowyn Avatar asked Dec 12 '15 17:12

Heowyn


People also ask

What does Socket Error 10054 mean?

Cause. Error 10054 occurs when the connection is reset by the peer application, usually due to an incorrect firewall configuration. Find more information about this error on Microsoft Development Center.

What is socket Recvfrom?

The recvfrom function reads incoming data on both connected and unconnected sockets and captures the address from which the data was sent. This function is typically used with connectionless sockets. The local address of the socket must be known.


1 Answers

In Windows, if host A use UDP socket and call sendto() to send something to host B, but B doesn't bind any port so that B doesn't receive the message, and then host A call recvfrom() to receive some message, recvfrom() will failed, and WSAGetLastError() will return 10054.

It's a bug of Windows. If UDP socket recv a ICMP(port unreachable) message after send a message, this error will be stored, and next time call recvfrom() will return this error.

There are 2 ways to solve this problem:

  1. Make sure host B has already bound the port you want to send to.
  2. Disable this error by using following code:
#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);

Reference: http://www.cnblogs.com/cnpirate/p/4059137.html

like image 196
simmerlee Avatar answered Sep 23 '22 01:09

simmerlee