/// <summary> /// Perpetually listen and relay traffic until cancelled. /// </summary> public Task ListenAsync(CancellationToken token) { // Start listening before returning from the menthod. this.listener.Start(BacklogLength); // All communication is then handled asynchronously. return(Task.Run(() => { using (token.Register(this.listener.Stop)) { while (this.ClientAcceptLimit == 0 || this.ClientAcceptLimit > this.ClientsAccepted) { try { var socket = this.listener.AcceptSocket(); if (this.policy.IsClientAllowed((IPEndPoint)socket.RemoteEndPoint)) { TraceSources.Compute.TraceInformation( "Connection from {0} allowed by policy", socket.RemoteEndPoint); } else { TraceSources.Compute.TraceWarning( "Connection from {0} rejected by policy", socket.RemoteEndPoint); socket.Close(); continue; } var clientStream = new SocketStream(socket, this.Statistics); var serverStream = new SshRelayStream(this.server); OnClientConnected(clientStream.ToString()); this.ClientsAccepted++; Task.WhenAll( clientStream.RelayToAsync(serverStream, token), serverStream.RelayToAsync(clientStream, token)) .ContinueWith(t => { TraceSources.Compute.TraceVerbose("SshRelayListener: Closed connection"); if (t.IsFaulted) { OnConnectionFailed(t.Exception); } OnClientDisconnected(clientStream.ToString()); }); } catch (SocketException e) when(e.SocketErrorCode == SocketError.Interrupted) { // Operation cancelled, terminate gracefully. break; } } } })); }