C#/.NET: Multithreading Sockets/Listening [Solved: Async Sockets]

Started by
9 comments, last by binchawpz 16 years, 4 months ago
My first and primary question: is calling Stop() on a TcpListener an acceptable way to halt a blocking AcceptTcpClient() called from the same TcpListener in another thread? Here is my code, cut down a bit, to show what I mean if it isn't clear:

class ClientManager {
	private TcpListener listener;
	private List<Client> currentClients = new List<Client>();
	private Thread acceptorThread;

	public void StartAcceptingClients() {
		listener.Start();

		accepterThread = new Thread( delegate() {
			ClientConnectionAcceptor();
		} );

		accepterThread.Start();
	}

	public void StopAcceptingClients() {
		listener.Stop();

		accepterThread.Join();
	}

	private void ClientConnectionAcceptor() {
		while( true ) {
			try {
				TcpClient connection = listener.AcceptTcpClient();
				Client client = new Client( connection );
				lock( currentClients ) {
					currentClients.Add( client );
				}
			}
			catch( SocketException socketException ) {
				bool listenerBlockInterrupted = (socketException.SocketErrorCode == SocketError.Interrupted);
				if( listenerBlockInterrupted && !acceptingClients ) {
					Log.append( this.ToString() + " listening correctly interrupted." );
				}
				else {
					throw socketException;
				}
			}
		}
	}
}




If that is not the preferred method, then what can I do? The reason I chose this method is to accept connections in the background while incurring minimal overhead. Is there a better method to do this? I may be overengineering it a bit, this is my first multithreaded networking code. Any suggestions to the code would be greatly appreciated. Thanks. [Edited by - binchawpz on December 12, 2007 3:11:39 AM]
Advertisement
I have never used the pre-built socket classes for TCP/UDP like the TcpListener, but from what I have read about them, they will only be good for that "do it quick and get it done" approach. Reminds me a bit of the Winsock control - very easy, very quick to use, but not very flexible. I believe they are all blocking, which seems to be true from looking at your code. This is fine if you are not looking for anything high-end (ie a moderately sized / fast paced multiplayer game), but if you want better performance at larger loads and more flexibility, you should probably use the asynchronous sockets. These are very easy in .NET to get started up, and theres quite a lot of examples out there for doing it.

Though if you already know about asynchronous sockets and have chosen this approach for a particular reason, then I apologize.
NetGore - Open source multiplayer RPG engine
Thanks Spodi.

TcpListener is pretty much a simplicity wrapper. Most of the listen functions it has are almost exactly what Socket has. So it does have some asynchronous functions for accepting connections. My problem is I don't know how to use them effectively. The advantage to the approach I chose here is that the listen thread will block and not use any resources (aside from occasionally checking to see if anyone is knocking on my door) while the rest of the application can move on. I understand that asynchronous accept would perform nearly the exact same task, however I'm not sure how to construct it to make it loop.

Since a Socket isn't much more complicated than a TcpListener I've begun rebuilding it using sockets instead. What I'm hoping to accomplish is create a background network thread which accepts connections, cleans out dead ones, and gets messages from the clients (and sends off events when these occur) without spin-waiting and using more resources than it needs to.

I'm having two difficulties with this:

1) How can I have one function/thread manage more than one asynchronous call and reset the calls that finish?

2) How can I have said function/thread block until something needs to be done?

I know that these two questions contradict each other, in a fashion, which is why I am here asking for advice. Maybe there is a better method for handling network operations in the background.
1. Yup. For example, for listening, you would have a primary method you call to create the listen socket and start listening. When you call BeginListen, you pass a delegate to an AsyncCallback method which will be called when a connection has been made. At this point, you call EndListen and retrieve the connection information. From here, you can call BeginListen right away again. The AsyncCallback method will be called, I believe, in the same thread that BeginListen was called from (not sure on this one). This allows you to have all your listen, send and receive calls in the same thread without much problem at all. This also allows you to spread things over multiple threads quite easily.

2. So basically you're asking if you could have a thread just for the networking, but have it idle until a receive/send/accept is made? Since the AsyncCallback method will be called when one of these events happen (depending on what you add async support for) you can just have the thread idle. I don't think theres any special measures you need to take into consideration. This is assuming, though, you're asking what I think you are. ;)
NetGore - Open source multiplayer RPG engine
Quote:Original post by Spodi
1. Yup. For example, for listening, you would have a primary method you call to create the listen socket and start listening. When you call BeginListen, you pass a delegate to an AsyncCallback method which will be called when a connection has been made. At this point, you call EndListen and retrieve the connection information. From here, you can call BeginListen right away again. The AsyncCallback method will be called, I believe, in the same thread that BeginListen was called from (not sure on this one). This allows you to have all your listen, send and receive calls in the same thread without much problem at all. This also allows you to spread things over multiple threads quite easily.


Nice! You know I could have sworn I thought about that and how much it wouldn't work. It makes sense though, and seems efficient. For some reason (prolly due to my inexperience with threading) I assumed that calling BeginAccept from the same thread I was EndAccept-ing the previous call would case a never ending recursion.

