C# 数据接收器 LoopReader

发布时间 2024-01-10 18:38:45作者: WmW

最近在使用C#的命名管道进行数据通讯,到了接收数据比较难搞,

由于不知道数据流具体的长度(调用NamedPipeServerStream的Length会报错),缓冲区的大小就无法确定,因此写了个功能类,用来接收这种数据流长度未知的数据

测试使用没有问题,但是不敢保证一定没有问题,请谨慎参考

    /// <summary>
    /// 数据接收器
    /// </summary>
    internal class LoopReader {
        /// <summary>
        /// 缓冲区的容量
        /// </summary>
        public readonly int Capacity;
        /// <summary>
        /// 接收数据的缓冲区
        /// </summary>
        byte[] buffer;
        /// <summary>
        /// 缓冲区容器,当申请新的缓冲区时,用来放置已经填充过数据的缓冲区
        /// </summary>
        readonly List<byte[]> list;
        /// <summary>
        /// 外部读取数据的函数
        /// </summary>
        readonly Func<byte[], int, int, int> read;
        /// <summary>
        /// 创建一个数据接收器,用来接收未知长度的数据流
        /// </summary>
        /// <param name="read"></param>
        /// <param name="capacity"></param>
        public LoopReader(Func<byte[], int, int, int> read, int capacity) {
            this.read = read;
            Capacity = capacity;
            buffer = new byte[capacity];
            list = new List<byte[]>();
        }
        /// <summary>
        /// 读取流中的所有数据
        /// </summary>
        /// <returns></returns>
        public (byte[], int) Read() {
            int length = 0;
            while (true) {
                int len = read(buffer, 0, buffer.Length); //调用外部的读取函数,填充缓冲区
                length += len;
                if (len == buffer.Length) { //有时候一次读取不玩流中的数据
                    list.Add(buffer); //就将已经读取的缓冲区放到集合中
                    buffer = new byte[Capacity]; //然后申请新的缓冲区继续读取
                } else {
                    break;
                }
            }
            if (list.Count > 0) { //如果本次读取到的数据由多个缓冲区组成
                using (MemoryStream ms = new MemoryStream()) {
                    foreach (var bs in list) {
                        ms.Write(bs, 0, bs.Length); //就按照顺序将所有的缓冲区写到一个流中
                    }
                    ms.Write(buffer, 0, buffer.Length);
                    buffer = ms.GetBuffer(); //并指向第一个缓冲区,避免后续不够用
                    list.Clear();
                }
            }
            return (buffer, length);
        }
    }