// // NamedPipeServer.cs // // Copyright (c) František Boháček. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Data; using System.IO.Pipes; using System.Xml; using NosSmooth.Comms.Data; using Remora.Results; namespace NosSmooth.Comms.NamedPipes; /// /// A server using named pipes. /// public class NamedPipeServer : IServer { private readonly List _connections; private readonly ReaderWriterLockSlim _readerWriterLock; private readonly string _pipeName; private bool _listening; /// /// Initializes a new instance of the class. /// /// The name of the pipe. public NamedPipeServer(string pipeName) { _readerWriterLock = new ReaderWriterLockSlim(); _pipeName = pipeName; _connections = new List(); } /// public IReadOnlyList Clients { get { _readerWriterLock.EnterReadLock(); try { return _connections.ToArray(); } finally { _readerWriterLock.ExitReadLock(); } } } /// public async Task> WaitForConnectionAsync(CancellationToken ct = default) { if (!_listening) { throw new InvalidOperationException("The server is not listening."); } var serverStream = new NamedPipeServerStream ( _pipeName, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous ); await serverStream.WaitForConnectionAsync(ct); var connection = new NamedPipeConnection(this, serverStream); _readerWriterLock.EnterWriteLock(); try { _connections.Add(connection); } finally { _readerWriterLock.ExitWriteLock(); } return connection; } /// public Task ListenAsync(CancellationToken stopToken = default) { _listening = true; stopToken.Register(Close); return Task.FromResult(Result.FromSuccess()); } /// public void Close() { _readerWriterLock.EnterReadLock(); IReadOnlyList connections; try { connections = new List(_connections); } finally { _readerWriterLock.ExitReadLock(); } foreach (var connection in connections) { connection.Disconnect(); } _listening = false; } private class NamedPipeConnection : IConnection { private readonly NamedPipeServer _server; private readonly NamedPipeServerStream _serverStream; public NamedPipeConnection(NamedPipeServer server, NamedPipeServerStream serverStream) { _server = server; _serverStream = serverStream; } public ConnectionState State => _serverStream.IsConnected ? ConnectionState.Open : ConnectionState.Closed; public Stream ReadStream => _serverStream; public Stream WriteStream => _serverStream; public void Disconnect() { _serverStream.Disconnect(); _serverStream.Close(); _server._readerWriterLock.EnterWriteLock(); try { _server._connections.Remove(this); } finally { _server._readerWriterLock.ExitWriteLock(); } } } }