using System.IO;
using System.IO.Pipes;
using System.Text;
namespace DeviceService.Service.NamePipe
{
///
/// 命名管道客户端连接封装。
/// 每个实例对应管道服务端上的一条已建立连接,收发均通过 / 完成。
/// 协议约定:每条消息为一行 UTF-8 文本(以 \n 结尾),发送端单次写入“内容+换行”,
/// 使 Message 模式下一条消息恰好是完整的一行。
///
public class NamePipeClient : IClient, IDisposable
{
/// 单条消息允许的最大字节数(16MB)。
internal const int MaxMessageLength = 16 * 1024 * 1024;
/// 等待发送锁的超时时间(毫秒)。
private const int SendLockTimeout = 5000;
/// 消息行结束符。StreamWriter 每次写入都会触发 AutoFlush,必须与内容在同一次 Write 中写入。
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 };
}
/// 连接是否仍然可用。
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);
}
///
/// 写入一行消息。内容与换行符必须单次 Write 写入,配合 AutoFlush 才能成为一条完整消息。
///
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();
}
}
}
///
/// 读取一行消息。返回 null 表示对端已关闭连接。
///
internal async Task 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();
}
}
}