Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/267.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# 如何使用Kestrel ConnectionHandler处理传入的TCP消息?_C#_.net Core_Tcp_Kestrel - Fatal编程技术网

C# 如何使用Kestrel ConnectionHandler处理传入的TCP消息?

C# 如何使用Kestrel ConnectionHandler处理传入的TCP消息?,c#,.net-core,tcp,kestrel,C#,.net Core,Tcp,Kestrel,我想为我的.NET核心项目创建一个TCP侦听器。我正在使用Kestrel并为此via配置了一个新的ConnectionHandler kestrelServerOptions.ListenLocalhost(5000, builder => { builder.UseConnectionHandler<MyTCPConnectionHandler>(); }); kestrelServerOptions.ListenLocalhost(5000,生成器=> { Use

我想为我的.NET核心项目创建一个TCP侦听器。我正在使用Kestrel并为此via配置了一个新的
ConnectionHandler

kestrelServerOptions.ListenLocalhost(5000, builder =>
{
    builder.UseConnectionHandler<MyTCPConnectionHandler>();
});
kestrelServerOptions.ListenLocalhost(5000,生成器=> { UseConnectionHandler(); }); 到目前为止,我得到的是

internal class MyTCPConnectionHandler : ConnectionHandler
{
    public override async Task OnConnectedAsync(ConnectionContext connection)
    {
        IDuplexPipe pipe = connection.Transport;
        PipeReader pipeReader = pipe.Input;

        while (true)
        {
            ReadResult readResult = await pipeReader.ReadAsync();
            ReadOnlySequence<byte> readResultBuffer = readResult.Buffer;

            foreach (ReadOnlyMemory<byte> segment in readResultBuffer)
            {
                // read the current message
                string messageSegment = Encoding.UTF8.GetString(segment.Span);

                // send back an echo
                await pipe.Output.WriteAsync(segment);
            }

            if (readResult.IsCompleted)
            {
                break;
            }

            pipeReader.AdvanceTo(readResultBuffer.Start, readResultBuffer.End);
        }
    }
}
内部类MyTCPConnectionHandler:ConnectionHandler
{
公共覆盖异步任务OnConnectedAsync(ConnectionContext连接)
{
IDuplexPipe管道=连接。运输;
PipeReader PipeReader=pipe.Input;
while(true)
{
ReadResult ReadResult=wait pipeReader.ReadAsync();
ReadOnlySequence readResultBuffer=readResult.Buffer;
foreach(readResultBuffer中的ReadOnlyMemory段)
{
//阅读当前消息
string messageSegment=Encoding.UTF8.GetString(segment.Span);
//回音
wait pipe.Output.WriteAsync(段);
}
如果(readResult.IsCompleted)
{
打破
}
pipeReader.AdvanceTo(readResultBuffer.Start、readResultBuffer.End);
}
}
}
当从TCP客户端向服务器应用程序发送消息时,代码工作正常。行
wait pipe.Output.WriteAsync(段)现在起着回音的作用

出现了一些问题

  • 我应该将什么样的响应发送回客户端,以使其不会超时
  • 我应该什么时候回复?当
    readResult.IsCompleted
    返回true时
  • 如何更改代码以获取客户端发送的整个消息?当
    readResult.IsCompleted
    返回true时,我是否应该将每个
    messageSegment
    存储在
    列表中,并将其连接到单个字符串
  • 这完全取决于协议;在很多情况下,你可以什么都不做;在其他情况下,如果你只想说“我还在这里”,则会有特定的“ping”/“pong”帧发送
  • “何时”完全取决于协议;等待
    readResult.IsCompleted
    意味着您正在等待入站套接字标记为关闭,这意味着您在客户端关闭出站套接字之前不会发送任何内容;对于单次激发协议,这可能很好;但在大多数情况下,您需要查找单个入站帧,然后回复该帧(并重复)
  • 听起来您可能确实在编写一个一次性通道,即客户端只向服务器发送一件事,之后:服务器只向客户端发送一件事;在这种情况下,您可以执行以下操作:
  • while(true)
    {
    var readResult=await pipeReader.ReadAsync();
    如果(readResult.IsCompleted)
    {
    //TODO:未显示;处理readResult.Buffer
    //告诉管道我们把所有东西都吃光了,然后离开
    管道读取器.AdvanceTo(readResultBuffer.End,readResultBuffer.End);
    打破
    }
    其他的
    {
    //等待客户端关闭其出站;告诉
    //我们不能消耗任何东西的管道
    pipeReader.AdvanceTo(readResultBuffer.Start、readResultBuffer.End);
    }
    
    至于:

    我是否应该将每个
    消息段
    存储在
    列表中
    并在

    这里首先要考虑的是,每个缓冲段不一定包含精确的字符数。因为使用UTF-8,这是一个多字节编码,一个段可能包含在开始和结束部分的字符,所以:解码它比它有更多的参与。

    因此,通常在缓冲区上检查
    IsSingleSegment
    ;如果这是真的,您可以使用简单的代码:

    if(buffer.IsSingleSegment)
    {
    字符串消息=Encoding.UTF8.GetString(s.FirstSpan);
    DoSomethingWith(消息);
    }
    其他的
    {
    //…更复杂
    }
    
    不连续缓冲区的情况要困难得多;基本上,这里有两种选择:

  • 将段线性化为连续缓冲区,可能从
    ArrayPool.Shared
    租用过大的缓冲区,并在租用缓冲区的正确部分使用
    UTF8.GetString
  • 在编码上使用
    GetDecoder()
    API,并使用该API填充新字符串,这在旧框架中意味着覆盖新分配的字符串,或者在新框架中意味着使用
    字符串。创建
    API
  • 坦率地说,“1”要简单得多。例如(未经测试):

    public static string GetString(在这个ReadOnlySequence负载中,
    编码=空)
    {
    编码???=encoding.UTF8;
    返回payload.IsSingleSegment?encoding.GetString(payload.FirstSpan)
    :GetStringSlow(有效负载、编码);
    静态字符串GetStringSlow(在ReadOnlySequence负载中,编码)
    {
    //线性化
    int length=已检查((int)payload.length);
    var oversized=ArrayPool.Shared.Rent(长度);
    尝试
    {
    有效载荷。复制到(超大);
    返回编码.GetString(超大,0,长度);
    }
    最后
    {
    ArrayPool.Shared.Return(超大);
    }
    }
    }