У меня используется следующая архитектура:
- [Client] - The enduser connecting to our service.
- [GameServer] - The game server on which the game is running.
- [GameLobby] - A server that is responsible for matching Clients with a GameServer.
Если у нас есть, например, 4 Клиента, которые хотят играть в игру и получить соответствие с GameLobby, то в первый раз все эти соединения будут выполнены правильно.
Однако, когда они решают провести повторный матч, один из Клиентов не будет правильно подключаться.
Соединение между всеми Клиентами и GameServer происходит одновременно.
Клиенты, которые реваншируют сначала, удаляют свое текущее соединение с GameServer и снова направляются в лобби.
Это соединение будет установлено успешно, ошибок не будет.Даже используя ChannelFuture, он показывает, что клиентское соединение было установлено правильно, следующие значения извлекаются, чтобы показать, что клиент думает, что соединение было правильным:
- ChannelFuture.isSuccess() = True
- ChannelFuture.isDone() = True
- ChannelFuture.cause() = Null
- ChannelFuture.isCancelled() = False
- Channel.isOpen() = True
- Channel.isActive() = True
- Channel.isRegistered() = True
- Channel.isWritable() = True
Таким образом, соединение было правильно установлено в соответствии с клиентом.Однако на GameServer в SimpleChannelInboundHandler метод ChannelRegistered / ChannelActive никогда не вызывается для этого конкретного клиента.Только для остальных 3 клиентов.
Все 4 клиента, GameServer и лобби работают на одном IP-адресе.
Поскольку это происходит только при (повторном) повторном подключении к GameServer, я подумал, что это связано с неправильным закрытием соединения.В настоящее время это делается с помощью:
try {
group.shutdownGracefully();
channel.closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
На GameServer вызывается ChannelUnregister, таким образом, это работает, и соединение разрывается.
Я попытался добавить слушателей в ChannelFuture неисправнойподключение к каналу, однако в соответствии с channelFuture все работает, но это не так.
Я попытался добавить ChannelOptions, чтобы увеличить количество клиентов, поставленных в очередь на сервер.
GameServer
Сервер GameServer инициализируется следующим образом:
// Create the bootstrap to make this act like a server.
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitialisation(new ClientInputReader(gameThread)))
.option(ChannelOption.SO_BACKLOG, 1000)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childOption(ChannelOption.TCP_NODELAY, true);
bossGroup.execute(gameThread); // Executing the thread that handles all games on this GameServer.
// Launch the server with the specific port.
serverBootstrap.bind(port).sync();
GameServer ClientInputReader
@ChannelHandler.Sharable
public class ClientInputReader extends SimpleChannelInboundHandler<Packet> {
private ServerMainThread serverMainThread;
public ClientInputReader(ServerMainThread serverMainThread) {
this.serverMainThread = serverMainThread;
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("[Connection: " + ctx.channel().id() + "] Channel registered");
super.channelRegistered(ctx);
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, Packet packet) {
// Packet handling
}
}
Неисправное соединение не вызывает ничего из SimpleChannelInboundHandler.Даже ExceptionCaught.
Инициализация канала GameServer
public class ChannelInitialisation extends ChannelInitializer<SocketChannel> {
private SimpleChannelInboundHandler channelInputReader;
public ChannelInitialisation(SimpleChannelInboundHandler channelInputReader) {
this.channelInputReader = channelInputReader;
}
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
// every packet is prefixed with the amount of bytes that will follow
pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
pipeline.addLast(new LengthFieldPrepender(4));
pipeline.addLast(new PacketEncoder(), new PacketDecoder(), channelInputReader);
}
}
Клиент
Клиент, создающий соединение GameServer:
// Configure the client.
group = new NioEventLoopGroup();
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitialisation(channelHandler));
// Start the client.
channel = b.connect(address, port).await().channel();
/* At this point, the client thinks that the connection was succesfully, as the channel is active, open, registered and writable...*/
Инициализация клиента:
public class ChannelInitialisation extends ChannelInitializer<SocketChannel> {
private SimpleChannelInboundHandler<Packet> channelHandler;
ChannelInitialisation(SimpleChannelInboundHandler<Packet> channelHandler) {
this.channelHandler = channelHandler;
}
@Override
public void initChannel(SocketChannel ch) throws Exception {
// prefix messages by the length
ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
ch.pipeline().addLast(new LengthFieldPrepender(4));
// our encoder, decoder and handler
ch.pipeline().addLast(new PacketEncoder(), new PacketDecoder(), channelHandler);
}
}
ClientHandler:
public class ClientPacketHandler extends SimpleChannelInboundHandler<Packet> {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
System.out.println("Channel active: " + ctx.channel().id());
ctx.channel().writeAndFlush(new PacketSetupClientToGameServer());
System.out.println("Sending setup packet to the GameServer: " + ctx.channel().id());
// This is successfully called, as the client thinks the connection was properly made.
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, Packet packet) {
// Reading packets.
}
}
Я ожидаю, что клиент сможет правильно подключиться к серверу.Поскольку другие клиенты правильно подключаются, и клиент ранее мог подключиться просто отлично.
TL; DR: Когда несколько клиентов пытаются создать новое соответствие, существует вероятность, что один, возможноболее того, клиент (-ы) не будет правильно соединяться с сервером после закрытия предыдущего соединения.