无法使用netty发送大型zip文件

时间:2014-08-19 22:01:16

标签: netty

我想从客户端向服务器发送一个大型zip文件,然后从服务器到客户端获得收到该文件的回复。我正在使用io.netty.handler.stream.ChunkedWriteHandler发送大文件。我的问题是如何知道服务器何时收到整个数据,因为在服务器端,读取数据的代码似乎无限期地运行。客户端和服务器端的代码如下:

客户端代码是:

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.stream.ChunkedFile;
import io.netty.handler.stream.ChunkedWriteHandler;


public class Client {


    private Bootstrap bootstrap;

    private boolean connected;

    /**
     * Port number of the socket server.
     */
    private final int port;

    /**
     * Host name of the socket server.
     */
    private final String hostName;


    private NioEventLoopGroup nioEventLoopGroup;

    private ChannelFuture futureChannel;


    /**
     * Initialize the socket details
     * 
     */
    public Client(final String hostName, final int port) {
        this.hostName = hostName;
        this.port = port;
        connected = false;
    }

    /**
     * Connects to the host and port.
     * 
     * 
     */
    public void connect() throws GridException {

        this.bootstrap = new Bootstrap();
        nioEventLoopGroup = new NioEventLoopGroup();

        this.bootstrap.group(nioEventLoopGroup).channel(NioSocketChannel.class)
        .handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                ChannelPipeline p = ch.pipeline();
                p.addLast(new ChunkedWriteHandler());
            }
        });

        // Make the connection attempt.
        try {
            futureChannel = bootstrap.connect(hostName, port).sync();
            connected = true;       

        } catch (InterruptedException e) {

        }
    }

    public boolean isConnected() {
        return connected;
    }

    public void close() {
        futureChannel.channel().closeFuture();
        nioEventLoopGroup.shutdownGracefully();
    }


    public void send(final ChunkedFile file) {
        futureChannel.channel().writeAndFlush(file);
    }

}

此类的方法send(final ChunkedFile file)用于发送文件。它的名称如下:

File file  = new File("file1.zip");
ChunkedFile chunkedFile;
try {
    chunkedFile = new ChunkedFile(file);
    client.send(chunkedFile);
} catch (IOException e) {
    e.printStackTrace();
}

服务器端代码是:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.stream.ChunkedWriteHandler;

public class Server {

    private EventLoopGroup eventLoopGroup;
    private EventLoopGroup slaveEventLoopGroup;
    private int packagePort;
    private ChannelHandler fileReqHandler;


    public void start() throws GridException {
        eventLoopGroup = new NioEventLoopGroup();
        slaveEventLoopGroup = new NioEventLoopGroup();
        ServerBootstrap server = null;

        server = new ServerBootstrap();
        server.group(eventLoopGroup, slaveEventLoopGroup)
        .channel(NioServerSocketChannel.class) // (3)
        .childHandler(new ChannelInitializer<SocketChannel>() { // (4)
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
                ch.pipeline().addLast(new ChunkedWriteHandler());
                ch.pipeline().addLast(fileReqHandler);
            }
        })
        .option(ChannelOption.SO_BACKLOG, 128)          // (5)
        .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)

        try {
            server.bind(getPackagePort()).sync();
        } catch (InterruptedException e) {
            throw new GridException(e);
        }        
    }

    @Override
    public void shutdown() throws GridException {
        eventLoopGroup.shutdownGracefully();
        slaveEventLoopGroup.shutdownGracefully();        
    }

    public int getPackagePort() {
        return packagePort;
    }

    public void setPackagePort(int packagePort) {
        this.packagePort = packagePort;
    }

    public ChannelHandler getFileReqHandler() {
        return fileReqHandler;
    }

    public void setFileReqHandler(ChannelHandler fileReqHandler) {
        this.fileReqHandler = fileReqHandler;
    }


}

上面类中定义的ChannelHandler(fileReqHandler)如下:

import java.io.File;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.stream.ChunkedFile;
import io.netty.util.ReferenceCountUtil;

public class FileChunkReqWriteHandler extends SimpleChannelInboundHandler<ChunkedFile> {
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        System.out.println("in channel active method");
    }

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

        if (ctx.channel().isActive()) {
            ctx.writeAndFlush("ERR: " +
                    cause.getClass().getSimpleName() + ": " +
                    cause.getMessage() + '\n').addListener(ChannelFutureListener.CLOSE);
        }
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ChunkedFile msg)
            throws Exception {
        System.out.println("in channelRead0");

    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        byte[] bytes = new byte[buf.readableBytes()];
        buf.readBytes(bytes);
    }

}

我已经覆盖了channelRead()方法来读取文件。问题是channelRead方法在无限循环中被调用,每次只读取少量字节。如何知道客户端发送的整个数据何时被读取?我想阅读从客户端发送的整个数据并重新构建压缩文件。我怎样才能做到这一点?

1 个答案:

答案 0 :(得分:0)

您需要以某种方式检测文件的结尾。例如,您可以编写自己的协议,该协议始终具有文件的预期长度,并且在接收端读取此信息以了解接收结束的时间。