C# ReadOnlySequence–;切片到给定的序列位置+;1.

C# ReadOnlySequence–;切片到给定的序列位置+;1.,c#,system.memory,C#,System.memory,我尝试从只读序列中读取一些数据。数据被格式化为帧。每个帧由一个空字节(八位字节0)终止 我的代码使用ReadOnlySequence.PositionOf搜索帧的结尾。当它找到一个空字节时,它将处理到空字节位置的所有字节。在处理之后,我想通过切片输入并重复前面的步骤来处理下一帧。 由于帧在空字节之前结束,如果我不再次切片输入数据(start=1),空字节将成为下一个字节序列的一部分 有三种方法可以将序列位置+1个项目/字节作为起始值来切片只读序列 我尝试使用SequencePosition.Ge

我尝试从
只读序列中读取一些数据。数据被格式化为帧。每个帧由一个空字节(八位字节0)终止

我的代码使用
ReadOnlySequence.PositionOf
搜索帧的结尾。当它找到一个空字节时,它将处理到空字节位置的所有字节。在处理之后,我想通过切片输入并重复前面的步骤来处理下一帧。 由于帧在空字节之前结束,如果我不再次切片输入数据(start=1),空字节将成为下一个字节序列的一部分

有三种方法可以将
序列位置
+1个项目/字节作为起始值来切片
只读序列

我尝试使用
SequencePosition.GetInteger
+1作为起始值,但这不起作用,因为
GetInteger
有时返回的值大于
ReadOnlySequence
的长度。切片到
GetInteger
返回的值会导致以下异常:
System.ArgumentOutOfRangeException:指定的参数超出有效值的范围。(参数“开始”)

最小可重复示例

