alielie
7 天以前 e57938fbda9bff702004038f76d009422968bff5
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
using System.IO;
using System.IO.Pipes;
using System.Text;
 
namespace DeviceService.Service.NamePipe
{
    /// <summary>
    /// 命名管道客户端连接封装。
    /// 每个实例对应管道服务端上的一条已建立连接,收发均通过 <see cref="StreamReader"/> / <see cref="StreamWriter"/> 完成。
    /// 协议约定:每条消息为一行 UTF-8 文本(以 \n 结尾),发送端单次写入“内容+换行”,
    /// 使 Message 模式下一条消息恰好是完整的一行。
    /// </summary>
    public class NamePipeClient : IClient, IDisposable
    {
        /// <summary>单条消息允许的最大字节数(16MB)。</summary>
        internal const int MaxMessageLength = 16 * 1024 * 1024;
 
        /// <summary>等待发送锁的超时时间(毫秒)。</summary>
        private const int SendLockTimeout = 5000;
 
        /// <summary>消息行结束符。StreamWriter 每次写入都会触发 AutoFlush,必须与内容在同一次 Write 中写入。</summary>
        private const char LineEnding = '\n';
 
        private readonly NamedPipeServerStream _stream;
        private readonly StreamWriter writer;
        private readonly StreamReader reader;
        private readonly SemaphoreSlim _sendLock = new SemaphoreSlim(1, 1);
        private volatile bool _disposed;
 
        public NamePipeClient(NamedPipeServerStream serverStream)
        {
            var encoding = new UTF8Encoding(false);
            _stream = serverStream;
 
            reader = new StreamReader(serverStream, encoding, false, 1024, true);
            writer = new StreamWriter(serverStream, encoding, 1024, true) { AutoFlush = true };
        }
 
        /// <summary>连接是否仍然可用。</summary>
        public bool IsConnected
        {
            get
            {
                if (_disposed)
                {
                    return false;
                }
                return _stream.IsConnected;
            }
        }
 
        public bool Send(string data, out string msg)
        {
            if (data == null)
            {
                msg = "待发送的数据不能为空。";
                return false;
            }
 
            return SendLine(data, Encoding.UTF8.GetByteCount(data) + 1, out msg);
        }
 
        public bool Send(byte[] data, out string msg)
        {
            if (data == null)
            {
                msg = "待发送的数据不能为空。";
                return false;
            }
 
            // 负载按 UTF-8 文本处理,与行协议保持一致
            return SendLine(Encoding.UTF8.GetString(data), data.Length + 1, out msg);
        }
 
        /// <summary>
        /// 写入一行消息。内容与换行符必须单次 Write 写入,配合 AutoFlush 才能成为一条完整消息。
        /// </summary>
        private bool SendLine(string text, int byteLength, out string msg)
        {
            if (byteLength > MaxMessageLength)
            {
                msg = $"数据长度 {byteLength} 字节超过单条消息上限 {MaxMessageLength} 字节。";
                return false;
            }
 
            if (_disposed)
            {
                msg = "管道连接已关闭,无法发送数据。";
                return false;
            }
 
            if (!IsConnected)
            {
                msg = "管道连接已断开,无法发送数据。";
                return false;
            }
 
            bool lockTaken = false;
            try
            {
                lockTaken = _sendLock.Wait(SendLockTimeout);
                if (!lockTaken)
                {
                    msg = "发送超时:上一条数据仍未写出。";
                    return false;
                }
 
                // writer 非 thread-safe,统一由 _sendLock 串行化
                writer.Write(text + LineEnding);
                writer.Flush();
                msg = string.Empty;
                return true;
            }
            catch (Exception ex)
            {
                msg = $"发送失败:{ex.Message}";
                return false;
            }
            finally
            {
                if (lockTaken)
                {
                    _sendLock.Release();
                }
            }
        }
 
        /// <summary>
        /// 读取一行消息。返回 null 表示对端已关闭连接。
        /// </summary>
        internal async Task<string?> ReadMessageAsync()
        {
            if (_disposed)
            {
                return null;
            }
 
            string? line = await reader.ReadLineAsync().ConfigureAwait(false);
            return line;
        }
 
        public void Dispose()
        {
            if (_disposed)
            {
                return;
            }
 
            _disposed = true;
            try
            {
                reader.Dispose();
            }
            catch
            {
                // 忽略关闭异常
            }
 
            try
            {
                writer.Dispose();
            }
            catch
            {
                // 忽略关闭异常
            }
 
            try
            {
                _stream.Dispose();
            }
            catch
            {
                // 关闭过程中的异常可以忽略
            }
 
            _sendLock.Dispose();
        }
    }
}