netty-第一部分-NIO-2 核心概念1 Channel

Channel与Buffer概念

ChannelBuffer常常一同使用,所以一起介绍会比较合理。
对于两个有关联关系的概念,我觉得需要先给一个总体上的认识。
二者的关系:
在这里插入图片描述
这个buffer不仅可以是我们说的Buffer的实现类,比如ByteBufferShortBuffer等,还可以是一个socket的缓冲区,也可能是一个文件。
示例:
比如我们使用FileChannel时,创建一个FileChannel对象,需要一个文件的输入或输出流,

//仅做示例,未写异常处理
String path = "text/a.txt";
FileChannel fc= new FileInputStream(path).getChannel();
//通过输入流获取FileChannel,FileChannel比较特殊,通过输入流获取只能读,输出流只能写。
Bytebuffer buffer = ByteBuffer.allocate(16);//获取ByteBuffer来存放我们要读进来的数据
fc.read(buffer);//从channel中把数据读到我们的ByteBuffer中

上面这个示例展示了ChannelBuffer的关系。
我们通过Channel读取文件或者某个Buffer缓冲区的数据写到我自己的Buffer中。
或者通过Channel把我自己ByteBuffer中的数据写到目标文件或者Buffer中。

这种关系放到SocketChannel上,就是把Socket接受缓冲区中的数据(其他人通过网络传输的数据)读到我的ByteBuffer中,方便后续处理。
或者把自己要发送的放在ByteBuffer中的数据写到Socket写缓冲区中发送给目标。
这就是Channel的作用,就是一个传输的桥梁。
Buffer则是用来存放我们读写数据的地方。

二者的关联我们梳理清了,接下来就要具体到两者各自的一些实现与使用了。
下面分别介绍ChannelByteBuffer

Channel

主要有
FileChannel(文件io)
SocketChannel(TCP读写操作)
ServerSocketChannel(TCP连接管理)
DatagramChannel(UDP读写操作)

这里主要说前三种的使用

FileChannel

由于重点是网络io,所以FileChannel简单看一下即可。
首先FileChannel只能通过 FileInputStreamFileOutputStream 或者 RandomAccessFile 来获取。调用getChannel()方法来获取
通过FileInputStream输入流获取的FileChannel 只可以读。
通过FileOutputStream 输出流获取的FileChannel 只可以写。
通过 RandomAccessFile 获取的则根据其读写模式决定。
下面是基本使用,涉及创建,读,写,释放

//创建
String path = "text/a.txt";
FileChannel channel1= new FileInputStream(path).getChannel();
//读
ByteBuffer buffer1 = ByteBuffer.allocate(16);
long nums = channel1.read(buffer1);

//写
ByteBuffer buffer2 = ByteBuffer.allocate(16);
buffer2.put("abc");//存入要写的数据
buffer2.filp();//切换读模式,因为我们接下来要读出buffer里的数据。后面会详细说明buffer的读写模式
FileChannel channel2 = new FileOutputStream(path).getChannel();
while(buffer2.hasRemaining()) {//hasRemaining()buffer中还有未读取数据返回true,否则返回false
    channel.write(buffer2);
}
//使用循环是因为write()方法并不能保证一次将 buffer 中的内容全部写入 channel,可能需要多次。(对于文件io并无此问题,在使用SocketChannel时要注意这个问题)

//关闭(资源释放)
//使用try-with-resources写法,把资源创建放在try后的括号中,一个语法糖
try (FileChannel a = new FileInputStream(path1).getChannel();
     FileChannel b = new FileOutputStream(path2).getChannel();
    ) {
    ....//做一些读写操作
} catch (IOException e) {
    e.printStackTrace();
}


//强制写入
//操作系统出于性能的考虑,会先把数据写到Page Cache中,之后在某些时机进行耍刷盘。可以调用force(true)方法将文件内容和元数据(文件的权限等信息)立刻刷盘
//又有新坑了,这个操作系统的知识可以写一篇

SocketChannel与ServerSocketChannel

二者实例化的同时都会创建socket对象,因为底层还是通过socket进行通信。
这个Channel就等同于我们与socket交流的通道,或者说对底层socket所产生的事件做一个监听。

