我最近开始尝试使用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后客户端输出。
收到自定义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
服务器输出。
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
答案 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失败,因为它无法在其中找到类类路径。为了使其正常工作,该类必须存在于服务器和客户端类路径中。