Netty初级
# Netty初级
# 1、通用API
channelActive:客户端链接后触发该函数
channelRead:客户端向服务端往通道缓存发送数据后触发该函数。服务端调用该api取数据。
writeAndFlush:发送ByteBuf字节码数据给客户端
# 2、解码器
解码器:服务端将接收到的字节码转换为String字符串
LineBasedFrameDecoder:换行解码器,碰到换行符号才会调用触发channelRead方法
StringDecoder:用于服务端接收数据解码转String,需要设置GBK。
StringEncoder:用于服务端向客户端发送数据,自动将String转化成字节码
# 3、DefaultChannelGroup
DefaultChannelGroup:服务端设置信道组,每次有客户端发来请求后将其放入通信组中,可以直接对通信组实现群发。
flush()方法,刷新内存队列,将数据写入到对端。
# 4、半包粘包
半包数据:客户端发送的一包数据到服务端,接收时拆分成多包
粘包数据:收到的数据包中结束符号标志后面还带有其它的数据。
因此需要在服务端自定义编码解码器来处理以上问题。
# 5、Netty字节缓冲区对象ByteBuf
readByte():读取缓冲区当前读指针位置的内容,然后readerIndex指针+1
readerIndex():获取当前数据包的开头读位置Index
readerIndex(num):将读指针位置置为num
# 6、ChannelInboundHandlerAdapter
ChannelInboundHandlerAdapter:拦截处理入站事件,对于服务端来说,包括客户端连接、读客户端发送过来的数据、断开连接。用户在自定义处理方法时需要重写事件方法
channelRead:信道缓冲区收到数据后,进行读数据。
channelActive:服务端监听到客户端连接,信道激活。
channelInactive:服务端监听到客户端断开连接。
Bootstrap#option:ChannelOption.AUTO_READ自动读模式
# 7、protostuff
基于protostuff传输对象数据。需要自己定义解码器和编码器,并在其中调用框架API实现字节码和对象之间的序列化和反序列化。
# 8、文件传输
底层通过java的RandomAccessFile类实现对文件的读写操作。客户端和服务端之间文件传输通过FileTransferProtocol自定义协议实现。
FileUtil#readFile:把客户端要上传的文件读到缓存中
FileUtil#writeFile:服务端把缓存中的数据写入最终指定位置,并返回客户端下次读取指针,
FileBurstData:文件数据。
FileBurstInstruct:文件传输指令。由服务端发给客户端,客户端把文件数据传过来写入通道缓冲区。
FileDescInfo:文件传输请求。用于初次请求,相当于建立连接。
①客户端向通道缓冲区发送FileDescInfo对象,请求进行文件传输
②服务端收到后,判断是FileDescInfo类型,首先读Cache查看是否有该文件的传输指令(有则表明客户端可能刚刚断开过链接),有则直接返回该对象,否则创建新的FileBurstInstruct对象返回写入缓冲区。
③客户端根据FileBurstInstruct对象从文件指定位置读取数据,并封装为FileBurstData对象发给服务端,写入通道缓冲区。
④服务端收到后,判断是FileBurstInstruct类型,则会把真正的文件数据通过java原生API写入指定的文件位置(服务端指定)。并在FileBurstInstruct中更新下一次客户端的文件读指针,并放入Cache。
# 9、ReferenceCountUtil.release()
ReferenceCountUtil.release():相当于ByteBuf.release()方法的包装,释放缓冲区资源。
netty4中的ByteBuf使用了引用计数(netty4实现了一个可选的ByteBuf池),每一个新分配的ByteBuf的引用计数值为1,每对这个ByteBuf对象增加一个引用,需要调用ByteBuf.retain()方法,而每减少一个引用,需要调用ByteBuf.release()方法。当这个ByteBuf对象的引用计数值为0时,表示此对象可回收。
# 10、Netty同步请求处理
Netty使用了事件通知机制(channelRead),因此它的IO是异步的。而要实现Netty同步请求,这里的方案是通过countDownlatch和future配合实现,当前客户端线程通过writeAndFlush发送消息后,使用countdownlatch将该线程阻塞,然后服务端响应回复后唤醒客户端线程,通过future.get()获取响应数据。整个流程如下:
①客户端通过writeAndFlush向缓冲区发送请求数据,并添加监听器,确保客户端发送成功。
然后客户端在writeFuture.get中线程阻塞,等待服务端响应数据,从而实现同步。(基于latch.await实现)此处给latch设置了超时时间,没有完全阻塞,超时后计数还没有为0则返回false。
②服务端通过channelRead收到请求数据,并将响应结果发送给客户端。
③客户端在channelRead中收到响应数据后,从Future缓存中取出对应的future对象,将响应数据封装进去,并countDown()唤醒前面的请求线程。