项目地址:https://download.csdn.net/download/weixin_42528855/12549389
1.消息粘包:
实际:M1 和 M2 同时到达,并且被同时接收 ,最后才接收M3.
此时 M1 和 M2 就出现了消息粘包
2.消息不完整:
解决:
如何解决有序的混传数据:
三种方案:
提倡使用第二种:固定头部
起止符方案:需要一个一个字节的依次检测(很耗费时间)
头部的4个字节保证了后面的100个字节可以一次性传输,无需检查(可以避免消息粘包和丢失)
代码还是基于NIO的多人聊天室进行改动,但是对于多线程的部分没有进行改动:
1. 首先提供了一个 :公共的数据封装 类:
为什么会使用packet?
这里的主要目的是构建3层缓存,对于发送的数据是多样性的,我们需要一个统一的封装,Packet就是不错的选择。
当我们在发布数据之前会先封装为统一的Packet,Packet可以提供基础的Stream操作,随后会到达第二层,也就是Frame层,将一个包转化为不同的帧。对于包和帧都是有意义的,有实际的反向解析操作。
当我们到达真实需要发送的位置会将帧转化为IoArgs,此时就没有上层业务意义了,更多的是数据的载体。
这样的方式可以做到3层缓冲,同时也可针对不同层面做不同的调度,这也增加整体的框架调度性能。
接收消息:
2. 分析一下 AsyncReceiveDispatcher 类(处理 StringReceivePacket的类【ReceivePacket的子类】 )
在 AsyncReceiveDispatcher 类 中 关注 两个函数:
start() 和 registerReceive()
在 registerReceive() 中启动了 接收者的 receiveAsync(ioArgs); 异步接收类,
注意:start() 是在 Connector 类中的 setup函数里 启动的:
// 启动接收
receiveDispatcher.start();
receive涉及了三个回调: 这是慕课网上一位 昵称为:慕勒1089785 的大佬总结的
这三个回调基本就完成了数据接受的一个过程
第一个就是 上级SocketChannelAdapter实现了HandlerproviderCallback的接口,并且将其注册到了IoSelectorProvider之中,当可以处理数据时,下层就回调了接口中的方法。上级就让其来真正的实现接受。如用IoArgs来读取数据之类的。
第二就是,上层ReceiverDispatcher实现了IoArgsEventListener接口,并在SocketChannelProv中注册了,当下层IoArgs读取到数据之后,就会回调给上层,ReceiverDispatcher就会处理这个IoArgs,打包成Packet。
第三就是,Connector实现了一个回调接口,并在ReceiverDispatcher注册了,当下层Packet打包完成之后,就会回调给上层,让Connector打印数据。(Server还需要广播)。
发送消息:
发送消息相对于 接收消息 要简单一些,但是也涉及到了回调函数:
第一个就是 上级SocketChannelAdapter实现了HandleOutputCallback的接口,并且将其注册到了IoSelectorProvider之中,当可以处理数据时,下层就回调了接口中的方法。上级就让其来真正的实现接受。将写线程交给线程池来处理。
第二就是,上层AsyncSendDispatcher实现了IoArgsEventListener接口,并在SocketChannelProv中注册了,当下层IoArgs读取完Packet数据之后,就会回调给上层,ReceiverDispatcher就会处理这个IoArgs,如果没有发完,则继续发送当前包。