//
// 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 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();
var connections = new List(_connections);
_readerWriterLock.ExitReadLock();
return connections.AsReadOnly();
}
}
///
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();
_connections.Add(connection);
_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();
var connections = new List(_connections);
_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 { get; private set; } = ConnectionState.Open;
public Stream ReadStream => _serverStream;
public Stream WriteStream => _serverStream;
public void Disconnect()
{
_serverStream.Disconnect();
_serverStream.Close();
State = ConnectionState.Closed;
_server._readerWriterLock.EnterWriteLock();
_server._connections.Remove(this);
_server._readerWriterLock.ExitWriteLock();
}
}
}