Tcp Netty客户端未从fast服务器接收消息

Tcp Netty客户端未从fast服务器接收消息,tcp,netty,broadcast,Tcp,Netty,Broadcast,我有一个示例服务器/客户机应用程序,使用netty进行它们之间的tcp通信 我面临的问题是,当服务器以较慢的速度将消息推送到连接的客户机时,客户机接收所有消息,但当从服务器推送数据之间没有延迟时,客户机不会接收所有消息。这是我的示例服务器代码 public class NettyServer { public NettyServer() throws InterruptedException { final ClientChannelHolder clientChannelHolder

我有一个示例服务器/客户机应用程序,使用netty进行它们之间的tcp通信

我面临的问题是,当服务器以较慢的速度将消息推送到连接的客户机时,客户机接收所有消息,但当从服务器推送数据之间没有延迟时,客户机不会接收所有消息。这是我的示例服务器代码

public class NettyServer {
public NettyServer() throws InterruptedException {
    final ClientChannelHolder clientChannelHolder = new ClientChannelHolder();
    NioEventLoopGroup group = new NioEventLoopGroup();
    ServerBootstrap bootstrap = new ServerBootstrap();
    bootstrap.group(group).channel(NioServerSocketChannel.class)
            .localAddress(new InetSocketAddress(9000)).childHandler(new ChannelInitializer<SocketChannel>() {
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            System.out.println("Client connected");
            ch.pipeline().addLast(new ClientChannelHandler(clientChannelHolder));
        }
    });
    ChannelFuture future = bootstrap.bind().addListener(new ChannelFutureListener(){
        @Override
        public void operationComplete(ChannelFuture future) throws Exception {
            System.out.println("Server bound ");
        }
    });
    synchronized (clientChannelHolder) {
        // wait for client to be conncted
        clientChannelHolder.wait();
    }
    for (int i = 0 ; i < 2; i++) {
        // loop to broadcast to all connected clients
        clientChannelHolder.broadCast(i);
        //Thread.sleep(1000); if I add this sleep client works fine and gets all messages
    }

}

public static void main(String[] args) throws InterruptedException {
    NettyServer nettyServer = new NettyServer();
}

// collection for all connected clients.
public static class ClientChannelHolder {
    private List<Channel> clientChannels = new LinkedList<>();

    private synchronized void addClientChannel(Channel socketChannel) {
        // notify the cleint is connected
        notify();
        clientChannels.add(socketChannel);
    }

    private synchronized void removeClientChannel(SocketChannel socketChannel) {
        clientChannels.remove(socketChannel);
    }

    public void broadCast(int msgId) {
        for (Channel channel : clientChannels) {
            byte[] bytes = ("server msg " + msgId).getBytes(CharsetUtil.UTF_8);
            System.out.println(bytes.length);
            ByteBuf byteBuf = Unpooled.buffer();
            byteBuf.writeByte((byte)1);
            byteBuf.writeInt(1);
            byteBuf.writeInt(bytes.length);
            byteBuf.writeBytes(bytes);
            channel.writeAndFlush(byteBuf).addListener(new GenericFutureListener<Future<? super Void>>() {
                @Override
                public void operationComplete(Future<? super Void> future) throws Exception {
                    if (future.isSuccess()) {
                        System.out.println("Write successfull");
                    }
                }
            });
        }
    }
}


public static class ClientChannelHandler extends ChannelInboundHandlerAdapter{

    private final ClientChannelHolder clientChannelHolder;

    public ClientChannelHandler(ClientChannelHolder clientChannelHolder) {
        this.clientChannelHolder = clientChannelHolder;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        clientChannelHolder.addClientChannel(ctx.channel());
        System.out.println("Client connected");
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
    }


    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf in = (ByteBuf) msg;
        in.markReaderIndex();
        byte version = in.readByte();
        int msgType = in.readInt();
        int msgLength = in.readInt();
        byte[] msgArray = new byte[msgLength];
        if (in.readableBytes() < msgLength) {
            in.resetReaderIndex();
            return;
        }
        in.readBytes(msgArray, 0, msgLength);
        System.out.println(
                "Server received: " + new String(msgArray, CharsetUtil.UTF_8));
    }
}
}
公共类NettyServer{
public NettyServer()抛出InterruptedException{
最终ClientChannelHolder ClientChannelHolder=新ClientChannelHolder();
NioEventLoopGroup组=新的NioEventLoopGroup();
ServerBootstrap bootstrap=newserverbootstrap();
bootstrap.group(group.channel)(NioServerSocketChannel.class)
.localAddress(新的InetSocketAddress(9000)).childHandler(新的ChannelInitializer()){
@凌驾
受保护的void initChannel(SocketChannel ch)引发异常{
System.out.println(“客户端连接”);
ch.pipeline().addLast(新的ClientChannelHandler(clientChannelHolder));
}
});
ChannelFuture=bootstrap.bind().addListener(新的ChannelFutureListener()){
@凌驾
public void operation complete(ChannelFuture future)引发异常{
System.out.println(“服务器绑定”);
}
});
已同步(clientChannelHolder){
//等待连接客户端
clientChannelHolder.wait();
}
对于(int i=0;i<2;i++){
//循环以广播到所有连接的客户端
客户频道持有人。广播(一);
//sleep(1000);如果我添加这个sleep客户端,它可以正常工作并获取所有消息
}
}
公共静态void main(字符串[]args)引发InterruptedException{
NettyServer NettyServer=新NettyServer();
}
//收集所有连接的客户端。
公共静态类ClientChannelHolder{
private List clientChannels=new LinkedList();
专用同步void addClientChannel(通道socketChannel){
//通知cleint已连接
通知();
clientChannels.add(socketChannel);
}
专用同步void removeClientChannel(SocketChannel SocketChannel){
clientChannels.移除(socketChannel);
}
公共无效广播(int msgId){
用于(频道:客户端频道){
字节[]字节=(“服务器消息”+msgId).getBytes(CharsetUtil.UTF_8);
System.out.println(字节.长度);
ByteBuf ByteBuf=unmooled.buffer();
byteBuf.writeByte((字节)1);
byteBuf.writeInt(1);
byteBuf.writeInt(字节.长度);
byteBuf.writeBytes(字节);

channel.writeAndFlush(byteBuf).addListener(新的GenericFutureListenerTL;DR:您编写的客户端处理程序假定通过teToMessageDecoder扩展
,但扩展的
ChannelInboundHandlerAdapter
替代

在Netty中,有不同的方法来读取传入数据,虽然这令人困惑,但这是因为TCP的编写方式,即基于流的方式。这意味着,虽然TCP保证所有数据按顺序进入应用程序,但它不保证任何数据包大小,也不保证传入数据包的大小

对于代码,您从最简单的类扩展而来,即
ChannelInboundHandlerAdapter
。这意味着您的程序在TCP向您的程序提供数据时获取所有传入数据。虽然这通常(但并不总是,请考虑数据包丢失)数据发送间隔1秒很好,当数据包发送得更频繁时,它会崩溃

要想知道为什么会出错,可以想象一个包含两个协议特定数据包的
ByteBuf
,如果执行了代码,那么基本上只读取第一个数据包

解决问题 通过查看原始客户端代码,它看起来像是为扩展
ByteToMessageDecoder
而编写的,而不是
ChannelInboundHandlerAdapter
,从
markReaderIndex
resetReaderIndex
的使用可以看出,因此更改父类(以及重写的方法)是第一步

public class ClientChannelHandler extends ByteToMessageDecoder {
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) { 
        in.markReaderIndex();
        byte version = in.readByte();
        int msgType = in.readInt();
        int msgLength = in.readInt();
        System.out.println(msgLength);
        byte[] msgArray = new byte[msgLength];
        if (in.readableBytes() < msgLength) {
            in.resetReaderIndex();
            return;
        }
        in.readBytes(msgArray, 0, msgLength);
        System.out.println(clientId +
            " Client received: " + new String(msgArray, CharsetUtil.UTF_8));
    }
}
解决消息低于3的问题 与前一个问题相比,解决这个问题很容易。我们只需要在消息太小时返回

public class ClientChannelDecoderHandler extends ByteToMessageDecoder {
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
        if (in.readableBytes() < 1 + 4 + 4) { // byte + int + int
            return;
        }
        in.markReaderIndex();
        ....
公共类ClientChannelDecoderHandler扩展ByteToMessageDecoder{
@凌驾
受保护的无效解码(ChannelHandlerContext ctx、ByteBuf输入、列表输出){
如果(in.readableBytes()<1+4+4){//byte+int+int
返回;
}
in.markReaderIndex();
....
public class ClientChannelDecoderHandler extends ByteToMessageDecoder {
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) { 
        in.markReaderIndex();
        byte version = in.readByte();
        int msgType = in.readInt();
        int msgLength = in.readInt();
        System.out.println(msgLength);
        byte[] msgArray = new byte[msgLength];
        if (in.readableBytes() < msgLength) {
            in.resetReaderIndex();
            return;
        }
        in.readBytes(msgArray, 0, msgLength);
        out.add(clientId +
            " Client received: " + new String(msgArray, CharsetUtil.UTF_8));
    }
}
public static class ClientChannelPrinterHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println((String)msg);
    }
}

// When making your bootstrap netty object:
 .handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                clientChannelHolder.addClientChannel(ch);
                ch.pipeline().addLast(new ClientChannelDecodeHandler(clientId));
                ch.pipeline().addLast(new ClientChannelPrintingHandler(clientId));
            }
        });
public class ClientChannelDecoderHandler extends ByteToMessageDecoder {
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
        if (in.readableBytes() < 1 + 4 + 4) { // byte + int + int
            return;
        }
        in.markReaderIndex();
        ....