0

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?

Enchanter
  • 117
  • 1
  • 10
  • This is unrelated to your problem, but you shouldn't use leading underscores with a capital for include guard macros as those identifiers are reserved http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier – b4hand Feb 20 '15 at 02:47
  • 1
    Simply declaring an fd_set and a timeval struct doesn't do anything; if you want to use those things, you need to pass them in to a select() call. select() will then return if the socket becomes readable (or when the timeout is reached), and you can use the FD_ISSET() macro to find out if your socket is readable or not. (Note that even if FD_ISSET() returns true, it can happen that recvfrom() will still block under certain circumstances, so it's best to set the socket to be non-blocking anyway) – Jeremy Friesner Feb 20 '15 at 07:02
  • And how would you set the socket to be non-blocking? – Enchanter Feb 20 '15 at 13:09

1 Answers1

1

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.

b4hand
  • 9,550
  • 4
  • 44
  • 49
  • Thanks but in Winsock the fcntl function has been replaced by the function - WSAAsyncSelect() - this function sets the socket to be nonblocking, I've used it and the program is not freezing any more. – Enchanter Feb 21 '15 at 19:40