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