Netty对象回显示例服务器channelRead方法无法使用我的自定义对象

时间:2017-03-22 06:52:47

标签: java object echo netty pojo

我最近开始尝试使用Netty来创建视频游戏服务器/客户端关系。我使用Netty 4.1.X源提供的Netty“Object Echo”示例。客户端和服务器包含在单独的项目中。 “VersionInfo”类在服务器和客户端项目中都可用,但具有不同的包名称。

我遇到的问题,当我从客户端向服务器发送自定义对象时,它没有收到。但是,如果我发送一个String对象,它可以很好地工作。

服务器:

    public class Server {
    private int port;

    public Server(int port) {
        this.port = port;
    }

    public void run() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();

            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ServerChannelInitializer())
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            // Bind and start to accept incoming connections.
            ChannelFuture channelFuture = serverBootstrap.bind(port).sync();

            // Wait until the server socket is closed.
            // In this example, this does not happen, but you can do that to gracefully
            // shut down your server.
            channelFuture.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        try {
            new Server(1337).run();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

服务器渠道初始化程序:

    public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {

    public void initChannel(SocketChannel channel) throws Exception {
        ChannelPipeline pipeline = channel.pipeline();

        pipeline.addLast(new ObjectEncoder());
        pipeline.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
        pipeline.addLast(new ObjectEchoServerHandler());
    }
}

对象回应服务器处理程序:

public class ObjectEchoServerHandler extends ChannelInboundHandlerAdapter {

    public static final String VERSION = "1.0.0";

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        System.out.println("[Server] Channel Active");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        // business logic here.
        System.out.println("[Server] channelRead()");

        if (msg instanceof VersionInfo) {
            VersionInfo versionInfo = (VersionInfo) msg;
            String clientVersion = versionInfo.version;
            System.out.println("[Server] Version Info Received! Client Version: " + clientVersion + " Local Version: " + VERSION);

            if (clientVersion.equals(VERSION)) {
                VersionInfo success = new VersionInfo();
                success.versionChecked = true;
                ctx.write(success);
                System.out.println("[Server] Version Check Passed!");

            } else {
                // send client version info with false boolean, meaning server version not the same.
                ctx.write(msg);
                System.out.println("[Server] Version Check Failed!");

            }
        } else if (msg instanceof String) {
            ctx.write("[Server] " + msg);
        } else {
            System.out.println("ERROR! Received unknown object! Class name: " + msg.getClass().getSimpleName());
        }
        //ctx.write(msg);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        System.out.println("ExceptionCaught");
        cause.printStackTrace();
        ctx.close();
    }
}

VersionInfo类(在客户端和服务器上,但包名称不同):

public class VersionInfo {
    public String version = "1.0.0";
    public boolean versionChecked = false;
}

现在转到客户端代码:

public class Client {
    public static void main(String[] args) throws Exception {

        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(workerGroup);
            bootstrap.channel(NioSocketChannel.class);
            bootstrap.option(ChannelOption.TCP_NODELAY, true);
            bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
            bootstrap.handler(new ClientChannelInitializer());

            // Start the client.
            ChannelFuture channelFuture = bootstrap.connect("localhost", 1337).sync(); // (5)

            // Wait until the connection is closed.
            channelFuture.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
        }
    }
}

ClientChannelInitializer:

public class ClientChannelInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    public void initChannel(SocketChannel channel) throws Exception {
        ChannelPipeline pipeline = channel.pipeline();

        pipeline.addLast(new ObjectEncoder());
        pipeline.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
        pipeline.addLast(new ObjectEchoClientHandler());
    }
}

最后是ObjectEchoClientHandler:

public class ObjectEchoClientHandler extends ChannelInboundHandlerAdapter {

    private final String string = "This is a test message! :)";
    private final VersionInfo versionInfo = new VersionInfo();


    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        // Send the first message if this handler is a client-side handler.
        System.out.println("[Client] Sending Version Info");
        ctx.writeAndFlush(versionInfo); // this.. does nothing?
        //ctx.writeAndFlush(string); // this works
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        System.out.println("[Client] channelRead()");

        // Check client version
        if (msg instanceof VersionInfo) {
            VersionInfo versionInfo = (VersionInfo) msg;
            System.out.println("[Client] Received Version Info");

            if (versionInfo.versionChecked) {
                System.out.println("[Client] Version Check Passed!");
            } else {
                ctx.close(); // try closing the connection
                System.out.println("[Client] Version Check Failed!");
            }
        } else if (msg instanceof String) {

            System.out.println(msg.toString());

            // Echo back the received object to the server.
            ctx.write(string);
        } else {
            System.out.println("ERROR! Received unknown object! Class name: " + msg.getClass().getSimpleName());
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

在ObjectEchoClientHandler中发送一个字符串channelActive方法可以正常工作。发送自定义POJO / Object不会调用ObjectEchoServerHandler channelRead方法。

导致这种情况的原因是什么?

编辑#1:按照尼古拉斯的建议添加以下日志记录文字。对于日志调试周围的blockquote感到抱歉。 Stack Overflow编辑器不允许我在没有它的情况下提交它。

更改了VersionInfo类以反映相同的包名称。新课程:

package com.nettytest.iocommon;

public class VersionInfo {
    public String version = "1.0.0";
    public boolean versionChecked = false;
}
发送自定义Pojo后

客户端输出

  

2017年3月22日下午1:09:43 io.netty.handler.logging.LoggingHandler   channelRegistered SEVERE:[id:0x237af0b8] 2017年3月22日注册   1:09:43 PM io.netty.handler.logging.LoggingHandler连接严重:   [id:0x237af0b8] CONNECT:localhost / 127.0.0.1:1337 [客户端]发送   版本信息2017年3月22日下午1:09:43   io.netty.handler.logging.LoggingHandler channelActive SEVERE:[id:   0x237af0b8,L:/127.0.0.1:52830 - R:localhost / 127.0.0.1:1337] ACTIVE
  2017年3月22日下午1:09:43 io.netty.handler.logging.LoggingHandler写道   严重:[id:0x237af0b8,L:/127.0.0.1:52830 -   R:localhost / 127.0.0.1:1337]写:   com.nettytest.iocommon.VersionInfo@7544bac 2017年3月22日下午1:09:43   io.netty.handler.logging.LoggingHandler flush SEVERE:[id:   0x237af0b8,L:/127.0.0.1:52830 - R:localhost / 127.0.0.1:1337] FLUSH

收到自定义Pojo后

服务器输出

  

2017年3月22日下午1:09:43 io.netty.handler.logging.LoggingHandler   channelRegistered [Server] Channel Active SEVERE:[id:0xcdffaece,   L:/127.0.0.1:1337 - R:/127.0.0.1:52830] 2017年3月22日   1:09:43 PM io.netty.handler.logging.LoggingHandler channelActive
  严重:[id:0xcdffaece,L:/127.0.0.1:1337 - R:/127.0.0.1:52830]   ACTIVE

1 个答案:

答案 0 :(得分:0)

调试此方法的一种方法是向服务器管道添加LoggingHandler。像这样:

import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.logging.LogLevel;
....
final LoggingHandler loggingHandler = new LoggingHandler(getClass(), 
LogLevel.ERROR);
....
pipeline.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
pipeline.addLast("logger", loggingHandler);
pipeline.addLast(new ObjectEchoServerHandler());
....

我最初的怀疑是,由于您传递的pojo类型在客户端和服务器上具有不同的包名称(意味着它们不是同一个类),因此客户端上的ObjectDecoder失败,因为它无法在其中找到类类路径。为了使其正常工作,该类必须存在于服务器和客户端类路径中。