SFML community forums

Help => Network => Topic started by: EiTkoCaT on December 18, 2010, 11:51:27 pm

Title: Handling Multiple TCP Connections
Post by: EiTkoCaT on December 18, 2010, 11:51:27 pm
Hey,

I have a problem handling multiple TCP connections on a C++ app that uses SFML library to send and read TCP messages.

Let's say I need to have 5 TCP connections to the server once (can be also 10K, just for example..). When client 1 sends a message, the server process it and then should send a message to clients 2-5, let's say in that case that he should send the same message.
How can I do that?

Thanks!
Title: Handling Multiple TCP Connections
Post by: nulloid on December 19, 2010, 05:43:49 am
What is your problem exactly?

I write a server for a game for about a month now. Here is how I do:

I have a [Server], [ClientPool] and a [ClientThread] class. If someone joins to the server, it creates a ClientThread for it, and drops it in the ClientPool. Now, if a client sends a message, the appropriate ClientThread calls the right method of the server (let's call newMessage(string message, ClientId from)), which takes care of things must be done. E.g. sending that message to every other client. (Well, this is not exactly how I do, but just simplified it for you. I'm in the middle of a redesigning process, creating message objects, command objects, etc, etc. :))

Was this answer helpful? :)
Title: Handling Multiple TCP Connections
Post by: EiTkoCaT on December 19, 2010, 09:22:45 pm
It's a server game. As I said, lot of clients needs to get connected.
When I need to do something like what I need, the user is always opens the first chat. But I heard that I can't connect multiple chats and keep them alive and also connect with each other (eg. send the data I got from client 1 to 2, 3, 4, 5), and I have to always close the chats, store the IP loged in into a DB (non-sql, of course..) and when I need to send a message to one of the clients I must start a new chat from the server to the client.

1. Is that correct? There isn't a way keeping alive lot of TCP chats the same time and connect between them?
2. If it is, I think I have a problem here. If the server needs to open the chat, then the client needs to listen to it always. But when the client connected to a wireless and there's a fiewwall, I can't connect him, can I? How do you solve that problem?
Title: Handling Multiple TCP Connections
Post by: tntexplosivesltd on December 19, 2010, 10:16:13 pm
Using the Socket Selector. It's a very useful tool for multiple connections.
http://www.sfml-dev.org/tutorials/1.6/network-selector.php

With the firewall, that's not problem with SFML. It's the firewall. The port you are using needs to be forwarded/allowed in the firewall settings.
Title: Handling Multiple TCP Connections
Post by: EiTkoCaT on December 19, 2010, 10:50:08 pm
Oh, thanks! I didn't knew about network selector. But how can I do with that what I wanted in first place (my first message)?

Thanks!
Title: Handling Multiple TCP Connections
Post by: tntexplosivesltd on December 19, 2010, 11:02:58 pm
So you effectively want to broadcast the message to everyone but the sender (and of course the listener)?
Title: Handling Multiple TCP Connections
Post by: EiTkoCaT on December 19, 2010, 11:09:30 pm
Yeah, because I can go on from there. It won't be like that in the game, but after I'll see a working code for broadcast the message to everyone but the sender, I'll know how to build what I want exactly. So yes :D

Thanks..!
Title: Handling Multiple TCP Connections
Post by: tntexplosivesltd on December 19, 2010, 11:12:45 pm
Right.
1. When a client connects, add their socket to a client array.
2. When a message comes in, loop through the client array sending the message to everyone except the person who sent it (test the socket of the client who sent the message against the array of clients to find out who to skip)
Title: Handling Multiple TCP Connections
Post by: EiTkoCaT on December 19, 2010, 11:16:39 pm
That's it!! Client array! How can I do that? I'm pretty new in C++, sorry for that, but yeah :X
I never worked with C++ and the client isn't written in C++, but I found SFML to be the best network library so I'm using it.

