TCP 粘包/拆包
- 当通过TCP协议传输多次数据的时候,TCP协议会把一些消息合并发送,来节省IP调用次数,这样的话,可能有一些消息就是多条消息的组合,有的消息也可能配拆开分成两条消息。
-
在Netty中,已经造好了许多类型的拆包器,我们直接用就好:
image.png
选好拆包器后,在代码中client段和server端将拆包器加入到chanelPipeline之中就好了:
客户端:
ch.pipeline().addLast(new FixedLengthFrameDecoder(31));
服务端:
ch.pipeline().addLast(new FixedLengthFrameDecoder(31));
零拷贝
传统意义的拷贝是在发送数据的时候,传统的实现方式是:
File.read(bytes)
-
Socket.send(bytes)
这种方式需要四次数据拷贝和四次上下文切换: - 数据从磁盘读取到内核的read buffer
- 数据从内核缓冲区拷贝到用户缓冲区
- 数据从用户缓冲区拷贝到内核的socket buffer
- 数据从内核的socket buffer拷贝到网卡接口(硬件)的缓冲区
零拷贝的概念
明显上面的第二步和第三步是没有必要的,通过java的FileChannel.transferTo方法,可以避免上面两次多余的拷贝(当然这需要底层操作系统支持)
- 调用transferTo,数据从文件由DMA引擎拷贝到内核read buffer
- 接着DMA从内核read buffer将数据拷贝到网卡接口buffer上面的两次操作都不需要CPU参与,所以就达到了零拷贝。
Netty中的零拷贝
主要体现在三个方面:
1、bytebufferNetty发送和接收消息主要使用bytebuffer,bytebuffer使用对外内存(DirectMemory)直接进行Socket读写。原因:如果使用传统的堆内存进行Socket读写,JVM会将堆内存buffer拷贝一份到直接内存中然后再写入socket,多了一次缓冲区的内存拷贝。DirectMemory中可以直接通过DMA发送到网卡接口
2、Composite Buffers传统的ByteBuffer,如果需要将两个ByteBuffer中的数据组合到一起,我们需要首先创建一个size=size1+size2大小的新的数组,然后将两个数组中的数据拷贝到新的数组中。但是使用Netty提供的组合ByteBuf,就可以避免这样的操作,因为CompositeByteBuf并没有真正将多个Buffer组合起来,而是保存了它们的引用,从而避免了数据的拷贝,实现了零拷贝。
3、对于FileChannel.transferTo的使用Netty中使用了FileChannel的transferTo方法,该方法依赖于操作系统实现零拷贝。
Netty 内部执行流程


1、创建ServerBootStrap实例
2、设置并绑定Reactor线程池:EventLoopGroup,EventLoop就是处理所有注册到本线程的Selector上面的Channel
3、设置并绑定服务端的channel
4、5、创建处理网络事件的ChannelPipeline和handler,网络时间以流的形式在其中流转,handler完成多数的功能定制:比如编解码 SSl安全认证
6、绑定并启动监听端口
7、当轮训到准备就绪的channel后,由Reactor线程:NioEventLoop执行pipline中的方法,最终调度并执行channelHandler
客户端


网友评论