TcpClient BeginConnect EndConnect with Task Async Pattern
David | November 18, 2014Last night, I needed to write a service that made 16 socket connections to 16 different servers. Rather than writing an old fashioned connection thread to manage the 16 connections I decided to write a re-usable library function to do the work.
The .NET Task Async Pattern (TAP) pattern provides a clean way to create a Task (thread) to manage the mundane job of connecting to the devices.
/// <summary> /// ConnectTaskAsync allows the user to connect a TcpClient to a server. It returns /// a Task that handles the asynchronous BeginConnect and EndConnect methods. /// /// .NET is pretty cool--there's no need for writing your own connection threads! /// </summary> /// <param name="socket"></param> /// <param name="endpoint"></param> /// <returns></returns> public static Task ConnectTaskAsync(TcpClient aClient, IPAddress anIPAddress, int aPort, AsyncCallback anAsyncCallback) { Task t = Task.Factory.FromAsync(aClient.BeginConnect(anIPAddress, aPort, anAsyncCallback, aClient), aClient.EndConnect); return t; }
After that I created a client class that provides a Connect method in the Task Async Pattern.
/// <summary> /// Connect uses the Task Async Pattern (TAP). /// /// When the code gets to the await keyword, it executes the ConnectTaskAsync method /// and returns to the method that called Connect(). /// /// The ConnectTaskAsync returns a Task that performs the BeginConnect and EndConnect /// methods asynchronously. /// /// When the BeginConnect method inside the Task completes it calls the ConnectCallback /// with an IAsyncResult. /// /// After the Task completes it returns to the await line and continues. /// </summary> public async void Connect() { log.Debug("ENTER"); try { await WCSLib.ConnectTaskAsync(mClient, mIPAddress, mPort, ConnectCallback); } catch (Exception e) { log.Error("IPAddress = " + mIPAddressString + ", Port = " + mPort + ", Exception: " + e.ToString()); } log.Info("ConnectTaskAsync returned to Connect method. IPAddress = " + mIPAddressString + ", Port = " + mPort); log.Debug("EXIT"); }
The client also provides a ConnectCallback that the Task can call when finished connecting. This method could be static, but it isn’t required.
/// <summary> /// ConnectCallback is called after the BeginConnect method completes. /// The IAsynncResult contains the TcpClient that the Task tried to connect /// to the Server. /// </summary> /// <param name="ar">The TcpClient we tried to connect to the server</param> public void ConnectCallback(IAsyncResult ar) { log.Debug("ENTER"); try { // Retrieve the socket from the state object. TcpClient lClient = ar.AsyncState as TcpClient;; if (lClient != null) { if (lClient.Connected) { log.Info("TcpClient connected to " + lClient.Client.RemoteEndPoint.ToString()); } else { log.Info("TcpClient failed to connect to " + lClient.Client.RemoteEndPoint.ToString()); } } else { log.Error("Callback TcpClient is null"); } } catch (Exception e) { log.Error("Exception: " + e.ToString()); } }
After that, a higher level manager/user of the client class can periodically check to see if the TcpCient is connected and attempt a connection if needed. Isn’t it fantastic how much work one can get done with the new TAP? .NET is amazing!