private void ProcessIntegratedSecurityAuthentication(IAsyncResult asyncResult) { ConnectState connectState = null; ReceiveState receiveState = null; SendState sendState = null; try { // Get the connect state from the async result connectState = (ConnectState)asyncResult.AsyncState; // Quit if this connection loop has been cancelled if (connectState.Token.Cancelled) return; try { // Complete the operation to authenticate with the server connectState.NegotiateStream.EndAuthenticateAsClient(asyncResult); } catch (InvalidCredentialException) { if (!m_ignoreInvalidCredentials) throw; } // 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 // and begin receiving data. 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 SslStream // object, so the SocketAsyncEventArgs is no longer needed connectState.ConnectArgs.Dispose(); } catch (SocketException ex) { // Log exception during connection attempt OnConnectionException(ex); // If connectState is null, we cannot proceed if ((object)connectState == null) return; // 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 string errorMessage = $"Unable to authenticate connection to server: {ex.Message}"; OnConnectionException(new Exception(errorMessage, ex)); // Terminate the connection if ((object)connectState != null) TerminateConnection(connectState.Token); } finally { if ((object)connectState != null) { // If the operation was cancelled during execution, // make sure to dispose of erroneously allocated resources; // otherwise, dispose of the NegotiateStream which is only used for authentication if (connectState.Token.Cancelled) { connectState.Dispose(); } else { connectState.NetworkStream.Dispose(); connectState.NegotiateStream.Dispose(); } } if ((object)receiveState != null && receiveState.Token.Cancelled) receiveState.Dispose(); if ((object)sendState != null && sendState.Token.Cancelled) sendState.Dispose(); } }
/// <summary> /// Callback method for asynchronous receive operation of payload data in "payload-aware" mode. /// </summary> private void ProcessReceivePayloadAware(ReceiveState receiveState) { try { // Quit if this receive loop has been cancelled if (receiveState.Token.Cancelled) return; // Determine if the server disconnected gracefully if (receiveState.ReceiveArgs.SocketError != SocketError.Success) throw new SocketException((int)receiveState.ReceiveArgs.SocketError); if (receiveState.ReceiveArgs.BytesTransferred == 0) throw new SocketException((int)SocketError.Disconnecting); // Update statistics and bytes received. UpdateBytesReceived(receiveState.ReceiveArgs.BytesTransferred); receiveState.Offset += receiveState.ReceiveArgs.BytesTransferred; if (receiveState.PayloadLength < 0) { // If we haven't parsed the length of the payload yet, attempt to parse it receiveState.PayloadLength = Payload.ExtractLength(receiveState.Buffer, receiveState.Offset, m_payloadMarker); if (receiveState.PayloadLength > 0) { receiveState.Offset = 0; if (receiveState.Buffer.Length < receiveState.PayloadLength) receiveState.Buffer = new byte[receiveState.PayloadLength]; } } else if (receiveState.Offset == receiveState.PayloadLength) { // We've received the entire payload so notify the user OnReceiveDataComplete(receiveState.Buffer, receiveState.PayloadLength); // Reset payload length receiveState.Offset = 0; receiveState.PayloadLength = -1; } // Continue asynchronous loop ReceivePayloadAwareAsync(receiveState); } catch (ObjectDisposedException) { // Make sure connection is terminated when client is disposed. TerminateConnection(receiveState.Token); } catch (SocketException ex) { // Log exception during receive operation OnReceiveDataException(ex); // Terminate connection when socket exception is encountered TerminateConnection(receiveState.Token); } catch (Exception ex) { try { // For any other exception, notify and resume OnReceiveDataException(ex); ReceivePayloadAwareAsync(receiveState); } catch { // Terminate connection if resume fails TerminateConnection(receiveState.Token); } } finally { // If the operation was cancelled during execution, // make sure to dispose of allocated resources if ((object)receiveState != null && receiveState.Token.Cancelled) receiveState.Dispose(); } }