using System; using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; namespace CanSimulator { /// /// CAN 模拟器 TCP 线协议分帧编解码器。 /// 线格式:4 字节大端长度前缀 + UTF8 文本行(与 BrokerIpc 同惯用法)。 /// 读侧负责粘包/半包重组:循环读满长度前缀与帧体后返回完整文本。 /// public static class SimulatorFraming { /// 长度前缀字节数 public const int HeaderSize = 4; /// 单帧最大字节数(防畸形长度前缀导致内存爆掉;命令串与 LOG 响应远小于此值) public const int MaxFrameBytes = 1024 * 1024; /// 把一条文本消息编码为完整帧(含长度前缀) public static byte[] Encode(string text) { if (text == null) throw new ArgumentNullException(nameof(text)); byte[] body = Encoding.UTF8.GetBytes(text); if (body.Length > MaxFrameBytes) throw new InvalidOperationException($"帧体超限: {body.Length} > {MaxFrameBytes}"); var frame = new byte[HeaderSize + body.Length]; WriteBigEndian(frame, 0, body.Length); Buffer.BlockCopy(body, 0, frame, HeaderSize, body.Length); return frame; } /// 解码已去掉长度前缀的帧体 public static string Decode(byte[] buffer, int offset, int count) => Encoding.UTF8.GetString(buffer, offset, count); /// /// 从流中读取一个完整帧。 /// 返回 null 表示对端已关闭或帧中途 EOF(连接失效,调用方应触发重连); /// 长度前缀非法(≤0 或超 MaxFrameBytes)抛 InvalidDataException(协议失步)。 /// public static async Task ReadFrameAsync(Stream s, CancellationToken ct) { var header = new byte[HeaderSize]; if (!await ReadExactAsync(s, header, HeaderSize, ct).ConfigureAwait(false)) return null; int len = ReadBigEndian(header, 0); if (len <= 0 || len > MaxFrameBytes) throw new InvalidDataException($"非法帧长度前缀: {len}"); var body = new byte[len]; if (!await ReadExactAsync(s, body, len, ct).ConfigureAwait(false)) return null; // 帧体中途 EOF return Decode(body, 0, len); } /// 循环读满 count 字节;期间 EOF(读到 0 字节)或 IO 异常时返回 false,由调用方按连接失效处理 private static async Task ReadExactAsync(Stream s, byte[] buf, int count, CancellationToken ct) { int off = 0; while (off < count) { int n; try { n = await s.ReadAsync(buf, off, count - off, ct).ConfigureAwait(false); } catch (IOException) { return false; } if (n <= 0) return false; off += n; } return true; } // net472 无 System.Buffers.Binary,手写大端读写 private static void WriteBigEndian(byte[] buf, int offset, int value) { buf[offset] = (byte)(value >> 24); buf[offset + 1] = (byte)(value >> 16); buf[offset + 2] = (byte)(value >> 8); buf[offset + 3] = (byte)value; } private static int ReadBigEndian(byte[] buf, int offset) => (buf[offset] << 24) | (buf[offset + 1] << 16) | (buf[offset + 2] << 8) | buf[offset + 3]; } }