Also in step 2, how can I access the sendes's IP? I mean, all I see here, in that selector is (in the SFML sample):
                sf::Packet Packet;
                if (Socket.Receive(Packet) == sf::Socket::Done)
                {
                    // Extract the message and display it
                    std::string Message;
                    Packet >> Message;
                    std::cout << "A client says : \"" << Message << "\"" << std::endl;
                }
Title: Handling Multiple TCP Connections
Post by: tntexplosivesltd on December 19, 2010, 11:36:36 pm
I don't see the need to access the sender's IP address when you recieve a message from them. You can sort of access their IP when they first connect:
Code: [Select]
       sf::IPAddress Address;
        sf::SocketTCP Client;
        Listener.Accept(Client, &Address);
        std::cout << "Client connected ! (" << Address << ")" << std::endl


For a dynamic array of Sockets (if you don't have a maximum allowable number of clients), look at using a vector:
Code: [Select]

#include vector
//other includes here

std::vector<sf::SocketTCP> Clients;


If you have a maximum number of clients:
Code: [Select]

sf::SocketTCP Clients[10];

Note that this example has a max of 10 clients - but you'll need to have checks in place. This only limits the size of the data structure.
Title: Handling Multiple TCP Connections
Post by: EiTkoCaT on December 19, 2010, 11:40:50 pm
That's exactly the problem, I need to know from what IP I'm getting every message. How can I test which IP sent me the message and skep him when I don't know any message's sender IP?
Title: Handling Multiple TCP Connections
Post by: tntexplosivesltd on December 19, 2010, 11:43:43 pm
Instead of skipping the IP, skip the socket. It's a lot easier.
e.g. from the SFML site:
Code: [Select]

    if (Socket == Listener)
    {
        // If the listening socket is ready, it means that we can accept a new connection
        sf::IPAddress Address;
        sf::SocketTCP Client;
        Listener.Accept(Client, &Address);
        std::cout << "Client connected ! (" << Address << ")" << std::endl;

        // Add it to the selector
        Selector.Add(Client);
    }


Here, they test the actual listener socket. You'll probably need pointers and stuff, but it's the same idea. Some of it you'll have to work out yourself. :wink:
Title: Handling Multiple TCP Connections
Post by: EiTkoCaT on December 19, 2010, 11:45:50 pm
Oh, now I get it...
Well, you really helped me! Thank you very much!
Title: Handling Multiple TCP Connections
Post by: tntexplosivesltd on December 19, 2010, 11:46:17 pm
Or, you could store an array of the IPs instead of the sockets. Then you can access the IP of the sender, and compare it to the array.
Title: Handling Multiple TCP Connections
Post by: EiTkoCaT on December 21, 2010, 08:19:55 pm
I don't undertand an important thing here.
Let's say I currently have 3 clients (could be 10K either..). Client A just connected to the server and send a log in request as a message with username and password. Now user B and C also connected and loged in. Surely they also got a OK message. Now, user B wants to change he's status from "Happy" to "Sad", so he sends a message to the server. So now I'm gonna need to send to clients A and C that client B's status changed. How can I send them a thing like that? How can I identify a client who is sending a message? The message that client B sends to the server should be, for example:
status sad
and then the server should send him back:
stats changed
and the server needs to send to user A and C:
user B status change sad

Just an example. So how can I do that? Thanks!
Title: Handling Multiple TCP Connections
Post by: tntexplosivesltd on December 23, 2010, 12:58:28 am
I'm not sure what you want to know... It's exactly the same as sending something to all clients except the sender. Skip over then in a client array.
Title: Handling Multiple TCP Connections
Post by: EiTkoCaT on December 25, 2010, 02:17:24 pm
Ok, so now it's not a problem to send all the connected users something I want.

But here's my problem now.. The server needs to handle that:
User A sends login request -> sent OK. Now the user is waiting.
User B sends login request -> sent OK. Now the user is waiting.
User C sends login request -> sent OK. Now the user is waiting.
Now user A just sent a load menu request. The server needs to find the menu of user A from the DB, and to send it to user A.
Now user B just sent a load menu request. The server needs to find the menu of user B from the DB, and to send it to user B.
Now user A just choosed the Enter World option from the menu. Then he sends to the server 'enter world'. The server is now searching in the DB where is in the world user A located. Then he sends to user A he's location.
Now user C just sent a load menu request. The server needs to find the menu of user C from the DB, and to send it to user C.
Now user C just choosed the Enter World option from the menu. Then he sends to the server 'enter world'. The server is now searching in the DB where is in the world user C located. Then he sends to user C he's location.
Now user A just jumped in the map. The server searching if there is another user in the same map. There is! User C. He sends to user C 'user A just jumped'


How can I handle that kind of things? It makes me crazy..! How can I recognize in the selector the reads the message what user sent the message and by the user search in the database?

Here's what I tried to do without a selector, but you can find my problem at http://www.sfml-dev.org/forum/viewtopic.php?p=25051#25051. It should just log in a user and wait for a load menu request. I can't handle here number of users trying to connect.


Code: [Select]
#include <iostream>
#include <sstream>
#include <string>
#include <SFML/Network.hpp>
#include <SFML/System.hpp>
#include "client/dbclient.h"
#include "Mongo.cpp"
#pragma comment( lib, "mongoclient" )

std::vector<sf::SocketTCP> Users;
std::vector<sf::IPAddress> UsersIP;
   sf::SocketTCP Server;
   sf::SocketTCP Client;
   sf::IPAddress ClientAddress;

int connectedClients = 0;
mongo::DBClientConnection c;
bool server_running;

bool LoginSeccedd;
void serverTCP(void* UserData);
sf::Thread ThreadTCP(&serverTCP);

using namespace std;


void startUserrt(sf::SocketTCP Client, sf::IPAddress ClientAddress) {

   /*Users.push_back(Client);
   UsersIP.push_back(ClientAddress);

   connectedClients++;
   
   for (int i=connectedClients; i<connectedClients;i++)
   {
      cout << ClientAddress[connectedClients] << endl;
   }*/

   char Message[256];
   std::size_t Received;
   if (Client.Receive(Message, sizeof(Message), Received) != sf::Socket::Done)
      return;
   // Show the message
   
   string stringAll, str3, str4;
   stringAll = Message;
   size_t found;
   size_t total = 0;
   string current_string[3];

   for (int i=0;i<3;i++)
   {
      cout << &Message[total] << endl;
      found = strlen(&Message[total]);
      current_string[i] = &Message[total];
      total += found + 1;
   }

   if (current_string[0] == "7L")
   {
      // Login
      cout << "Login requst" << endl;
      mongo::BSONObjBuilder query;
      query.append( "email" , current_string[1] ).append( "password", current_string[2] );
      mongo::BSONObj res = c.findOne( "test.users" , query.obj() );
      string userID = res["email"];
      cout << userID << endl;
   //   session_userEmail = userID;
   //   session_userID = res["_id"];
      if (userID == "EOO")
      {
         char ToSend2[] = "8L 0 not-found\0";
         if (Client.Send(ToSend2, sizeof(ToSend2)) != sf::Socket::Done)
            return;
         cout << ToSend2 << endl;
      }
      else
      {
         char ToSend2[] = "8L 1 1\0";
         if (Client.Send(ToSend2, sizeof(ToSend2)) != sf::Socket::Done)
            return;
         cout << ToSend2 << endl;
         LoginSeccedd = true;
      }
   }
   else if (current_string[0] == "7R")
   {
      // Register
      cout << "Register requst" << endl;
       mongo::BSONObjBuilder query;
      query.append( "email" , current_string[1] ).append( "password", current_string[2] );
      mongo::BSONObj res = c.findOne( "test.users" , query.obj() );
      string userID = res["email"];
      cout << userID << endl;
      if (userID == "EOO")
      {
         char ToSend2[] = "8R 1\0";
         // INSERT USER!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!***********************************************************************
         if (Client.Send(ToSend2, sizeof(ToSend2)) != sf::Socket::Done)
            return;
         cout << ToSend2 << endl;
         LoginSeccedd = true;
      }
      else
      {
         char ToSend2[] = "8R 0 3\0";
         if (Client.Send(ToSend2, sizeof(ToSend2)) != sf::Socket::Done)
            return;
         cout << ToSend2 << endl;
      }
   }
   else
   {
      cout << "Unknow Request" << endl;
   }


   if (LoginSeccedd = true)
   {
      char Message[256];
      std::size_t Received;
      if (Client.Receive(Message, sizeof(Message), Received) != sf::Socket::Done)
         return;
      cout << Message << endl;
      if (Message == "7MM")
      {
         // Load Main Menu - send character information
         /*string ToSend2 = "8MM";
         mongo::BSONObjBuilder query;
         query.append( "user_id" , session_userID );

         auto_ptr<DBClientCursor> cursor =
         c.query("test.characters", query.obj() );
         while( cursor->more() ) {
            BSONObj p = cursor->next();
            cout << p["name"] << endl;
            ToSend2 += p["name"];
         }
         cout << ToSend2 << endl;*/
           
      }
      else if (Message == "7CC")
      {

      }
         
   }
}


void serverTCP(void* UserData) {
   while (server_running)
   {
      // Wait for a connection
       if (Server.Accept(Client, &ClientAddress) != sf::Socket::Done)
         return;
      cout << "Client connected : " << ClientAddress << std::endl;
      startUserrt(Client, ClientAddress);

            /*User *current;
         current = new User;
      current->startUserrt(Client);*/
   }
}




void startServer() {
   server_running = true;
       if (!Server.Listen(2435))
      return;
   
   cout << "Server is listening to port " << 2435 << ", waiting for connections... " << std::endl;
   ThreadTCP.Launch();
}

int main()
{
     c.connect("localhost"); //"192.168.58.1");
    cout << "connected ok" << endl;
startServer();

    // Wait until the user presses 'enter' key
    std::cout << "Press enter to exit..." << std::endl;
    std::cin.ignore(10000, '\n');
    std::cin.ignore(10000, '\n');

    return EXIT_SUCCESS;

}
Title: Handling Multiple TCP Connections
Post by: tntexplosivesltd on December 26, 2010, 09:44:40 am
Use a 2-dimensional array, e.g. Clients[][]. In Clients[n][0], store the sockets of the clients. In Clients[n][1], store the corresponding username that was sent from that socket at login, when login succeeded. Then, you can search for the socket, get the corresponding username, and use that as you want.
Title: Handling Multiple TCP Connections
Post by: tntexplosivesltd on January 02, 2011, 10:33:47 am
Have you managed to get it to work yet?
Title: Handling Multiple TCP Connections
Post by: Grimshaw on January 07, 2011, 09:09:33 pm
Just something (maybe)not directly related to the topic:

If you create one thread per client, and there is a lot of clients, per say, 100... Don't you think 100 threads concurrently could be a bottleneck?

I'm not 100% sure of this, but i REALLY think that would simply waste the performance : ) Threads can be your friend, but also your enemy ;D
Title: Handling Multiple TCP Connections
Post by: Spodi on January 07, 2011, 09:49:41 pm
If you want to have a large number of high-performance connections, then yes, thread-per-socket is a horrible way to go. You'll want to use a different library for your networking, and use IOCP (when available) or asynchronous sockets to let the OS manage the threading for you and allow you to use event-based sockets.
Title: Handling Multiple TCP Connections
Post by: Grimshaw on January 07, 2011, 10:01:44 pm
Exactly, i use ENET ;D
SFML Network is also awesome, but not for fast paced gaming in my opinion : ) I use it the most for HTTP stuff, like transfering files and apples : )