Netty Client соединяется с сервером, но сервер не запускает канал Активен / зарегистрирован - PullRequest
3 голосов
/ 24 сентября 2019

У меня используется следующая архитектура:

- [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: Когда несколько клиентов пытаются создать новое соответствие, существует вероятность, что один, возможноболее того, клиент (-ы) не будет правильно соединяться с сервером после закрытия предыдущего соединения.

...