Advertisement

FD_READ's strange behaviour

Started by April 26, 2006 07:17 PM
8 comments, last by hplus0603 18 years, 9 months ago
I am trying to write a simple client/server app/game using asynchronous sockets in windows. After the server connects to the client, the server sends a simple message to the client (for debugging purposes). the message is a simple string: "123456789abcdefg". I did this in order to find out what's going on with fd_read. well, as usually, i used a recv call to handle the fd_read message. When the buffer passed to the recv() function is large enough, everything is ok and the steam of bytes returned is the string mentioned before. Then, I tried passing to the Recv a small buffer, let's say 5 bytes long (I did this to test what happens when the buffer in the recv is smaller than the data ready to be received in the tcp buffer). Something strange happened at this time. As I expected, the system issued the FD_READ message multiple times until all data is read. However, it did it in a strange way. I put some debugging messabox calls after every recv to see the data returned. Instead of seeing: "12345" after the 1st call, "6789a" in the 2nd, then "bcdef" then "g", I got: "g", then "bcdef", then "6789a", then "12345". Well, I put some more debugging messabox code and I've found that IMMEDIATELY after each call to recv the FD_READ code is invoked again in a recursive manner. That is, FIRST all the recv calls are made and THEN the code below the recv function (that fills and processes buffers etc) is executed, and in this way, the code from the last recv call is executed first etc etc.. If this is the case, then how can I feel a buffer with the entire message? shouldn't the system wait until I finish the processing inside the FD_READ case, that is , e.g., storing the buffer read by recv in a temp buffer, before reinvoking the FD_READ code? any help would be greatly appreciated. thanks
The right answer: Don't use Windows Messages based sockets. They don't scale well, and have all kinds of problems (such as what you just found). I suggest using select() instead, and if that's not good enough in the future, use I/O completion ports.

The other answer: Keep a queue of messages received, and allocate the buffer to receive into in this queue, in order. Process this queue in your event loop when it is idle. That way, you'll always process data in order.
enum Bool { True, False, FileNotFound };
Advertisement
The thing is, if i don't use windows messages how can the game function correctly? for example, how can the player do stuff like moving things around etc, if the program is stuck in a select command, waiting for the next piece of data to be read from the tcp buffer? this is why i used asynchronous sockets.

thanks for your response, btw
You can poll the socket, by specifying a zero timeout value (Not a NULL timeout, but set both members of the struct to 0), or you could go multithreaded.

Personally, I'd go for the multithreaded solution.
thanks for the replies.

anyway, the thing is that since winsock supports this option, that is have a filled tcp buffer produce a fd_read message, it should be able to work properly. So, does anybody know how to make this work or what's wrong with my design? I mean, in MSDN it is stated that if there are e.g. 100 bytes in the tcp buffer and a recv in the fd_read reads only 50, then fd_read will be invoked again so that a 2nd recv reads the remaining bytes. This is logical. so why isn't the code below recv executed immediately after recv, but it waits until all subsequent receives are executed and the code is executed in a recursive manner, that is first is executed the code after the last receive etc?

Please, if someone knows something about this specific problem, help me out here...


again, thanks a lot for all the replies
Quote:
Original post by akostop
thanks for the replies.

anyway, the thing is that since winsock supports this option, that is have a filled tcp buffer produce a fd_read message, it should be able to work properly. So, does anybody know how to make this work or what's wrong with my design? I mean, in MSDN it is stated that if there are e.g. 100 bytes in the tcp buffer and a recv in the fd_read reads only 50, then fd_read will be invoked again so that a 2nd recv reads the remaining bytes. This is logical. so why isn't the code below recv executed immediately after recv, but it waits until all subsequent receives are executed and the code is executed in a recursive manner, that is first is executed the code after the last receive etc?

Please, if someone knows something about this specific problem, help me out here...


again, thanks a lot for all the replies


From what it sounds like, if you just use select instead of the windows events, then it would work properly...

I think VB even does this... For some reason it occasionally, runs the function a second time while the first function is ONLY on the second line of a 20 line function... I never understood that, but it sounds identicle to what you describe.

Select by nature won't do that (not that I have heard of... you may have to do something really odd, and then it wouldn't be the select actually doing it but your code being odd =)
Advertisement
You will note that I actually described how to solve your problem even when using asynchronous sockets.

Something along the lines of:

struct Buffer {  int count;  char data[100];  sockaddr_in addr;}std::deque<Buffer> gBuffers;void HandleAsyncRead(){  std::deque<Buffer>::iterator buf;  gBuffers.push_back(Buffer());  buf = gBuffers.end();  --buf;  (*buf).count = ::recvfrom(socket, (*buf).data, sizeof((*buf).data), 0, (sockaddr *)&buf.addr, sizeof(buf.addr));}


Note that this code is safe, in that even if HandleAsyncRead() gets re-entered, the buffers as found in the gBuffers array will be in order. You can then go on to actually handle the buffers in response to a WM_TIMER event, or when GetMessage() returns NULL.

Also, as an aside: make sure you're starting WinSock with MAKEDWORD(2,2) as the version number; using version 1.x is just inviting all kinds of bugs into your program.
enum Bool { True, False, FileNotFound };
thanks a lot hplus0603. I'll try your suggestion, although it'll might take me some time, since i don't really know this std::deque construct and i'll have to read about it ( I'm actually a C, not C++ guy, but I'm working on it :) )

thanks a lot anyway

thanks a lot hplus0603. I'll try your suggestion, although it'll might take me some time, since i don't really know this std::deque construct and i'll have to read about it ( I'm actually a C, not C++ guy, but I'm working on it :) )

thanks a lot anyway

A Deque is a standard collection in computer science, much like a vector or a list (or a string, or a rope, or ...). It's like a list, but with better cache performance and most expensive deletion in the middle.
enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement