Netty默认通道管道异常捕获

3

很遗憾,我不理解来自Netty服务器的输出:

BUILD SUCCESSFUL
Total time: 3 seconds
Jul 27, 2014 2:04:44 AM io.netty.handler.logging.LoggingHandler channelRegistered
INFO: [id: 0xcad25a31] REGISTERED
Jul 27, 2014 2:04:44 AM io.netty.handler.logging.LoggingHandler bind
INFO: [id: 0xcad25a31] BIND(0.0.0.0/0.0.0.0:4454)
Jul 27, 2014 2:04:44 AM io.netty.handler.logging.LoggingHandler channelActive
INFO: [id: 0xcad25a31, /0:0:0:0:0:0:0:0:4454] ACTIVE
Jul 27, 2014 2:04:59 AM io.netty.handler.logging.LoggingHandler logMessage
INFO: [id: 0xcad25a31, /0:0:0:0:0:0:0:0:4454] RECEIVED: [id: 0xff40b8a2, /127.0.0.1:37558 => /127.0.0.1:4454]
Jul 27, 2014 2:04:59 AM net.bounceme.dur.netty.ServerHandler <init>
INFO: starting..
Jul 27, 2014 2:04:59 AM io.netty.channel.DefaultChannelPipeline$TailContext exceptionCaught
WARNING: An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
io.netty.handler.codec.TooLongFrameException: Adjusted frame length exceeds 1048576: 2901213193 - discarded
    at io.netty.handler.codec.LengthFieldBasedFrameDecoder.fail(LengthFieldBasedFrameDecoder.java:501)
    at io.netty.handler.codec.LengthFieldBasedFrameDecoder.failIfNecessary(LengthFieldBasedFrameDecoder.java:477)
    at io.netty.handler.codec.LengthFieldBasedFrameDecoder.decode(LengthFieldBasedFrameDecoder.java:403)
    at io.netty.handler.codec.serialization.ObjectDecoder.decode(ObjectDecoder.java:68)
    at io.netty.handler.codec.LengthFieldBasedFrameDecoder.decode(LengthFieldBasedFrameDecoder.java:343)
    at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:241)
    at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:149)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:333)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:319)
    at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:787)
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:125)
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:511)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:468)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:382)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:354)
    at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:116)
    at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
    at java.lang.Thread.run(Thread.java:744)

^Cthufir@dur:~/NetBeansProjects/AgentServer$ 
thufir@dur:~/NetBeansProjects/AgentServer$ 

可能是基于Netty的服务器在某些方面收到了错误的数据,导致出现问题?

客户端代码:

package net.bounceme.dur.client.gui;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import net.bounceme.dur.client.jdbc.Title;

public final class ApplicationDriver {

    private static final Logger log = Logger.getLogger(ApplicationDriver.class.getName());
    private TitlesGUI gui = null;
    private Handler handler = null;

    public ApplicationDriver() throws IOException, ClassNotFoundException {
        handler = new FileHandler("application.log");
        handler.setFormatter(new SimpleFormatter());
        log.setLevel(Level.INFO);
        log.addHandler(handler);
        log.info("starting log..");
        MyProps p = new MyProps();
        String host = p.getHost();
        int port = p.getServerPort();
        guiThread();
        readWrite(host, port);
    }

    private void guiThread() {
        Thread g;
        g = new Thread() {
            @Override
            public void run() {
                try {
                    gui = new TitlesGUI();
                } catch (IOException ex) {
                    log.severe(ex.toString());
                }
                gui.setVisible(true);
            }
        };
        g.start();
    }

    public static void main(String... args) throws IOException, ClassNotFoundException {
        new ApplicationDriver();
    }

    private void readWrite(final String host, final int port) throws IOException {
        Thread inputOutput;
        final Socket socket = new Socket(host, port);
        inputOutput = new Thread() {
            @Override
            public void run() {
                while (true) {
                    try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
                            ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream())) {
                        gui.setTitle((Title) objectInputStream.readObject());
                        Thread.sleep(1000);
                    } catch (IOException | ClassNotFoundException | InterruptedException ex) {
                        log.severe(ex.toString());
                    }
                }
            }

        };
        inputOutput.start();
    }
}

