以下是一个使用 Netty 框架处理 TCP 半包问题的示例代码,它演示了一个简单的 C/S 结构网络程序,展示了如何使用 LengthFieldBasedFrameDecoderLengthFieldPrepender 解决粘包和拆包问题。

服务端代码

public class Server {
    public static void main(String[] args) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(1024, 0, 4, 0, 4));
                            ch.pipeline().addLast(new ServerHandler());
                        }
                    });
            ChannelFuture f = bootstrap.bind(8888).sync();
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
}

class ServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        try {
            int length = buf.readInt();
            byte[] content = new byte[length];
            buf.readBytes(content);
            String message = new String(content, CharsetUtil.UTF_8);
            System.out.println('Received message: ' + message);
            ctx.writeAndFlush(Unpooled.copiedBuffer(('Server received: ' + message).getBytes()));
        } finally {
            buf.release();
        }
    }

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

客户端代码

public class Client {
    public static void main(String[] args) throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new LengthFieldPrepender(4));
                            ch.pipeline().addLast(new ClientHandler());
                        }
                    });
            ChannelFuture f = bootstrap.connect('localhost', 8888).sync();
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNextLine()) {
                String message = scanner.nextLine();
                ByteBuf buf = Unpooled.copiedBuffer(message.getBytes());
                f.channel().writeAndFlush(buf);
            }
            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }
}

class ClientHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        try {
            int length = buf.readInt();
            byte[] content = new byte[length];
            buf.readBytes(content);
            String message = new String(content, CharsetUtil.UTF_8);
            System.out.println('Received message: ' + message);
        } finally {
            buf.release();
        }
    }

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

运行结果

启动服务端后,客户端连接上来后可以输入任意字符串,服务端会将收到的字符串打印出来,并回复一个消息。例如:

Client: hello
Server: Received message: hello
Client: world
Server: Received message: world

解释

  • LengthFieldBasedFrameDecoder:用于解码接收到的数据,根据数据包头部长度信息,将粘包的数据分割成多个独立的包。
  • LengthFieldPrepender:用于在发送数据之前添加长度信息,方便服务端解码。

通过以上代码示例,我们可以看到使用 Netty 框架处理 TCP 半包问题非常简单,只需添加相应的解码器和编码器即可。

Netty TCP 半包处理 (粘包、拆包) 示例:C/S 结构网络程序

原文地址: https://www.cveoy.top/t/topic/j56E 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录