2. NIO-Channel

FileChannel

 如何从FileChannel中读取数据?

 public static void main(String[] args) throws IOException {
        //1.创建FileChannel
        RandomAccessFile accessFile=new RandomAccessFile("E:\\testData\\FileChannel01.txt","rw");//rw表示的是读和写
        FileChannel channel=accessFile.getChannel();
        //2.创建Buffer
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        //3.读取数据到buffer中
        int read = channel.read(buffer);//返回的值如果是-1就表示已经读到最后一行了
        while(read!=-1){
            System.out.println("读取了"+read);
            //3.1.翻转读写模式
            buffer.flip();
            //3.2.buffer是否有剩余的内容
            while (buffer.hasRemaining()){
                System.out.println((char)buffer.get());
            }
            //3.3.清除buffer
            buffer.clear();
            //3.4.让他继续往下读
            read=channel.read(buffer);
        }
        accessFile.close();;
        System.out.println("操作结束");
    }

对以上代码做出解释:

在使用fileChannel之前必须先打开它,但是我们无法直接打开它,所以只能通过RandomAccessFile来获取一个FileChannel实例

  RandomAccessFile accessFile=new RandomAccessFile("E:\\testData\\FileChannel01.txt","rw");
  FileChannel channel=accessFile.getChannel();

调用多个read方法之一从FileChannel中读取数。首先分配一个buffer,从FileChannel中读取的数据将被读到Buffer中,然后调用read方法,该方法将数据从FileChannel读取到Buffer中

ByteBuffer buffer = ByteBuffer.allocate(1024);
int read = channel.read(buffer);

如何向FileChannel写数据

private static void WriteDataToFileChannel() throws IOException{
        //1.打开FileChannel
        RandomAccessFile accessFile=new RandomAccessFile("E:\\testData\\FileChannel01.txt","rw");
        FileChannel channel = accessFile.getChannel();
        //2.创建一个Buffer对象
        ByteBuffer buffer=ByteBuffer.allocate(1024);
        String newData="wuyimin";
        buffer.clear();
        //3.写入内容
        buffer.put(newData.getBytes());
        //3.1转换为读模式
        buffer.flip();
        //3.2FileChannel完成最终的实现
        //必须在循环里调用write方法,因为无法保证一次能写入多少字节
        while (buffer.hasRemaining()){
            channel.write(buffer);
        }
        //4.关闭fileChannel
        channel.close();
    }

FileChannel的position方法

有时候可能需要在FileChannel的某个特定的位置进行数据的读写操作,可以通过调用position方法来获取到FileChannel的当前位置,也可以通过调用position方法设置FileChannel的当前位置:

long pos=channel.position();

channel.position(pos+123);

注意点:

  • 如果将位置设置在文件结束符之后,然后试图冲文件通道中获取数据,读方法将返回-1。
  • 如果就将位置设置在文件结束符之后,然后向通道中写数据,文件将撑大到当前位置写入数据,这可能导致文件空袭,磁盘上物理文件中写入的数据间有空隙

FileChannel的size方法

返回实例关联文件的大小

FileChannel的truncate方法

截取一个文件,比如channel.truncate(1024),截取前1024个文件

FileChannel的force方法

将通道里尚未写道磁盘的数据强制写到磁盘上,操作系统会将数据缓存在内存中,无法保证写入到FileChannel里的数据一定会及时写在磁盘上,要保证这一点,需要调用force方法。

该方法有个boolean类型的参数,指明是否同时将文件元数据写道磁盘上

FileChannel的transferTo和transferFrom方法

用于通道之间的数据传输,如果to的文件有原有内容会被from的内容覆盖掉

private static void transferFrom() throws IOException {
        //创建两个accessFile
        RandomAccessFile accessFile1 = new RandomAccessFile("E:\\testData\\FileChannel01.txt", "rw");
        RandomAccessFile accessFile2 = new RandomAccessFile("E:\\testData\\FileChannel02.txt", "rw");
        FileChannel from = accessFile1.getChannel();
        FileChannel to = accessFile2.getChannel();
        //我们想把from的数据传输到to里去
        long pos=0;
        long size=from.size();
        //从第0个位置开始传输
        to.transferFrom(from,pos,size);
        //关闭文件
        accessFile1.close();
        accessFile2.close();
        System.out.println("传输完毕");
    }

Socket通道--(Datagram,Socket,ServerSocket)

DaragramChannel和SocketChannel实现定义读和写的功能接口而ServerSocketChannel不实现,ServerSocketChannel负责监听传入的连接和创建新的SocketChannel对象,它本身从不传输数据

socket和socket通道之间的关系:通道是一个连接IO服务导管并且提供与该服务交互的方法,就某个socket而言,它不会再次实现与之对应的socket通道类中的socket协议api,已经存在的socket通道可以被大多数协议操作重复使用

ServerSocketChannel

它是一个基于通道的socket监听器

