19年写的一个基础的TCP服务框架,内置了一个简单IOC容器,当时的目标是一方面能作为组件供第三方集成实现TCP通讯相关功能,另一方面作为提供一种服务框架范式 。所以框架核心点主要还是通过适度的封装,隐藏底层的通讯细节,最终调用者接受到的是经过合包分包处理的字节数组,不涉及具体的协议解析,大家如果使用可以再基于业务进行适度的封装 。
好,废话不多说,简单介绍下整个架构和源码细节 。
Jtcp-cmmonJtcp-cmmon主要放置一些基础配置与工具类 。1、这里注意的服务配置类与默认配置项 JtcpConfig、JtcpOptions,JtcpConfig 顾名思义就是配置类,而JtcpOptions则定义了默认值; 2、RouteEnum枚举中列出了几种通用的网络通讯事件类型,作为注解中的字段定义路由
public enum RouteEnum { OnConnect, //链接 OnDisconnect, //链接断开 OnRecevie, //数据接收 OnSessionTimeOut, //超时 OnException //异常 }
Jtcp-transportJtcp-transport 基于Netty提供了TCP服务与报文解析功能,这里我针对常规固定字节起始的协议,通过递归方式对报文粘包、半包等进行了处理
/** * state = 0 开始解析 * state = 1 解析(递归处理粘包) * state = 2 半包 */ private void parseCompletePackets(ChannelHandlerContext ctx, byte[] bytesReady, List<Object> out, int magicByteBegin, int magicByteEnd) throws IOException { if (state == 0) { // 开始解析 dataStream = new ByteArrayOutputStream(); // 包数据开始状态,查找开始标识 if (bytesReady[0] != magicByteBegin) {//第一包必须从协议报文头开始 return; } state = 1; } if (state > 0) { int pos = indexOfMagicByte(bytesReady, magicByteEnd);//寻找尾部标识index,跳过头部标识位从1开始 if(state == 2) {//半包状态 if(bytesReady[0] == magicByteEnd) {//半包状态,但下段报文7E开头,明显是不正常的 dataStream.reset(); //只能清除目前已累积的所有数据 } } if (pos != -1) { // 结束标识 dataStream.write(bytesReady, 0, pos); byte[] ad = dataStream.toByteArray(); // 读取完整一个报文 out.add(ad); // 重置为包开始处理状态 state = 0; // 将剩余字节写入内存字节流中 if (pos != bytesReady.length) { byte[] remainBytes = new byte[bytesReady.length - pos]; System.arraycopy(bytesReady, pos, remainBytes, 0, remainBytes.length); parseCompletePackets(ctx, remainBytes, out, magicByteBegin, magicByteEnd); } } else { // 无结束标识,非完成报文,继续后续处理 state = 2; //报文体读取状态,直接将当前数据写内存字节流中 // 在下一次数据过来时处理结束标识 dataStream.write(bytesReady, 0, bytesReady.length); } } }
经验总结扩展阅读
- 常用的论证方法有哪些
- 什么花花语是错过的爱
- 用电器是将什么能转化成什么能
- Docker | 镜像浅析,以及制作自己的镜像
- 车公庙丰盛町有什么好吃的
- 贫血食补补什么
- 禁止焦虑的签名短句 引人深思的好听签名
- 青菜肉丝面的做法
- 心累的低调又含蓄的签名 星星说它累了让我别许愿了
- 美国NBA的球星乔丹的全名