🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Async vs. Nonblocking

Started by
14 comments, last by fprefect 23 years, 4 months ago
I just finished reading the new GameDev article "Programming with Asynchronous Sockets" by Drew Sikora, and found it very useful. I sent him a private email regarding his usage of async nonblocking sockets, and he suggested that it would be a good topic for open discussion. Here is the text of that message: In your article at GameDev.Net, you write:
quote: NOTE: It's important to know that all asynchronous functions will return the error WSAWOULDBLOCK at some point.
You should change "asynchronous" to "nonblocking" in the statement above. It seems you are discussing async/nonblocking sockets in your article, and I *think* you should be using async/blocking sockets. Let me start by defining exactly what these terms mean: * Synchronous: Functions don't return until they actually read or send the data, or until an error occurs. * Asynchronous: Functions return immediately. You get a message (or signal or notifier) when the operation completes or an error occurs. * Blocking: If the underlying network drivers don't have enough buffers to handle your request, then they keep retrying it until they do. * Nonblocking: If the underlying network drivers don't have enough buffers to handle your request, you get an EWOULDBLOCK error *immediately* and have to try your request at a later time. Notice that sync/async determines how your application interacts with the WinSock API, but blocking/nonblocking controls how WinSock handles buffer depletion and flow control internally. These modes of operation can be mixed in 4 different ways: 1. Sync+Blocking: Functions don't return until they actually read or send the data, or until a *network* error occurs (eg, stream was broken). 2. Sync+Nonblocking: Functions return immediately if there aren't enough buffers to handle request (EWOULDBLOCK), otherwise they wait for the read or send operation to complete (or fail on the wire). 3. Async+Blocking: Functions return immediately. You get a message (or signal or notifier) when the operation completes or an error occurs. If the underlying network drivers don't have enough buffers to handle your request, then WinSock retries them *asynchronously* until they do. 4. Async+Nonblocking: Functions return immediately, and if there aren't enough buffers at that time, you get an EWOULDBLOCK error. Otherwise, you get a message (or signal or notifier) when the operation completes or a *network* error occurs. Evaluating these categories then: 1. Works just like you expect synchronous stuff to work. It's suitable for lockstep protocols that send a request then wait for the response. 2. A little more work because you have to watch for EWOULDBLOCK errors when WinSock is busy or the endpoint is under flow control, then retry the request at a later time. Useful if you want to monitor data flow, but requires extra error checking and buffering in your code. 3. Works like you expect asynchronous stuff to work, just hands off the request and comes back when it completes (success or failure). You never see EWOULDBLOCK errors. 4. Just like #3, except you also get EWOULDBLOCK errors. Only a masochist or a real networking expert would want this. The differences are subtle, especially if you are new to asynchronous networking, but basically there is no good reason to use #4 over #3. It just makes work for you that could be handled by WinSock automatically, and network error handling is already hard enough. Now, I'm not an expert on WinSock programming, but I've done alot of network coding on other platforms (notably BSD Sockets) and this is standard behavior for these operation modes. There could be differences in WinSock asynchronous and blocking/nonblocking modes, but I doubt it. My suggestion is to try blocking asynchronous, to see if you can get rid of those EWOULDBLOCK (or WSAWOULDBLOCK) errors and simplify the code a bit. Either way, you should clarify which mode you are using and correct the statement that I pointed out. That said, I think the article was a very well written tutorial, and I will be replacing the threaded BSD code from my original port with async WinSock calls sometime soon. Thanks alot! Edited by - fprefect on February 5, 2001 8:56:39 PM
Matt Slot / Bitwise Operator / Ambrosia Software, Inc.
Advertisement
You will break clean multithreadead code to make awful and shitty a-sockets out of them ? I think they should remove a-sockets from the WinSock spec, I don''t see the point of them ;-(

Tim

--------------------------
glvelocity.gamedev.net
www.gamedev.net/hosted/glvelocity
Tim--------------------------glvelocity.gamedev.netwww.gamedev.net/hosted/glvelocity
From a quick peruse of the article, your not talking about the same thing. The article is talking about the windows asynchonrious socket management scheme using completions ports and events, unique to the winsock API, i beleive. Its a scheme, where you let the windows OS handle the sockets and you just recive event notifications on completion of events such as read/write/connect etc.. using the standard windows message loop.

Here is a good article about completion ports and winsock:

http://msdn.microsoft.com/msdnmag/issues/1000/Winsock/Winsock.asp

Well why would you use this? From the discussion on the winsock discussion newsgroup, this method is massively scalable while the blocking/thread approach is not. I''m talking about 10,000+ users connected to a server kind of scale. If your making an action multiplayer game with only say 100 players, then either approach will work, however don''t think your going to make the next everquest using the thread/blocking approach on windows anyways (the everquest servers only handle about 2000 users per server, mentioned in another discussion).

For windows the asynchornious sockets are more efficient and arugrably easier to write. However they are unique only to winsock, and if your contemplating porting over your server to another platform (mac/linux/etc..) you cant use them, as there is no analog on those platforms. Unless your willing to write your own socket managmenet/event notification backbone on those system, it''s best to stick to pure bsd socket usage under winsock, in that case.

As mentioned non-blocking sockets do suck under winsock, and proably other platforms. That is why people dont use them, as far as i know.

Good Luck

-ddn
This completion port stuff is actually a very powerful mechanism, but only available under 2000 and maybe NT4. WinSock Asynchronous sockets are a horrible way of dealing with connections. I still think the multithreaded approach works very easy and very intuitive. I don''t think that you''ll run into serious performance problems with this one. But stay away from this WSock specific A Socket scheme ;-)

