Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使用websocket ReceiveAsync(Memory<;byte>;)和MemoryPool<;字节>;_C#_Sockets_.net Core - Fatal编程技术网

C# 如何使用websocket ReceiveAsync(Memory<;byte>;)和MemoryPool<;字节>;

C# 如何使用websocket ReceiveAsync(Memory<;byte>;)和MemoryPool<;字节>;,c#,sockets,.net-core,C#,Sockets,.net Core,我有一个ClientWebSocket通过ReceiveAsync(ArraySegment buffer,CancellationToken CancellationToken)方法接收数据块: while (webSocket.State == WebSocketState.Open && cancellationToken.IsCancellationRequested == false) { var result = await webSocket.Receive

我有一个
ClientWebSocket
通过
ReceiveAsync(ArraySegment buffer,CancellationToken CancellationToken)
方法接收数据块:

while (webSocket.State == WebSocketState.Open && cancellationToken.IsCancellationRequested == false)
{
    var result = await webSocket.ReceiveAsync(buffer, cancellationToken);
    var isEndOfMessage = resultProcessor.Receive(result, buffer, out var frame);

    if (isEndOfMessage)
    {
        if (frame == null)
            break; // End of message with no data means socket closed - break.
        else
            await this.targetBlock.SendAsync(frame); // Process It.
    }
}
我将部分帧块存储在一个
只读序列中

public bool Receive(WebSocketReceiveResult result, ArraySegment<byte> buffer, out ReadOnlySequence<byte> frame)
{
    if (result.EndOfMessage && result.MessageType == WebSocketMessageType.Close)
    {
        frame = default;
        return false;
    }

    var slice = buffer
                    .Slice(0, result.Count)
                    .ToArray(); // Take a local copy to avoid corruption as buffer is reused by caller.

    if (startChunk == null)
        startChunk = currentChunk = new Chunk<byte>(slice);
    else
        currentChunk = currentChunk.Add(slice);
        
    if(result.EndOfMessage && startChunk != null)
    {
        if (startChunk.Next == null)
            frame = new ReadOnlySequence<byte>(startChunk.Memory);
        else
            frame = new ReadOnlySequence<byte>(startChunk, 0, currentChunk, currentChunk.Memory.Length);

        startChunk = currentChunk = null; // Reset so we can accept new chunks from scratch.
        return true;
    }
    else
    {
        frame = default;
        return false;
    }
}    
在我当前的配置中,我在
ArraySegment
和我的区块结构之间使用新数组进行复制,以避免内存损坏(因为缓冲区不断被套接字覆盖)

