network design

Started by
12 comments, last by GameDev.net 18 years, 3 months ago
i know how sockets work, how the functions work etc.. i'm here to find out the best way i should set up a server for my online game. first i'll say that i made the game before in an interpreted language but it was slow, so i'm now making it in c++. that being said i know most of what i'm going to need, and hopefully you'll be able to tell me the best techniques to use to go about programming my server. the server sends everything tcp (even movement, trust me the way i do it is almost as fast as udp). the server doesn't do a whole lot of work besides query from a database, and control movement. it does control mobs' ai, but the ai executes fast; there really isn't much to it. the packets that the server send are usually fairly small, about 10-50 bytes worth of data. the movement packets however are fairly large, about 50-500 bytes worth of data. finally the server usually serves about 5-20 people at a time. i was thinking of just having an array of sockets and doing all the networking in one thread, doing all the querying in another thread, and everything else in the main thread. i don't really know a whole lot about multithreading, i'm still learning, so i don't know if thats the best way to go or not. what do you think? thank you for your time.
Advertisement
Hi,
That actually is really a matter of taste! I do not like threads, here is how I would do it.
Set up your sockets. Than, in the main loop do something like:

while(running)
{
DoOtherStuff();
if(DataWaiting())
GetAndProcessData();
}

You can check if data is waiting in your sockets using select. You you want your application to do nothing until data is coming, you can also give select a timeout value.

As I said, other prefer threads. It is a matter of opinion. I prefer this way because I find it to be less complicated.

Nathan
If a movement packet is 500 bytes, and you have 20 clients, then the movement data for 20 clients (assuming one packet/second) is 10 kilobytes per client -- times 20! Meaning you need 200 kilobytes (about 2000 kilobits with framing) per second of upstream bandwidth from the server.

I would let the kernel buffer the data, and do a single-threaded server, much like LonelyStar suggests.

while( running ) {  select()  read from all ready sockets  process messages from those sockets  run one simulation step  write to all sockets that have data pending}


The kernel will buffer before the read, and the stuff in write, so it'll actually receive/send the network messages asynchronously from you issuing the calls.

Note that you usually have to worry about the output buffer filling up, and the write blocking, unless you select() for write or use non-blocking sockets, and do your own queuing.
enum Bool { True, False, FileNotFound };
the movement packet is 10 bytes per charecter within a set distance of the player. there are usually only about 5-10 characters within the set distance, but when a player is in a crowded area with lots of npc it can get out of hand. the movement packet for 20 players usually only takes 12-20kb bandwidth per second. its not that out of hand.

anyways, i've read in several threads that a thread for the network is the way to go. but it seems like here you have a different oppinion. what are my advantages and disadvantages for using a thread for networking? i thought it was suppose to be much faster using a thread for networking, but i guess i could be wrong.
If you have a second CPU sitting around doing nothing, and a good mechanism of handing off data between the processing thread and the networking thread, then the networking thread may give you some additional performance (because of the CPU parallelism), at the cost of synchronization and context switching (cache thrashing).

If you have a single CPU, don't worry about threads, because there's only one piece of hardware doing the work.
enum Bool { True, False, FileNotFound };
ok, well without threads how would i listen for connections and do everything else? i could set it to non block, is that what i have to do?
select() is your friend. You don't necessarily need non-blocking sockets if you have select() (although it still helps making the writing case slightly simpler).
enum Bool { True, False, FileNotFound };

recommend a multi-threaded pool approach with appropriate dispatcher to handle tasks.

Would suggest using async sockets with a hidden window for receiving messages or overlapped io.

As for network speed , the problem with TCP is that TCP windows cause real-time networking delays. If you have TCP up and running you can always modify it to use UDP at a latter stage.

good luck
Cubex
I agree with hplus. Either use a signle thread for everything, or if you have 2 processors, or a hyperthreading processor then do a thread for the network, and a thread for the game.

Your network card and IP stack will work asynchronously from the process your game is running in. Odds are, the game itself is only going to process input once in it's loop, and send output back to the network once in it's loop. So having 2 threads really doesn't save you much latency. (It could save you the time it takes to read data from a socket, because that would be happening at the same time the game loop is running), but thats really not a very large chunk of time.

I do not think you will gain anything by having a thread pool for network. Threads work well when one thread stops doing something for a while, and gives the other threads time to process. So it would go somewhat like this:

Thread 1:

anything on socket 1? nope? ok.. sleep;
Thread 2 starts processing

Thread 2:
anything on socket 2? yep? ok, read from socket.. sleep;

Thread3:
anything on socket 3?
... etc..

you get the point.. There is no real point in having more threads than the processor can simultaneously handle. if the processor is single core, doing a select() and then cycling through your fd list is probably FASTER than dealing with the context switch and thread safe mechanisms of the threaded version.


Single threaded model:

Check to see which sockets have data waiting.

for each socket:

is there data waiting? yes? read. no? continue;


Threads are neat concepts, but I see them being abused so much. As hardware moves to be more multicore, and suports multiple native threads, then these models will be much more practical, but you still don't want to have more threads than your hardware can natively support.





If you use blocking calls, and select(), you will get the same responsiveness as asynchronous sockets, but without the added headache that is associated with them.
Yes I agree, that single threaded with select is a pretty good way to go. Not only does it work perfectly well and as the other said even with two cpu it's doubtful wether or not you'll see improvements but you have to consider how much more simple the logic is going to be. Multiple threads can be quite a headache. Also, we do have a dual processor machine but we're using the second cpu to do more useful things like running a part of the simulation or handling incremental saves.

This topic is closed to new replies.

Advertisement