using System.Buffers.Binary; namespace Lskj.AgentPet.Host.Core.ErpBridge; public static class BridgeFrameCodec { public const int MaxMessageBytes = 1024 * 1024; public static async Task WriteAsync( Stream stream, ReadOnlyMemory body, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(stream); if (body.Length is <= 0 or > MaxMessageBytes) throw new HostError("bridge_protocol_error", "ERP 桥请求长度无效或超过 1 MB。"); byte[] header = new byte[4]; BinaryPrimitives.WriteInt32LittleEndian(header, body.Length); await stream.WriteAsync(header, cancellationToken).ConfigureAwait(false); await stream.WriteAsync(body, cancellationToken).ConfigureAwait(false); await stream.FlushAsync(cancellationToken).ConfigureAwait(false); } public static async Task ReadAsync( Stream stream, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(stream); byte[] header = new byte[4]; await ReadExactlyAsync(stream, header, cancellationToken).ConfigureAwait(false); int length = BinaryPrimitives.ReadInt32LittleEndian(header); if (length is <= 0 or > MaxMessageBytes) throw new HostError("bridge_protocol_error", "ERP 桥响应长度无效或超过 1 MB。"); byte[] body = new byte[length]; await ReadExactlyAsync(stream, body, cancellationToken).ConfigureAwait(false); return body; } private static async Task ReadExactlyAsync( Stream stream, Memory target, CancellationToken cancellationToken) { int offset = 0; while (offset < target.Length) { int count = await stream.ReadAsync(target[offset..], cancellationToken).ConfigureAwait(false); if (count == 0) throw new HostError("bridge_disconnected", "ERP 桥在完整消息到达前关闭。"); offset += count; } } }