我看到了
webSocket.ReceiveAsync
替代方法,它使用
内存。我认为,使用这一范式的高层次过程将是:

  • 从内存池管理器请求内存块,让
    websocket.ReciveAsync
    写入此租用内存
  • WebSocketReceiveResultProcessor
    函数中,将内存链接成块(我们的
    ReadOnlySequenceSegment
    实现)
  • 发送到应用程序(即解码json或类似文件)
  • 将内存返回到内存池管理器
  • 伪代码如下:

    while (webSocket.State == WebSocketState.Open && cancellationToken.IsCancellationRequested == false)
    {
        var buffer = memoryPoolManager.Rent();
        var result = await webSocket.ReceiveAsync(buffer, cancellationToken);
    
        // Modify the resultProcessor.Receive to receive Memory<byte> and return 
        // the same ReadOnlySequenceSegment implementation of Chunk chains.
        var isEndOfMessage = resultProcessor.Receive(result, buffer, out var frame);
    
        if (isEndOfMessage)
        {
            if (frame == null)
                break; // End of message with no data means socket closed - break.
            else
                await this.targetBlock.SendAsync(frame); // Process It.
    
            // Go through the loop of chunks and return back to the memory pool.
        }
    }
    
    while(webSocket.State==WebSocketState.Open&&cancellationToken.IsCancellationRequested==false)
    {
    var buffer=memoryPoolManager.Rent();
    var结果=等待webSocket.ReceiveAsync(缓冲区、取消令牌);
    //修改resultProcessor.Receive以接收内存并返回
    //块链的相同ReadOnlySequenceSegment实现。
    var isEndOfMessage=resultProcessor.Receive(结果、缓冲区、输出var帧);
    如果(isEndOfMessage)
    {
    if(frame==null)
    break;//没有数据的消息结尾表示套接字已关闭-break。
    其他的
    等待这个.targetBlock.SendAsync(frame);//处理它。
    //遍历区块循环并返回内存池。
    }
    }
    

    我正在寻找已知的github代码,用于使用websocket的这个dequeing/过程,请澄清上述过程。提前感谢。

    我通过以下帮助程序功能实现了这一点:

    class Chunk<T> : ReadOnlySequenceSegment<T>
    {
        public Chunk(ReadOnlyMemory<T> memory)
        {
            Memory = memory;
        }
        public Chunk<T> Add(ReadOnlyMemory<T> mem)
        {
            var segment = new Chunk<T>(mem)
            {
                RunningIndex = RunningIndex + Memory.Length
            };
    
            Next = segment;
            return segment;
        }
    }
    
    sealed class WebSocketReceiveResultProcessor : IDisposable
    {
        Chunk<byte> startChunk = null;
        Chunk<byte> currentChunk = null;
        private readonly bool isUsingArrayPool;
    
        public WebSocketReceiveResultProcessor(bool isUsingArrayPool)
        {
            this.isUsingArrayPool = isUsingArrayPool;
        }
    
        public bool Receive(WebSocketReceiveResult result, ArraySegment<byte> buffer, out ReadOnlySequence<byte> frame)
        {
            if (result.EndOfMessage && result.MessageType == WebSocketMessageType.Close)
            {
                frame = default;
                return false;
            }
    
            // If not using array pool, take a local copy to avoid corruption as buffer is reused by caller.
            var slice = isUsingArrayPool ? buffer.Slice(0, result.Count) : buffer.Slice(0, result.Count).ToArray();
    
            if (startChunk == null)
                startChunk = currentChunk = new Chunk<byte>(slice);
            else
                currentChunk = currentChunk.Add(slice);
    
            if (result.EndOfMessage && startChunk != null)
            {
    
                if (startChunk.Next == null)
                    frame = new ReadOnlySequence<byte>(startChunk.Memory);
                else
                    frame = new ReadOnlySequence<byte>(startChunk, 0, currentChunk, currentChunk.Memory.Length);
    
                startChunk = currentChunk = null; // Reset so we can accept new chunks from scratch.
                return true;
            }
            else
            {
                frame = default;
                return false;
            }
        }
    
        public void Dispose()
        {
            if (this.isUsingArrayPool)
            {
                var chunk = startChunk;
    
                while (chunk != null)
                {
                    if (MemoryMarshal.TryGetArray(chunk.Memory, out var segment))
                        ArrayPool<byte>.Shared.Return(segment.Array);
    
                    chunk = (Chunk<byte>)chunk.Next;
                }
            }
    
            // Suppress finalization.
            GC.SuppressFinalize(this);
        }
    }
    
    如果使用阵列池(
    isUsingArrayPool=true
    ),则
    调度
    调用中的高级函数必须返回缓冲区:

    // Where frame is the ReadOnlySequence<byte> passed into dispatch function.
    foreach (var chunk in frame)
    {
        if (MemoryMarshal.TryGetArray(chunk, out var segment))
            ArrayPool<byte>.Shared.Return(segment.Array);
    }
    
    //其中frame是传递到dispatch函数的ReadOnlySequence。
    foreach(框架中的变量块)
    {
    if(MemoryMarshal.TryGetArray(块,out-var段))
    ArrayPool.Shared.Return(segment.Array);
    }
    
    池中的内存缓冲区是一个完美的解决方案,因为-除了转换或传输之外,您实际上不需要这些字节。我的意思是,你要么通过压缩、加密等方式转换这些数据,要么将其反序列化为某个结构化流/对象,要么保持原样,在你的应用程序中进一步发送它们。这两种情况都适用于任何阵列,无论是来自池还是LOH中的新阵列。他们不会管理它们,也不会改变它们,只是阅读。总共-消除所有ToArray()调用并使用预分配的缓冲区。
    // Snipped the rest of the connection class.
    byte[] GetBuffer()
    {
        if (this.isUsingArrayPool)
            return ArrayPool<byte>.Shared.Rent(ReceiveChunkSize); // Rent a buffer.
        else
        {
            if(this.internalBufferIfArrayPoolNotUsed == null)
                this.internalBufferIfArrayPoolNotUsed = new byte[ReceiveChunkSize];
    
            return this.internalBufferIfArrayPoolNotUsed;
        }
    }
    
    async Task Loop(IEnumerable<string> tickers, Func<ReadOnlySequence<byte>,Task> dispatch, CancellationToken cancellationToken)
    {           
        while (cancellationToken.IsCancellationRequested == false)
        {
            var webSocket = new ClientWebSocket();
            webSocket.Options.KeepAliveInterval = this.keepAliveInterval;
            var resultProcessor = new WebSocketReceiveResultProcessor(this.isUsingArrayPool);
    
            try
            {
                this.logger.LogInformation($"Connecting.");
                await webSocket.ConnectAsync(uri, cancellationToken);           
    
                while (webSocket.State == WebSocketState.Open && cancellationToken.IsCancellationRequested == false)
                {
                    var buffer = GetBuffer();
                    var result = await webSocket.ReceiveAsync(buffer, cancellationToken);
                    var isEndOfMessage = resultProcessor.Receive(result, buffer, out var frame);
    
                    if (isEndOfMessage)
                    {
                        if (frame.IsEmpty == true)
                            break; // End of message with no data means socket closed - break so we can reconnect.
                        else
                            await dispatch(frame);
                    }
                }
            }
            catch (WebSocketException ex)
            {
                this.logger.LogError(ex, ex.Message);
            }
            catch (Exception ex)
            {
                this.logger.LogCritical(ex, ex.Message);
                return;
            }
            finally
            {
                webSocket.Dispose();
                resultProcessor.Dispose();
            }
        }
    }
    
    // Where frame is the ReadOnlySequence<byte> passed into dispatch function.
    foreach (var chunk in frame)
    {
        if (MemoryMarshal.TryGetArray(chunk, out var segment))
            ArrayPool<byte>.Shared.Return(segment.Array);
    }