Enchanter
Enchanter

Reputation: 117

Program Freezing Due To recvfrom Function Blocking Program Execution

Currently I am in the midst of trying to create a UDP peer-to-peer network program. I have a UDP class which encapsulates the data and functions necessary to set up the socket and port for a single connection to another client on the network and functions allowing the program to send and receive data packets across the network. The trouble is although second client application receives the data packet from the first client and updates its output accordingly, the first client does not receive the packet update sent from the second client. This is despite the fact that the networking code governing both applications is effectively the same. – I have called the bind() function for each application (which will eventually be run on different machines) as I read it is better if the machine is having to listen for incoming connections. Also there is an fd_set in both applications which should ensure that the program does not block on the recvfrom function and should continue if no message is received (but this is only ever the case when the applications are starting up). My question is: why is the recvfrom function halting the program execution in the UDP class?

Here is the code for the client:

#ifndef _UDPCLASS_H_
#define _UDPCLASS_H_

 #include <iostream>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string>
 #include <winsock2.h>
 #include "Player.h"
 using namespace std;

 //Client port and IP
 #define SERVERIP "127.0.0.1"
 #define    LISTENINGPORT 4444

class UDPClass
{

public:

//Functions
UDPClass();        //Constructor
~UDPClass();       //Destructor

struct Message
{
    int objectID;       //the ID number of the object
    int x, y;           //position
};

//Variables
SOCKET sock;
char message[MESSAGESIZE];      //Array of chars to store messages
sockaddr_in toAddr;             //The address of the client socket

//Function Prototypes
//Functions related to sending packets to other players
void Initialise();              //Set up client socket
void sendMessage();             //Send data from player via message struct to server 

//RECIEVING
//Functions related to recieving packets from other players
void recieveMessage();
void checkID(Message* mess);

//Variables
Message* playerStats;           //Variable to hold the player's coordinates and ID
Message* player2Stats;          //Variable to store player 2's statitstics such as position data in a message recieved from the server
PlayerClass* PlayerU;           //Instance of class player to transfer data into message
PlayerClass* PlayerO;           //Instance of player class to represent otehr player
private:
};

#endif

UDPClass .cpp file:

#include "UDPClass.h"

UDPClass::UDPClass()
{
//Initialises the socket to be a UDP socket
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == INVALID_SOCKET) {
    printf("socket failed with error: %ld\n", WSAGetLastError());
    WSACleanup();
}
PlayerU = NULL;
PlayerO = NULL;
}

UDPClass::~UDPClass()
{
closesocket(sock);
WSACleanup();
}

void UDPClass::Initialise()
{ 
// Fill out a sockaddr_in structure with the address that
// we want to send to.
toAddr.sin_family = AF_INET;
// htons converts the port number to network byte order (big-endian).
toAddr.sin_port = htons(LISTENINGPORT);
toAddr.sin_addr.s_addr = inet_addr(SERVERIP);

//Bind the socket to the address of this address.
if (bind(sock, (LPSOCKADDR) &toAddr, sizeof(toAddr)) != 0)
{
    printf("bind failed");
}
//Initialise an instance of type player class for the player of THIS client
PlayerU = new PlayerClass;
//NEW - in version 7.2 initialise an instance of the player class to store the data recieved in messages from other machines
PlayerO = new PlayerClass;

//Now we initialise a struct of type Message to contain data that we wish the other client to recieve 
playerStats =  new Message;
playerStats->objectID = PlayerU->getID();
//Put the players current position into Message struct containing data that will be sent to the server
playerStats->x = PlayerU->getX(); 
playerStats->y = PlayerU->getY();
printf("Control the player using the directional arrow keys ");
}

void UDPClass::sendMessage()
{
    fflush(stdout);
    playerStats->x = PlayerU->getX(); 
    playerStats->y = PlayerU->getY();

     int count =  sendto(sock,
                 (char*) playerStats, sizeof(Message), 0, (SOCKADDR *)&  toAddr, sizeof (toAddr));

    if(count == SOCKET_ERROR)
    {
        printf("ERROR: %d \n", WSAGetLastError());
    }
}

void UDPClass::recieveMessage()
{
    //Variables describing the struct that defines the sockets the    client is interested in listening to
     fd_set readable;
    FD_ZERO(&readable);
    FD_SET(sock, &readable);

    // The structure that describes how long to wait for something to happen.
     timeval timeout;
    // We want a 2.5-second timeout.
     timeout.tv_sec = 2;
     timeout.tv_usec = 500000;
     fflush(stdin);
    player2Stats = new Message;
    // Read a response back from the server (or from anyone, in fact).
    sockaddr_in fromAddr;
    int fromAddrSize = sizeof(fromAddr);
    int count = recvfrom(sock, (char*)player2Stats, sizeof(Message), 0,
                         (sockaddr *) &fromAddr, &fromAddrSize);


    //Call checkID to give player two representation latest updates
    checkID(player2Stats);

}

void UDPClass::checkID(Message* mess)
{
//Set the player stats of the other player this player is playing with to the values of the incoming message
    PlayerO->SetID(mess->objectID);
    PlayerO->SetX(mess->x);
    PlayerO->SetY(mess->y);
}

In the main application there is a continuous while loop which executes the network functions like so:

//Declare instance of a client to send and recieve messages
UDPClass client1;

//Initialise clients
client1.Initialise();
while(TRUE)
{
    //Update the 2nd player position
    client1.recieveMessage();
    updateOther(client1);

    client1.checkPlayPos(Player.x, Player.y);
    client1.sendMessage();
}

With the updateOther function definition looking something like this:

 void updateOther(UDPClass &theClient)
{
//Update the position of the 2nd player on screen by making the coordinates equal to data recieved from incoming message
Player2.x = theClient.PlayerO->getX();
Player2.y = theClient.PlayerO->getY();
}

In the main while loop if the two lines of code relating to the recieving of messages are commented out like so:

/*client1.recieveMessage();
updateOther(client1);*/

The program runs fine but if they are executed the program freezes so I am obviously doing something wrong with the recvfrom function - I was wondering if it was blocking?

Upvotes: 0

Views: 1716

Answers (1)

b4hand
b4hand

Reputation: 9770

recvfrom is always blocking by default unless your sockets are non-blocking.

You can use fcntl to use non-blocking sockets and then recvfrom will return EWOULDBLOCK instead of blocking.

Upvotes: 1

Related Questions