10
\$\begingroup\$

I am creating a multiplayer game (for under 64 players). I already decided to have a separate thread for the network loop, but I was wondering if it would be better to create an extra thread for receiving UDP or set the receiving socket on non-blocking (without extra thread).

Or is it better to use another method like Asynchronous Sockets? Better methods are always welcome!

\$\endgroup\$
6
\$\begingroup\$

Okay, first of all, if you have something and it's working, it's usually a good idea to leave it like that. Why fix what's not broken?

But if you're having trouble, and would really like to rewrite your networking code, I think you have four main choices:

  1. Multithreaded blocking code (what you're doing right now)
  2. Non-blocking sockets with level-triggered notification
  3. Non-blocking sockets with readiness change notification
  4. Asynchronous sockets

Having written lots of multiplayer (from peer-to-peer to massively multiplayer) clients and servers, I like to think that option 2 leads to the least complexity, with pretty good performance, both for the server and client parts of the game. As a close second, I would go with option 4, but that usually requires you to rethink your entire program, and I mostly find it useful for servers and not clients.

In particular, I would like to advise against blocking sockets in a multithreaded environment, as doing so usually leads to locking and other synchronization features which not only greatly increase the complexity of the code, but may also degrade its performance, as some threads wait for others.

But above all that, most socket implementations (and most I/O implementations at that) are non-blocking at the lowest level. Blocking operations are simply provided to simplify the development of trivial programs. When a socket is blocking, the CPU in that thread is completely idle, so why build a non-blocking abstraction over the blocking abstraction over an already non-blocking task?

Non-blocking socket programming is a bit daunting if you haven't tried it, but it turns out it is quite simple, and if you're already doing input polling, you already have the mindset to do non-blocking sockets.

The first thing you want to do is to set the socket to non-blocking. You do that with fcntl().

After that, before you do send(), recv(), sendto(), recvfrom(), accept() (connect() is a bit different) or other calls that could block the thread, you call select() on the socket. select() tells you whether or not a subsequent read or write operation can be performed on the socket without it blocking. If that is the case, you can safely do the operation you want, and the socket won't block.

Including this in a game is quite simple. If you already have a game loop, for example like this:

while game_is_running do
    poll_input()
    update_world()
    do_sounds()
    draw_world()
end

you could change it to look like this:

while game_is_running do
    poll_input()
    read_network()
    update_world()
    do_sounds()
    write_network()
    draw_world()
end

where

function read_network()
    while select(socket, READ) do
        game.net_input.enqueue(recv(socket))
    end
end

and

function write_network()
    while not game.net_output.empty and select(socket, WRITE) do
        send(socket, game.net_output.dequeue())
    end
end

In terms of resources, the only book I think everybody must have in their bookshelves, even if it's the only book they have, is "Unix Network Programming, Vol 1." by the late Richard Stevens. It doesn't matter if you do Windows or other OS or language socket programming. Do not think you understand sockets until you read this book.

Another resource where you can find a general overview of the available solutions in terms of multiple socket programming (mostly relevant for server programming) is this page.

\$\endgroup\$
2
  • \$\begingroup\$ I just started my game engine, so rewriting is not an issue. This answer was really useful and thank you for the book recommendation. Do you also know a book that is more specific about networking in games? \$\endgroup\$ – Yannick Lange Oct 31 '13 at 15:16
  • 3
    \$\begingroup\$ @YannickLange: No, but I wouldn't recommend you look for game-specific books, since networking is pretty much genre-agnostic, and also no other book is up to the level of Stevens' book. Kudos for using UDP though, as using a message-oriented protocol is a good idea when writing message-oriented programs (like most games). Many people tend to end up writing a message abstraction over TCP, which is by itself a stream-oriented abstraction built over a message-oriented protocol (IP). \$\endgroup\$ – Panda Pajama Oct 31 '13 at 15:22
-1
\$\begingroup\$

You didn't write which programming-language you are using, but most languages provide good asynchronous socket frameworks which often utilize features of the underlying operating system.

When you write your own threaded implementation based on blocking sockets (remember: when you have multiple clients, you need a thread for every single socket), you are just going to reinvent what the asynchronous socket framework already provides.

So I would recommend you to use asynchronous sockets.

\$\endgroup\$
7
  • 4
    \$\begingroup\$ why does he need a thread for every single socket? Especially with async sockets? Many heavyweight servers use a "thread pool" model to process socket data, where each thread services N clients and they are spawned or killed as needed. Just think this answer needs an improvement :D \$\endgroup\$ – Grimshaw Oct 31 '13 at 12:20
  • \$\begingroup\$ @Grimshaw I think you misunderstood my answer. I think I have now clarified that a multi-threaded model would be required when using blocking sockets. \$\endgroup\$ – Philipp Oct 31 '13 at 12:22
  • \$\begingroup\$ @Philipp I didn't tell which programming language I use because, I thought it wasn't really relevant to the question (I use c++ by the way). I thank you for your recommendation to use asynchronous socket. Do you know a recommended book/webpage for learning these types of methods in game(engines)? \$\endgroup\$ – Yannick Lange Oct 31 '13 at 13:12
  • \$\begingroup\$ @Grimshaw: And how does each thread service N clients? If you're blocking, and you want to make sure each socket gets its data processed regardless of the state of the rest of the sockets, you have to have one thread per socket. That, or don't use blocking sockets. \$\endgroup\$ – Panda Pajama Oct 31 '13 at 14:42
  • \$\begingroup\$ My comment was to the first version of your answer, in the meanwhile, you edited and corrected it, forget what I said :D \$\endgroup\$ – Grimshaw Oct 31 '13 at 14:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.