###################################################################################
后续文章中都会对一些代码逻辑进行说明,但原文的英文注释一般不会直译,进行保留,只会说明那些没有注释的地方
###################################################################################
本文中关联的所有文章的总目录可以参看:系列文章目录
文章目录
- 1. 前言
- 2. Netty的Channel接口
- 2.1 Channel 接口的描述
- 2.2 Channel 接口中的方法
- 2.2.1 id()
- 2.2.2 eventLoop()
- 2.2.3 parent()
- 2.2.4 config()
- 2.2.5 isOpen()
- 2.2.6 isRegistered()
- 2.2.7 isActive()
- 2.2.8 localAddress()
- 2.2.9 remoteAddress()
- 2.2.10 closeFuture()
- 2.2.11 isWritable()
- 2.2.12 bytesBeforeUnwritable()
- 2.2.13 bytesBeforeWritable()
- 2.2.14 unsafe()
- 2.2.15 pipeline()
- 2.2.16 alloc()
- 2.2.17 read()
- 2.2.18 flush()
1. 前言
了解socket编码的都知道socekt的三个重要接口Byte,Channel,Selector.而Netty框架也是缺少不了这些东西,只不过是对这些东西进行了重写,但要实现的功能都是一样的;
1.1 jdk中的Channel讲解
对于jdk中的Channel接口,里面的方法很少,只有两个
isOpen() 告诉调用者这个通道是否打开,如果打开反就true,否则返回false
close() 关闭这个通道。
1)在通道关闭后如果有调用I/O操作的,那么就会报 _ClosedChannelException_异常;
2)如果这个通道已经关闭了,再重复调用close方法不会产生任何效果;
3)这个方法可以在任何时候调用;如果有某个线程已经调用这个close方法,那么其它的线程来调用时将会阻塞,直到第一个线程调用完成,然后这些线程执行是不会产生任何效果的;
jdk中的Channel接口的具体描述如下所示:
通道可以表示诸如硬件设备,文件,网络套接字或程序组件之类的实体的开放连接,该实体能够执行一个或多个不同的I / O操作(例如,读取或写入)。
通道只能是打开或者关闭的。 通道在创建时即打开,一旦关闭,便保持关闭状态。 通道关闭后,任何在其上调用I / O操作的尝试都将引发ClosedChannelException。 可以通过调用isOpen方法来测试通道是否打开。
通道通常旨在对多线程访问安全,对接口规范以及扩展和实现此接口的类中所述。
2. Netty的Channel接口
现在我们回归正题,我们现在是要熟悉Netty的Channel接口,看看这个接口到底是做了哪些东西?
2.1 Channel 接口的描述
它是网络套接字或能够进行I / O操作(例如读取,写入,连接和绑定)的组件的钮带
通道为用户提供的功能有:
- 通道的当前状态(例如:是否打开?是否已连接?)
- 通道的配置参数(例如接收缓冲区大小),
- 通道支持的I / O操作(例如,读取,写入,连接和绑定),
- 以及ChannelPipeline,处理所有与通道关联的I / O事件和请求。
所有的I/O操作都是异步的
Netty中的所有I / O操作都是异步的。这意味着任何I / O调用都将立即返回,而不能保证在调用结束时已经完成了请求的I / O操作。相反,将返回一个带有ChannelFuture类的实例,该实例将在请求的I / O操作成功,失败或取消时通知您。
渠道是层级的
渠道可以有一个父项,具体取决于其创建方式。例如,ServerSocketChannel接受的SocketChannel将返回ServerSocketChannel作为其parent()的父级。
层次结构的语义取决于Channel所属的传输实现。例如,您可以编写一个新的Channel实现,以创建共享一个套接字连接的子通道,就像BEEP和SSH一样。
一些特定的传输的操作需要其子类实现
一些传输会公开特定于该传输的其他操作。将Channel向下转换为子类型以调用此类操作。例如,对于旧的I / O数据报传输,DatagramChannel提供了多播连接/离开操作。
释放资源
一旦完成Channel,调用close()或close(ChannelPromise)释放所有资源很重要。这样可以确保所有的资源以适当的方式释放,例如:filehandles。
2.2 Channel 接口中的方法
2.2.1 id()
这个接口如下所示:
//返回一个全局唯一Channel的标识
ChannelId id();
在Netty中,实现ChannelId的方法有很四个:
CustomChannelId:这个是自定义的渠道id生成,由用户自己实现
DefaultChannelId: 这个是默认的渠道id生成,里面生成id用的是雪花算法。感兴趣的朋友可以看下
EmbeddedChannelId:虚拟的id生成器,里面hashCode,toString方法的值都是固定的。
Http2StreamChannelId:这个主要是用于http协议的。相比较其它的而言,里面会多一个parentId;
2.2.2 eventLoop()
这个接口如下所示:
// 返回注册Channel的EventLoop对象
EventLoop eventLoop();
EventLoop对象的作用是:一但Channel注册成功后,它将处理这个通道所有的I/O操作。一般一个EventLoop的实现类能够处理多个Channel。该接口的具体的功能会在一个单独的篇章中进行讲解;
2.2.3 parent()
// 如果该渠道没有父渠道,那么这个方法就会返回null
Channel parent();
2.2.4 config()
// 返回这个渠道的具体配置信息
ChannelConfig config();
ChannelConfig接口说明
它是Channel配置属性的集合
对于设置特殊的属性,我们需要使用其特定实现的子类的配置类型来处理。比如SocketChannelConfig 类我们可以如下设置:
Channel ch = ...;
SocketChannelConfig cfg = (SocketChannelConfig) ch.getConfig();
cfg.setTcpNoDelay(false);
选项map集合
选项映射属性是动态只写属性,它允许Channel配置的ChannelConfig不用转换成真正的子类而进行设置。 要更新选项集合,请调用setOptions(Map)这个方法。
所有ChannelConfig具有以下选项:
名称 | 关联的设置方法 |
---|---|
ChannelOption.CONNECT_TIMEOUT_MILLIS | ChannelConfig.setConnectTimeoutMillis(int) |
ChannelOption.WRITE_SPIN_COUNT | ChannelConfig.setWriteSpinCount(int) |
ChannelOption.WRITE_BUFFER_WATER_MARK | ChannelConfig.setWriteBufferWaterMark(WriteBufferWaterMark) |
ChannelOption.ALLOCATOR | ChannelConfig.setAllocator(ByteBufAllocator) |
ChannelOption.AUTO_READ | ChannelConfig.setAutoRead(boolean) |
2.2.5 isOpen()
// 通道如果已经打开并且在后续会可以被激活,就返回true.
boolean isOpen();
2.2.6 isRegistered()
// 如果Channel已向EventLoop注册,则返回true。
boolean isRegistered();
2.2.7 isActive()
// 如果通道处于活动状态且已连接,则返回true。
boolean isActive();
2.2.8 localAddress()
// 返回此通道绑定到的本地地址。 应该将返回的SocketAddress转换为更具体的类型(例如InetSocketAddress)以检索详细信息。
// 返回:此通道的本地地址。 如果未绑定此通道,则为null。
SocketAddress localAddress();
2.2.9 remoteAddress()
// 返回此通道连接到的远程地址。 因此,应该将SocketAddress转换为更具体的类型(例如InetSocketAddress)以检索详细信息。
// 返回:此通道的远程地址。 如果未连接此通道,则为null。如果未连接此通道,因为此方法将返回null,但它可以从任意远程地址接收消息(例如DatagramChannel,请使用DatagramPacket.recipient()确定接收到的消息的来源。
SocketAddress remoteAddress();
2.2.10 closeFuture()
// 当这个通道关闭时,会返回ChannelFuture。 这个方法会一直返回同一个ChannelFuture实例
ChannelFuture closeFuture();
2.2.11 isWritable()
// 当且仅当I / O线程将立即执行所要求的写操作时,才返回true。
// 当此方法返回false时发出的所有写请求都将排队,直到I/O线程准备好处理排队的写请求为止。
boolean isWritable();
2.2.12 bytesBeforeUnwritable()
// 获取直到isWritable()返回false之前可以写入的字节数。此数量始终为非负数。 如果isWritable()为false,则为0。
long bytesBeforeUnwritable();
2.2.13 bytesBeforeWritable()
// 直到isWritable()返回true之前,必须从底层缓冲区中耗尽多少字节。此数量始终为非负数。 如果isWritable()为true,则为0。
long bytesBeforeWritable();
2.2.14 unsafe()
// 返回一个Unsafe 操作对象仅供内部使用
Unsafe unsafe();
Unsafe 这个类不应该被用户编写的代码来调用。它仅提供以下方法来进行实际的传输,并且必须从I/O线程中调用;这些方法包含:
•localAddress()
•remoteAddress()
•closeForcibly()
•register(EventLoop, ChannelPromise)
•deregister(ChannelPromise)
•voidPromise()
2.2.15 pipeline()
// 返回分配的ChannelPipeline
ChannelPipeline pipeline();
2.2.16 alloc()
// 返回分配的ByteBufAllocator,它将用于分配ByteBufs。
ByteBufAllocator alloc();
2.2.17 read()
//请求将数据从Channel读取到第一个入站缓冲区,如果已读取数据,则触发
// ChannelInboundHandler.channelRead(ChannelHandlerContext,Object)事件,
// 并触发channelReadComplete事件,以便处理程序可以决定继续读取。 如果已经有一个待处理的读取操作,则此方法不执行任何操作。
//这个方法会导致调用Channel的ChannelPipeline中包含的下一个ChannelOutboundHandler的ChannelOutboundHandler.read(ChannelHandlerContext)方法。
@Override
Channel read();
对于读写一个重要的ChannelPipeline这个接口,我们会在单独的篇章中再详细讲解
2.2.18 flush()
// 通过此ChannelOutboundInvoker请求刷新所有缓冲的消息。
@Override
Channel flush();