WSAEWOULDBLOCK forever
Hi all,
I'm writing a simple client\server messaging app. The client is an ActiveX control that connects to the server, and then waits on the socket with WSAAsyncSelect(). When it receives an FD_READ message, it reads in the packet and does what it's meant to do with the contents.
LRESULT CExFrameworkClientCtrl::OnNetwork(WPARAM wParam, LPARAM lParam)
{
using namespace ExFrameworkProtocol;
switch( lParam )
{
case FD_READ:
{
PACKET_HEADER head;
ReceivePacket( m_Socket, reinterpret_cast< char* >( &head ), sizeof( head ) );
if( strcmp( head.ref, m_Reference ) == 0 )
{
switch( head.message )
{
case MSG_TEXT:
{
CString msg;
ReceivePacket( m_Socket, msg.GetBufferSetLength( head.message_length ), head.message_length );
msg.ReleaseBuffer();
ReceivedMessage( msg );
}
break;
}
}
}
break;
}
return 0;
}
void ReceivePacket(SOCKET sock, char* data, unsigned long data_size )
{
if( sock != INVALID_SOCKET && data != NULL )
{
unsigned long remaining_data = data_size;
unsigned long data_chunk = data_size;// < 1024 ? data_size : 1024;
int bytes_recieved = 0;
while( remaining_data > 0 && data_chunk > 0 )
{
bytes_recieved = recv( sock, data + ( data_size - remaining_data ), data_chunk, 0 );
if( bytes_recieved != SOCKET_ERROR )
{
remaining_data -= bytes_recieved;
}
else
{
if( WSAGetLastError() != WSAEWOULDBLOCK )
{
DieWithError( WSAGetLastError() );
}
else
{
data_chunk = data_chunk > 1 ? data_chunk - 1 : 1;
}
}
}
}
}
For some reason, every so often recv() will always give me a SOCKET_ERROR and WSAGetLastError() always returns WSAEWOULDBLOCK so my code gets caught in an infinite loop even when just trying to read a single byte from the socket. By packet sniffing the port I know there is data incoming.
[2318 byte] By [
GyroTech] at [2007-11-20 11:40:12]