通过bind方法绑定一个socket

ServerSocketChannel的accept方法会返回一个SocketChannel对象,SocketChannel可以在非阻塞模式下运行

 private static void ServerSocketChannelTest() throws IOException, InterruptedException {
        //端口号
        int port=8888;
        //buffer
        ByteBuffer buffer=ByteBuffer.wrap("hello wuyimin".getBytes());
        //绑定
        ServerSocketChannel ssc=ServerSocketChannel.open();
        ssc.bind(new InetSocketAddress(port));
        //设置非阻塞模式
        ssc.configureBlocking(false);
        //一直监听有没有新的连接传入
        while (true){
            SocketChannel sc=ssc.accept();
            //如果没有新的连接传入
            if(sc==null){
                System.out.println("无连接传入");
                Thread.sleep(2000);
            }else{
                System.out.println("进来的连接:"+sc.socket().getRemoteSocketAddress());
                buffer.rewind();//指针指向0,即位置0
                sc.write(buffer);
                sc.close();
            }
        }
    }

输出效果:

无连接传入
无连接传入
进来的连接:/127.0.0.1:51728
进来的连接:/127.0.0.1:58135
无连接传入
进来的连接:/127.0.0.1:58544

SocketChannel

作用

  • SocketChannel是用来连接Socket套接字的
  • SocketChannel主要用于处理网路IO的通道
  • SocketChannel基于TCP连接传输
  • SocketChannel实现了可选择通道,可以被多路复用

特征:

  • 对于已经存在的socket不可以创建socketChannel
  • SocketChannel中提供open接口创建的Channel并没有进行网络级联,需要使用connect接口连接到指定的地址
  • 未进行连接的SocketChannel执行IO操作时会抛出异常
  • 支持阻塞IO和非阻塞IO
  • 支持异步关闭
  • 支持设定参数

public static void  SocketChannelDemoTest() throws IOException {
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("www.baidu.com", 80));
        //如果设置为阻塞模式,下面的语句就不会打印
        socketChannel.configureBlocking(false);//设置非阻塞模式
        //读操作
        ByteBuffer buffer=ByteBuffer.allocate(16);
        socketChannel.read(buffer);
        socketChannel.close();
        System.out.println("读完了");
    }

DatagramChannel

之前介绍的两个channel面向的是TCP协议,而这个面向的是UDP协议

//发送
    @Test
    public void sendDatagram() throws IOException {
        DatagramChannel sendChannel=DatagramChannel.open();
        InetSocketAddress socketAddress=new InetSocketAddress("127.0.0.1",9999);
        while (true){
            ByteBuffer buffer = ByteBuffer.wrap("数据包...".getBytes("UTF-8"));
            sendChannel.send(buffer,socketAddress);
            System.out.println("已经完成了发送");
            try{ TimeUnit.SECONDS.sleep(2); }catch (InterruptedException e){ e.printStackTrace(); }
        }
    }
    //接受
    @Test
    public void receiveDatagram() throws IOException {
        DatagramChannel receiveChannel=DatagramChannel.open();
        InetSocketAddress receiveAddress=new InetSocketAddress(9999);
        //绑定
        receiveChannel.bind(receiveAddress);
        //buffer
        ByteBuffer buffer=ByteBuffer.allocate(1024);
        //接受
        while(true){
            SocketAddress socketAddress=receiveChannel.receive(buffer);
            System.out.println("发送地址"+socketAddress.toString());
            buffer.flip();
            System.out.println(Charset.forName("UTF-8").decode(buffer));
        }
    }

接收端输出:

发送地址/127.0.0.1:65050
数据包...
发送地址/127.0.0.1:65050
数据包...
发送地址/127.0.0.1:65050
数据包...

 以上操作对应于:

//read和write
    @Test
    public void testConnection() throws IOException {
        //打开DatagramChannel
        DatagramChannel conChannel=DatagramChannel.open();
        //绑定
        conChannel.bind(new InetSocketAddress(9999));
        //连接
        conChannel.connect(new InetSocketAddress("127.0.0.1",9999));
        //write
        conChannel.write(ByteBuffer.wrap("数据包...".getBytes("UTF-8")));
        //read
        ByteBuffer buffer=ByteBuffer.allocate(1024);
        while (true){
            buffer.clear();
            conChannel.read(buffer);
            buffer.flip();
            System.out.println(Charset.forName("UTF-8").decode(buffer));
        }
    }

Scatter和Gather(分散和聚集)

分散:从Channel中读取是指在读操作的时候将数据写入多个buffer中,因此Channel中读取的数据分散到多个Buffer中

聚集:写入Channel是指写操作的时候将多个buffer的数据写入聚集到同一个Channel

scatter/gather经常用于需要将传输的数据分开处理的场合,例如传输一个由消息头和消息体组成的消息,你可能会将消息体和头分散到不同的buffer中,这样可以方便处理头和体

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值