using System;
using System.Buffers;
using System.IO.Pipelines;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    public class Program
    {
        private static IDuplexPipe _pipe;

        public static async Task Main( String[] args )
        {
            var pipe = new Pipe();
            _pipe = new DuplexPipe( pipe.Reader, pipe.Writer );

            var firstMessage = Encoding.UTF8.GetBytes( "CONNECTED\nversion:1.1\nsession:2a840965\nserver:ActiveMQ-Artemis/2.8.0 ActiveMQ Artemis Messaging Engine\nheart-beat:10000,10000\n\n\0\n" );
            await _pipe.Output.WriteAsync( firstMessage );
            await _pipe.Output.FlushAsync();

            var secondMessage =
                Encoding.UTF8.GetBytes(
                    "\n\nMESSAGE\nsubscription:test-839c7766-0f38-4579-a3fc-74de35408536Sub1\ncontent-length:4\nmessage-id:2147486350\ndestination:/queue/TestQ\nexpires:1572278642017\nredelivered:false\npriority:5\npersistent:true\ntimestamp:1572278582050\ndestination-type:ANYCAST\nreceipt:2\ntest:test\nNMSXDeliveryMode:true\ntransformation:jms-byte\ntimestamp:1572278582017\n\nHello World\0\n" );
            await _pipe.Output.WriteAsync( secondMessage );
            await _pipe.Output.FlushAsync();

            var readResult = await _pipe.Input.ReadAsync();
            var buffer = readResult.Buffer;
            while ( TryParseFrame( ref buffer ) )
            {
                // ...
            }

            _pipe.Input.AdvanceTo( buffer.Start, buffer.End );

            Console.ReadLine();
        }

        private static Boolean TryParseFrame( ref ReadOnlySequence<Byte> inputBuffer )
        {
            var endOfFrame = inputBuffer.PositionOf( ByteConstants.Null );
            if ( endOfFrame == null )
                return false;

            var frameBuffer = inputBuffer.Slice( 0, endOfFrame.Value );
            // parse and process the frame...

            // This works....
            //inputBuffer = inputBuffer.Slice( frameBuffer.End );
            //inputBuffer = inputBuffer.Slice( 1 );

            // This does NOT.
            try
            {
                var end = frameBuffer.End.GetInteger();
                var length = inputBuffer.Length;
                Console.WriteLine( $" END: {end}, LENGTH: {length} " );
                inputBuffer = inputBuffer.Slice( end + 1 );
            }
            catch ( Exception ex )
            {
                Console.WriteLine( ex );
                // Make sure we can read the next frame...
                inputBuffer = inputBuffer.Slice( frameBuffer.End );
                inputBuffer = inputBuffer.Slice( 1 );
            }

            return true;
        }
    }

    public class DuplexPipe : IDuplexPipe
    {
        public DuplexPipe( PipeReader input, PipeWriter output )
        {
            Input = input;
            Output = output;
        }

        public PipeReader Input { get; }
        public PipeWriter Output { get; }
    }

    public static class ByteConstants
    {
        public const Byte HeaderDelimiter = 58;
        public const Byte LineFeed = 10;
        public const Byte Null = 0;
    }
}
使用系统;
使用系统缓冲区;
使用系统IO管道;
使用系统文本;
使用System.Threading.Tasks;
名称空间控制台EAPP1
{
公共课程
{
专用静态IDuplexPipe _管道;
公共静态异步任务主(字符串[]args)
{
var管道=新管道();
_管道=新的双工管道(pipe.Reader、pipe.Writer);
var firstMessage=Encoding.UTF8.GetBytes(“已连接\n转换:1.1\n会话:2a840965\n服务器:ActiveMQ Artemis/2.8.0 ActiveMQ Artemis消息传递引擎\n艺术节拍:1000010000\n\n\0\n”);
wait_pipe.Output.WriteAsync(firstMessage);
wait_pipe.Output.FlushAsync();
第二条消息=
Encoding.UTF8.GetBytes(
“\n\n消息\n订阅:test-839c7766-0f38-4579-a3fc-74de35408536子1\n内容长度:4\n消息id:2147486350\n目标:/queue/TestQ\n主题:1572278642017\n已传递:false\n优先级:5\n持续:true\n目标标记:1572278582050\n目标类型:选播\n接收:2\n测试:test\nMSXDeliveryMode:true\n转换:字节\n目标标记:157227”8582017\n\nHello World\0\n“;
wait_pipe.Output.WriteAsync(第二条消息);
wait_pipe.Output.FlushAsync();
var readResult=await_pipe.Input.ReadAsync();
var buffer=readResult.buffer;
while(TryParseFrame(ref缓冲区))
{
// ...
}
_pipe.Input.AdvanceTo(buffer.Start、buffer.End);
Console.ReadLine();
}
私有静态布尔TryParseFrame(ref ReadOnlySequence inputBuffer)
{
var endOfFrame=inputBuffer.PositionOf(ByteConstants.Null);
if(endOfFrame==null)
返回false;
var frameBuffer=inputBuffer.Slice(0,endOfFrame.Value);
//解析并处理框架。。。
//这是有效的。。。。
//inputBuffer=inputBuffer.Slice(frameBuffer.End);
//inputBuffer=inputBuffer.Slice(1);
//但事实并非如此。
尝试
{
var end=frameBuffer.end.GetInteger();
变量长度=inputBuffer.length;
WriteLine($“END:{END},LENGTH:{LENGTH}”);
inputBuffer=inputBuffer.Slice(end+1);
}
捕获(例外情况除外)
{
控制台写入线(ex);
//确保我们可以阅读下一帧。。。
inputBuffer=inputBuffer.Slice(frameBuffer.End);
inputBuffer=inputBuffer.Slice(1);
}
返回true;
}
}
公共类双工管道:IDuplexPipe
{
公共双工管道(管道读取器输入、管道写入器输出)
{
输入=输入;
输出=输出;
}
公共管道读取器输入{get;}
公共PipeWriter输出{get;}
}
公共静态类字节常量
{
public const Byte HeaderDelimiter=58;
公共常量字节换行=10;
公共常量字节Null=0;
}
}

最近我玩了一次管道游戏,很快就放弃了操作
SequencePosition
结构

ReadOnlySequence
公开了一个
GetPosition(Int64,SequencePosition)
方法,其中第一个参数是从提供的
SequencePosition
开始的偏移量(读取的距离)(在您的例子中是为终止的空字节返回的位置)

考虑以下几点:

private static bool TryParseFrame(ref ReadOnlySequence<byte> inputBuffer)
{
    var endOfFrame = inputBuffer.PositionOf(ByteConstants.Null);
    if (endOfFrame == null)
        return false;

    // Get SequencePosition 1 place after endOfFrame
    var sliceEnd = inputBuffer.GetPosition(1, endOfFrame.Value);

    inputBuffer = inputBuffer.Slice(0, sliceEnd);

    return true;
}
私有静态bool TryParseFrame(ref ReadOnlySequence inputBuffer)
{
var endOfFrame=inputBuffer.PositionOf(ByteConstants.Null);
if(endOfFrame==null)
返回false;
//获取序列内帧后的位置1
var sliceEnd=inputBuffer.GetPosition(1,endOfFrame.Value);
inputBuffer=inputBuffer.Slice(0,sliceEnd);
返回true;
}
不确定上述内容是否正是您想要的,但希望
GetPosition()
is能够帮助您


免责声明:我不知道如果您的偏移量超出了
ReadOnlySequence
,会发生什么情况,但我想您在尝试分割缓冲区时会遇到问题。但是,在您的特定情况下,您已经知道字节在那里。

frameBuffer.End+1
?(当然你需要检查IndexOutfrange)@MartinVerjans
frameBuffer.End
是一个
SequencePosition
。尝试添加int会导致以下编译器错误:
error CS0019运算符“+”不能应用于“SequencePosition”和“int”类型的操作数