LinuxQuestions.org
Help answer threads with 0 replies.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices

Reply
 
Search this Thread
Old 12-17-2005, 09:04 AM   #1
clinux_rulz
Member
 
Registered: Nov 2005
Distribution: Gentoo
Posts: 51

Rep: Reputation: 16
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.
 
Old 12-17-2005, 10:31 AM   #2
clinux_rulz
Member
 
Registered: Nov 2005
Distribution: Gentoo
Posts: 51

Original Poster
Rep: Reputation: 16
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.

Last edited by clinux_rulz; 12-17-2005 at 10:33 AM.
 
Old 12-17-2005, 01:39 PM   #3
Cirdan
Member
 
Registered: Nov 2005
Distribution: Arch Linux
Posts: 84

Rep: Reputation: 15
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.
 
Old 12-17-2005, 01:58 PM   #4
Thinking
Member
 
Registered: Oct 2003
Posts: 249

Rep: Reputation: 30
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
 
Old 12-18-2005, 08:48 AM   #5
clinux_rulz
Member
 
Registered: Nov 2005
Distribution: Gentoo
Posts: 51

Original Poster
Rep: Reputation: 16
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???

 
Old 12-18-2005, 09:57 AM   #6
clinux_rulz
Member
 
Registered: Nov 2005
Distribution: Gentoo
Posts: 51

Original Poster
Rep: Reputation: 16
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.

Last edited by clinux_rulz; 12-18-2005 at 09:59 AM.
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
List total number of directories paraiso Linux - Newbie 5 04-18-2005 04:04 AM
Port number.... help rajsun Programming 2 04-12-2005 09:20 AM
How to check the network port number? sceadu Linux - Networking 2 11-15-2004 10:05 PM
how to do a http network install from a web server not on port 80 lothario Linux - Software 0 05-27-2004 04:25 AM
Command to list total number of files. WillieB_72 Linux - General 3 01-29-2003 09:25 PM


All times are GMT -5. The time now is 03:36 AM.

Main Menu
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration