50

At first, I must ask that which is the best in which states ? For example a real-time MMORPG server. What if i create a thread per client instead of using non-blocking sockets ? Or What if i use one thread that contains all non-blocking sockets ? Can you explain me the advantages ?

deniz
  • 2,427
  • 4
  • 27
  • 38

2 Answers2

49

Your question deserves a much longer discussion but here's a short stab at an answer:

  • using blocking sockets means that only one socket may be active at any time in any one thread (because it blocks while waiting for activity)
  • using blocking sockets is generally easier than non-blocking sockets (asynchronous programming tends to be more complicated)
  • you could create 1 thread per socket as you stated but threads have overhead and are extremely inefficient compared to the non-blocking solutions;
  • with non-blocking sockets you could handle a much larger volume of clients: it could scale to hundreds of thousands in a single process - but the code becomes a little bit more complicated

With non-blocking sockets (on Windows) you have a couple of options:

  • polling
  • events based
  • overlapped I/O

Overlapped I/O will give you the best performance (thousands of sockets / process) at the expense of being the most complicated model to understand and implement correctly.

Basically it comes down to performance vs. programming complexity.

NOTE

Here's a better explanation of why using a thread/socket model is a bad idea:

In windows, creating a large number of threads is highly inefficient because the scheduler is unable to properly determine which threads should be receiving processor time and which shouldn't. That coupled with the memory overhead of each thread means that you will run out of memory (because of stack space) and processor cycles (because of overhead in managing threads) at the OS level long before you will run out of capacity to handle socket connections.

Mike Dinescu
  • 54,171
  • 16
  • 118
  • 151
  • If you're running on an 80-way box, you can rip out hundreds of threads no problem. – John Dibling May 18 '12 at 14:23
  • @John - Just because it's possible to create hundreds of threads (if you have enough memory) it doesn't mean it's a good idea. In fact it is a bad idea! – Mike Dinescu May 18 '12 at 14:29
  • 10
    My point, admittedly not well-made, is that you can't just draw an arbitrary line at 20 and call any number of threads beyond that "bad." – John Dibling May 18 '12 at 14:30
  • 5
    You also should not universally decry the thread/socket model. Not everything is a web server. – John Dibling May 18 '12 at 14:34
  • Agree with John. Also, you failed to mention fibers/lightweight threads. – Jonathan Leonard May 18 '12 at 14:44
  • But It is logical to use one more thread for socket things in client ? One is main game thread, one is socket in/out. – deniz May 19 '12 at 14:28
  • @MikeDinescu you said that "usually anything more than 10-20 threads is considered bad practice". Is it 10-20 threads/core or per machine or what? – PHcoDer Aug 29 '16 at 17:38
  • 2
    It needs to be stated firmly and clearly that this '10-20 threads' business is nonsense. Practically any Java program is going to have ten threads, and there are certainly servers out there using hundreds of thousands of them. – user207421 Jul 09 '17 at 00:42
  • 1
    This might have been true when the author wrote this, but I highly doubt it's still the case since the improved scheduling in >= W8. I just tried it with 5000 server threads + 5000 client threads (plus 5000 additional writer threads), all with blocking sockets, on the same machine (16/32 core), 1000 ping pong connections (~20 chars) per connection. Works awesome - average runtime of clients is 52s, minimum 44s, maximum 62s. Nearly no thread stalling or such, the workload has been excellently shared between threads. Try it out yourself. – user2328447 Dec 04 '17 at 14:20
16

I will go on record as saying that for almost anything except toy programs, you should use non-blocking sockets as a matter of course.

Blocking sockets cause a serious problem: if the machine on the other end (or any part of your connection to it) fails during a blocking call, your code will end up blocked until the IP stack's timeout. In a typical case, that's around 2 minutes, which is completely unacceptable for most purposes. The only way1 to abort that blocking call is to terminate the thread that made it -- but terminating a thread is itself almost always unacceptable, as it's essentially impossible to clean up after it and reclaim whatever resources it had allocated. Non-blocking sockets make it trivial to abort a call when/if needed, without doing anything to the thread that made the call.

It is possible to make blocking sockets work sort of well if you use a multi-process model instead. Here, you simply spawn an entirely new process for each connection. That process uses a blocking socket, and when/if something goes wrong, you just kill the entire process. The OS knows how to clean up the resources from a process, so cleanup isn't a problem. It still has other potential problems though: 1) you pretty much need a process monitor to kill processes when needed, and 2) spawning a process is usually quite a bit more expensive than just creating a socket. Nonetheless, this can be a viable option, especially if:

  1. You're dealing with a small number of connections at a time
  2. You generally do extensive processing for each connection
  3. You're dealing only with local hosts so your connection to them is fast and dependable
  4. You're more concerned with optimizing development than execution

1. Well, technically not the only possible way, but most of the alternatives are relatively ugly--to be more specific, I think by the time you add code to figure out that there's a problem, and then fix the problem, you've probably done more extra work than if you just use a non-blocking socket.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • 10
    It is possible to abort a blocked socket operation without terminating the blocked thread by disconnecting the socket from another thread context. That will cause the blocked operation to fail with an error code, then the blocked thread can move on and do other things, like normal cleanup. – Remy Lebeau May 18 '12 at 19:36
  • This looks quite radical to say the least. The manual for Linux `recv()` says, that it will block on a blocking socket *if there's no message available*. I believe you'd just need to `select()` or `poll()` before receiving, and you wouldn't have to worry about any of the stuff that this post mentions. – Błażej Michalik Mar 26 '20 at 16:59
  • @BłażejMichalik But the man page further states: *"On Linux, select() may report a socket file descriptor as "ready for reading", while nevertheless a subsequent read blocks. This could for example happen when data has arrived but upon examination has the wrong checksum and is discarded. There may be other circumstances in which a file descriptor is spuriously reported as ready. Thus it may be safer to use O_NONBLOCK on sockets that should not block."* So blocking sockets may not be a viable option. – Harith Jan 29 '23 at 12:31