【问题标题】:ChannelRead event does not fire in DotNetty's ServerHandler在 DotNetty 的 ServerHandler 中不会触发 ChannelRead 事件
【发布时间】:2023-04-02 00:55:01
【问题描述】:

我正在试验 DotNetty 在企业应用程序中使用它。

在我的基本示例中,当我在 CLIENT 中使用 DotNetty 的 API 调用服务器时,一切都很好,但是当我使用纯 C# TcpClient 时,只有 ChannelReadComplete 会触发,而 ServerHandler 中不会触发 ChannelRead。

ServerHandler:

public class EchoServerHandler : ChannelHandlerAdapter
{
    public override void ChannelRead(IChannelHandlerContext context, object message)
{
    var buffer = message as IByteBuffer;
    if (buffer != null)
    {
        Console.WriteLine("Received from client: " + buffer.ToString(Encoding.UTF8));
    }
    context.WriteAsync(message);
}

public override void ChannelReadComplete(IChannelHandlerContext context) => context.Flush();

public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
{
    Console.WriteLine("Exception: " + exception);
    context.CloseAsync();
  }
 }
}

客户:

TcpClient client = new TcpClient();
client.Connect("127.0.0.1", 8583);

var data = Encoding.UTF8.GetBytes("Hello world");
Stream stm = client.GetStream();

stm.Write(data, 0, data.Length);

byte[] readBytes = new byte[100];
stm.Read(readBytes, 0, readBytes.Length);
stm.Close();
client.Close();

有什么想法吗?

【问题讨论】:

  • 您不能将 127.0.0.1 与作为环回地址的连接方法一起使用。您必须使用真实的 IP 地址。服务端 TCPListener 使用环回地址监听,但在 Net 库中连接 127.0.0.1 会导致异常。
  • @jdweng 好吧,我不是 TCP/IP 专家,但相同的配置适用于 DotNetty 客户端项目。无论如何,我将IP更改为实际IP,并且没有任何异议。 ServerHandler 中仍然只有 ChannelReadComplete 会触发。
  • 客户端可以有两个定义。一个在应用程序级别,您有一个连接到服务器的客户端。另一个定义是在客户端和服务器应用程序都有客户端套接字的套接字级别。我认为您在上面发布的客户端是服务器上的客户端。然后你应该使用 TcpListener(不是 TcpClient),就像下面的网站一样:msdn.microsoft.com/en-us/library/…。在应用程序客户端连接之前不会发生通道读取。您的客户端应用程序在哪里?
  • @jdweng 不,这段代码是客户端应用程序!服务器正在运行,客户端应用程序能够连接到端口并发送数据。甚至服务器也会对请求做出反应,但不是 ChannelRead,而是只有 ChannelReadComplete fiers。
  • TCP 连接是一个流,当流关闭时会发生流结束 TCP 将消息拆分为数据报,数据报的最大大小约为 1500 字节。我认为 DotNettys ChannelReadCompletes 在连接关闭时触发(不确定)并且在连接被应用程序关闭或出现错误时发生。接收到每个数据报时应触发读取。如果在收到数据报后立即关闭连接,您可能只会得到 ChannelReadComplete (好猜测)。请参阅以下帖子:stackoverflow.com/questions/23083868/…

标签: c# .net tcpclient


【解决方案1】:

客户端应用程序没有任何问题。我只需要在 DotNetty ServerBootstrap 配置中删除下面的行(在他们的默认示例中提到)。

pipeline.AddLast(new LengthFieldBasedFrameDecoder(ushort.MaxValue, 0, 2, 0, 2));

感谢完全缺乏文档!

【讨论】:

    【解决方案2】:

    我遇到了类似的问题,我想我找到了真正的问题:Endianness

    默认情况下,DotNetty 是 Big-Endian,BitConverter 是否取决于您的平台架构,因此很可能您正在发送 Little-Endian 消息长度并且您的 LengthFieldBasedFrameDecoder 卡住了(因为仍在等待数据准备好)。例如,我的消息长度为 84 字节。我以 Little-Endian 形式发送长度,以

    结尾

    字节[] {84, 0}

    但是,当从 Big-Endian 编码转换时,它的消息长度为 21504。不足为奇的是,LengthFieldBasedFrameDecoder 卡住了,等待剩下的 (21504-84) 字节数据。

    使用 Big-Endian 编码时:

    字节[] {0, 84}

    计算的长度是正确的:84。这一次,LengthFieldBasedFrameDecoder 准确地找到了它期望的大小并继续执行,将准确的 84 字节数据委托给 ChannelHandlerAdapter(86 减去 2 字节的长度)。

    我认为您的代码在删除该行后可以正常工作,因为它不再需要任何帧而不是实际数据,因此它会读取所有内容并委托给 ChannelHandlerAdapter。不幸的是,这样你将需要自己处理前两个长度字节,这应该让 Netty 为你解决。

    您可以使用两种方法解决此问题:

    • 将 DotNetty 配置为使用 Little-Endian
    • 发送 Big-Endian 消息长度

    我使用第二种方法解决了这个问题。请务必在转换之前检查您的平台是使用小端还是大端(如果您在客户端平台中做出假设,则可能根本不需要):

    var messageLength = BitConverter.GetBytes((ushort)messagePayload.Length);
    
    if (BitConverter.IsLittleEndian)
    {
        messageLength = messageLength.Reverse().ToArray();
    }
    

    正如您所指出的,缺少 DotNetty 文档,所以我不知道如何将其配置为使用 Little-Endian,但无论如何您需要在客户端部分进行转换,所以我认为第二种方法是最好的。

    【讨论】:

      猜你喜欢
      • 2021-07-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-18
      • 2010-11-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多