Advertisement

Retrieving the list of clients from the c ++ socket

Started by May 07, 2018 05:38 PM
13 comments, last by hplus0603 6 years, 6 months ago

I have a question about downloading the list of connected clients of the c++ sockets, how to send the first connected client information about the other one who connected right after it?
the first client connects, writes the socket to std::vector <int> connection using connection.push_back (socket)
starts a thread, then it increases by one int clients. When the second client connects, puts socket into the vector, runs the thread, increases clients++ and has all the information about the first client, and the client first has no information about the second client, the vector connection does not change, nor does the clients increase.
The second client can send the message to himself and the client first using the for loop (int i = 0; i <clients + 1; i ++) send ("message"), but the client can only do the same for himself, how to deal with it?

global variables:


    std::vector <int> connection;
    int clients;

code:


    size = sizeof(client_addr);
    client = accept( server, ( struct sockaddr * ) & client_addr, & size );
    connection.push_back(client);
    //thread
    if(!fork())
    {
        Client hClient;
        hClient.Thread();
    }
    //thread
    clients++;

 

There are a few problems here.

First, fork() doesn't just create a new thread, it creates a new process.

Second, creating a thread OR a process per player is generally way too inefficient. (Although processes may be reasonable, if you want a very strong separation guarantee for data. Most games don't want to pay this cost.)

Third, you are not properly capturing the return value of fork(). You will need the process ID, because at some point, you need to wait() for the process, to avoid creating zombie processes. And fork() may return -1, which you need to treat special, because sending signals to pid -1 is a Really Dangerous Thing (tm.)

Look into select(). This lets you accept and receive data from many players at one time, without blocking the game loop. You can then send messages to all clients by simply iterating over your array of sockets. If you need to serve more than, say, 1000 players in the same process, then select() may start becoming a bottleneck; at that point you can look at various evented I/O mechanisms (such as boost::asio.) However, at that player count, you might also want to stop using TCP and use UDP instead, because you can get more efficiency out of a UDP protocol. You'd then just sit in a loop, reading messages off a single UDP socket, and you can allocate threads to your other players whatever way you want. (Typically, one thread per core, and players handled in a first-come, first-serve manner.)

enum Bool { True, False, FileNotFound };
Advertisement

@hplus0603

    I think I'm doing it wrong, can you help me?
    
   I made this code, but the accept still blocks the loops of my game for new clients:
    


if( listen( server, 50) < 0)
{
  perror("listen");
  exit(1);
}


fd_set read_fds;
int fdmax = server;

while(1)
{
  FD_ZERO( &read_fds );
  FD_SET( server, &read_fds);

  select( fdmax + 1, & read_fds, NULL, NULL, NULL);
  for(int i = 0; i < fdmax; i++)
  {
    if(FD_ISSET( server, & read_fds ) )
    {
      size = sizeof( client_addr );
      if( ( client = accept( server, ( struct sockaddr * ) & client_addr, & size ) ) < 0 )
      {
        perror("accept");
      }
      if (client > fdmax )
        fdmax = client;
      
      connection.push_back(client);
      hClient.Thread();
      clients++;
    }
  }
}

 

Blocking happens in the select() call in your code, because you're not specifying a timeout.

Also, what is hClient? Is it a single object instance? Should you really call a Thread() function on the same object for each connecting client? Does the Thread() function block, or spawn a new thread, or what?

There are tons of select() tutorials on the web, which go into much more detail than I can do in a forum post format.

enum Bool { True, False, FileNotFound };

hClient is my class, and the Thread is a function of the hClient class, this function does all my game.

This feature receives and sends packets, all the time is enabled until the client leaves the game

So you have TWO parts that block your main thread: the select() call, and the Thread() call.

You should re-write your logic as a state machine, where everything is queued, and nothing blocks.

If you want to create a thread per client (not generally a good idea) you need to call pthread_create() before you call the Thread function, but remember that the list of sockets is globally mutable, as is your general world state, so you would need to protect any mutable shared state with a mutex. (This is one of many reasons why thread-per-client is bad.)

 

enum Bool { True, False, FileNotFound };
Advertisement

Would you give any advice, or send a guide on how to create such a state machine? If the threads are bad, I will choose something else. 

This is a huge topic, which you have to learn through practice, and reading through the code of other games.

The source code for a number of popular networked games is available for download. You can look at the Quake or Doom III series, or you can look at the Unreal Engine, for examples of things that have worked well.

enum Bool { True, False, FileNotFound };

There is no simpler way to do this without state machines?

Not really.

How much experience do you have in game programming? And in networking? Because game networking programming is a skill in itself, that requires that you first understand game programming, and network programming, separately.

If you haven't built some networked application, and some game, already, perhaps you could build a non-networked game and a non-game network application first?

 

enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement