NIO学记:三、通道Channel

通道(Channel )定义

通道是连接IO服务(如:文件、套接字)的管道,并提供与该服务进行交互的方法。若需要使用 NIO 系统,需要获取用于连接 IO 设备的通道以及用于容纳数据的缓冲区。然后操作缓冲区,对数据进行处理。
Channel相比IO中的Stream更加高效,可以异步双向传输,但是需要和buffer缓冲区一起使用。

通道与传统IO流的区别

  1. 通道可以是双向的,既可以读取数据,又可以写数据到通道。但流只能读取或者只能写入。
  2. 通道可以异步的读写。
  3. 通道不能直接访问数据,需要和Buffer进行交互。

通道的分类

  • File通道:FileChannel
    • 获取方式:
      1.通过RandomAccessFile、FileOutPutSteam、FileInputStream这样的打开的对象的getChannel方法获取。
      2.通过FileChannel的open方式获取
  • Socket通道:SocketChannel、ServerSocketChannel、DatagramSocketChannel
    • 获取方式:
      1、通过Socket、ServerSocket、DatagramSocket的getChannel()方法获取
      2、通过SocketChannel、ServerSocketChannel、DatagramSocketChannel的open方法获取

主要实现类

  • FileChannel:用于读写文件中的数据。
  • SocketChannel:用于TCP的数据读写,一般是客户端实现
  • ServerSocketChannel:一般是服务器实现,允许我们监听TCP连接请求,每个请求会创建会一个SocketChannel
  • DatagramChannel:用于UDP的数据读写

通道直接的数据传输方式

  1. read和write
    outChannel.write(buffer)//将数据从缓冲区写入通道
    inChannel.read(buffer)//将数据从通道写入缓冲区
       //利用通道完成文件的复制,非直接缓冲区
public static void readAndWrite() throws Exception{
    // 指定需要读取和写入的文件
    RandomAccessFile readFile = new RandomAccessFile("F://readFile.txt", "rw");
    RandomAccessFile writeFile = new RandomAccessFile("F://readFile.txt", "rw");
    // 获取源文件和目标文件的通道channel
    FileChannel inChannel = readFile.getChannel();
    FileChannel outChannel = writeFile.getChannel();
    // 创建间接缓冲区并指定大小
    ByteBuffer buff = ByteBuffer.allocate(1024);
    // 将源文件通道里的数据存入缓存区buffer
    while(inChannel.read(buff)!=-1){
        // 将缓冲区的模式切换为读取
        buff.flip();
        // 将缓存区中的数据写入目标文件通道
        outChannel.write(buff);
        // 清空缓冲区-将position职位0,limit置为position当前值
        buff.clear();
    }
    outChannel.close();
    inChannel.close();
    readFile.close();
    writeFile.close();
}
  1. TransFrom
    从源信道读取字节到这个通道的文件中。如果源通道的剩余空间小于 count 个字节,则所传输的字节数要小于请求的字节数。这种方法可能比从源通道读取并写入此通道的简单循环更有效率。
        // transferFrom方法的使用
public static void transferFrom() throws Exception {
    // 指定读取的文件
    RandomAccessFile rw = new RandomAccessFile("F://readFile.txt", "rw");
    // 获取源文件的通道
    FileChannel readFile = rw.getChannel();
    // 指定写入的文件
    RandomAccessFile rwd = new RandomAccessFile("F://writeFile.txt", "rw");
    // 获取目标文件的通道
    FileChannel writeFile = rwd.getChannel();
    // public abstract long transferFrom(ReadableByteChannel src,long position, long count) throws IOException;
    // src指的是源文件的通道,position指文件读取的开始位置,count指需要传输的字节大小
    writeFile.transferFrom(readFile, 0, readFile.size());
    readFile.close();
    writeFile.close();
}
  1. TransTo
    将字节从这个通道的文件传输到给定的可写字节通道。
   // transferTo方法的使用
public static void transferTo() throws Exception {
    // 指定读取的文件
    RandomAccessFile rw = new RandomAccessFile("F://readFile.txt", "rw");
    // 获取源文件的通道
    FileChannel readFile = rw.getChannel();
    // 指定写入的文件
    RandomAccessFile rwd = new RandomAccessFile("F://writeFile.txt", "rw");
    // 获取目标文件的通道
    FileChannel writeFile = rwd.getChannel();
    // public abstract long transferTo(long position, long count,WritableByteChannel target) throws IOException;
    // target指的是目标文件的通道,position指文件读取的开始位置,count指需要传输的字节大小
    readFile.transferTo(0, readFile.size(), writeFile);
    readFile.close();
    writeFile.close();
}

常用方法说明

方 法描 述
int read(ByteBuffer dst)从Channel到中读取数据到ByteBuffer
long read(ByteBuffer[] dsts)将Channel到中的数据“分散”到ByteBuffer[]
int write(ByteBuffer src)将ByteBuffer到中的数据写入到Channel
long write(ByteBuffer[] srcs)将ByteBuffer[]到中的数据“聚集”到Channel
long position()返回此通道的文件位置
FileChannel position(long p)设置此通道的文件位置
long size()返回此通道的文件的当前大小
FileChannel truncate(long s)将此通道的文件截取为给定大小
void force(boolean metaData)强制将所有对此通道的文件更新写入到存储设备中
MappedByteBuffer map (MapMode mode, long position, long size)内存映射文件

Socket通道

  1. 非阻塞模式
    Socket通道可以在非阻塞,需要我们手动设置configureBlocking,true-阻塞、false-非阻塞
    socketChannel.configureBlocking(false);
    判断当前socket通道是否阻塞
    socketChannel.isBlocking();
    获取 configureBlocking和register方法同步的锁
    socketChannel.blockingLock();
  2. ServerSocketChannel的Api说明
方 法描 述
ServerSocketChannel open()用于创建ServerSocketChannel对象,使用时需要与SocketChannel绑定
final int validOps()同选择器一起使用,获取感兴趣的操作
ServerSocketChannel bind(SocketAddress local)绑定并指定端口的ServerSocket
abstract SocketChannel accept()当创建ServerSocketChannel对象并绑定一个ServerSocket关联的通道之后,调用该方法可以监听客户端的连接请求
SocketAddress getLocalAddress()获取绑定的Socket地址
    public static void serverSocketChannelTest() throws Exception {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open().bind(new InetSocketAddress("127.0.0.1",8080));
        serverSocketChannel.configureBlocking(false);
        ByteBuffer buffer = ByteBuffer.wrap("Hello".getBytes());
        while (true) {
            SocketChannel socketChannel = serverSocketChannel.accept();
            if (socketChannel == null) {
                Thread.sleep(2000);
            } else {
                buffer.rewind();
                socketChannel.write(buffer);
//                socketChannel.close();
            }
        }
    }
  1. SocketChannel的API说明
方 法描 述
SocketChannel open()静态方法,打开套接字通道(创建SocketChannel实例)
SocketChannel open(SocketAddress remote)静态方法,打开套接字通道并将其连接到远程地址
final int validOps()返回一个操作集,标识此通道所支持的操作
abstract SocketChannel bind(SocketAddress local)用于将Socket绑定到一个端口
abstract Socket socket()获取该SocketChannel关联的Socket(套接字)
abstract boolean isConnected()判断是否已连接此通道的网络套接字
abstract boolean isConnectionPending()判断此通道上是否正在进行连接操作。
abstract boolean connect(SocketAddress remote)用于SocketChannel连接到远程地址
abstract int read(ByteBuffer dst)、abstract long read(ByteBuffer[] dsts, int offset, int length)从通道中读取数据到缓冲区中
abstract int write(ByteBuffer src)、abstract long write(ByteBuffer[] srcs, int offset, int length)将缓冲区的数据写入到通道中
   public static void socketChannelTest() throws Exception {
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.configureBlocking(false);
        socketChannel.connect(new InetSocketAddress("127.0.0.1", 8080));
        while (true) {
            if (!socketChannel.finishConnect()) {
                Thread.sleep(1000);
            } else {
                ByteBuffer readBuffer = ByteBuffer.allocate(512);
                while (socketChannel.read(readBuffer) > 0){
                    byte[] bytes = new byte[readBuffer.position()];
                    readBuffer.get(bytes);
                    System.out.println(new String(bytes));
                    readBuffer.clear();
                }
            }
        }
    }
  1. DatagramChannel的API说明
方法描述
static DatagramChannel open()创建DatagramChannel实例
abstract DatagramChannel bind(SocketAddress local)将通道的套接字绑定到本地地址
abstract DatagramSocket socket()获取该DatagramChannel关联的DatagramSocket对象
abstract boolean isConnected()是否已连接到套接字
abstract DatagramChannel connect(SocketAddress remote)用于DatagramChannel连接到远程地址
abstract SocketAddress receive(ByteBuffer dst)通道此DatagramChannel接受到的数据包
abstract int send(ByteBuffer src, SocketAddress target)通过此DatagramChannel发送数据包
abstract int read(ByteBuffer dst)、abstract long read(ByteBuffer[] dsts, int offset, int length)从此通道读取数据包
abstract int write(ByteBuffer src)、final long write(ByteBuffer[] srcs)将数据写入到此通道
 public static void datagramChannelTest() throws Exception {
        DatagramChannel channel = DatagramChannel.open();

        channel.socket().bind(new InetSocketAddress("127.0.0.1",8088));
        // 发送数据包
        String newData = "This time:" +  System.currentTimeMillis();
        ByteBuffer buffer2 = ByteBuffer.allocate(48);
        buffer2.clear();
        buffer2.put(newData.getBytes());
        buffer2.flip();
        int bytesSent = channel.send(buffer2, new InetSocketAddress("127.0.0.1", 8090));

        // 接收数据包
        ByteBuffer buffer = ByteBuffer.allocate(48);
        buffer.clear();
        // 将接收到的数据包写入缓冲区
        channel.receive(buffer);
        byte[] bytes = new byte[buffer.position()];
        buffer.get(bytes);
        System.out.println(new String(bytes));

    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值