TCP servers with .NET: Scenario 1 - Simple TCP servers
This post is part of a series, to see the other posts in the series go to the series index.
Writing a TCP server is something that modern business .NET developers don’t need to worry about—we have WCF, which has abstracted us from understanding TCP servers and clients for business apps—but for some systems, we still need to write a TCP server or client.
Microsoft has optimized the .NET framework for common scenarios—for example, doing something like SMTP, where a server runs continuously and clients connect when they have data, provide the data, and disconnect. So what you might do is start a server (in its own thread so you can still perform other tasks in the app) and wait for client connections. At this point, you’re blocking processing until a client connects. When a client connects, you retrieve the data and process it (in a new thread so you can allow more clients to connect).
The advantage of this is that the model is simple and fairly easy to set up, but there are a few problems with this approach:
- You have this blocking point while waiting for clients, making shutdown difficult. Since a server’s role is to always be up, this is not an optimized path—there’s no graceful way to stop it. Essentially, you’ll need to use Task Manager to kill the process.
- You deal with extensive threading and must manually handle threads. Threads are difficult to understand and prone to failure, so this opens up potential problems.
- You assume the client will connect, send data at once, and then disconnect. Again, this is the “normal” scenario (think HTTP or SMTP), but there are cases where this is not possible.
Below is a sample implementation for a console application:
class Program
{
private const int BufferSize = 4096;
private static bool ServerRunning = true;
static void Main(string[] args)
{
new Thread(RunTCPServer).Start();
Console.WriteLine("Press <kbd>Enter</kbd> to shut down");
Console.ReadLine();
ServerRunning = false;
}
private static void RunTCPServer()
{
var tcpServer = new TcpListener(IPAddress.Any, 9000);
try
{
tcpServer.Start();
while (ServerRunning)
{
var tcpClientConnection = tcpServer.AcceptTcpClient();
Console.WriteLine("Client connected");
// Spawn thread to handle the connection
new Thread(ClientConnection).Start(tcpClientConnection);
}
}
finally
{
tcpServer.Stop();
}
}
private static void ClientConnection(object state)
{
var tcpClientConnection = state as TcpClient;
var stream = tcpClientConnection.GetStream();
var buffer = new byte[BufferSize];
var amountRead = stream.Read(buffer, 0, BufferSize);
var message = Encoding.ASCII.GetString(buffer, 0, amountRead);
Console.WriteLine("Client sent: {0}", message);
}
}