Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
79 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Contents
 Introduction
 Setting Up
 Notification
 Messages

 Shaking Hands
 Sending and
 Receiving


 Printable version
 Discuss this article
 in the forums


Sending and Receiving Data

I wanted to split this into two parts, but I decided just to explain FD_READ first and save the more complicated FD_WRITE for last. That said, the FD_READ event is easy enough to handle. WinSock will notify you when the network buffer has more data for you to collect. For every FD_READ event you will call recv() just like so:

int bytes_recv = recv(wParam, &data, sizeof(data), 0);

Nothing terribly different, except you can’t forget to stick in that wParam. Also, a word of warning: You are not guaranteed that you will receive the entire buffer in one shot. This means your data may not come through all at once. Be sure to implement a test using the bytes received (which happens to be the return value of the recv() function) to determine if you have all of your data before manipulating it.

Now the FD_WRITE event is a bit more complicated. Mainly because of the way it is called. First of all, an FD_WRITE event is always generated when you first establish a connection with another socket. So you think all you have to do is plunk in a send() function with whatever else you’ll need and you’ll be fine. Oh if that were only true. Listen carefully: the FD_WRITE event is triggered only when there is more room in the buffer with which to write data. Huh? Allow me to explain.

First off, the buffer, as I use it here, is the network buffer, which you write information to. That information is then sent over the network (intranet or internet) to the receiving socket. So you’d think that since there will always be space left to write data if you don’t fill it up all at once, that FD_WRITE events will just keep coming and coming right? Wrong! Read the rules again. It says when there is more room. Not enough room, more room. This means you have to fill it up first! How do you do that?

The general idea is to create an infinite while loop in which you will continuously send data until you max out the buffer. When is happens, send() will return the error WSAWOULDBLOCK. This means that if it were a blocking socket, it would wait (stop execution) for more room in the buffer and then send. But since it isn’t, you get the error. So now that you’ve filled up the buffer, you just have to wait until more room becomes available so you can write again. And bingo! Up pops another FD_WRITE event. Do you have any idea how much trouble I had to go through to figure this out? You people are so darned lucky! Here’s an example of an FD_WRITE event handler:

case FD_WRITE:  // we can send data
  {
    // enter an infinite loop
    while(TRUE)
    {
      // read in more data from the file and store it in packet.data.
      in.read((char*)&packet.data, MAX_PACKET_SIZE);

      // increment the amount of data sent
      data_sent += strlen(packet.data);

      // send the packet off to the Server if it is filled
      if (send(wparam, (char*)(&packet), sizeof(PACKET), 0) == SOCKET_ERROR)
      {
        // check if the network buffer is full and can send no more
        // data. If so then break from the loop
        if (WSAGetLastError() == WSAEWOULDBLOCK)
        {
          // break from the loop - buffer is full
          break;
        }
        else // another error
        {
          // display an error message and clean up
          CleanUp();
          return(0);
        }
      }
    }
  } break;

There, you see? The implementation isn’t so hard after all! It was only the concept that was messing with your head. If you use this technique, the loop will end when the network buffer becomes full. Then, when more space is available - another FD_WRITE event will be triggered, and you can send more data.

Before you jump to make use of your newfound knowledge, allow me to clarify the use of the FD_WRITE event. Do not expect to be using this event at all if you are not going to be sending large pieces of data at once. The reason for this is simple - if you base all you information transactions on FD_WRITE and never send enough to fill up the buffer, after that first trial FD_WRITE event generated at the beginning of the connection - no other FD_WRITE event will be triggered! Therefore for games, where you send as little data as possible, just forget about setting a socket for the FD_WRITE event and use send() wherever you need it. FD_WRITE is best used for transferring files, in my experience.

Conclusion

Well, so far this is the longest article I’ve written. I’ve tried to keep it as short as possible to hold your attention but there was just so much information to cover. Asynchronous sockets can be really confusing at first if they are not explained correctly, and I hope I did a good job of teaching you how to use them properly. If you have any questions or comments, please email me. I will try to answer as many as I can. Also, by emailing me, I’ll have your address in case I have any new information or corrections to give out. Hope you enjoyed reading the article as much as I enjoyed writing it. I’m looking forward to my next one. Until then…