This is solution of two console projects: Client and Server.
These examples use asynchronous methods for all communication. The benefit of that is that you get multithreaded server (it is more important for server) without the need to mess around with managing threads yourself. When asynchronous methods are used (asynchronous = do not block when called), threads are managed by .Net runtime, which uses Winsock Overlapped socket API internally, and gives best performance possible for .NET implementation.
So I thought some might find it useful, because it just sits there in my project list.
To understand asynchronous methods:
Asynchronous Method Invocation - CodeProject
Using an Asynchronous Server Socket
To describe briefly how it all works (server, for example):
- From console (main) thread, set up server listen socket.
- From console thread start accepting connections asynchronously (nothing blocks).
- Wait in console thread for any user commands, pass them to socket.
When you begin accepting asynchronously, you specify what method callback should be called when there is incoming connection:
serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), serverSocket);
"AcceptCallback" is the function to be called when there is incoming connection. We can also pass additional object to it as second parameter (i pass serverSocket here).
The crucial thing to remember about these Callback methods is this: there is no guarantee about thread which will be executing it. That means it is not thread safe to manipulate any objects or variables outside this method. Not thread safe means there is possibility of race conditions. However, you can avoid race conditions by using locks over objects appropriately (google it with whatever).
But we don't need locks in AcceptCallback method, because it gets all data it needs to operate over the second method parameter. This data object is serverSocket that I pass to it. No use of locks is what actually makes asynchronous way so much better.
So, in AcceptCallback, when we have incoming connection, we get new socket. New socket for every new connection on server. There are actually two kinds of sockets: listen sockets, for accepting new connections (only), and client sockets, for getting/sending data to client. This is the place to use lock to add this connection to our list of connections. I use lock because this list is outside the method (this is simple rule to follow):
lock (connections) connections.Add(connection);
And we start waiting for data on that client socket by using asynchronous method. This time we pass our connection object as optional parameter. It is possible to put all necessary information about the client into that object. Also, the fact that this callback method is fired means that if we simply exit, this listen socket no longer will listen for new connections. So we start asynchronous accept on listen socket too:
// Start Receive connection.Socket.BeginReceive(connection.Buffer, 0, connection.Buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), connection); // Start new Accept serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), result.AsyncState);
So, these are the basics. The main point of this example was to keep it very simple, yet to provide good reference if you need to implement this into another program.
Edited by Delever, 08 March 2010 - 01:35 PM.
Topic does not include language