/// <summary> /// Begins listening for client connections in a separate background thread. /// This method waits when pipe will be created(or throws exception). /// </summary> /// <exception cref="InvalidOperationException"></exception> /// <exception cref="IOException"></exception> public async Task StartAsync(CancellationToken cancellationToken = default) { if (IsStarted) { throw new InvalidOperationException("Server already started"); } await StopAsync(cancellationToken); var source = new TaskCompletionSource <bool>(); using var registration = cancellationToken.Register(() => source.TrySetCanceled(cancellationToken)); ListenWorker = new TaskWorker(async token => { while (!token.IsCancellationRequested) { try { var connectionPipeName = $"{PipeName}_{++NextPipeId}"; // Send the client the name of the data pipe to use try { #if NETSTANDARD2_0 using var serverStream = CreatePipeStreamFunc?.Invoke(PipeName) ?? PipeServerFactory.Create(PipeName); #else await using var serverStream = CreatePipeStreamFunc?.Invoke(PipeName) ?? PipeServerFactory.Create(PipeName); #endif PipeStreamInitializeAction?.Invoke(serverStream); source.TrySetResult(true); await serverStream.WaitForConnectionAsync(token).ConfigureAwait(false); await using var handshakeWrapper = new PipeStreamWrapper(serverStream); await handshakeWrapper.WriteAsync(Encoding.UTF8.GetBytes(connectionPipeName), token) .ConfigureAwait(false); } catch (Exception exception) { if (WaitFreePipe) { throw; } source.TrySetException(exception); break; } // Wait for the client to connect to the data pipe var connectionStream = CreatePipeStreamFunc?.Invoke(connectionPipeName) ?? PipeServerFactory.Create(connectionPipeName); PipeStreamInitializeAction?.Invoke(connectionStream); try { await connectionStream.WaitForConnectionAsync(token).ConfigureAwait(false); } catch { #if NETSTANDARD2_0 connectionStream.Dispose(); #else await connectionStream.DisposeAsync().ConfigureAwait(false); #endif throw; } // Add the client's connection to the list of connections var connection = ConnectionFactory.Create <T>(connectionStream, Formatter); connection.MessageReceived += (sender, args) => OnMessageReceived(args); connection.Disconnected += (sender, args) => OnClientDisconnected(args); connection.ExceptionOccurred += (sender, args) => OnExceptionOccurred(args.Exception); connection.Start(); Connections.Add(connection); OnClientConnected(new ConnectionEventArgs <T>(connection)); } catch (OperationCanceledException) { throw; } // Catch the IOException that is raised if the pipe is broken or disconnected. catch (IOException) { await Task.Delay(TimeSpan.FromMilliseconds(1), token).ConfigureAwait(false); } catch (Exception exception) { OnExceptionOccurred(exception); break; } } }, OnExceptionOccurred); try { await source.Task.ConfigureAwait(false); } catch (Exception) { await StopAsync(cancellationToken); throw; } }
/// <summary> /// Begins listening for client connections in a separate background thread. /// This method waits when pipe will be created(or throws exception). /// </summary> /// <exception cref="InvalidOperationException"></exception> /// <exception cref="IOException"></exception> public async Task StartAsync(CancellationToken cancellationToken = default) { if (IsStarted) { throw new InvalidOperationException("Server already started"); } await StopAsync(cancellationToken); var source = new TaskCompletionSource <bool>(); using var registration = cancellationToken.Register(() => source.TrySetCanceled(cancellationToken)); ListenWorker = new TaskWorker(async token => { while (!token.IsCancellationRequested) { try { if (Connection != null && Connection.IsConnected) { await Task.Delay(TimeSpan.FromMilliseconds(1), cancellationToken).ConfigureAwait(false); continue; } if (Connection != null) { await Connection.DisposeAsync().ConfigureAwait(false); } // Wait for the client to connect to the data pipe var connectionStream = CreatePipeStreamFunc?.Invoke(PipeName) ?? PipeServerFactory.Create(PipeName); try { PipeStreamInitializeAction?.Invoke(connectionStream); source.TrySetResult(true); await connectionStream.WaitForConnectionAsync(token).ConfigureAwait(false); } catch { #if NETSTANDARD2_0 connectionStream.Dispose(); #else await connectionStream.DisposeAsync().ConfigureAwait(false); #endif throw; } var connection = ConnectionFactory.Create <T>(connectionStream, Formatter); try { connection.MessageReceived += (sender, args) => OnMessageReceived(args); connection.Disconnected += (sender, args) => OnClientDisconnected(args); connection.ExceptionOccurred += (sender, args) => OnExceptionOccurred(args.Exception); connection.Start(); } catch { await connection.DisposeAsync().ConfigureAwait(false); throw; } Connection = connection; OnClientConnected(new ConnectionEventArgs <T>(connection)); } catch (OperationCanceledException) { if (Connection != null) { await Connection.DisposeAsync().ConfigureAwait(false); Connection = null; } throw; } // Catch the IOException that is raised if the pipe is broken or disconnected. catch (IOException exception) { if (!WaitFreePipe) { source.TrySetException(exception); break; } await Task.Delay(TimeSpan.FromMilliseconds(1), token).ConfigureAwait(false); } catch (Exception exception) { OnExceptionOccurred(exception); break; } } }, OnExceptionOccurred); try { await source.Task.ConfigureAwait(false); } catch (Exception) { await StopAsync(cancellationToken); throw; } }