Forum Discussion

JVD66's avatar
JVD66
Copper Contributor
Mar 09, 2021

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

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 ?

  • JVD66's avatar
    JVD66
    Copper Contributor
    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 ?
  • JVD66's avatar
    JVD66
    Copper Contributor
    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 .
    • JVD66's avatar
      JVD66
      Copper Contributor

       

      /*
      	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 

      • JVD66's avatar
        JVD66
        Copper Contributor
        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.

Resources