Tim

--------------------------
glvelocity.gamedev.net
www.gamedev.net/hosted/glvelocity
Tim--------------------------glvelocity.gamedev.netwww.gamedev.net/hosted/glvelocity
Hee heee, i didn''t read the orignal post very well, please ignore my rant on asynchronous sockets etc... You are talking about asyncrhonous sockets under windows and not non-blocking sockets.

-ddn
With real preemptive threads available, you don''t gain *that* much by using asynchronous sockets. In fact, Windows requires you to create a window just to listen for events. However, I''d recommend that anyone who is looking for serious performance try it both ways and see which works best for them.

Also UNIX/Linux and MacOS both support asynchronous networking using various platform-specific mechanisms. On UNIX, you install a handler for SIGIO and SIGURG signals (but you have to figure out which socket it applies to). On MacOS, you register a callback to receive various events and completion events associated with an endpoint.
(For a cooperative system like the MacOS, it''s almost essential that you use asynch networking for serious server software).

Finally, I''d recommend that anyone who wants to write a MMORPG do so on UNIX and not Windows or a Mac. You won''t get 10,000 players on a single box... but you can take a large chunk, and distribute the rest across several servers.
Matt Slot / Bitwise Operator / Ambrosia Software, Inc.
How does overlapped io fit into all this?

MSDN "...overlapped [socket] I/O operations do not block", so If overlapped io is async, then async sockets never block on Win32.

...
Clearly blocking io sucks, however simpler it is, so a generic reusable efficent non-blocking io handler would be useful.

I've started working on an overlapped io thread for serial communications, but I'd like to eventually adapt it to work with files, sockets, pipes, etc... as well.

Right now I queue up one write (if data is available to send) & one read and WaitForMultipleObjects(...) until either the exit event signals, or one of the overlapped io's complete. For the comm one, it also waits on commstatus io when appropreiate.
The thread runs at real time priority, I was trying to achieve minimual latency from data received to reply - with no polling.

Is this an optimal method? or is there a more effective, more efficent way? I read a little bit about completeion ports & APCs, are they better to use than you're own thread?

...
How much does a decent UNIX server cost? One with some gigs on RAID5, a couple of ECC processors & a ton of RAM, etc... A high performance WindowsNT server runs about $25k and can handle a few hundred users; Sound reasonable? I don't think you could get an equal HPUX/AIX/IRIX/etc machine for 25k, maybe around $50k, but it would probably handle more users...
Could do Linux on the same NT hardware and save a couple k

...
And EQ handles a few thousand users per world cluster, which is composed of several servers. Each 'zone' gets it's own virtual IP, but multiple zones run on a given server. A world has about 60 zones now. It's not clear how many zones run on how many servers...

Edited by - Magmai Kai Holmlor on February 5, 2001 11:49:45 PM
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
You don''t have to worry about the cost of server hardware anyway, the bandwidth costs are way higher. But we already had this discussion a short time ago.

As for the actual topic, don''t use asynchronous sockets because they''re harder to port. Most server programs use either of one technologies:
method A) Have a master-thread which listens on our port. When we get a connection request, fork a new thread that will inherit&handle the connection. This is for example how Apache and many FTP servers work. A server like this would probably use blocking sockets.
method B) The single-threaded model. While you could still use blocking sockets, it''s ineffective and lots of work (because you always have to call into the OS to check whether data is still waiting etc...), so non-blocking is better for this one. This is used by IRC-servers for example.

As for which method you should choose: Apache and the like use method A because the several requests can be handled independantly from each other, so you don''t have shared data between the threads (in fact, it''s no longer threads as far as I know). As soon as all the connections interact and affect each other, method B is by far the better one, because you don''t have to mess with inter-process locking etc..

cu,
Prefect

---
Sanity is the trademark of a weak mind.
Widelands - laid back, free software strategy
quote:
A) Have a master-thread which listens on our port. When we get a connection request, fork a new thread that will inherit&handle the connection. This is for example how Apache and many FTP servers work. A server like this would probably use blocking sockets.
method

Isn''t this an enormous waste of resources using an entire thread for each connection? And would you need two threads for each connections, one for reads and one for writes? Or did you mean to use a thread pool to serve the request?


It seems to me (though I haven''t gotten there quite yet) to maximize the performance, the throughput & scalibility, that you can''t use a thread per connection and cannot poll (though those methods would explain an artificial limit of 200).

Also, waiting in the msg pump to process sokcet messages seems like it would add a ton of latency to the transmission.

Magmai Kai Holmlor
- The disgruntled & disillusioned
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
Of course it''s only one thread per connection. Seperating reads and writes would be stupid, right?
Whether you''re using one thread per connection or a thread pool.. well, theoretically it doesn''t matter at all. However, due to the OS overhead of thread management, having a resizeable thread pool is probably the best way to handle the problem. That''s how Apache does it anyway. However, having a thread pool still means one thread per connection. Imagine though, Web-servers usually don''t have that many connections at a time, because every single request is quite short.

A single-threaded message loop is still the best way for a server where different connections interact, i.e. affect each other. It shouldn''t add too much latency either unless your framerate drops dramatically, and you have to avoid that for a gameserver anyway.

cu,
Prefect

---
Sanity is the trademark of a weak mind.
Widelands - laid back, free software strategy

This topic is closed to new replies.

Advertisement