views:

382

answers:

2

I'm using the WSAEventSelect I/O model in Windows Sockets and now I want to know that how may I know that my send and receive operations have sent and received all of the data?

After I know that, how should I design a way so that it sends the data fully? Any examples would be really appreciated.

Here is my code (not mine actually sample code from the book I'm learning)

SOCKET SocketArray [WSA_MAXIMUM_WAIT_EVENTS];
WSAEVENT EventArray [WSA_MAXIMUM_WAIT_EVENTS],
NewEvent;
SOCKADDR_IN InternetAddr;
SOCKET Accept, Listen;
DWORD EventTotal = 0;
DWORD Index, i;
WSANETWORKEVENTS NetworkEvents;


// Set up socket for listening etc...
// .... 

NewEvent = WSACreateEvent();

WSAEventSelect(Listen, NewEvent,
         FD_ACCEPT │ FD_CLOSE);

listen(Listen, 5);

SocketArray[EventTotal] = Listen;
EventArray[EventTotal] = NewEvent;
EventTotal++;

while(TRUE)
{
    // Wait for network events on all sockets
    Index = WSAWaitForMultipleEvents(EventTotal,
     EventArray, FALSE, WSA_INFINITE, FALSE);
    Index = Index - WSA_WAIT_EVENT_0;

    // Iterate through all events to see if more than one is signaled
    for(i=Index; i < EventTotal ;i++
    {
     Index = WSAWaitForMultipleEvents(1, &EventArray[i], TRUE, 1000, 
      FALSE);
     if ((Index == WSA_WAIT_FAILED) ││ (Index == WSA_WAIT_TIMEOUT))
      continue;
     else
     {
      Index = i;
      WSAEnumNetworkEvents(
       SocketArray[Index],
       EventArray[Index], 
       &NetworkEvents);

      // Check for FD_ACCEPT messages     
      if (NetworkEvents.lNetworkEvents & FD_ACCEPT)
      { 
       if (NetworkEvents.iErrorCode[FD_ACCEPT_BIT] != 0)
       {
        printf("FD_ACCEPT failed with error %d\n", 
         NetworkEvents.iErrorCode[FD_ACCEPT_BIT]);
        break;
       }

       // Accept a new connection, and add it to the
       // socket and event lists
       Accept = accept(
        SocketArray[Index],
        NULL, NULL);

       NewEvent = WSACreateEvent();

       WSAEventSelect(Accept, NewEvent,
        FD_READ │  FD_CLOSE);

       EventArray[EventTotal] = NewEvent;
       SocketArray[EventTotal] = Accept;
       EventTotal++;
       printf("Socket %d connected\n", Accept);
      }

      // Process FD_READ notification
      if (NetworkEvents.lNetworkEvents & FD_READ)
      {
       if (NetworkEvents.iErrorCode[FD_READ_BIT] != 0)
       {
        printf("FD_READ failed with error %d\n", 
         NetworkEvents.iErrorCode[FD_READ_BIT]);
        break;
       }

       // Read data from the socket
       recv(SocketArray[Index - WSA_WAIT_EVENT_0],
        buffer, sizeof(buffer), 0);

       // here I do some processing on the data received
       DoSomething(buffer);

       // now I want to send data
       send(SocketArray[Index - WSA_WAIT_EVENT_0],
                  buffer, sizeof(buffer), 0);
       // how can I be assured that the data is sent completely

      }

      // FD_CLOSE handling here
      // ......
      // ......
     }
    }
}

What I thought, that I would set a boolean flag to determine that the receive has completed (the message will have its length prefixed) and then start processing that data. But what about send()? Can you please tell me the possibilities.

EDIT:See the FD_READ event part

A: 

Unless the protocol (application layer) you are handling gives you any information about how many data you're about to receive, the only way to determine if there is nothing more to received is when the peer disconnects. If the server simply stop sending, you can't determine if its the end or its just busy. It ends when it ends. You also can't determine if the server disconnected because its the end or because the connection was broken.

Thats why most protocols inform the peer about how many bytes it is going to be sent before sending it, or by placing a boundary in the end of the data.

About sending, you must be aware of the buffer you're using. When you send(), it goes to a buffer (with 64KB by default). send() returns the number of bytes placed in the buffer, if its less then the bytes you were trying to send, you have to manage it to try again in the next time you receive a FD_WRITE event.

You can't have sure about how much data was already received by the peer unless it keeps you informed (mIRC DDC does that).

Not sure it clearfyed your doubts, hope it helped :)

Havenard
The problem is I'm not waiting for FD_WRITE event, because for that I'll have to send enough data so that the internal buffer is full and it sends a FD_WRITE event again. What I'm doing is sending asynchronously, but then I need to know that the data has been sent successfully. How do I do that?
hab
Its simple, you wait for the FD_WRITE :)
Havenard
since I'm sending short messages (1, 2 bytes) I won't get another FD_WRITE event unless the internal buffer is full.
hab
Then you can't know if the data has been received, unless it tells you "I received". But the peer probably will receive it, in less than 80 millisecound.
Havenard
A: 

When you are doing the recv, you need to save the return status to determine if the data was received. recv returns the number of bytes received, and I would use the flag MSG_WAITALL instead of zero for the fourth parameter to receive all of the message (based on the buffer size). If the status recv returns is negative, there was an error of some nature, such as connection was close from the other end or there was some other issue.

As for the send, you should save the return value as it also as the status, but in this case, there is not a flag to have all the data sent before returning. You will have to determine the amount send and adjust the buffer and send size based on the value. As with recv, a negative value indicates an error has occurred.

I would read the function descriptions on the Microsoft website for recv and send for more information on the return values and flags.

Glenn
MSG_WAITALL is for SOCK_DGRAM (UDP and RAW). SOCK_STREAM (TCP) would hang.
Havenard