Home > Enterprise >  Waiting on sockets using the function WaitForMultipleObjects (msdn)
Waiting on sockets using the function WaitForMultipleObjects (msdn)

Time:06-03

I am trying to implement a unit test to see if using the function "WaitForMultipleObjects" works properly with socket. To do so I implemented a simple UDP server-client protocol. The server bind his socket and wait to receive data from receiver using the function "WaitForMultipleObjects". Once the server receives data from the client, it displays it then waits 5 seconds. The problem here that if the client try to send 2 messages during this five seconds, the first one is displayed while the second one will block the function "WaitForMultipleObjects" forever. I know that I can use the "Select" function to do that since the socket is the only object I am waiting for, but it's just a unit test. In my real project I need to wait on a socket and another object's type which is a windows event (which is of type HANDLE) at the same time. And that's why i am trying to use "WaitForMultipleObjects" on sockets.

Here's the server code:

/*
    Simple UDP Server
*/

#include<winsock2.h>
#include<windows.h>
#include <ws2tcpip.h>
#include<stdio.h>
#include<iostream>

#pragma comment(lib,"ws2_32.lib") //Winsock Library

#define BUFLEN 512  //Max length of buffer
#define PORT 8888   //The port on which to listen for incoming data





int main()
{
    SOCKET s;
    struct sockaddr_in server, si_other;
    int slen , recv_len;
    char buf[BUFLEN];
    WSADATA wsa;
    HANDLE SEvent;
    

    slen = sizeof(si_other) ;
    
    //Initialise winsock
    printf("\nInitialising Winsock...");
    if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)
    {
        printf("Failed. Error Code : %d",WSAGetLastError());
        exit(EXIT_FAILURE);
    }
    printf("Initialised.\n");
    
    //Create a socket
    if((s = socket(AF_INET , SOCK_DGRAM , 0 )) == INVALID_SOCKET)
    {
        printf("Could not create socket : %d" , WSAGetLastError());
    }
    printf("Socket created.\n");
    
    //Prepare the sockaddr_in structure
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons( PORT );
    
    //Bind
    if( bind(s ,(struct sockaddr *)&server , sizeof(server)) == SOCKET_ERROR)
    {
        printf("Bind failed with error code : %d" , WSAGetLastError());
        exit(EXIT_FAILURE);
    }


    puts("Bind done");


    SEvent = WSACreateEvent();
    WSAEventSelect( s, SEvent, FD_READ);
    //keep listening for data
    while(1)
    {
        printf("Waiting for data...");
        fflush(stdout);
        
        //clear the buffer by filling null, it might have previously received data
        memset(buf,'\0', BUFLEN);

            INT r = WaitForMultipleObjectsEx(1,&SEvent,FALSE,INFINITE,TRUE);
            if( r == WAIT_OBJECT_0)
            {
                if (recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen) == SOCKET_ERROR)
                {
                    printf("recvfrom() failed with error code : %d" , WSAGetLastError());
                    exit(EXIT_FAILURE);
                }

                //print details of the client/peer and the data received
                printf("Received packet from %s:%d\n", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port));
                printf("Data: %s\n" , buf);

                //now reply the client with the same data
                if (sendto(s, buf, recv_len, 0, (struct sockaddr*) &si_other, slen) == SOCKET_ERROR)
                {
                    printf("sendto() failed with error code : %d" , WSAGetLastError());
                    exit(EXIT_FAILURE);
                }
                ResetEvent(SEvent);
                Sleep(5000);
            }
            else
            {
                std::cerr<<"WaitForMultipleObject() Error ( "<<GetLastError()<<" )"<<std::endl;
                exit(0);
            }
        
    }

    closesocket(s);
    WSACleanup();
    
    return 0;
}

Here's the client code:


/*
    Simple udp client
*/
#include<stdio.h>
#include<winsock2.h>

#pragma comment(lib,"ws2_32.lib") //Winsock Library

#define SERVER "127.0.0.1"  //ip address of udp server
#define BUFLEN 512  //Max length of buffer
#define PORT 8888   //The port on which to listen for incoming data

int main(void)
{
    struct sockaddr_in si_other;
    int s, slen=sizeof(si_other);
    char buf[BUFLEN];
    char message[BUFLEN];
    WSADATA wsa;

    //Initialise winsock
    printf("\nInitialising Winsock...");
    if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)
    {
        printf("Failed. Error Code : %d",WSAGetLastError());
        exit(EXIT_FAILURE);
    }
    printf("Initialised.\n");
    
    //create socket
    if ( (s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == SOCKET_ERROR)
    {
        printf("socket() failed with error code : %d" , WSAGetLastError());
        exit(EXIT_FAILURE);
    }
    
    //setup address structure
    memset((char *) &si_other, 0, sizeof(si_other));
    si_other.sin_family = AF_INET;
    si_other.sin_port = htons(PORT);
    si_other.sin_addr.S_un.S_addr = inet_addr(SERVER);
    
    //start communication
    while(1)
    {
        printf("Enter message : ");
        gets(message);
        
        //send the message
        if (sendto(s, message, strlen(message) , 0 , (struct sockaddr *) &si_other, slen) == SOCKET_ERROR)
        {
            printf("sendto() failed with error code : %d" , WSAGetLastError());
            exit(EXIT_FAILURE);
        }
        
        //receive a reply and print it
        //clear the buffer by filling null, it might have previously received data
        /*memset(buf,'\0', BUFLEN);
        //try to receive some data, this is a blocking call
        if (recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen) == SOCKET_ERROR)
        {
            printf("recvfrom() failed with error code : %d" , WSAGetLastError());
            exit(EXIT_FAILURE);
        }
        
        puts(buf);*/
    }

    closesocket(s);
    WSACleanup();

    return 0;
}

Note: I didn't write all the code. I used a code that was already written (from the internet) but made some changes.

Does anyone know how to fix this problem? (using "WaitForMultipleObjects" on socket properly)

CodePudding user response:

The problem here that if the client try to send 2 messages during this five seconds, the first one is displayed while the second one will clock the function "WaitForMultipleObjects" forever.

Your server code has a race condition.

Your should call WSAResetEvent/ResetEvent before calling recvfrom, not afterwards. Otherwise, there is a possiblity that new data will arrive between the call to recvfrom and WSAResetEvent, setting the event object to signalled. In that case, WSAResetEvent will set the event back to non-signalled, causing you to lose the notification of new data being available.

Also, after reading data from a socket, if more data is available to be read, the event will automatically be set to signalled again, in order to indicate that more data is available. If you call WSAResetEvent afterwards, then you will set the event back to non-signalled, causing you to lose the notification of new data being available. This is probably the reason for the behavior you describe in the question.

You should rather call WSAResetEvent/ResetEvent immediately after WSAWaitForMultipleEvents/WaitForMultipleObjectsEx. See the documentation for the function WSAWaitForMultipleEvents for a code example (that example uses overlapped I/O instead of WSAEventSelect, though).

  • Related