Single Threaded UDP

Started by
18 comments, last by Megahertz 18 years, 8 months ago
Currently my servers for my small MMO project have been written using multiple threads. I'm considering rewriting the network portion of it so that it all operates inside a single thread (The databse stuff will still have to be threaded so that queries can be done asynchronosly). Right now the loop that recieves data off the listening socket runs in it's own thread. It sits on a select call until data shows up and once it does the select call falls through and the incomming data is processed and the packets are put on the respective clients incomming queue where it'll be pulled off and dealt with by the main thread. Does moving this stuff to the main thread mean that I'll no longer be using select to block on the socket and instead just call recvfrom to see if there's data? I'm guessing that I just keep looping calling recvfrom until I get to a point where I get no data from the socket, then just fall through and run the logic for the server. Ideally, I'd like to do all this in a manner so that it doesnt hog up the CPU because the server is just running in a infinite busy loop. Ideas? -=[ Megahertz ]=-
-=[Megahertz]=-
Advertisement
You'd have to make your socket non-blocking, but I suppose it'd work. In today's world of multi-cpu/core hyperthreaded machines, I just don't understand why people keep writing single-threaded server code ...

Robert
If you ever consider improving your server model try IOCPs for Windows NT+, epoll for Linux 2.5+, or kqueue for FreeBSD 4.1+.
Well epoll sounded worth checking out since ultimately im going to be developing on linux.


Quote:
DESCRIPTION

epoll is a variant of poll(2) that can be used either as Edge or Level
Triggered interface and scales well to large numbers of watched fds.


Doesn't seem like it would really help in a udp situation where theres only one fd thats going to be watched.


As far as threaded vs non-threaded I've seen arguments for one being better than the other and vice versa. I really don't want this thread to end up being a war on which is better. Once I get a solid single threaded implementation done, I can run some tests and see which one wins out.

-=[ Megahertz ]=-
-=[Megahertz]=-
Quote:Original post by rmsimpson
You'd have to make your socket non-blocking, but I suppose it'd work. In today's world of multi-cpu/core hyperthreaded machines, I just don't understand why people keep writing single-threaded server code ...
There's no need to make the socket non-blocking if you use select() as it is intended, to determine when it is possible to send/recv more data.
Single-threaded code is much easier to write is one reason.
You probably don't really need multiple threads at all.

You can use select() or some similar call to wait for a message, or a timeout. If the timeout happens, then you process your time-based events (i.e. tick the game objects) and send any messages arising. If a message arrives from the (single) UDP socket, you process that, send any response necessary and then go back into select.

This approach can be used with TCP as well, but there are more sockets.

If you're using UDP and only have one socket, using these other fancy methods of waiting won't improve anything, as they are just really intended for quick switching between a large number of sockets (i.e. thousands).

If your game server uses sufficiently little processor time that it can run on one CPU easily, there is very little point in making it multithreaded.

Mark
Quote:Original post by markr
You can use select() or some similar call to wait for a message, or a timeout. If the timeout happens, then you process your time-based events (i.e. tick the game objects) and send any messages arising. If a message arrives from the (single) UDP socket, you process that, send any response necessary and then go back into select.

Mark


Well having a timeout on select was somewhat of a concern. I see it takes a timeval struct as a parameter telling it how long to wait (currently i just pass in NULL so it waits forever, but since its currently off in another thread thats fine). Whats a good timeout value? Would something as low as 10ms be unreasonable? Should I go lower? Higher?

It seems that a MMORPG would need to have the game loop run pretty fast (few hundered iterations per second). Things like attack timers and damage over time timers would need to get checked quite frequently so that events happen as accurately as possible.

Everquest had the notion of a "tic" that was 6 seconds. Wow I think uses 2 seconds. So if a DOT spell was cast on an NPC in EQ the damage that spell did would be applied every 6 seconds. Obviously their game loop ran much faster than this and im quite sure they just had a timer with a timeout value of 6 seconds that just got checked every iteration.

I guess I'll have to write a bare bones example of the above and see how well it does to get an idea of the practicality of it all.

-=[ Megahertz ]=-

-=[Megahertz]=-
Quote:... Would something as low as 10ms be unreasonable? Should I go lower? Higher?
You can set the timeout to be zero, which is presumably what you would want if your program is single-threaded. man page for select() says:

To effect a poll, the timeout argument should be non-nil, pointing to a zero-valued timeval structure.

I don't even bother with select(), I set the socket non-blocking and check if I got a WOULDBLOCK error from it.
I'm with markr in saying there is no necessity for multiple threads. If you think about it any socket based app is already multithreaded anyway, the OS is managing the data in the socket buffers in between your app and the transport layer. Even a simple app with a socket listening for connections will have two threads, check in the task manager.
If you're reading from a single socket, using select() just means you make a useless kernel call. It's more efficient to just set the socket in non-blocking mode, and reading from the socket until it returns no more data; then go through your regular game loop.

Non-threaded game loops mean that you don't have to worry about locking your world state, which is a performance improvement. However, on a multi-core CPU, you'll probably do OK by doing receipt in one thread, and put data on a queue for the main world processing thread to pick up; you only need to lock this queue once per game tick which is a negligible cost. You can even double-buffer the queue if you're so inclined, although that may incur a latency penalty.
enum Bool { True, False, FileNotFound };
It all depends how much work is done besides pulling and pushing packets through UDP. If your game mechanics can require alot of calculations and monopolize the CPU (constantly or only periodicly) it can stall your communications (more noticeable if your game has a high update cycle frequency).

Does your server also do file requests (ie- rollin/out operations) ?? If they are numerous enough, you may need a seperate thread for your FileIO (and you will be stuck with multi-threading anyway...). If your server has a monitor console
that too probably would be on a seperate thread.

If you have stuff like pathfinding which can grab long slices of CPU you might need to have a seperate thread to break its operation up via a round robin
load leveling method.


You might be lucky to have a game with lightweight game mechanics/server overhead/low packet thruput and be able to do everything in one thread.


It might be a matter of opinion whether multithread code is uglier than a single thread 'giant loop' with interruption logic to keep the loop cycle running at a constant frequency (prioritizing packet service...)


This topic is closed to new replies.

Advertisement