~ruther/NosSmooth.Comms

ref: ecfc8cd7dc9143e9550044f3f5971bf8631f51b7 NosSmooth.Comms/src/Core/NosSmooth.Comms.NamedPipes/NamedPipeServer.cs -rw-r--r-- 3.8 KiB
ecfc8cd7 — Rutherther feat: add local injectable and injector to allow making connections to nostale processes 2 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//
//  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;

/// <summary>
/// A server using named pipes.
/// </summary>
public class NamedPipeServer : IServer
{
    private readonly List<IConnection> _connections;
    private readonly ReaderWriterLockSlim _readerWriterLock;
    private readonly string _pipeName;
    private bool _listening;

    /// <summary>
    /// Initializes a new instance of the <see cref="NamedPipeServer"/> class.
    /// </summary>
    /// <param name="pipeName">The name of the pipe.</param>
    public NamedPipeServer(string pipeName)
    {
        _readerWriterLock = new ReaderWriterLockSlim();
        _pipeName = pipeName;
        _connections = new List<IConnection>();
    }

    /// <inheritdoc />
    public IReadOnlyList<IConnection> Clients
    {
        get
        {
            _readerWriterLock.EnterReadLock();
            try
            {
                return _connections.ToArray();
            }
            finally
            {
                _readerWriterLock.ExitReadLock();
            }
        }
    }

    /// <inheritdoc />
    public async Task<Result<IConnection>> 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;
    }

    /// <inheritdoc />
    public Task<Result> ListenAsync(CancellationToken stopToken = default)
    {
        _listening = true;
        stopToken.Register(Close);
        return Task.FromResult(Result.FromSuccess());
    }

    /// <inheritdoc />
    public void Close()
    {
        _readerWriterLock.EnterReadLock();
        IReadOnlyList<IConnection> connections;
        try
        {
            connections = new List<IConnection>(_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();
            }
        }
    }
}
Do not follow this link