使用Netty进行文件传输

一、简介

使用Netty进行文件传输主要涉及到FileChannel文件通道,它用来连接文件,可以通过这个通道读写文件。在使用FileChannel之前必须先打开它,FileChannel无法直接打开,可以通过InputStream、OutputStream或RandomAccessFile来获取FileChannel实例,比如:

RandomAccessFile file=new RandomAccessFile("D:\test.txt","rw");
FileChannel channel=file.getChannel();

如果需要从FileChannel中读取数据,要申请一个ByteBuffer,将数据从FileChannel读取到缓冲区中。read()方法返回的int值表示有多少个字节被读取到了缓冲区中,如果返回-1,表示读取到了文件末尾。

如果需要通过FileChannel向文件中写入数据,需要将数据复制或直接写入到ByteBuffer中,然后调用FileChannel.write()方法进行写操作,比如:

String content="test filechannel";
ByteBuffer writeBuffer=ByteBuffer.allocate(1024);
writeBuffer.put(content.getBytes());
writeBuffer.flip();
channel.write(buf);

使用完FileChannel之后需要通过close()方法关闭文件句柄,否则可能出现句柄泄漏。

可以通过FileChannel的position(long pos)方法设置文件的位置指针,通过这种方式可以实现文件的随机读写。

二、Netty文件传输服务端

实现步骤如下:

1)Netty文件服务器启动,绑定8888作为内部监听端口;

2)在命令提示符窗口,通过telnet和文件服务器建立TCP连接;

3)在控制台输入需要下载的文件的绝对路径;

4)文件服务器接收到请求后进行合法性判断,如果文件存在,就将文件发送给控制台;

5)控制台打印文件名和文件内容。

下面是服务端实现:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
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.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.CharsetUtil;

public class FileServer
{
    public void run (int port)throws Exception{
        EventLoopGroup bossGroup=new NioEventLoopGroup();
        EventLoopGroup workerGroup=new NioEventLoopGroup();
        try
        {
            ServerBootstrap b=new ServerBootstrap();
            b.group(bossGroup,workerGroup)
            .channel(NioServerSocketChannel.class)
            .option(ChannelOption.SO_BACKLOG, 100)
            .handler(new LoggingHandler(LogLevel.INFO))
            .childHandler(new ChannelInitializer<SocketChannel>()
            {

                @Override
                protected void initChannel(SocketChannel ch)
                    throws Exception
                {
                    ch.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8),
                                          //按照回车换行符对数据包进行解码
                                          new LineBasedFrameDecoder(1024),
                                          new StringDecoder(CharsetUtil.UTF_8),
                                          new FileServerHandler());
                }
            });
            ChannelFuture f=b.bind(port).sync();
            System.out.println("Start file server at port : "+port);
            f.channel().closeFuture().sync();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally{
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
    
    public static void main(String[] args)throws Exception
    {
        int port =8888;
        try
        {
            if (args!=null&&args.length>0)
            {
                port=Integer.valueOf(args[0]);
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        new FileServer().run(port);
    }
}

下面是网络IO事件的处理:

import java.io.File;
import java.io.RandomAccessFile;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.DefaultFileRegion;
import io.netty.channel.FileRegion;
import io.netty.channel.SimpleChannelInboundHandler;

public class FileServerHandler extends SimpleChannelInboundHandler<String>
{
    //操作系统识别的换行符
    private static final String CR=System.getProperty("line.separator");
    
    @Override
    protected void messageReceived(ChannelHandlerContext ctx, String msg)
        throws Exception
    {
        File file=new File(msg);
        if (file.exists())
        {
            if (!file.isFile())
            {
                //写入换行符表示文件结束
                ctx.writeAndFlush("Not a file: "+file+CR);
                return;
            }
            //换行符表示文件结尾
            ctx.write(file+" "+file.length()+CR);
            RandomAccessFile randomAccessFile=new RandomAccessFile(msg, "r");
            FileRegion region=new DefaultFileRegion(
                randomAccessFile.getChannel(), 0, randomAccessFile.length());
            ctx.write(region);
            //写入换行符表示文件结束
            ctx.writeAndFlush(CR);
            randomAccessFile.close();
        }else {
            ctx.writeAndFlush("File not found: "+file+CR);
        }
    }
    
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx,Throwable cause){
        cause.printStackTrace();
        ctx.close();
    }
}

三、测试

启动文件服务器服务端后,打开命令提示符窗口,输入telnet localhost 8888和文件服务器建立TCP连接,然后输入一个文件路径进行验证:

参考书籍《Netty权威指南》

  • 3
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值