Thread: FIONREAD + WSARecv = Operation not supported (10054)

  1. #1
    Registered User Tonto's Avatar
    Join Date
    Jun 2005
    Location
    New York
    Posts
    1,465

    FIONREAD + WSARecv = Operation not supported (10054)

    I am trying to read data from an HTTP server asynchronously, and trying to respond to FD_READ notifications properly. I made code to respond by doing WSAIoctl + FIONREAD to determine the data left in the buffer, and then allocated memory for it, and read from the buffer. Here is my code in response to an FD_READ

    Code:
    					DWORD         bytesOutput;
    					DWORD         bytesRecvd;
    					DWORD         flags;
    					WSABUF        wsaBuffer;
    
    					int           ret;
    					char *        buf;
    					unsigned long toBeRead;
    
    					ret = ::WSAIoctl
    						(wParam, FIONREAD, 0, 0, &toBeRead, 
    						sizeof toBeRead, &bytesOutput, 0, 0);
    
    					if(ret != SOCKET_ERROR && bytesOutput == sizeof toBeRead)
    					{
    						wsaBuffer.buf = new char[toBeRead + 1];
    						wsaBuffer.len = toBeRead;
    
    						ret = ::WSARecv
    							(wParam, &wsaBuffer, 1, &bytesRecvd, &flags, 0, 0);
    
    						if(ret == SOCKET_ERROR)
    						{
    							ErrorMessage
    								(_T("WSARecv: %d"), ::WSAGetLastError());
    						}
    
    						wsaBuffer.buf[bytesRecvd] = 0;
    					}
    The first FD_READ notifications results in WSARecv failing with error 10045 - Operation not supported

    Quote Originally Posted by MSDN
    The attempted operation is not supported for the type of object referenced. Usually this occurs when a socket descriptor to a socket that cannot support this operation is trying to accept a connection on a datagram socket
    In addition, I get an exception (don't know if it matters)

    First-chance exception at 0x00401407 in Dialog Testing - Modal + Icon.exe: 0xC0000005: Access violation writing location 0x00dfee60.
    Unhandled exception at 0x00401407 in Dialog Testing - Modal + Icon.exe: 0xC0000005: Access violation writing location 0x00dfee60.

  2. #2
    Registered User Tonto's Avatar
    Join Date
    Jun 2005
    Location
    New York
    Posts
    1,465
    So I am trying to make it very simple. No determining how much data is in the buffer, very basic, this code is failing.

    Code:
    		DWORD         bytesRecvd;
    		DWORD         flags;
    		WSABUF        wsaBuffer;
    
    		int           ret;
    		bool          succeeded = true;
    
    
    		wsaBuffer.buf = new char[256];
    		wsaBuffer.len = 255;
    
    		ret = ::WSARecv
    			(wParam, &wsaBuffer, 1, &bytesRecvd, &flags, 0, 0);
    
    		if(ret == SOCKET_ERROR)
    		{
    			ErrorMessage
    				(_T("WSARecv: %d"), ::WSAGetLastError());
    			succeeded = false;
    		}
    		else
    		{
    			wsaBuffer.buf[bytesRecvd] = 0;
    		}
    And I get very unexplainable (by me ) behavior.

    Most of the time, I am able to read most of the data successfully with WSARecv, until I get an FD_CLOSE and then I get a 10038 (Operation on non-socket) error.

    But some of the time, that code will be run several times, each time WSARecv returning an error 10045 (Operation not supported), and then I get an FD_CLOSE, closesocket the socket, and then get a 10038 as it's trying to still read some data (and failing anyways).

    Any ideas?

  3. #3
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    I'm assuming that you are using WSAsyncSelect. Is is possible that you have a message conflict? Typically, you should use a message id above WM_APP (eg. WM_APP + 1), although any message value above WM_USER should be fine. Other than that, could you be missing a break statement?

  4. #4
    Registered User Tonto's Avatar
    Join Date
    Jun 2005
    Location
    New York
    Posts
    1,465
    I do not think I am missing a break statement. My message is WM_ASYNC_IO defined as WM_USER + 4337 (I say someone use their address for it, I did too). But I think it is okay, and I think I got it to kind of work like this. First, I found that MSDN said this:

    FD_CLOSE should only be posted after all data is read from a socket, but an application should check for remaining data upon receipt of FD_CLOSE to avoid any possibility of losing data.
    So I did something kind of roundabout. After every FD_READ message, I ran this code:

    Code:
    					ret = ::WSAIoctl
    						(wParam, FIONREAD, 0, 0, &toBeRead, 
    						sizeof toBeRead, &bytesOutput, 0, 0);
    
    					if(ret != SOCKET_ERROR && bytesOutput == sizeof toBeRead)
    					{
    						if(toBeRead == 0)
    						{
    							stillDataLeft = false;
    							::SendMessage(hwnd, WM_ASYNC_IO, wParam, MAKELPARAM(FD_CLOSE, 0));
    							break;
    						}
    					}
    stillDataLeft is a static boolean. I synthesize an FD_CLOSE message if there is no more data left. In FD_CLOSE, I check if there isStillDataLeft

    Code:
    					if(!stillDataLeft)
    					{
    						closesocket(wParam);
    					}
    It very much of the time. But on 2 seperate occaisions since I've implemented this code, I have still gotton 10045 errors. It seems to be extremely infrequent, but it's still a little troubling. I may test out this code a little more, I think that this is kind of an okay thing that I have done. Synthesizing the FD_CLOSE notification was a little wierd, but I think it's okay. If it isn't please tell me. I'm also trying to figure out an intelligent way to handle the winsock errors, but I think I can figure it out.

  5. #5
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    I would add some sanity code to check the value of wParam and make sure it is actually your socket.

    For the closing issue, have a look at the documentation for the shutdown function.

  6. #6
    Registered User Tonto's Avatar
    Join Date
    Jun 2005
    Location
    New York
    Posts
    1,465
    Okay. I get it. I am getting 10045 errors when Norton firewall is enabled. I had been disabling it for five minutes, then compiling and testing, and then get the errors once it switched back on, and I was too oblivious to notice it. Jesus christ, I can't believe I didn't notice that. Is there any way that I can fix that, or handle it responsibly? Because it generates those exceptions which isn't very nice.

    Edit 1: Is there any reason why wParam would not be my socket? Should I make like, WSASocket store a static socket so I can compare?

    Edit 2: Is this reasonably handling the FD_CLOSE

    Code:
    				case FD_CLOSE:
    					switch(WSAGETSELECTERROR(lParam))
    					{
    					case 0:
    						shutdown(wParam, SD_SEND);
    						break;
    					default: 
    						closesocket(wParam);
    					}
    
    
    					if(!stillDataLeft)
    					{
    						closesocket(wParam);
    					}
    					break;
    Edit 3: That does not seem reasonable. I changed it, based on what I have read in: Graceful Shutdown, Linger Options, and Socket Closure And now have it just so that shutdown is after I first send the HTTP request in FD_WRITE.

    Code:
    				case FD_WRITE:
    				{
    					char * request = 					
    						"GET / HTTP/1.1\r\n" \
    						"Host: www.google.com\r\n" \
    						"User-agent: test\r\n" \
    						"Connection: close\r\n\r\n";
    
    					send(wParam, request, strlen(request), 0);
    					shutdown(wParam, SD_SEND);
    					break;
    				}
    It seems to work pretty well, even with Norton asking for permissions. I'll make a new post if it starts falling apart again
    Last edited by Tonto; 08-05-2006 at 10:55 AM.

  7. #7
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    I'm not sure why Norton would be causing these errors, certainly I don't think it would cause exceptions. There is something weird happening here, but I'm not sure what. If you like, you could post a minimal program that demonstrates the errors and I will take a look.

    In FD_CLOSE, you should just receive any remaining data with WSARecv, and then call closesocket. You should use shutdown when you want to finish the connection rather than synthesising an FD_CLOSE notification.

    >> Edit 1: Is there any reason why wParam would not be my socket? Should I make like, WSASocket store a static socket so I can compare? <<

    No particular reason, but when you are getting unexplained errors, it is a good idea to add sanity tests. You can store the socket value in a global and check that wParam is equal.

  8. #8
    Registered User Tonto's Avatar
    Join Date
    Jun 2005
    Location
    New York
    Posts
    1,465
    Okay. The exception was the result of trying to null-terminate my recieved buffer unconditionally like

    Code:
    wsaBuffer.buf[bytesRecvd] = 0;
    And bytesRecvd being uninitialized would overwrite things. Now I do it only if the recieve is successful. And now I'm having some bizarre times with these 10045 errors. Kind of really bizarre! I get thousands (literally!) of 10045 errors, and then get my data somehow! Here's the debug output. The number on the right is the line number in the debug output.

    Werd FD_CONNECT 0
    WSARecv: 10045 1
    WSARecv: 10045 2
    WSARecv: 10045 3
    WSARecv: 10045 4
    WSARecv: 10045 5
    WSARecv: 10045 6
    WSARecv: 10045 7
    WSARecv: 10045 8

    ...
    ... It gives me these errors for so so long! ...
    ...

    WSARecv: 10045 25211
    WSARecv: 10045 25212
    WSARecv: 10045 25213
    WSARecv: 10045 25214
    WSARecv: 10045 25215
    WSARecv: 10045 25216
    255 25217
    255 25218
    255 25219
    255 25220
    255 25221
    255 25222
    255 25223
    255 25224
    255 25225
    255 25226
    184 25227
    Werd FD_CLOSE 25228
    And I finally get HTTP data! It's so so wierd!
    Last edited by Tonto; 08-05-2006 at 11:29 AM.

  9. #9
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    !! I've just noticed that flags is an [in/out] argument and you are passing it to WSARecv uninitialised. I think this is probably the primary cause of the errors you are getting. Setting it to zero should be fine.

  10. #10
    Registered User Tonto's Avatar
    Join Date
    Jun 2005
    Location
    New York
    Posts
    1,465
    Okay. This is so convincing me to initialize everything. Many many thanks!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 5
    Last Post: 12-04-2008, 08:15 PM
  2. Replies: 16
    Last Post: 11-23-2007, 01:48 PM
  3. UNICODE and GET_STATE
    By Registered in forum C++ Programming
    Replies: 1
    Last Post: 07-15-2002, 03:23 PM
  4. Serial Communications in C
    By ExDigit in forum Windows Programming
    Replies: 7
    Last Post: 01-09-2002, 10:52 AM