LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   Network Server List from port number C/C++ (https://www.linuxquestions.org/questions/programming-9/network-server-list-from-port-number-c-c-393846/)

clinux_rulz 12-17-2005 09:04 AM

Network Server List from port number C/C++
 
Hi all,

I have been working on a platform independant 3d space fighter game which is almost completed. All I need to do now is the programing of the networking section.

I need to know how to write code that will retrieve a list of servers that a client can connect to. Say I have 2 or more servers binded to some ip addresses and use the port number 4567, how would I write code for a client to retrieve a list of ip addresses binded to port 4567?

I used some allegro for making the plugins for the input, sound and video. I do not think I can use allegro for the networking, it would be nice if examples given showed me how to code the client using SDL_net.

I have written some networking code before, but it was only for connecting the clients to the server when the ip address of the server was already known.

I know this type of networking is done in wesnoth (one of my favourite games), I check my synaptic package manager for the libraries it uses and found that it used SDL_net, so I think what I am after can be done in SDL_net. Posibly using UDP. I currently only know TCP.

Any help that anyone can give would be very much appreciated.

clinux_rulz 12-17-2005 10:31 AM

I just thought of a way this might be done, and that is too make the client connect to the special ip address of "255.255.255.255" with the same port number as the server, and then let the client send its ip address to the server, then the server sents its ip address to the client.

I read somewhere that 255.255.255.255 is a special ip address to connect to all local servers.

I'm not sure if this will work or not.

Any comments would be appreciated. This such as the range of port numbers I should selected from.

Cirdan 12-17-2005 01:39 PM

You need to create a "master server", which is how it is done in quake games. The client connects to this master server, then the master server sends a list to the client. Then on the other side each game server sends a heartbeat to the master server saying that its online, or giving the server its name and how many players are playing on that server.

Thinking 12-17-2005 01:58 PM

you could do a udp broadcast on a local network
1. client does udp broadcast to 255.255.255.255 to a specific port (e.g. 4567)
2. the master servers can respond to a specific port on the client

for a method which should work with the internet you need a master server like Cirdan said

clinux_rulz 12-18-2005 08:48 AM

I see, so using 255.255.255.255 only works on and over the local network, right?.

Could a connection over the net be done straight through an ip address, once an ip address is known?, because a master server sounds like a lot of work.

What I really want is to have the client be able to retrieve a list of servers on a local network.
The code below show how I currently plan to make the server work.
I dout the code would work yet, because I have not yet got around to debugging it, but the general ideas are still there.

The interface for the base class of the GameServer:
Code:

///////////////////////////////////////////////////////////////////////////////
// $Id$
// $Revision$
///////////////////////////////////////////////////////////////////////////////

#ifndef _SERVER_H
#define _SERVER_H

#include <list>
#include <string>
#include <stdint.h>
#include <SDL/SDL_thread.h>
#include <SDL/SDL_mutex.h>
#include "ClientSocket.h"
#include "ServerSocket.h"

// Really handy server class, if a client leaves with this server, they can
// still come back into the game while the same game is still running.
class Server {
  public:
    Server(uint16_t port, uint16_t maxConnections = 8);
    virtual ~Server();
 
    // Methods
    virtual void Kick(uint32_t ip);
    virtual void Band(uint32_t ip);
    virtual void Unband(uint32_t ip);
 
    // Events
    virtual void OnRecieve(Socket* pSender, std::string const& data) = 0;
 
  protected:
    ServerSocket        mServerSocket;
    int                mMaxConnections;
    volatile bool      mbAlive;
    SDL_Thread**        mpThreads;
    std::list<Socket*>  mConnections;
    SDL_mutex*          mpConnectionsMutex;
    std::list<uint32_t> mBandConnections;
    SDL_mutex*          mpBandConnectionsMutex;
 
    static int HandleClient(void* pArgs);
};

#endif // _SERVER_H

///////////////////////////////////////////////////////////////////////////////
// $Log$
///////////////////////////////////////////////////////////////////////////////

// vim:ts=2:sw=2:sts=2:et

The implementation for the base class of the GameServer:
Code:

///////////////////////////////////////////////////////////////////////////////
// $Id$
// $Revision$
///////////////////////////////////////////////////////////////////////////////

#include <iostream>
#include "Server.h"

using namespace std;

//-----------------------------------------------------------------------------

int Server::HandleClient(void* _pServer)
{
  try {
    Server* pServer = (Server*)_pServer;
    Socket client;
    while (pServer->mbAlive) {
      // Wait to connect to a client, if it can not connect then reloop to see
      // if the server is still alive, then try again. This accept statement is
      // excepted to fail serveral times due to time out errors while waiting
      // for the client to be ready.
      try {
        pServer->mServerSocket.Accept(&client);
      } catch (string const& e) {
        //cout << "Server::HandleClient: " << e << endl;
        continue;
      }
     
      // Make sure that this client is not already connected.
      SDL_mutexP(pServer->mpConnectionsMutex);
      {
        list<Socket*>::const_iterator i;
        for (
            i = pServer->mConnections.begin();
            i != pServer->mConnections.end();
            ++i
            ) {
          if (client.ipAddress() == (*i)->ipAddress()) {
            break;
          }
        }
        if (i != pServer->mConnections.end()) {
          client.Close();
          continue;
        }
      }
      SDL_mutexV(pServer->mpConnectionsMutex);
     
      // Check if the client is in the band list, if so disconnect him/her and
      // be ready to connect to a new client.
      SDL_mutexP(pServer->mpBandConnectionsMutex);
      {
        uint32_t ip = client.ipAddress();
        list<uint32_t>::const_iterator i;
        for (
            i = pServer->mBandConnections.begin();
            i != pServer->mBandConnections.end();
            ++i
            ) {
          if (ip == *i) {
            try {
              client.Close();
            } catch (...) {
            }
            break;
          }
        }
        if (i != pServer->mBandConnections.end()) {
          continue;
        }
      }
      SDL_mutexV(pServer->mpBandConnectionsMutex);
     
      // Add the connected client to the list of clients.
      SDL_mutexP(pServer->mpConnectionsMutex);
      pServer->mConnections.push_back(&client);
      SDL_mutexV(pServer->mpConnectionsMutex);
     
      // Perform the message loop until either the client disconnects ("BYE!!!")
      // or until the server is not alive (i.e. it disconnects.).
      while (pServer->mbAlive) {
        string data;
        client.Recieve(&data);
        if ((data == "BYE!!!") || (data.length() == 0)) {
          break;
        } else {
          pServer->OnRecieve(&client, data);
        }
      }
     
      // At this state the client is no longer connected to the server, so
      // remove it from the connections list.
      SDL_mutexP(pServer->mpConnectionsMutex);
      {
        list<Socket*>::iterator i;
        for (
            i = pServer->mConnections.begin();
            i != pServer->mConnections.end();
            ++i
            ) {
          if (*i == &client) {
            break;
          }
        }
        if (i != pServer->mConnections.end()) {
          pServer->mConnections.erase(i);
        }
      }
      SDL_mutexV(pServer->mpConnectionsMutex);
     
      // Close the client.
      client.Close();
    }
    return 0;
  } catch (string const& e) {
    cerr << "Server::HandleClient: " << e << endl;
    return 1;
  }
}

//-----------------------------------------------------------------------------

Server::Server(uint16_t port, uint16_t maxConnections):
  mServerSocket(port), mMaxConnections(maxConnections), mbAlive(true),
  mpThreads(new SDL_Thread*[maxConnections])
{
  for (int i = 0; i < mMaxConnections; ++i) {
    mpThreads[i] = 0;
  }
  mpConnectionsMutex = SDL_CreateMutex();
  mpBandConnectionsMutex = SDL_CreateMutex();
  for (int i = 0; i < mMaxConnections; ++i) {
    mpThreads[i] = SDL_CreateThread(Server::HandleClient, this);
  }
}

//-----------------------------------------------------------------------------

Server::~Server()
{
  // Tell each of the clients that the server is shutting down.
  {
    list<Socket*>::iterator i;
    for (i = mConnections.begin(); i != mConnections.end(); ++i) {
      (*i)->Send("BYE!!!");
    }
  }
 
  // Flag that the server is shutdown, and wait for the connection listening
  // threads are done.
  mbAlive = false;
  for (int i = 0; i < mMaxConnections; ++i) {
    int status;
    SDL_WaitThread(mpThreads[i], &status);
  }
 
  SDL_DestroyMutex(mpConnectionsMutex);
  SDL_DestroyMutex(mpBandConnectionsMutex);
  delete [] mpThreads;
}

//-----------------------------------------------------------------------------

void Server::Kick(uint32_t ip)
{
  SDL_mutexP(mpConnectionsMutex);
  list<Socket*>::iterator i;
  for (i = mConnections.begin(); i != mConnections.end(); ++i) {
    if (ip == (*i)->ipAddress()) {
      (*i)->Close();
      break;
    }
  }
  if (i != mConnections.end()) {
    mConnections.erase(i);
  }
  SDL_mutexV(mpConnectionsMutex);
}

//-----------------------------------------------------------------------------

void Server::Band(uint32_t ip)
{
  SDL_mutexP(mpBandConnectionsMutex);
  list<uint32_t>::const_iterator i;
  for (i = mBandConnections.begin(); i != mBandConnections.end(); ++i) {
    if (ip == *i) {
      break;
    }
  }
  if (i == mBandConnections.end()) {
    mBandConnections.push_back(ip);
  }
  SDL_mutexV(mpBandConnectionsMutex);
  Kick(ip);
}

//-----------------------------------------------------------------------------

void Server::Unband(uint32_t ip)
{
  SDL_mutexP(mpBandConnectionsMutex);
  list<uint32_t>::iterator i;
  for (i = mBandConnections.begin(); i != mBandConnections.end(); ++i) {
    if (ip == *i) {
      break;
    }
  }
  if (i != mBandConnections.end()) {
    mBandConnections.erase(i);
  }
  SDL_mutexV(mpBandConnectionsMutex);
}

//-----------------------------------------------------------------------------

///////////////////////////////////////////////////////////////////////////////
// $Log$
///////////////////////////////////////////////////////////////////////////////

// vim:ts=2:sw=2:sts=2:et

I do not believe I implemented any of this in a standard way, I just took a guess at how it could be done, I know for shore that my Kick() code is rather bad, as it relies on a read error from the clients Recieve() by closing while it is being read to kick them off.

What I plan on doing for the client is connect to the ip 255.255.255.255 with the port number of say 4567, and hope it will connect to my server, which is where the client and server will shake hands exchanging information. So that the clients can get their server lists, and the servers can get the clients to put in the Loby. I have a feeling thow that this will not work now, since I used the ip address 255.255.255.255 already to create my server,
as can be seen in the following code. The INADDR_ANY stores 0xFFFFFFFF (255.255.255.255). The code for reading and writing data is in the Socket class it inherits from.

Code:

///////////////////////////////////////////////////////////////////////////////
// $Id$
// $Revision$
///////////////////////////////////////////////////////////////////////////////

#include <string>
#include "ServerSocket.h"

using namespace std;

//-----------------------------------------------------------------------------

ServerSocket::ServerSocket(uint16_t port): mPort(0)
{
  IPaddress ip;
  ip.host = INADDR_ANY;
  ip.port = mPort;
  mSocket = SDLNet_TCP_Open(&ip);
  if (mSocket == 0) {
    throw string("ServerSocket::ServerSocket: ") += SDLNet_GetError();
  }
}

//-----------------------------------------------------------------------------

ServerSocket::~ServerSocket()
{
}

//-----------------------------------------------------------------------------

void ServerSocket::Accept(Socket* pSocket)
{
  pSocket->mSocket = SDLNet_TCP_Accept(mSocket);
  if (mSocket == 0) {
    throw string("ServerSocket::Accept: ") += SDLNet_GetError();
  }
}

//-----------------------------------------------------------------------------

///////////////////////////////////////////////////////////////////////////////
// $Log$
///////////////////////////////////////////////////////////////////////////////

// vim:ts=2:sw=2:sts=2:et

Does anyone have a better way of doing what I tring to do???

:newbie:

clinux_rulz 12-18-2005 09:57 AM

Sorry it turns out INADDR_ANY actually holds 0.0.0.0, I got it mixed up with INADDR_BROADCAST.

I think I have seen something like this before, if i used 169.254.0.0 as the server ip, does that mean that any clients that has an ip that starts with 169.254 can connect to this server?, sort of like a bitmask.

Like if <IP> & <MASK> == <MASK>, then ip is on the same subnet.


All times are GMT -5. The time now is 12:27 AM.