/// <summary> /// Disconnect the client and dispose of background workers. /// Do not reuse the object after disposal. /// </summary> /// <param name="disposing">Indicate if resources should be disposed.</param> protected virtual void Dispose(bool disposing) { if (disposing) { _Settings.Logger?.Invoke(_Header + "disposing"); if (Connected) { Disconnect(); } if (_WriteLock != null) { _WriteLock.Dispose(); } if (_ReadLock != null) { _ReadLock.Dispose(); } _Settings = null; _Events = null; _Callbacks = null; _Statistics = null; _Keepalive = null; _SourceIp = null; _ServerIp = null; _Client = null; _DataStream = null; _TcpStream = null; _SslStream = null; _SslCertificate = null; _SslCertificateCollection = null; _WriteLock = null; _ReadLock = null; _DataReceiver = null; _MonitorSyncResponses = null; } }
/// <summary> /// Connect to the server. /// </summary> public void Connect() { if (Connected) { throw new InvalidOperationException("Already connected to the server."); } _Client = new TcpClient(); _Statistics = new WatsonTcpStatistics(); IAsyncResult asyncResult = null; WaitHandle waitHandle = null; bool connectSuccess = false; if (!_Events.IsUsingMessages && !_Events.IsUsingStreams) { throw new InvalidOperationException("One of either 'MessageReceived' or 'StreamReceived' events must first be set."); } if (_Keepalive.EnableTcpKeepAlives) { EnableKeepalives(); } if (_Mode == Mode.Tcp) { #region TCP _Settings.Logger?.Invoke(_Header + "connecting to " + _ServerIp + ":" + _ServerPort); _Client.LingerState = new LingerOption(true, 0); asyncResult = _Client.BeginConnect(_ServerIp, _ServerPort, null, null); waitHandle = asyncResult.AsyncWaitHandle; try { connectSuccess = waitHandle.WaitOne(TimeSpan.FromSeconds(_Settings.ConnectTimeoutSeconds), false); if (!connectSuccess) { _Client.Close(); throw new TimeoutException("Timeout connecting to " + _ServerIp + ":" + _ServerPort); } _Client.EndConnect(asyncResult); _SourceIp = ((IPEndPoint)_Client.Client.LocalEndPoint).Address.ToString(); _SourcePort = ((IPEndPoint)_Client.Client.LocalEndPoint).Port; _TcpStream = _Client.GetStream(); _DataStream = _TcpStream; _SslStream = null; Connected = true; } catch (Exception e) { _Events.HandleExceptionEncountered(this, new ExceptionEventArgs(e)); throw; } finally { waitHandle.Close(); } #endregion TCP } else if (_Mode == Mode.Ssl) { #region SSL _Settings.Logger?.Invoke(_Header + "connecting with SSL to " + _ServerIp + ":" + _ServerPort); _Client.LingerState = new LingerOption(true, 0); asyncResult = _Client.BeginConnect(_ServerIp, _ServerPort, null, null); waitHandle = asyncResult.AsyncWaitHandle; try { connectSuccess = waitHandle.WaitOne(TimeSpan.FromSeconds(_Settings.ConnectTimeoutSeconds), false); if (!connectSuccess) { _Client.Close(); throw new TimeoutException("Timeout connecting to " + _ServerIp + ":" + _ServerPort); } _Client.EndConnect(asyncResult); _SourceIp = ((IPEndPoint)_Client.Client.LocalEndPoint).Address.ToString(); _SourcePort = ((IPEndPoint)_Client.Client.LocalEndPoint).Port; if (_Settings.AcceptInvalidCertificates) { _SslStream = new SslStream(_Client.GetStream(), false, new RemoteCertificateValidationCallback(AcceptCertificate)); } else { _SslStream = new SslStream(_Client.GetStream(), false); } _SslStream.AuthenticateAsClient(_ServerIp, _SslCertificateCollection, SslProtocols.Tls12, !_Settings.AcceptInvalidCertificates); if (!_SslStream.IsEncrypted) { throw new AuthenticationException("Stream is not encrypted"); } if (!_SslStream.IsAuthenticated) { throw new AuthenticationException("Stream is not authenticated"); } if (_Settings.MutuallyAuthenticate && !_SslStream.IsMutuallyAuthenticated) { throw new AuthenticationException("Mutual authentication failed"); } _DataStream = _SslStream; Connected = true; } catch (Exception e) { _Events.HandleExceptionEncountered(this, new ExceptionEventArgs(e)); throw; } finally { waitHandle.Close(); } #endregion SSL } else { throw new ArgumentException("Unknown mode: " + _Mode.ToString()); } _TokenSource = new CancellationTokenSource(); _Token = _TokenSource.Token; _DataReceiver = Task.Run(() => DataReceiver(), _Token); _MonitorSyncResponses = Task.Run(() => MonitorForExpiredSyncResponses(), _Token); _Events.HandleServerConnected(this, new ConnectionEventArgs((_ServerIp + ":" + _ServerPort))); _Settings.Logger?.Invoke(_Header + "connected"); }