//
// 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();
}
}
}
}