编程语言
首页 > 编程语言> > 首页> C#> NetworkStream的ReadAsync和读在同一方法

首页> C#> NetworkStream的ReadAsync和读在同一方法

作者:互联网

我正在尝试构建具有持久TCP连接的可伸缩服务器应用程序.我使用的序列化库是同步的,将其转换为APM会导致很大的开销(我已经对其进行了测试).

数据包的格式始终是数据包ID的一个字节,其后是更多的标头字段和有效负载.我想知道是否创建了一个异步方法,例如:

public async Task<Packet> Deserialize(NetworkStream stream)
{
    //Omitting parameters for the methods below for simplicity.
    var id = await stream.ReadAsync();
    var size = stream.Read();
    //Read the rest of the fields synchronously and deserialize.
}

>如果同步,我是否会冒其他插座导致饥饿的风险
对其中之一进行读取会花费过多(由于TCP碎片
例)?
>我想到了通过ReadAsync读取数据包的所有字节(大小是标头中的第二个字段),然后对它们进行反序列化的过程-因为这是一个非阻塞操作-但这迫使我将有效负载反序列化上下文与标头中的一个,这将导致我编写很多重复的代码.如果以上问题的答案是肯定的,是否有更可行的解决方案?

解决方法:

Will I risk to cause starvation for other sockets

如果对于“套接字”,我们读为“线程”,答案是否定的.任务调度是自适应的;如果您的任务是调度程序确定为“长期运行”的任务,则将根据需要将更多工作线程起草到服务中.为任务提供服务的线程池经过精心设计,可以动态响应情况.

主要问题不是您的线程用完了(我的意思是,您创建的应用程序可能停止响应了很多,但可能性很小),而是通过异步I / O抛弃了可扩展性的全部构想.如果同步部分占主导地位,这可能比仅在线程上完成全部操作更糟糕.

Is there a more viable solution

总体思路是通过缓冲使反序列化与读取数据脱钩.读取一些字节,对其进行缓冲,确定您的缓冲区是否保存一个或多个完整的数据包,将其从缓冲区中删除并反序列化(保留一些未反序列化的数据).这要求您要么完全不对数据包进行反序列化就知道该数据包的大小,要么就可以将多个字节提供给反序列化逻辑,这将使您返回错误“需要更多数据”.除非解串器有副作用,否则始终可以使它起作用.

The problem is that for example even just the size is not a simple
numeric value, and even its size isn’t a fixed amount of bytes. I
could create an instance of the deserializer exclusively to read the
size synchronously (just about 3-4 bytes usually), then read the
payload asynchronously and then finally deserialize the latter but
that adds quite some pressure on the GC, as well as making the code
even more divided.

这里有两件事:

>您不需要反序列化大小.只需给解串器提供一整块数据,包括大小,然后看看它会吐出什么.这行不通的唯一方法是,如果坚持要求您递给它确切的字节数(即不是“太多”).但是由于您的解串器是基于流的,所以我认为这不是问题.
>如果您通过使用the appropriate constructor(用于包装数组段而不是复制数据)直接在缓冲区上构造MemoryStream,则GC压力应受到相当大的限制.现在,您只需要担心MemoryStream对象本身,但是通常只创建短暂的对象,这些对象会在第1代中被清除,因此不会太大.

总的想法是这样的:

byte[] buffer;
int offset = 0;
int bytesRead = await Stream.ReadAsync(buffer, offset, buffer.Length - offset);
int bytesRemaining = bytesRead;
while (bytesRemaining != 0 && haveCompletishPacket(buffer, offset, bytesRemaining)) {
    using (var memoryStream = new MemoryStream(buffer, offset, bytesRead)) {
        int size = deserializer.Deserialize(memoryStream);
        // deserialize as much as possible, if you run out of data, 
        // just reinit the deserializer and return

        // if we got here we have a packet, produce it
        offset += memoryStream.Position;
        bytesRemaining -= memoryStream.Position;
    }
 }

确保正确维护缓冲区的细节很容易出错,因此在上面的代码中我可能弄错了.但我希望这个想法很明确.

显然,如果haveCompletishPacket可以100%准确地告诉您缓冲区中是否有完整的数据包,然后再尝试使用反序列化器,这将是最好的选择(如果您的数据包始终以恒定大小的长度类型进行帧化,则有可能),但是只要我们尽力而为,它将“足够好”,只要我们读取足够的数据和数据包不会太大.

标签:networkstream,multithreading,async-await,server,c
来源: https://codeday.me/bug/20191120/2045936.html