【BeetleX重构】实现一个可读写的ReadOnlySequence适配器

        ReadOnlySequence只是一个可读取非连续内存结构,它的使用更多了现在Pipe中。由Pipe的Writer负责非连续内存数据写入(一般挂载到Socket的接收端),而对应的Reader则获取相应可读的ReadOnlySequence数据。Pipe是一个高效的异步读写模型,它可以更好地分离Socket读写逻辑,从而让Socket收发的异步处理更高效复位到相关状态工作。

        由于Pipe是基于异步的,对于IO分离读写的确很适合。但对于内存读写的处理从效率上来说就不太适合了。在编写网络服务时一般都可以挂Pipe用作底层处理,那上层的SslStream就并不适合了。为了上层的统一处理方式那就有必要把SslStream的数据转成ReadOnlySequence。到这里可能有个疑问直接把SslStream的数据读出来然后返回对应的ReadOnlySequence就可以了啊,为什么还多花时间写个适配器?但网络数据处理上有些不同,并不是一次读到的数据就是一个完全协议包,往往是由多次不连续的网络数据组成;这个时候就需要把每次接收到的数据存放在ReadOnlySequence里处理。而还有一个更重要的问题是ReadOnlySequence读取完的内存怎处理?而实现适配器的重要作用也就解决这些问题,非连续内存的写入和读取循环复用。

        实现ReadOnlySequence适配器先了解是如何创建它的,以下是它的构造函数

public ReadOnlySequence(ReadOnlyMemory<T> memory);
public ReadOnlySequence(T[] array);
public ReadOnlySequence(T[] array, int start, int length);
public ReadOnlySequence(ReadOnlySequenceSegment<T> startSegment, int startIndex, ReadOnlySequenceSegment<T> endSegment, int endIndex);

从构造函数上来看显然需要通过ReadOnlySequenceSegment<T>来创建。

31fa857213955b805527eb6a2c772029.png
看帮助这个类似乎没有内置实现,那就说只能自己去实现一个了。

internal class MemorySegment : ReadOnlySequenceSegment<byte>
        {
            public MemorySegment(ReadOnlyMemory<byte> memory)
            {
                Memory = memory;
            }


            public MemorySegment Append(ReadOnlyMemory<byte> memory)
            {
                var segment = new MemorySegment(memory)
                {
                    RunningIndex = RunningIndex + Memory.Length
                };


                Next = segment;


                return segment;
            }
        }

为什么添加Append方法,主要原因是ReadOnlySequence只支持开始和结束两个ReadOnlySequenceSegment<T>对象,分别构成非连续内存链表的开始和结束。

        ReadOnlySequenceSegment<T>只是一个单向链表内存指向,实际使用还需要一个内存结构类

class MemoryBlock : IDisposable
{
    public MemoryBlock(int length, int segmentMinSize)
    {
        Data = MemoryPool<byte>.Shared.Rent(length < segmentMinSize ? segmentMinSize : length);
        Length = Data.Memory.Length;
        Memory = Data.Memory;
    }
    public IMemoryOwner<byte> Data { get; set; }


    public Memory<byte> Memory { get; set; }


    public int Length { get; set; }


    public int Allocated { get; set; }


    public int Postion { get; set; }


    public MemoryBlock Next { get; set; }


    public void ReadAdvanceTo(int count)
    {
        Postion += count;
    }


    public void AdvanceTo(int count)
    {
        Allocated += count;
    }


    public Memory<byte> Allot(int size)
    {
        return Memory.Slice(Allocated, size);
    }
    public bool TryAllot(int size)
    {
        return Memory.Length - Allocated >= size;
    }


    public Memory<byte> GetUseMemory()
    {
        return Memory.Slice(Postion, Allocated - Postion);
    }


    public void Dispose()
    {
        Data.Dispose();
        Memory = null;
        Data = null;
    }
}

同样这个内存类也要构建成一个链表,所以也定义了Next属性指向下一下内存存储类。

        有了以上两个类就可以构建ReadOnlySequence的适配器了,定义对应的内存分配方法

public Span<byte> GetSpan(int length)
        {
            return GetMemory(length).Span;
        }


        public Memory<byte> GetMemory(int length)
        {
            if (_end == null)
            {
                CreateMemory(length);
            }
            if (!_end.TryAllot(length))
                CreateMemory(length);
            return _end.Allot(length);
        }


        private void CreateMemory(int length)
        {
            MemoryBlock result = new MemoryBlock(length, SegmentMinSize);
            if (_first == null)
                _first = result;
            if (_end == null)
            {
                _end = result;
            }
            else
            {
                _end.Next = result;
                _end = result;
            }
        }

当内存分配后填充数据完成就需要提交对应写入数据的长度

public void WriteAdvanceTo(int length)
        {
            _end.AdvanceTo(length);
        }

数据写入完成后就可以提交数据并构建对应可读的ReadOnlySequence了

public void Flush()
{
    var start = _first.GetUseMemory();
    if (_first == _end)
    {
        _readOnlySequence = new ReadOnlySequence<byte>(start);
    }
    else
    {
        MemorySegment first = new MemorySegment(start);
        MemoryBlock last = _first.Next;
        MemorySegment next = first;
        while (last != null)
        {
            next = next.Append(last.GetUseMemory());
            last = last.Next;


        }
        _readOnlySequence = new ReadOnlySequence<byte>(first, 0, next, next.Memory.Length);
    }
}

