网络-Netty(一)-NIO概念Channel是一个对象,可以通过它读取和写入数据。可以把它看做IO中的流。但是它和流相比还有一些不同:

NIO:New IO。

NIO和IO有相同的作用和目的,但实现方式不同:IO处理的是流,NIO主要用到的是

在Java API中提供了两套NIO,一套是针对标准输入输出NIO,另一套是网络处理NIO。本文档只讨论网络NIO。

一、BufferChannel

Channel:是一个通道。任何来源和目的数据都必须通过一个Channel对象。

Buffer:是一个容器对象。发给Channel的所有数据首先放到Buffer中,从Channel中读取的数据都要先读到Buffer中。

1.1 Buffer

Buffer是一个对象,它包含要写入或读出的数据,是NIO读写数据的中转池

在NIO中,数据是放入buffer对象的,而在IO中,数据是直接写入或者读到Stream对象的。

应用程序不能直接对 Channel 进行读写操作,而必须通过 Buffer 来进行,即 Channel 是通过 Buffer 来读写数据的。

Buffer实质上是一个数组,通常是一个字节数据,但也可以是其他类型的数组。他又不仅仅是一个数组,它提供了对数据的结构化访问,而且还可以跟踪系统的读写进程。

例如:ByteBuffer、CharBuffer、FloatBuffer、IntBuffer等等。

使用 Buffer 读写数据一般遵循以下四个步骤:

  1. 写入数据到 Buffer;
  2. 调用 flip() 方法;
  3. 从 Buffer 中读取数据;
  4. 调用 clear() 方法或者 compact() 方法。
当向 Buffer 写入数据时,Buffer 会记录下写了多少数据。一旦要读取数据,需要通过 flip() 方法将 Buffer 从写模式切换到读模式。在读模式下,可以读取之前写入到 Buffer 的所有数据。

一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。有两种方式能清空缓冲区:调用 clear() 或 compact() 方法。

clear() 方法会清空整个缓冲区。compact() 方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。

1.2 Channel

Channel是一个对象,可以通过它读取和写入数据。可以把它看做IO中的流。但是它和流相比还有一些不同:

  1. Channel是双向的,既可以读又可以写,而流是单向的
  2. Channel可以进行异步的读写
  3. 对Channel的读写必须通过buffer对象

在Java NIO中Channel主要有如下几种类型:

  • FileChannel:从文件读取数据
  • DatagramChannel:读写UDP网络协议数据
  • SocketChannel:读写TCP网络协议数据
  • ServerSocketChannel:可以监听TCP连接

二、NIO中的读和写

IO中的读和写,对应的是数据和Stream,NIO中的读和写,则对应的就是通道和缓冲区。
NIO的读:创建一个缓冲区,然后让通道读取数据到缓冲区。
NIO的写:创建一个缓冲区,用数据填充它,然后让通道用这些数据来执行写入。
--->从文件中读数据
按照上面的“读”数据的规则,从文件读取数据需要如下三步:
  1. 获得Channel
  2. 创建Buffer
  3. 让Channel读取数据到Buffer
第一步:获得Channel
因为是从文件中读数据,所以要从FileInputStream获取Channel
FileInputStream fin = new FileInputStream( "文件.txt" );
FileChannel fc = fin.getChannel();  
第二步:创建Buffer
ByteBuffer buffer = ByteBuffer.allocate( 1024 );
第三步:让Channel读取数据到Buffer
fc.read( buffer );

--->写数据到文件中
按照上面的“写”数据的规则,写数据到文件需要如下三步:
  1. 获得Channel
  2. 创建Buffer,并且用数据填充它
  3. 把Buffer数据写入到Channel中
第一步:获得Channel
FileOutputStream fout = new FileOutputStream( "文件.txt" );
FileChannel fc = fout.getChannel();
第二步:创建Buffer,将数据放入缓冲区
ByteBuffer buffer = ByteBuffer.allocate( 1024 );
for (int i=0; i<message.length; ++i) {
    buffer.put( message[i] );
}
buffer.flip();
第三步:把缓冲区数据写入通道中
fc.write( buffer );
--->综合案例:读写数据CopyFile

处理流程:创建一个Buffer,然后从源文件读取数据到缓冲区,然后再将缓冲区写入目标文件。

public static void copyFileUseNIO(String src,String dst) throws IOException
{
	//声明源文件和目标文件
	FileInputStream fi=new FileInputStream(new File(src));
	FileOutputStream fo=new FileOutputStream(new File(dst));
	//获得传输通道channel
	FileChannel inChannel=fi.getChannel();
	FileChannel outChannel=fo.getChannel();
	//创建数据buffer
	ByteBuffer buffer=ByteBuffer.allocate(1024);
	while(true){
		//判断是否读完文件
		int eof =inChannel.read(buffer);
		if(eof==-1){
			break;  
		}
		//使用flip,将 Buffer 从写模式切换到读模式
		//重设一下buffer的position=0,limit=position
		buffer.flip();
		//开始写
		outChannel.write(buffer);
		//写完要重置buffer,重设position=0,limit=capacity
		buffer.clear();
	}
	inChannel.close();
	outChannel.close();
	fi.close();
	fo.close();
}

通过CopyFile,看看程序编写要注意的点:

1、当没有更多的数据时,拷贝就算完成,此时 read() 方法会返回 -1 ,我们可以根据这个方法判断是否读完。

2、Buffer类的flip、clear方法

控制buffer状态的三个变量

  • position:跟踪已经写了多少数据或读了多少数据,它指向的是下一个字节来自哪个位置
  • limit:代表还有多少数据可以取出或还有多少空间可以写入,它的值小于等于capacity。
  • capacity:代表缓冲区的最大容量,一般新建一个缓冲区的时候,limit的值和capacity的值默认是相等的。
flip、clear这两个方法便是用来设置这些值的。

flip方法的源码:

public final Buffer flip() {
    limit = position;
    position = 0;
    mark = -1;
    return this;
 }


在上面的FileCopy程序中,写入数据之前我们调用了buffer.flip();方法。
这个方法把当前的指针位置position设置成了limit,再将当前指针position指向数据的最开始端,我们现在可以将数据从缓冲区写入通道了。 position 被设置为 0,这意味着我们得到的下一个字节是第一个字节。 limit 已被设置为原来的 position,这意味着它包括以前读到的所有字节,并且一个字节也不多。

clear方法的源码

public final Buffer clear() {
    position = 0;
    limit = capacity;
    mark = -1;
    return this;
}


在上面的FileCopy程序中,写入数据之后也就是读数据之前,我们调用了 buffer.clear();方法,这个方法重设缓冲区以便接收更多的字节。上图显示了在调用 clear() 后缓冲区的状态。

阅读更多
文章标签: 网络 Netty NIO
个人分类: 网络-Netty
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