tabs ↹ over ␣ ␣ ␣ spaces

by Jiří {x2} Činčura

TcpListener and TcpClient (an easy-to-use example) – part 2

22 Jul 2014 .NET

Back in 2005 I wrote a blogpost TcpListener and TcpClient (an easy-to-use example). It was really just a piece of code I wrote to quickly finish what I was doing and I put it on my blog. The code was throwaway code, but even after the years it’s still visited a lot on my blog.

Given the code was written 9 years ago, when my skills were 9 years younger and when the world around us was slightly different, different tools etc. I decided to give it a little facelift. Kind of 2014 edition.

class Program
{
	public static void Main(string[] args)
	{
		MainAsync().Wait();
	}

	static async Task MainAsync()
	{
		Console.WriteLine("Starting...");
		var server = new TcpListener(IPAddress.Parse("0.0.0.0"), 66);
		server.Start();
		Console.WriteLine("Started.");
		while (true)
		{
			var client = await server.AcceptTcpClientAsync().ConfigureAwait(false);
			var cw = new ClientWorking(client, true);
			cw.DoSomethingWithClientAsync().NoWarning();
		}
		// :)
		server.Stop();
	}
}

class ClientWorking
{
	TcpClient _client;
	bool _ownsClient;

	public ClientWorking(TcpClient client, bool ownsClient)
	{
		_client = client;
		_ownsClient = ownsClient;
	}

	public async Task DoSomethingWithClientAsync()
	{
		try
		{
			using (var stream = _client.GetStream())
			{
				using (var sr = new StreamReader(stream))
				using (var sw = new StreamWriter(stream))
				{
					await sw.WriteLineAsync("Hi. This is x2 TCP/IP easy-to-use server").ConfigureAwait(false);
					await sw.FlushAsync().ConfigureAwait(false);
					var data = default(string);
					while (!((data = await sr.ReadLineAsync().ConfigureAwait(false)).Equals("exit", StringComparison.OrdinalIgnoreCase)))
					{
						await sw.WriteLineAsync(data).ConfigureAwait(false);
						await sw.FlushAsync().ConfigureAwait(false);
					}
				}

			}
		}
		finally
		{
			if (_ownsClient && _client != null)
			{
				(_client as IDisposable).Dispose();
				_client = null;
			}
		}
	}
}

static class TaskExtensions
{
	[MethodImpl(MethodImplOptions.AggressiveInlining)]
	public static void NoWarning(this Task t) { }
}

It’s really nothing special. I just replaced the thread with asynchronous calls. Not that it was not available in 2005. But I had no idea that creating thread is a stupid waste of resources. And programming with APM was (and still is) touch bit harder that “normal” sequential code (that’s where the async/await comes handy). The rest is just some small cleanups and so on.

Maybe I’ll put a task for 2023 to write an – at that time current – version.

Profile Picture Jiří Činčura is an independent developer focusing on data and business layers, language constructs, parallelism and databases. Specifically Entity Framework, asynchronous and parallel programming, cloud and Azure. He's Microsoft Most Valuable Professional and you can read his articles, guides, tips and tricks at www.tabsoverspaces.com.