写入的功能已经完成,接下来就是读取提交的代码

public void ReaderAdvanceTo(long length)
{
    _readOnlySequence = _readOnlySequence.Slice(length);
    while (length > 0)
    {
        var memory = _first;
        var len = memory.Allocated - memory.Postion;
        if (length >= len)
        {
            length -= len;
            memory.Dispose();
            _first = memory.Next;
            if (_first == null)
            {
                _end = null;
                return;
            }
        }
        else
        {
            memory.ReadAdvanceTo((int)length);
            length = 0;
        }
    }
}

这一环节最重要的工作就是检查每个内存块是否已经读取完成,如果完成就把内存回收到池里。接下来再实现一个相应的Stream方便兼容第三方使用

public class ReadOnlySequenceAdapterStream : Stream, ISpanSequenceNetStream
{


    public ReadOnlySequenceAdapterStream()
    {


        this.ReadOnlySequenceAdapter = new ReadOnlySequenceAdapter();
        this.ReadOnlySequenceAdapter.SegmentMinSize = 1024 * 4;
    }


    public ReadOnlySequenceAdapter ReadOnlySequenceAdapter { get; private set; }


    public override bool CanRead => true;


    public override bool CanSeek => throw new NotImplementedException();


    public override bool CanWrite => throw new NotImplementedException();


    public override long Length => ReadOnlySequenceAdapter.ReadOnlySequence.Length;


    public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }


    public Span<byte> Allot(int count)
    {
        var result = ReadOnlySequenceAdapter.GetSpan(count);
        WriteAdvance(4);
        return result;
    }


    public override void Flush()
    {
        this.ReadOnlySequenceAdapter.Flush();
    }


    public ReadOnlySequence<byte> GetReadOnlySequence()
    {
        return ReadOnlySequenceAdapter.ReadOnlySequence;
    }


    public Memory<byte> GetWriteMemory(int count)
    {
        return ReadOnlySequenceAdapter.GetMemory(count);
    }


    public override int ReadByte()
    {
        if (Length == 0)
            return -1;
        int result = ReadOnlySequenceAdapter.ReadOnlySequence.FirstSpan[0];
        ReadAdvance(1);
        return result;
    }


    public override int Read(byte[] buffer, int offset, int count)
    {
        if (buffer.Length == 0)
            return 0;
        if (Length > count)
        {
            ReadOnlySequenceAdapter.ReadOnlySequence.CopyTo(new Span<byte>(buffer, offset, count));
            ReadAdvance(count);
            return count;
        }
        else
        {
            var len = (int)Length;
            ReadOnlySequenceAdapter.ReadOnlySequence.CopyTo(new Span<byte>(buffer, offset, len));
            ReadAdvance(len);
            return len;
        }
    }


    public void ReadAdvance(long count)
    {
        this.ReadOnlySequenceAdapter.ReaderAdvanceTo(count);
    }


    public override long Seek(long offset, SeekOrigin origin)
    {
        throw new NotImplementedException();
    }


    public override void SetLength(long value)
    {
        throw new NotImplementedException();
    }


    public override void WriteByte(byte value)
    {
        var memory = ReadOnlySequenceAdapter.GetMemory(1);
        memory.Span[0] = value;
        WriteAdvance(1);
    }


    public bool TryRead(int count, out ReadOnlySequence<byte> data)
    {
        data = default;
        if (Length > count)
        {
            data = ReadOnlySequenceAdapter.ReadOnlySequence.Slice(0, count);
            return true;
        }
        return false;
    }


    public override void Write(byte[] buffer, int offset, int count)
    {
        var memory = ReadOnlySequenceAdapter.GetSpan(count);
        Span<byte> span = new Span<byte>(buffer, offset, count);
        span.CopyTo(memory);
        WriteAdvance(count);
    }


    public Span<byte> GetWriteSpan(int count)
    {
        return ReadOnlySequenceAdapter.GetSpan(count);
    }


    private long _catchWriteLength = 0;


    public void StartWriteLength()
    {
        _catchWriteLength = 0; ;
    }


    public int EndWriteLength()
    {
        return (int)_catchWriteLength;
    }


    public void WriteAdvance(int count)
    {
        _catchWriteLength += count;
        ReadOnlySequenceAdapter.WriteAdvanceTo(count);
    }


    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
        if (disposing)
        {
            ReadOnlySequenceAdapter.Dispose();
        }
    }


    public void Import(Stream stream)
    {
        var span = GetWriteSpan(1024 * 4);
        var len = stream.Read(span);
        while (len > 0)
        {
            WriteAdvance(len);
            len = stream.Read(span);
        }
        Flush();
    }
}

这样一个标准化并支持ReadOnlySequence的Stream就实现了,如果想这个类需要支持Socket和SslStream的中间层还需扩展它的异步方法,并支持Socket异步读写,SslStream握手验证的时候需要使用到Stream的异步方法。

BeetleX

开源跨平台通讯框架(支持TLS)

提供HTTP,Websocket,MQTT,Redis,RPC和服务网关开源组件

个人微信:henryfan128    QQ:28304340

关注公众号

12b267285acbb3bfd14f071db0f54835.jpeg

https://github.com/beetlex-io/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值