如何找出哪个地址导致 Netty 5 UDP 中的异常?

How to find out which address caused exception in Netty 5 UDP?

简介

我是 Java 的 JRakNet, a networking library which implements the basics of the latest protocol RakNet 的创造者。我创建了这个库,这样我就可以为使用 RakNet 的 Minecraft: Pocket Edition 创建一个多人游戏服务器。

问题

然而,我在服务器和客户端遇到的一个问题是处理程序的异常处理。每当发送无效数据包时,都会错误地读取数据并像往常一样抛出异常。但是,我需要找到一种方法来找出导致异常的外部地址。

为什么我无法解决问题

通常,在使用 ServerBootstrap 的 TCP 服务器上,我能够将 boss 和 worker 组分组,所以当异常被捕获时,我知道它来自哪里,因为客户端有自己的专用处理程序给它。但 RakNet 是 UDP,这意味着它是无连接的,数据包可以来自任何地方,所以我只有一个处理程序。因此,每当抛出异常时,我都不知道是谁引起的。我试过有一个被忽略的异常列表,但我认为 非常 效率低下。我试过在代码中使用一个名为 lastSender 的变量,因此每当抛出异常时,它都会查找导致错误的人。这是找出异常原因的好方法吗?或者是否有另一种更好的方法来找出谁在 UDP Netty 服务器上造成了错误?

发布时的源文件

The RakNetClient class at the time of posting

The RakNetClientHandler classa at the time of posting

Here is the whole project code at the time of posting if needed

TL;DR

我需要找到一个好方法来找出哪个地址在原始 Bootstrap Netty UDP server/client 上引起了异常。我已尝试将 ServerBootstrap 与 UDP 一起使用,但它似乎仍将只使用一个通道,如

所述

Netty 有一个不错的线程模型since 4.0

特别

Netty will never call a ChannelHandler's methods concurrently, unless the ChannelHandler is annotated with @Sharable

有轻微的change in 5.0但是

From a developer's point of view, the only thing that changes is that it's no longer guaranteed that a ChannelHandler will always be executed by the same thread. It is, however, guaranteed that it will never be executed by two or more threads at the same time. Furthermore, Netty will also take care of any memory visibility issues that might occur. So there's no need to worry about thread-safety and volatile variables within a ChannelHandler.

你可以利用这个优势,解决问题只需要

static class UdpHandler extends SimpleChannelInboundHandler<DatagramPacket> {
    private InetSocketAddress lastSender;

    @Override
    protected void messageReceived(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
        lastSender = packet.sender();
        throw new RuntimeException();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("Evil sender was: " + lastSender);
    }
}

在引擎盖下 exceptionCaught 方法在 messageReceived 之后直接调用,如

public static void invokeChannelReadNow(final ChannelHandlerContext ctx, final Object msg) {
    try {
        ((AbstractChannelHandlerContext) ctx).invokedThisChannelRead = true;
        ctx.handler().channelRead(ctx, msg); // -> calls messageReceived
    } catch (Throwable t) {
        notifyHandlerException(ctx, t);      // -> calls exceptionCaught
    }
}

(source)

并且局部变量的值仍将包含您在 messageReceived 中设置的值。