winsock2 's recvfrom and recv both return extended error 10045 for bound UDP socket - why?

Copper Contributor

I have some C code using winsock2 :

     int fd = socket ( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
     struct sockaddr_in sin = { ... };
     if (fd == -1)
       _ERR_( ... )
     ...
     if ( bind ( fd, (struct sockaddr*)&sin, sizeof(struct sockaddr_in)) != 0 )
      _ERR_( ... )
     ...
     fd_set s_fds, s_e_fds;
     UL_t
     bytes_available=0;
     register
     U32_t s_n_fds = 0;
     do
     { bytes_available=0;
       FD_ZERO(&s_fds);
       FD_SET(fd,&s_fds);
       FD_ZERO(&s_e_fds);
       FD_SET(fd,&s_e_fds);
       struct timeval tv = { 0, 1000 };
       s_n_fds = select( 1, &s_fds, NULL, &s_e_fds, &tv);
       if (s_n_fds > 1)
         _ERR_( ERROR_SYSTEM | ERROR_FATAL | ERROR_LOG
                , ERR_ACT_RETURN, GetLastError() , 1 , "select failed"
               );

       else

       if ( s_n_fds == 1 )
       { if ( FD_ISSET( fd, &s_fds ) )
         {more_bytes:
          r = ioctlsocket( fd, FIONREAD, &bytes_available );
          if ( r != 0 )
            _ERR_( ERROR_SYSTEM | ERROR_FATAL | ERROR_LOG
                    , ERR_ACT_RETURN, GetLastError(), 1, "ioctlsocket failed"
                  );
          say("RD: %u",bytes_available);
          if( bytes_available > 4)
          { struct sockaddr_in snsin={0};
            U32_t sin_len = sizeof(struct sockaddr_in);
            r = recvfrom((SOCKET) fd, (char*) &_iob_.b[0], bytes_available, MSG_WAITALL,

                        (struct sockaddr*)&snsin, &sin_len );
            say("RCV: %u %u WSAE: %u",fd,r,WSAGetLastError());
            if ( r < 0 )
             _ERR_(...);

           ....
     } while (fd != -1 ) ;


 

 


The above code prints:
$ .\${MY_EXE}
${MY_EXE}[9692]: RD: 121.
${MY_EXE}[9692]: RCV: 440 4294967295 WSAE: 10045.
src\logio.exe[9692] : logio.c:115@(main) : Error: 10045:'The attempted operation is not supported for the type of object referenced.
' - recv failed.
     
So, for the bound UDP socket 'fd', the select() succeeded, the ioctlsocket() succeeded, returning
a 'bytes_available' of 121 bytes, but any attempt to then call either 'recv' or 'recvfrom' with that 'fd'
socket then returns SOCKET_ERROR with extended error code 10045, 'Operation Not Supported'.

Please, can anyone enlighten me what the 'Supported' way of reading a UDP packet is on Windows, if neither winsock2's 'recv' nor its 'recvfrom' can be used ?  I would like to know the sender's address,
so was using 'recvfrom' . What method am I meant to be using to receive a single packet from a bound UDP
socket ,  and the address it was sent from ?

4 Replies
I have tried using recv, recvfrom, and ReadFile. recv and recvfrom return SOCKET_ERROR + Extended Error 10045, while ReadFile returns error 87,
'The parameter is incorrect'.
It appears that somehow ioctlsocket( fd, FIONREAD, &bytes_available) , though it returns 0 (success) , and returns a positive number in 'bytes_available', is somehow making 'fd' an invalid file handle ?
Are there any known workarounds, or a good open-source replacement for WinSock2.DLL ?
Aha! I have verified this is definitely a bug with 'ioctlsocket' .

I modified the example here :

https://www.binarytides.com/udp-socket-programming-in-winsock/

to simply call 'ioctlsocket(s, FIONREAD, &bytes_available) before calling recvfrom with
the positive 'bytes_available' result, and that call also then generates extended error 10045, whereas without the call to 'ioctlsocket' , and receiving BUFLEN, with a call to recvfrom, it works fine.

There is no documentation on either the 'recvfrom' page or on the 'ioctlsocket' pages stating that a call to ioctlsocket with FIONREAD with a bound UDP socket will make the socket parameter into an invalid socket - this is a BUG.
It is a BUG that ioctlsocket() cannot be used before a call to 'recv' or 'recvfrom'().

I will raise these as microsoft bugs (somehow), but is there any better (preferably open-source) alternative to using WinSock2 for BSD sockets programming on WIndows ? The WinSock2 library does not seem to be coded very reliably .

 

/*
	Simple UDP Server
*/

#include<stdio.h>
#include<winsock2.h>

#pragma comment(lib,"ws2_32.lib") //Winsock Library

#define BUFLEN 512	//Max length of buffer
#define PORT 8888	//The port on which to listen for incoming data

int main()
{
	SOCKET s;
	struct sockaddr_in server, si_other;
	int slen , recv_len;
	char buf[BUFLEN];
	WSADATA wsa;

	slen = sizeof(si_other) ;
	
	//Initialise winsock
	printf("\nInitialising Winsock...");
	if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)
	{
		printf("Failed. Error Code : %d",WSAGetLastError());
		exit(EXIT_FAILURE);
	}
	printf("Initialised.\n");
	
	//Create a socket
	if((s = socket(AF_INET , SOCK_DGRAM , 0 )) == INVALID_SOCKET)
	{
		printf("Could not create socket : %d" , WSAGetLastError());
	}
	printf("Socket created.\n");
	
	//Prepare the sockaddr_in structure
	server.sin_family = AF_INET;
	server.sin_port = htons( 60514 );
        server.sin_addr.s_addr =htonl((127<<24)|1);
	//Bind
	if( bind(s ,(struct sockaddr *)&server , sizeof(server)) == SOCKET_ERROR)
	{
		printf("Bind failed with error code : %d" , WSAGetLastError());
		exit(EXIT_FAILURE);
	}
	puts("Bind done");

	//keep listening for data
        unsigned int bytes_available = 0;

	while(1)
	{
		printf("Waiting for data...");
		fflush(stdout);
		
		//clear the buffer by filling null, it might have previously received data
		memset(buf,'\0', BUFLEN);

                bytes_available = 0;
                if ( ioctlsocket(s, FIONREAD, &bytes_available) != 0 )
                {
			printf("ioctlsocket() failed with error code : %d" , WSAGetLastError());
			exit(EXIT_FAILURE);
                }

                if ( bytes_available > 0 )
                {
                  //try to receive some data, this is a blocking call
                  if ((recv_len = recvfrom(s, buf, bytes_available, MSG_WAITALL, (struct sockaddr *) &si_other, &slen)) == SOCKET_ERROR)
                  {
			printf("recvfrom() failed with error code : %d" , WSAGetLastError());
			exit(EXIT_FAILURE);
                  }
                  //print details of the client/peer and the data received
                  printf("Received packet from %s:%d\n", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port));
                  printf("Data: %s\n" , buf);
                }else
                  Sleep(1000);
	}

	closesocket(s);
	WSACleanup();
	
	return 0;
}

@JVD66 

This program prints:

Compiled with normal $INCLUDE and $LIB set from vcvarsall.bat :

$ cl winsock_ex.c /link /NODEFAULTLIB:MSVCRT shell32.lib Ws2_32.lib kernel32.lib
Microsoft (R) C/C++ Optimizing Compiler Version 19.28.29337 for x86
Copyright (C) Microsoft Corporation. All rights reserved.

winsock_ex.c
Microsoft (R) Incremental Linker Version 14.28.29337.0
Copyright (C) Microsoft Corporation. All rights reserved.

/out:winsock_ex.exe
/debug
/NODEFAULTLIB:MSVCRT
Synchronization.lib
shell32.lib
Ws2_32.lib
kernel32.lib
winsock_ex.obj


$ ./winsock_ex.exe

Initialising Winsock...Initialised.
Socket created.
Bind done
Waiting for data...Waiting for data...Waiting for data...Waiting for data...Waiting for data...Waiting for data...Waiting for data...Waiting for data...Waiting for data...Waiting for data...Waiting for data...Waiting for data...Waiting for data...recvfrom() failed with error code : 10045

Commenting out the bit where it calls ioctlsocket() allows it to recvfrom() OK.
Looks like a bug to me.