客户端使用常规套接字而不是Netty是否有问题?在客户端和服务器端都发送POJO对象。(Title类是可序列化的,serialVersionUID值相匹配。)

来自GUI客户端的方法(有点大,它是一个Netbeans Swing JFrame):

public void setTitle(Title title) {
    this.title = title;
    text.setText(title.toString());
}

以上方法的要点是将对象发送到GUI,然后相应地更新。同样地,我想触发更新或以其他方式将GUI连接到套接字I/O。

我真的不理解netty服务器的输出。服务器使用netty而客户端使用sockets是否有问题?两者都使用相同的POJO,具有serialVersionUID值。下面是netty handler代码:

package net.bounceme.dur.netty;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.util.logging.Logger;
import net.bounceme.dur.jdbc.Title;

public class ServerHandler extends SimpleChannelInboundHandler<Title> {

    private static final Logger log = Logger.getLogger(ServerHandler.class.getName());

    public ServerHandler() {
        log.info("starting..");
    }

    @Override
    public boolean acceptInboundMessage(Object msg) throws Exception {
        log.info(msg.toString());
        return true;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        log.info(msg.toString());
        ctx.write(new Title());
    }

    @Override
    protected void channelRead0(ChannelHandlerContext chc, Title title) throws Exception {
        log.info(title.toString());
        chc.write(new Title());
    }
}

显然,在客户端连接后立即发生了一切崩溃,因此没有执行任何服务器处理程序代码。

服务器代码:

package net.bounceme.dur.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import java.security.cert.CertificateException;
import java.util.logging.Logger;
import javax.net.ssl.SSLException;

public final class Server {

    private static final Logger log = Logger.getLogger(Server.class.getName());

    public static void main(String[] args) throws Exception {
        MyProps p = new MyProps();
        int port = p.getServerPort();
        new Server().startServer(port, false);
    }

    private void startServer(int port, boolean ssl) throws CertificateException, SSLException, InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline p = ch.pipeline();
                            p.addLast(
                                    new ObjectEncoder(),
                                    new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
                                    new ServerHandler());
                        }
                    });
            b.bind(port).sync().channel().closeFuture().sync();
            log.info("connected!");
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

2
乍一看,我会说你的服务器处理程序需要处理异常。目前,异常通过管道被刷新并在最后掉落。覆盖方法`exceptionCaught'并处理异常。我不认为它接收到了错误数据。只是对于当前设置来说太大了。 - Moh-Aw
@Moh-Aw [java] 2014年7月27日 上午9:25:02 net.bounceme.dur.netty.ServerHandler exceptionCaught [java] 信息: io.netty.handler.codec.TooLongFrameException: 调整后的帧长度超过1048576:2901213193 - 已丢弃现在服务器上--所以,看起来更好了。谢谢。 - Thufir
1个回答

6
LengthFieldBasedFrameDecoder引发的TooLongFrameException意味着以下情况之一:
  • 远程对等方发送了超过限制的非常大的消息。默认消息最大长度为1 MiB。如果您期望接收大于该值的消息,请在构造LengthFieldBasedFrameDecoder时指定替代最大长度。
  • 您向LengthFieldBasedFrameDecoder传递了错误的参数,因此它正在解码消息中的错误位置。在这种情况下,您最好重新阅读LengthFieldBasedFrameDecoder的Javadoc以指定正确的值。

谢谢回复。我会再回来看看这个问题。我可以正常地发送数据报,所以可能需要这样做?当然,我更愿意使用POJO。我可能会在本周晚些时候开一个新的问题。在接受答案之前,我需要阅读一些内容,但那很有道理。一个奇怪的地方是这些都是小的POJO,只是String Beans,可能只有五个字段。 - Thufir
感谢@trustin的帮助,"消息的默认最大长度为1 MiB"真的很有用 :) - Lrrr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接