# 1 Re: WSAEWOULDBLOCK forever
I notice a few issues with your code, although I don't know if any of them are causing your problems.
First, it's only the low word of lParam that encodes the network event. The high word encodes any possible netwrok errors. So, your switch statement should switch on WSAGETSELECTEVENT(lParam), which is a macro that extracts the low word. The network error can be extracted using WSAGETSELECTERROR(lParam).
Second, you are assuming the the network event occurred for m_Socket, but you don't check to be certain. You probably should compare socket handles using wParam, to be certain that you are getting an event for the socket you expect.
Third, you are calling recv() more than once in response to FD_READ. The proper response to FD_READ is to call recv() exactly once. This single call to recv() re-enables the network read event, and if more data is received, or if there is any data still left in the receive buffer after the single call to recv(), then another FD_READ will be issued. If you call recv() more than once, then you will get spurious FD_READs where there is no data in the buffer.
In your case, your code assumes that since it received an FD_READ, there absolutely MUST be at least one byte in the buffer that will be available for reading. But as explained above, if your code has caused the generation of spurious FD_READs, then that assumption is flase, and your code might therefore end up in the infinite loop that you are describing.
Mike
# 2 Re: WSAEWOULDBLOCK forever
Thanks for the ideas :-)
Is there a way to call recv() without triggering an FD_READ event\message?? Or to find out how much data is on the socket before reading to make sure it's not empty?? I ask because the only way I can think of only calling recv() when I get an FD_READ message would be to keep a char buffer open and always read into that, then memcpy() the contents elsewhere when I need to, but that brings up lots of quite nasty little problems when I need to do things like adding file transfer too.
As for the other issues, I changed my switch statement and the client only has one socket, and only calls WSAAsyncSelect() on that one socket so I'm not sure how I'd be getting messages for another socket but I have added checks to make sure it only processes it's own socket :-) Thanks for that :-)
# 3 Re: WSAEWOULDBLOCK forever
Ok, I replaced the line in my ReceivePacket() function unsigned long data_chunk = data_size; with unsigned long data_chunk = recv( sock, data, data_size, MSG_PEEK ); which means that I only read however much data is ready on the socket, if that is zero, I don't attempt another recv() and so the function exits cleanly.
The problem I have now is that my client seems to be sitting in a suspended state after OnNetwork() exits. The incomeing messages is processed properly, but the app falls into not responding (and isn't stuck in an infinite loop, no CPU usage shows for the process at all) and I have to kill it manually.
Oddly enough, the server can receive many messages from the client quite happily, whereas a single message sent to the client causes it to wait indefinitely. The only difference between the server and client is that the server is multi-threaded and each connection thread uses WSAEventSelect() and WaitForMutlipleObjects() to wait on the socket while the client uses WSAAsyncSelect() and the windows own message pump with OnNetwork() to wait on the socket. Could this be causing the client to stay suspended??
# 4 Re: WSAEWOULDBLOCK forever
Gah, I meant that I replaced unsigned long data_chunk = data_size; with int data_waiting = recv( sock, data, data_size, MSG_PEEK ); otherwise I'd get the same WSAEWOULDBLOCK error if recv() ever returned -1 ;-)
Anyways, I'm still getting a loop somewhere if it manages to recv() enough bytes to build a header, but not enough to recv() the rest of the message. If msg is ever empty, it stalls.
Gotta keep working on it ;-)
# 5 Re: WSAEWOULDBLOCK forever
Right, I've been messing about with this quite a bit and the only way it is going to work is if I, as you suggest, only ever call recv() once per FD_READ event.
My problem is that this causes nasty buffer issues as I see it. I have to have a member that is a char* buffer that I constantly resize and many other member variables tracking what I have received, if it's enough to try and read a packet header with, then even more to read how much of the actual data has come though and how much I am expecting and all sorts of tracking like that.
This to me seems a particularly cumbersome way of having to deal with the situation. Is there a neater method that I could employ??
# 6 Re: WSAEWOULDBLOCK forever
Is there a neater method that I could employ??
No, that's the price that you pay for asynchronous notifications: you are required to implement a mini-state-machine that remembers your current place in the receipt of data, from one FD_READ event to the next.
A single dedicated thread does not have this requirement, since it receives notifications that are synchronous, which is apparently why you did not experience it in the development of the server.
Incidentally, MSG_PEEK is on the winsock lame list. Experienced network programmers always recommend to avoid its use. One reason is inefficiencies. Another is the fact that the winsock stack runs in its own threads, so that the information returned by MSG_PEEK might change between the time that you call it and the time that you use the returned result. See http://tangentsoft.net/wskfaq/articles/lame-list.html at items 12 and 16.
It is possible to disable FD_READs while recv()'ing. Before calling your first recv(), turn off asynchronous notifications with a call to WSAAsyncSelect(), using an empty event set. This stops all asynchronous notifications. Then call recv() as many times as you like. When you're done, re-set the socket's notifications with another call to WSAAsyncSelect(... FD_READ ...). This call will trigger new notifications if there is new data pending, so nothing is lost.
Frankly, it's not worth the effort, since you still need to implement a mini-state-machine anyway. The reason is that TCP is not a message-based protocol; it's a stream-based protocol. It simply sends out a stream of bytes, and doesn't care about the relation of one byte to the next. Thus, even though the server might send out a single chunk of bytes, and do so successfully in one single call to send(), TCP is free to piggyback those bytes onto a pending segment before sending the segment out. Or it's free to break those bytes into multiple segments before sending them out. So, it might take multiple calls to recv(), across multiple network events, before the recipient is able to assemble all bytes together. Thus, when using TCP, it's always necessary for the application to implement some kind of application-level protocol so that the recipient can know when a complete message has been received, and so that it can know where one message ends and another begins.
One common protocol is for the sender to pre-pend each message with an integer which encodes the length of the message. That way, the recipient can tell how many bytes are in the message, and determine when the message is received completely, such that any other bytes received at the end must belong to a next message. Note that TCP might break up the integer too (if it's more than one byte), so your mini-state-machine needs to confirm that all four (say) bytes of the integer have been received before acting on them.
Mike
# 7 Re: WSAEWOULDBLOCK forever
Thanks for all the info. I went ahead and set up the mini-state-machine and it all seems to work just great. Well, my FD_READ event handler is approximately 18 bajillion times bigger and need to be fairly heavily annotated, but hey, it's my job ;-) Plus, since I am only ever calling Recv() once per FD_READ event, I no longer rely on MSG_PEEK.
I was wondering how I would handle large streams of data, such as files. I do add my own header, with the length of the message, to each message I send() but with files, that length could be hundreds of MB, and any call to recv() trying to pull that much data out will result in a SOCKET_ERROR response and a WSAEWOULDBLOCK error. Now, would another FD_READ event fire since there is still the data waiting on the socket even though the call to recv() was unsuccessful, and if so, how would I attempt to read whatever data is ready and waiting without knowing how much is there to read?? I could technically solve this by only ever recv()'ing one byte at a time but that really offends my sense of efficiency ;-)
Thanks for all your help on this...
EDIT: After reading the link, even a failed recv() would result in another FD_READ event so I can't just call it again requesting a smaller data chunk. I guess I have to add how much data I want to my state machine and decrement it each time a failed recv() happens
# 8 Re: WSAEWOULDBLOCK forever
For transfer of large files, send the length of the entire file just once, at the beginning. On the recipient's side, at each call to recv(), whatever is recv'd should be stored to a local file. It doesn't matter how many bytes are received in each call to recv(): simply monitor how many bytes have actually been received, and stop calling recv() when you have received all of the file's content, as indicated in that very first transmission.
See http://www.codeproject.com/internet/SocketFileTransfer.asp , which uses synchronous sockets, but which implements the "send the length just once at the beginning" technique.
Mike
# 9 Re: WSAEWOULDBLOCK forever
Sorry, I misstated my enquiry... I mean after I get the file length in from the header, I make a call to recv() trying to read however many bytes are left to receive (at first call this would be the entire message length ie the whole file). This, most of the time, causes a WSAEWOULDBLOCK, I have to then decrement how many bytes I want to attempt to read the next time I get an FD_READ event - which will be straight away since the recv() failed.
# 10 Re: WSAEWOULDBLOCK forever
You should not try to receive the whole file in one chunk, nor should the sender try to send it that way.
The sender should send the file in reasonably-sized buffers, and the recipient should do the same. They do not need to use the same-sized buffers. "Reasonable" means something like 8K or 16K bytes.
Read the linked-to article, which explains this.
Mike
# 11 Re: WSAEWOULDBLOCK forever
Thanks once again for your advice. I now simple check my expected data length against SO_MAX_MSG_SIZE and only recv() the minimal value of the two.
# 12 Re: WSAEWOULDBLOCK forever
Slightly different issue now. Previously, I was using an enum to define what sort of packet was incoming. for example
enum message_type { MSG_ERR = 0, MSG_LOGIN = 1, MSG_LOGOUT = 2, MSG_TEXT = 3, MSG_FILE = 4 };
I am, however, now doubting this to be a complete safe way of marking a packet type. Various C++ implementations could use different-sized data structures to store the enumeration. So am I right in that I should convert my enum type into a known-sized data type (char or short, for instance) before committing that to network byte-order (if > 1 byte) and calling send()
# 13 Re: WSAEWOULDBLOCK forever
As far as I can recall, enumeration constants are always integer data types, pursuant to the C++ standard. So, there should not be a need to convert to int or char or whatever, although you are correct about the need to call ntohl() and htonl(), to ensure a match in endianness.
A different issue is byte packing for structures. Say you are sending a structure like this:
struct MyStruct{
char oneChar;
int oneInt;
};
MyStruct oneStruct = {'a', 1};
send( mySock, (char*)oneStruct, sizeof(MyStruct));
Different compilers will give different byte packings for the structure, so on some compilers, sizeof(MyStruct) will be 8 (most will) whereas others might give 6 or 5.
To ensure proper operation cross-platform and cross-compilers, surround the structure's definition with a #pragma pack(1), so as to force the compiler to pack the structure to one-byte alignment. The above example, of course, should be re-ordered so that meaningful information is d-word aligned, as much as possible.
Mike
# 14 Re: WSAEWOULDBLOCK forever
Thanks once again for your advice. I now simple check my expected data length against SO_MAX_MSG_SIZE and only recv() the minimal value of the two.
I think SO_MAX_MSG_SIZE is the size of a maximum UDP datagram. If so, then it's unrelated to TCP transmissions and the use of this value in this context is not sensible.
Mike
# 15 Re: WSAEWOULDBLOCK forever
I think SO_MAX_MSG_SIZE is the size of a maximum UDP datagram. If so, then it's unrelated to TCP transmissions and the use of this value in this context is not sensible.
Mike
OK, so just an arbitrary 8k const as a maximal limiter on send() and recv() then, with no specific Winsock limit??
As for the packet-header, I just send along each variable in the struct separately instead, and reconstruct on the other side.
Is the #pragma pack directive an MS-specific one or C++ general??