Netty:TCP客户端服务器文件传输:异常工具OngFrameException:

Netty:TCP客户端服务器文件传输:异常工具OngFrameException:,tcp,netty,file-transfer,Tcp,Netty,File Transfer,我是netty的新手,我正在尝试设计一个解决方案,如下所示,用于通过TCP将文件从服务器传输到客户端: 1. Zero copy based file transfer in case of non-ssl based transfer (Using default region of the file) 2. ChunkedFile transfer in case of SSL based transfer. 客户端-服务器文件传输的工作方式如下: 1. The client sends

我是netty的新手,我正在尝试设计一个解决方案,如下所示,用于通过TCP将文件从服务器传输到客户端:

1. Zero copy based file transfer in case of non-ssl based transfer (Using default region of the file)
2. ChunkedFile transfer in case of SSL based transfer.
客户端-服务器文件传输的工作方式如下:

1. The client sends the location of the file to be transfered
2. Based on the location (sent by the client) the server transfers the file to the client
文件内容可以是任何内容(字符串/图像/pdf等)和任何大小

现在,我在服务器端得到了这个toolongframeeexception:,尽管服务器只是在解码从客户端接收到的路径,以便运行下面提到的代码(服务器/客户端)

现在,我的问题是:

  • 编码器和解码器的顺序及其配置是否有误?如果是这样,正确的配置方法是什么,以便从服务器接收文件
  • 我浏览了一些相关的StackOverflow帖子。我了解了LengthFieldBasedDecoder,但不知道如何在服务器(编码端)配置相应的LengthFieldPrepender。甚至有必要吗
  • 请给我指一下正确的方向

    文件客户端:

    public final class FileClient {
    
        static final boolean SSL = System.getProperty("ssl") != null;
        static final int PORT = Integer.parseInt(System.getProperty("port", SSL ? "8992" : "8023"));
        static final String HOST = System.getProperty("host", "127.0.0.1");
    
        public static void main(String[] args) throws Exception {
            // Configure SSL.
            final SslContext sslCtx;
            if (SSL) {
                SelfSignedCertificate ssc = new SelfSignedCertificate();
                sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
            } else {
                sslCtx = null;
            }
    
            // Configure the client
            EventLoopGroup group = new NioEventLoopGroup();
    
            try {
    
                Bootstrap b = new Bootstrap();
                b.group(group)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.SO_KEEPALIVE, true)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        if (sslCtx != null) {
                            pipeline.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT));
                        }
                        pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(64*1024, 0, 8));
                        pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
                        pipeline.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
                        pipeline.addLast(new ObjectEncoder());
                        pipeline.addLast( new FileClientHandler());                }
                 });
    
    
                // Start the server.
                ChannelFuture f = b.connect(HOST,PORT).sync();
    
                // Wait until the server socket is closed.
                f.channel().closeFuture().sync();
            } finally {
                // Shut down all event loops to terminate all threads.
                group.shutdownGracefully();
            }
        }
    }
    
    文件服务器:

    /**
     * Server that accept the path of a file and echo back its content.
     */
    public final class FileServer {
    
        static final boolean SSL = System.getProperty("ssl") != null;
        static final int PORT = Integer.parseInt(System.getProperty("port", SSL ? "8992" : "8023"));
    
        public static void main(String[] args) throws Exception {
            // Configure SSL.
            final SslContext sslCtx;
            if (SSL) {
                SelfSignedCertificate ssc = new SelfSignedCertificate();
                sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
            } else {
                sslCtx = null;
            }
    
            // Configure the server.
            EventLoopGroup bossGroup = new NioEventLoopGroup(1);
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            try {
                ServerBootstrap b = new ServerBootstrap();
                b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
                        .option(ChannelOption.SO_KEEPALIVE, true).handler(new LoggingHandler(LogLevel.INFO))
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            public void initChannel(SocketChannel ch) throws Exception {
                                ChannelPipeline pipeline = ch.pipeline();
                                if (sslCtx != null) {
                                    pipeline.addLast(sslCtx.newHandler(ch.alloc()));
                                }
                                pipeline.addLast("frameDecoder",new LengthFieldBasedFrameDecoder(64*1024, 0, 8));
                                pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
                                pipeline.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
                                pipeline.addLast(new ObjectEncoder());
    
                                pipeline.addLast(new ChunkedWriteHandler());
                                pipeline.addLast(new FileServerHandler());
                            }
                        });
    
                // Start the server.
                ChannelFuture f = b.bind(PORT).sync();
    
                // Wait until the server socket is closed.
                f.channel().closeFuture().sync();
            } finally {
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        }
    }
    
    /**
    *接受文件路径并回显其内容的服务器。
    */
    公共最终类文件服务器{
    静态最终布尔SSL=System.getProperty(“SSL”)!=null;
    静态final int PORT=Integer.parseInt(System.getProperty(“PORT”,SSL?“8992”):“8023”);
    公共静态void main(字符串[]args)引发异常{
    //配置SSL。
    最终SslContext sslCtx;
    如果(SSL){
    SelfSignedCertificate ssc=新的SelfSignedCertificate();
    sslCtx=SslContextBuilder.forServer(ssc.certificate(),ssc.privateKey()).build();
    }否则{
    sslCtx=null;
    }
    //配置服务器。
    EventLoopGroup bossGroup=新的NioEventLoopGroup(1);
    EventLoopGroup workerGroup=新的NioEventLoopGroup();
    试一试{
    ServerBootstrap b=新的ServerBootstrap();
    b、 组(bossGroup,workerGroup).channel(nioserverssocketchannel.class)
    .option(ChannelOption.SO_KEEPALIVE,true).handler(新的LoggingHandler(LogLevel.INFO))
    .childHandler(新的通道初始值设定项(){
    @凌驾
    public void initChannel(SocketChannel ch)引发异常{
    ChannelPipeline=通道管道();
    如果(sslCtx!=null){
    pipeline.addLast(sslCtx.newHandler(ch.alloc());
    }
    addLast(“frameDecoder”,新的LengthFieldBasedFrameDecoder(64*1024,0,8));
    pipeline.addLast(“帧编码器”,新的LengthFieldPrepender(4));
    addLast(新的ObjectDecoder(ClassResolvers.cacheDisabled(null));
    addLast(新的ObjectEncoder());
    addLast(新的ChunkedWriteHandler());
    addLast(新的FileServerHandler());
    }
    });
    //启动服务器。
    ChannelFuture f=b.bind(PORT.sync();
    //等待服务器套接字关闭。
    f、 通道().closeFuture().sync();
    }最后{
    bossGroup.shutdownGracefully();
    workerGroup.shutdownGracefully();
    }
    }
    }
    
    文件服务器处理程序:

    public class FileServerHandler extends ChannelInboundHandlerAdapter {
    
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object obj) throws Exception {
            RandomAccessFile raf = null;
            long length = -1;
            try {
                ByteBuf buff = (ByteBuf)obj;
    
                byte[] bytes = new byte[buff.readableBytes()];
                buff.readBytes(bytes);
    
                String msg = new String(bytes);
    
                raf = new RandomAccessFile(msg, "r");
                length = raf.length();
            } catch (Exception e) {
                ctx.writeAndFlush("ERR: " + e.getClass().getSimpleName() + ": " + e.getMessage() + '\n');
                return;
            } finally {
                if (length < 0 && raf != null) {
                    raf.close();
                }
            }
    
            if (ctx.pipeline().get(SslHandler.class) == null) {
                // SSL not enabled - can use zero-copy file transfer.
                ctx.writeAndFlush(new DefaultFileRegion(raf.getChannel(), 0, length));
            } else {
                // SSL enabled - cannot use zero-copy file transfer.
                ctx.writeAndFlush(new ChunkedFile(raf));
            }
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            cause.printStackTrace();
            System.out.println("Exception server.....");
        }
    }
    
    公共类FileServerHandler扩展ChannelInboundHandlerAdapter{
    @凌驾
    public void channelRead(ChannelHandlerContext ctx,Object obj)引发异常{
    RandomAccessFile raf=null;
    长长度=-1;
    试一试{
    ByteBuf buff=(ByteBuf)obj;
    byte[]bytes=新字节[buff.readableBytes()];
    buff.readBytes(字节);
    String msg=新字符串(字节);
    raf=新随机访问文件(msg,“r”);
    长度=raf.length();
    }捕获(例外e){
    ctx.writeAndFlush(“错误:“+e.getClass().getSimpleName()+”:“+e.getMessage()+”\n”);
    回来
    }最后{
    if(长度<0&&raf!=null){
    raf.close();
    }
    }
    if(ctx.pipeline().get(SslHandler.class)==null){
    //SSL未启用-可以使用零拷贝文件传输。
    writeAndFlush(新的DefaultFileRegion(raf.getChannel(),0,length));
    }否则{
    //启用SSL-无法使用零拷贝文件传输。
    writeAndFlush(新的ChunkedFile(raf));
    }
    }
    @凌驾
    公共无效例外情况(ChannelHandlerContext ctx,可丢弃原因){
    cause.printStackTrace();
    System.out.println(“异常服务器…”);
    }
    }
    

    我参考了

    中的示例并对其进行了编码。您的服务器/客户端存在多个问题。首先是SSL,对于客户端,您不需要为服务器初始化SslContext,而是可以执行以下操作:

    sslCtx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
    
    new LengthFieldBasedFrameDecoder(64 * 1024, 0, 4, 0, 4)
    
    //to encode the String
    string.getBytes(StandardCharsets.UTF_8);
    
    //to decode the String
    new String(bytes, StandardCharsets.UTF_8);
    
    在服务器端,您使用的是
    SelfSignedCertificate
    ,它本身并没有错,但希望提醒您,它只应用于调试目的,而不应用于生产。此外,您还使用了
    channel选项。因此_KEEPALIVE
    不推荐使用,因为KEEPALIVE间隔取决于操作系统。此外,您还向管道中添加了
    对象En-/Decoder
    ,在您的情况下,该管道没有做任何有用的事情,因此您可以删除它们

    此外,由于参数列表不完整且错误,您的
    LengthFieldBasedFrameDecoder
    配置错误。在中,您需要定义
    lengthFieldLength
    initialBytesToStrip
    的构造函数版本。除了不剥离长度字段外,您还定义了错误的
    lengthFieldLength
    ,它应该与
    LengthFieldPrepender
    lengthFieldLength相同
    
    new LengthFieldBasedFrameDecoder(64 * 1024, 0, 4, 0, 4)
    
    //to encode the String
    string.getBytes(StandardCharsets.UTF_8);
    
    //to decode the String
    new String(bytes, StandardCharsets.UTF_8);
    
    new ChunkedNioFile(randomAccessFile.getChannel())
    
    public class FileTransferHandler extends SimpleChannelInboundHandler<ByteBuf> {
    
        private final Path path;
        private final int size;
        private final int hash;
    
        private OutputStream outputStream;
        private int writtenBytes = 0;
        private byte[] buffer = new byte[0];
    
        protected FileTransferHandler(Path path, int size, int hash) {
            this.path = path;
            this.size = size;
            this.hash = hash;
        }
    
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {
            if(this.outputStream == null) {
                Files.createDirectories(this.path.getParent());
                if(Files.exists(this.path))
                    Files.delete(this.path);
                this.outputStream = Files.newOutputStream(this.path, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
            }
    
            int size = byteBuf.readableBytes();
            if(size > this.buffer.length)
                this.buffer = new byte[size];
            byteBuf.readBytes(this.buffer, 0, size);
    
            this.outputStream.write(this.buffer, 0, size);
            this.writtenBytes += size;
    
            if(this.writtenBytes == this.size && MurMur3.hash(this.path) != this.hash) {
                System.err.println("Received file has wrong hash");
                return;
            }
        }
    
        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            if(this.outputStream != null)
                this.outputStream.close();
        }
    }