ServerSocketChannel 的作用是监听连接请求,创建新的 SocketChannel 对象,它本身不传输数据。也就是说它就是一个负责创建连接的监听器。它监听到的连接请求处理后,我们可以获得一个对应的 SocketChannel 用于在该连接中传输数据。
ServerSocketChannel处理连接请求的方法是accept() ,接收到连接请求并建立起连接后,返回一个SocketChannel对象用于连接的数据传输。
Socket通信我并不了解,但是可以想见到的是这两个Channel相当于隔离了原本直接使用的Socket,等于加上了一层,让网络编程更简单可用。类似于加了一层封装的感觉,这里是一个Socket关联一个SocketChannel
这里是个人理解,如有错误,烦请批评指正。

SocketChannel的读写也是read()write()方法。
二者均支持阻塞与非阻塞模式
二者都支持多路复用,将在Selector篇章讲解。

这两个Channel的其他方法在下文介绍

ServerSocketChannel使用

注意下文的非阻塞模式将是配合Selector实现多路复用的关键。关于Selector(即NIO中第三个核心概念)与多路复用,会在下篇文章讲解。这里仅需要知道阻塞与非阻塞的区别即可。(SocketChannel的非阻塞模式同理)
ServerSocketChannel只监听连接,不具备读写的功能

//打开ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

//绑定监听端口
serverSocketChannel.bind(new InetSocketAddress(8080));

// 切换为非阻塞模式
serverSocketChannel.configureBlocking(false); 

//监听连接到达
//阻塞模式下,ServerSocketChannel.accept() 会在没有连接建立时让线程暂停
//非阻塞模式则在没有连接建立时,会返回 null,继续运行
serverSocketChannel.accept();

//关闭
serverSocketChannel.close();

SocketChannel使用

通过SocketChannel我们就可以传输数据,进行网络io的读写了。

//创建ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8080));
//获取SocketChannel
SocketChannel socketChannel = serverSocketChannel.accept();

//另一种获取方式
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("http://google.com", 80));

//阻塞与非阻塞模式设置
socketChannel.configureBlocking(false);

//读
//阻塞模式下SocketChannel.read() 会在没有数据可读时让线程暂停
//非阻塞模式下SocketChannel.read() 在没有数据可读时,会返回 0,但线程不必阻塞,可以去执行其它 SocketChannel 的 read 或是去执行 ServerSocketChannel.accept 
//读数据还要考虑buffer容量不够多次读取的情况,在下篇Selector中会讨论这个问题
ByteBuffer byteBuffer = ByteBuffer.allocate(16);
socketChannel.read(byteBuffer);

//写
ByteBuffer buffer = ByteBuffer.allocate(16);
buffer.put("abcdefdsadasdasdaddsadasdad");
buffer.flip();//切换为读模式,从中读出数据写到其他地方
//这里使用循环的原因在上面FileChannel的示例已经提前提过
//因为write()方法并不能保证一次将 buffer 中的内容全部写入 channel,可能需要多次write(),直到都写完为止。
while(buffer.hasRemaining()) {//hasRemaining()buffer中还有未读取数据返回true,否则返回false
    channel.write(buf);
}

//连接校验,这个部分课程中没咋用,看博客看到的
socketChannel.isOpen(); // 测试 SocketChannel 是否为 open 状态
socketChannel.isConnected(); //测试 SocketChannel 是否已经被连接
socketChannel.isConnectionPending(); //测试 SocketChannel 是否正在进行连接
socketChannel.finishConnect(); //校验正在进行套接字连接的 SocketChannel是否已经完成连接



//关闭
socketChannel.close();

Buffer

在下一篇文章
https://blog.youkuaiyun.com/qq_42939279/article/details/140379191?spm=1001.2014.3001.5501

演示示例

只给代码感觉不太直观,下篇Buffer的写完结尾会演示效果。
并且总结一下简单的通信流程
https://blog.youkuaiyun.com/qq_42939279/article/details/140379191?spm=1001.2014.3001.5501

结语

这部分写起来需要重新组织一下结构,因为课程给我感觉一些东西没有单独讲解,而是耦合在一起,比如SocketChannel是在对比阻塞非阻塞时讲的,我就把它提出来单独写在Channel部分了,对于阻塞非阻塞多路复用,应该在下篇Selector中讲。写完三个核心概念应该直接开始Netty了。不在NIO多做停留。

后面关于IO模型可能会写一篇,因为前两天花了一些时间去看这部分。

写了好久好久,发现写博客能很容易的让我进入心流状态,一口气写了3个多小时。

文章如有错误,还请指出,谢谢大家观看。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值