/// <summary> /// Connects the <see cref="TcpClient"/> to the server asynchronously. /// </summary> /// <exception cref="InvalidOperationException">Attempt is made to connect the <see cref="TcpClient"/> when it is not disconnected.</exception> /// <returns><see cref="WaitHandle"/> for the asynchronous operation.</returns> public override WaitHandle ConnectAsync() { ConnectState connectState = null; Match endpoint; string integratedSecuritySetting; if (CurrentState == ClientState.Disconnected && !m_disposed) { try { // If we do not already have a wait handle to use // for connections, get one from the base class if ((object)m_connectWaitHandle == null) m_connectWaitHandle = (ManualResetEvent)base.ConnectAsync(); // Create state object for the asynchronous connection loop connectState = new ConnectState(); // Store connectState in m_connectState so that calls to Disconnect // and Dispose can dispose resources and cancel asynchronous loops m_connectState = connectState; OnConnectionAttempt(); m_connectWaitHandle.Reset(); // Overwrite config file if integrated security exists in connection string if (m_connectData.TryGetValue("integratedSecurity", out integratedSecuritySetting)) m_integratedSecurity = integratedSecuritySetting.ParseBoolean(); #if MONO // Force integrated security to be False under Mono since it's not supported m_integratedSecurity = false; #endif // Initialize state object for the asynchronous connection loop endpoint = Regex.Match(m_connectData["server"], Transport.EndpointFormatRegex); connectState.ConnectArgs.RemoteEndPoint = Transport.CreateEndPoint(endpoint.Groups["host"].Value, int.Parse(endpoint.Groups["port"].Value), m_ipStack); connectState.ConnectArgs.SocketFlags = SocketFlags.None; connectState.ConnectArgs.UserToken = connectState; connectState.ConnectArgs.Completed += (sender, args) => ProcessConnect((ConnectState)args.UserToken); // Create client socket connectState.Socket = Transport.CreateSocket(m_connectData["interface"], 0, ProtocolType.Tcp, m_ipStack, m_allowDualStackSocket); // Initiate the asynchronous connection loop ConnectAsync(connectState); } catch (Exception ex) { // Log exception during connection attempt OnConnectionException(ex); // Terminate the connection if ((object)connectState != null) TerminateConnection(connectState.Token); // Ensure that the wait handle is set so that operations waiting // for completion of the asynchronous connection loop can continue if ((object)m_connectWaitHandle != null) m_connectWaitHandle.Set(); } finally { // If the operation was cancelled during execution, // make sure to dispose of erroneously allocated resources if ((object)connectState != null && connectState.Token.Cancelled) connectState.Dispose(); } } // Return the wait handle that signals completion // of the asynchronous connection loop return m_connectWaitHandle; }
/// <summary> /// Callback method for asynchronous connect operation. /// </summary> private void ProcessConnect(ConnectState connectState) { ReceiveState receiveState = null; SendState sendState = null; try { // Quit if this connection loop has been cancelled if (connectState.Token.Cancelled) return; // Increment the number of connection attempts that // have occurred in this asynchronous connection loop connectState.ConnectionAttempts++; // Check the SocketAsyncEventArgs for errors during the asynchronous connection attempt if (connectState.ConnectArgs.SocketError != SocketError.Success) throw new SocketException((int)connectState.ConnectArgs.SocketError); // Set the size of the buffer used by the socket to store incoming data from the server connectState.Socket.ReceiveBufferSize = ReceiveBufferSize; if (m_integratedSecurity) { #if !MONO // Check the state of cancellation one more time before // proceeding to the next step of the connection loop if (connectState.Token.Cancelled) return; // Create the SslStream object used to perform // send and receive operations on the socket connectState.NetworkStream = new NetworkStream(connectState.Socket, false); connectState.NegotiateStream = new NegotiateStream(connectState.NetworkStream, true); connectState.NegotiateStream.BeginAuthenticateAsClient(m_networkCredential ?? (NetworkCredential)CredentialCache.DefaultCredentials, string.Empty, ProcessIntegratedSecurityAuthentication, connectState); #endif } else { // Initialize the SocketAsyncEventArgs for receive operations connectState.ReceiveArgs = FastObjectFactory<SocketAsyncEventArgs>.CreateObjectFunction(); connectState.ReceiveArgs.SetBuffer(new byte[ReceiveBufferSize], 0, ReceiveBufferSize); if (m_payloadAware) connectState.ReceiveArgs.Completed += (sender, args) => ProcessReceivePayloadAware((ReceiveState)args.UserToken); else connectState.ReceiveArgs.Completed += (sender, args) => ProcessReceivePayloadUnaware((ReceiveState)args.UserToken); // Initialize the SocketAsyncEventArgs for send operations connectState.SendArgs = FastObjectFactory<SocketAsyncEventArgs>.CreateObjectFunction(); connectState.SendArgs.SetBuffer(new byte[SendBufferSize], 0, SendBufferSize); connectState.SendArgs.Completed += (sender, args) => ProcessSend((SendState)args.UserToken); // Initialize state object for the asynchronous send loop sendState = new SendState(); sendState.Token = connectState.Token; sendState.Socket = connectState.Socket; sendState.ReceiveArgs = connectState.ReceiveArgs; sendState.SendArgs = connectState.SendArgs; sendState.SendArgs.UserToken = sendState; // Store sendState in m_sendState so that calls to Disconnect // and Dispose can dispose resources and cancel asynchronous loops m_sendState = sendState; // Check the state of cancellation one more time before // proceeding to the next step of the connection loop if (connectState.Token.Cancelled) return; // Notify of established connection m_connectWaitHandle.Set(); OnConnectionEstablished(); // Initialize state object for the asynchronous receive loop receiveState = new ReceiveState(); receiveState.Token = connectState.Token; receiveState.Socket = connectState.Socket; receiveState.Buffer = connectState.ReceiveArgs.Buffer; receiveState.ReceiveArgs = connectState.ReceiveArgs; receiveState.ReceiveArgs.UserToken = receiveState; receiveState.SendArgs = connectState.SendArgs; // Store receiveState in m_receiveState so that calls to Disconnect // and Dispose can dispose resources and cancel asynchronous loops m_receiveState = receiveState; // Start receiving data if (m_payloadAware) ReceivePayloadAwareAsync(receiveState); else ReceivePayloadUnawareAsync(receiveState); // Further socket interactions are handled through the ReceiveArgs // and SendArgs objects, so the ConnectArgs is no longer needed connectState.ConnectArgs.Dispose(); } } catch (SocketException ex) { // Log exception during connection attempt OnConnectionException(ex); // If the connection is refused by the server, // keep trying until we reach our maximum connection attempts if (ex.SocketErrorCode == SocketError.ConnectionRefused && (MaxConnectionAttempts == -1 || connectState.ConnectionAttempts < MaxConnectionAttempts)) { // Server is unavailable, so keep retrying connection to the server. try { ConnectAsync(connectState); } catch { TerminateConnection(connectState.Token); } } else { // For any other reason, clean-up as if the client was disconnected. TerminateConnection(connectState.Token); } } catch (Exception ex) { // Log exception during connection attempt OnConnectionException(ex); // Terminate the connection TerminateConnection(connectState.Token); } finally { // If the operation was cancelled during execution, // make sure to dispose of erroneously allocated resources if ((object)connectState != null && connectState.Token.Cancelled) connectState.Dispose(); if ((object)receiveState != null && receiveState.Token.Cancelled) receiveState.Dispose(); if ((object)sendState != null && sendState.Token.Cancelled) sendState.Dispose(); } }
private void ProcessConnect(ConnectState connectState) { Match endpoint; try { // Quit if this connection loop has been cancelled if (connectState.Token.Cancelled) return; // Increment the number of connection attempts that // have occurred in this asynchronous connection loop connectState.ConnectionAttempts++; // Check the SocketAsyncEventArgs for errors during the asynchronous connection attempt if (connectState.ConnectArgs.SocketError != SocketError.Success) throw new SocketException((int)connectState.ConnectArgs.SocketError); // Set the size of the buffer used by the socket to store incoming data from the server connectState.Socket.ReceiveBufferSize = ReceiveBufferSize; // Create the SslStream object used to perform // send and receive operations on the socket connectState.NetworkStream = new NetworkStream(connectState.Socket, true); connectState.SslStream = new SslStream(connectState.NetworkStream, false, m_remoteCertificateValidationCallback ?? CertificateChecker.ValidateRemoteCertificate, m_localCertificateSelectionCallback); // Load trusted certificates from // the trusted certificates directory LoadTrustedCertificates(); // Begin authentication with the TlsServer endpoint = Regex.Match(m_connectData["server"], Transport.EndpointFormatRegex); if (!connectState.Token.Cancelled) connectState.SslStream.BeginAuthenticateAsClient(endpoint.Groups["host"].Value, m_clientCertificates, m_enabledSslProtocols, m_checkCertificateRevocation, ProcessTlsAuthentication, connectState); } catch (SocketException ex) { // Log exception during connection attempt OnConnectionException(ex); // If the connection is refused by the server, // keep trying until we reach our maximum connection attempts if (ex.SocketErrorCode == SocketError.ConnectionRefused && (MaxConnectionAttempts == -1 || connectState.ConnectionAttempts < MaxConnectionAttempts)) { try { ConnectAsync(connectState); } catch { TerminateConnection(connectState.Token); } } else { // For any other socket exception, // terminate the connection TerminateConnection(connectState.Token); } } catch (Exception ex) { // Log exception during connection attempt OnConnectionException(ex); // Terminate the connection TerminateConnection(connectState.Token); } finally { // If the operation was cancelled during execution, // make sure to dispose of erroneously allocated resources if (connectState.Token.Cancelled) connectState.Dispose(); } }