However doing it this way essentially puts the connection accepting on auto-pilot for the remainder of the application. This brings me back to the question in my beginning post: is it appropriate to halt a BeginAccept/Accept call by Stop-ing the listening socket? Or is that against networking rules? If it is, how else could I stop an Accept I have already started?

Quote:
2. So basically you're asking if you could have a thread just for the networking, but have it idle until a receive/send/accept is made? Since the AsyncCallback method will be called when one of these events happen (depending on what you add async support for) you can just have the thread idle. I don't think theres any special measures you need to take into consideration. This is assuming, though, you're asking what I think you are. ;)

Well what I don't want is the idle thread looping indefinitely and consuming unnecessary CPU time which is how I arrived at my original solution. The goal here is a network thread, however by using asynchronous methods I am essentially creating a thread for each task which will, in general, be completely self sufficient. This raises a couple questions:

#1: how do you control them and tell them to stop? I can't call EndAccept until it stops, as that will block until a connection is accepted. The only method I can think of, as I mentioned above, is stopping the listening socket from another thread.

#2: How would I keep the management thread from eating CPU time when it is bored? Do I spinwait by checking a variable/lock, sleeping for 50ms, and looping that?

As you can see I'm having a bit of trouble understanding effective use of threads, but this is definitely helping. Thanks for the input so far.
1. It depends on how you want to stop it. For example, if you have a "Stop Listening" button on a form, you can raise a method in the networking thread to stop it. But how exactly the stop is done I am not sure. I assume you can just call EndListen. For Send/Recv, theres another method (Disconnect() is it?) that allows you to specify if to stop sending, receiving or both. I can't help you more than that, though, since I have never actually wanted to stop midway yet. ;)

2. I haven't actually dealt much in multi-threading, but my guess is just whatever keeps the thread alive. I'm sure others can give a much better answer to your question than myself.
NetGore - Open source multiplayer RPG engine
I was under the impression that Async worked like this
[Connect] --> [Data] --> [Disconnect]

Would it not be huge strain to keep connecting and disconnecting if it does work this way?

I have been searching for a good way to handle connections in a game setting and I have considered async a few times. I do not find that it would be the best method. All of the examples do not allow for a constant connection which puts a halt to that.

I am wondering if a threadpool would be a better method of creating a multi-threaded networked application.
If by Connect / Disconnect, you mean the connection itself opens and closes - no, not unless you actually tell it to, which would not be a very wise idea. The examples you are seeing are probably just incredibly simple "one packet" example. For instance, the server is created and starts an Async listen and receive while the client just connects to the server, starts and Async send and terminates when it finishes. These are just bare-minimum examples show the utmost basics in an often misleading and not-as-useful manner.

Networking gurus that visit here like hplus and Antheus can give a much better description, but basically the difference between blocking and asynchronous are:

Blocking:
- Call Socket.Send()
- Wait until Socket.Send() is completely finished
- Continue

Async:
- Call Socket.BeginSend()
- Continue
- When Socket.BeginSend() finishes, do post-send processing at the AsyncCallback method for sending

Or, for receive:

Blocking:
- Call Socket.Receive()
- Wait until something is received
- Process received packet
- Continue

Async:
- Call Socket.BeginReceive()
- Continue
- When the socket receives something, do post-receive processing at the AsyncCallback method for receiving

How it works is that instead of the, for example, Send() method being handled in your thread, it is forwarded to an OS-level thread in a threadpool for your application where it is processed there. When the OS finishes your Send(), it will return to you result information through a parameter in your AsyncCallback method.

I have seen benchmarks where people use Async sockets and blocking sockets pretty much in the exact same matter in a "fight to see who pushes data fastest on a single thread", and typically, blocking will come in top. Async sockets shine not in benchmarks like these, but in more realistic scenarios where you have a bunch of remote connections (not LAN or localhost) sending / being sent tons of data. It is a very good system, and works great for most scenarios you will be dealing with.

As for your threadpool idea for multiplexed IO, this is pretty much what async sockets are doing, but at an OS level. This will work, and work decently, but not as well as an async socket, along with it'd be a hell of a lot more work. No point in redesigning when theres already a much more powerful implementation.

Another thing I like about a async I/O is that it becomes very easy to buffer your data. Instead of having a bunch of small packets in line waiting for their chance at Send() and letting the Nagle Algorithm buffer your packets, you just check if theres an async send already in progress (between BeginSend and EndSend - can just keep a local bool to tell you so). If there is any in progress, throw the packet into a queue and move on. When your AsyncCallback for the send comes around, after your EndSend, you just check if there is any packets in the queue. If so, grab out as many as you can fit into your buffer, then start the send right there. If there is no packets in the queue, you just end the send and wait for more packets. Very clean and easy. :)
NetGore - Open source multiplayer RPG engine
In my application i do the following (Pseudocode)

TCPListener listener = new TCPListener();List<Client> lstClients = new List<Client>();void StartServer(){ listener.Start(); new Thread(WaitForClient).Start();}void WaitForClient(){ Client client = new Client(listener.Accept()); while( true )   {     lstClients.Add(client);     new Thread(client.Poll).Start();     client = new Client(listener.Accept());   }}
I figured out how to do it properly. I will post example code and an explanation when I get home from work.

This topic is closed to new replies.

Advertisement