19年写的一个基础的TCP服务框架,内置了一个简单IOC容器,当时的目标是一方面能作为组件供第三方集成实现TCP通讯相关功能,另一方面作为提供一种服务框架范式。所以框架核心点主要还是通过适度的封装,隐藏底层的通讯细节,最终调用者接受到的是经过合包分包处理的字节数组,不涉及具体的协议解析,大家如果使用可以再基于业务进行适度的封装。

好,废话不多说,简单介绍下整个架构和源码细节。

Jtcp-cmmon

Jtcp-cmmon主要放置一些基础配置与工具类。 1、这里注意的服务配置类与默认配置项 JtcpConfig、JtcpOptions,JtcpConfig 顾名思义就是配置类,而JtcpOptions则定义了默认值; 2、RouteEnum枚举中列出了几种通用的网络通讯事件类型,作为注解中的字段定义路由

    public enum RouteEnum {
        OnConnect, //链接
        OnDisconnect, //链接断开
        OnRecevie, //数据接收
        OnSessionTimeOut, //超时
        OnException //异常
    }

Jtcp-transport

Jtcp-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);
            }
        }
    }

Jtcp-core

自定义实现一个IOC容器,可对消息处理handler进行管理,并通过注解的方式制定消息转发机制 首先遍历main函数下所有class类,并缓存所有指定注解@JtcpComponet的class类对象并注入sproutBeanFactory实例工厂

    /**
     * 缓存所有指定注解的class<?>类对象
     * @param packageName
     * @return
     * @throws Exception
     */
    public static Map<String, Class<?>> getBean(String packageName) throws Exception {

        if (componetMap == null) {
            Set<Class<?>> clsList = getClasses(packageName);

            if (clsList == null || clsList.isEmpty()) {
                return componetMap;
            }

            componetMap = new HashMap<>(16);
            for (Class<?> cls : clsList) {

                Annotation annotation = cls.getAnnotation(JtcpComponet.class);
                if (annotation == null) {
                    continue;
                }

                JtcpComponet sproutComponet = (JtcpComponet) annotation;
                componetMap.put(sproutComponet.value() == null ? cls.getName() : sproutComponet.value(), cls);

            }
        }
        return componetMap;
    }

实现方法路由,通过@JtcpRoute并结合上面定义链接、断开、消息接收、超时、异常等事件枚举类型,把触发的网络通信事件转发至指定的业务方法中处理

    /**
     * 根据注解调用方法
     * @param method
     * @param annotation
     * @param args
     * @throws Exception
     */
    public void invoke(RouteEnum routeEnum, Object[] args) throws Exception {
        Method method = RouterScanner.getInstance().routeMethod(routeEnum);
        if (method == null) {
            return;
        }
        Object bean = applicationContext.getBean(method.getDeclaringClass().getName());
        if (args == null) {
            method.invoke(bean);
        } else {
            method.invoke(bean, args);
        }
    }

channelRead接收数据并转发

        /**
         * 接收消息事件
         */
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object source) {
            try {
                byte[] dataBytes = (byte[]) source;
                JtcpContext sproutContext = new JtcpContext(ctx, dataBytes);
                RouteMethod.getInstance().invoke(RouteEnum.OnRecevie, new Object[] { sproutContext });
            } catch (Exception ex) {
            }
        }

Jtcp-example

示例代码

    public static void main(String[] args) throws Exception {
        JtcpBootstrap bootstrap = new JtcpBootstrap();
        bootstrap.config().setHost("127.0.0.1");
        bootstrap.config().setPort(8030);
        bootstrap.start();
    }

    @JtcpComponet
    public class DemoHandler{

        @JtcpRoute(RouteEnum.OnRecevie)
        public void res(JtcpContext jtcpContext) {
            jtcpContext.context.writeAndFlush(jtcpContext.getRecvBytes());
            //System.err.println(BytesUtils.toHexString(context.getRecvBytes()));
        }

        @JtcpRoute(RouteEnum.OnConnect)
        public void onConnect(JtcpContext context ) {
            System.err.println("连接成功");
        }
    }

好的以上就是框架代码的基本构造,涉及到了Netty的应用、粘包半包处理,实例缓存与方法路由等内容,整体并不复杂,这里只是提供了一种服务端编码的思路,供初学者参考。

github地址:https://github.com/dafanjoy/jtcp

基于Netty的TCP服务框架的更多相关文章

  1. 微言Netty:分布式服务框架

    1. 前言 几年前,我就一直想着要设计一款自己的实时通讯框架,于是出来了TinySocket,她是基于微软的SocketAsyncEventArgs来实现的,由于此类提供的功能很简洁,所以当时自己实现 ...

  2. 基于thrift的微服务框架

    前一阵开源过一个基于spring-boot的rest微服务框架,今天再来一篇基于thrift的微服务加框,thrift是啥就不多了,大家自行百度或参考我之前介绍thrift的文章, thrift不仅支 ...

  3. 基于.NET CORE微服务框架 -surging的介绍和简单示例 (开源)

    一.前言 至今为止编程开发已经11个年头,从 VB6.0,ASP时代到ASP.NET再到MVC, 从中见证了.NET技术发展,从无畏无知的懵懂少年,到现在的中年大叔,从中的酸甜苦辣也只有本人自知.随着 ...

  4. 基于netty的微服务架构

    基于netty的微服务架构 微服务一篇好文章 http://san-yun.iteye.com/blog/1693759 教程 http://udn.yyuap.com/doc/essential-n ...

  5. 基于netty的socket服务端触发了channelInactive方法,但实际连接没有断开的问题

    背景: 一个中小型H5游戏,后端使用基于 netty 的socket服务 服务端 分为 分发服务器 & 业务服务器,业务服务器可负载 用户客户端与分发服务器连接 分发服务器再作为客户端与每台业 ...

  6. 基于Spring-Cloud的微服务框架设计

    基于Spring-Cloud的微服务框架设计 先进行大的整体的框架整理,然后在针对每一项进行具体的详细介绍

  7. 如何基于gRPC沟通微服务框架

    本文我们来讲解一下如何使用 gRPC构建微服务,gRPC是一个开源框架,可用于构建可扩展且高性能的微服务并创建服务之间的通信. 背景 随着企业越来越多地转向微服务,对构建这些微服务的低延迟和可扩展框架 ...

  8. 基于.NET CORE微服务框架 -浅析如何使用surging

    1.前言 surging受到大家这么强烈的关注,我感到非常意外,比如有同僚在公司的分享会上分享surging, 还有在博客拿其它的RPC框架,微服务做对比等等,这些举动都让我感觉压力很大,毕竟作为个人 ...

  9. 基于netty手写RPC框架

    代码目录结构 rpc-common存放公共类 rpc-interface为rpc调用方需要调用的接口 rpc-register提供服务的注册与发现 rpc-client为rpc调用方底层实现 rpc- ...

随机推荐

  1. Flink Window&Time 原理

    Flink 中可以使用一套 API 完成对有界数据集以及无界数据的统一处理,而无界数据集的处理一般会伴随着对某些固定时间间隔的数据聚合处理.比如:每五分钟统计一次系统活跃用户.每十秒更新热搜榜单等等 ...

  2. 使用JAVA CompletableFuture实现流水线化的并行处理,深度实践总结

    大家好,又见面啦. 在项目开发中,后端服务对外提供API接口一般都会关注响应时长.但是某些情况下,由于业务规划逻辑的原因,我们的接口可能会是一个聚合信息处理类的处理逻辑,比如我们从多个不同的地方获取数 ...

  3. Java开发学习(十八)----AOP通知获取数据(参数、返回值、异常)

    前面的博客我们写AOP仅仅是在原始方法前后追加一些操作,接下来我们要说说AOP中数据相关的内容,我们将从获取参数.获取返回值和获取异常三个方面来研究切入点的相关信息. 前面我们介绍通知类型的时候总共讲 ...

  4. 使用python3.7配置开发钉钉群自定义机器人(2020年新版攻略)

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_132 最近疫情比较严重,很多公司依靠阿里旗下的办公软件钉钉来进行远程办公,当然了,钉钉这个产品真的是让人一言难尽,要多难用有多难用 ...

  5. 心动不如行动,基于Docker安装关系型数据库PostgrelSQL替代Mysql

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_171 最近"全栈数据库"的概念甚嚣尘上,主角就是PostgrelSQL,它最近这几年的技术发展不可谓不猛,覆盖 ...

  6. 万答#18,MySQL8.0 如何快速回收膨胀的UNDO表空间

    欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答 GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 背 ...

  7. Java NIO全面详解(看这篇就够了)

    很多技术框架都使用NIO技术,学习和掌握Java NIO技术对于高性能.高并发网络的应用是非常关键的@mikechen NIO简介 NIO 中的 N 可以理解为 Non-blocking,不单纯是 N ...

  8. Jenkins初始化界面一直显示Please wait while Jenkins is getting ready to work ...

    第一次访问jenkins时,会提示如下界面:  注:如果这个界面初始化的时间过长,则需要修改相关配置文件. 原因:因为访问官网太慢.我们只需要换一个源,不使用官网的源即可. 1.找到 jenkins工 ...

  9. rh358 001 Linux网络与systemd设置

    358 rhel7 ce ansible 部署服务 dhcp nginx vanish haproxy 打印机服务 服务管理自动化 systemd与systemctl systemctl 来管理sys ...

  10. 好好回答下 TCP 和 UDP 的区别

    写了这么多篇关于 TCP 和 UDP 的文章,还没有好好聊过这两个协议的区别,这篇文章我们就来开诚布公的谈一谈. 关于 TCP 和 UDP ,想必大家都看过一张这样的图. 有一个小姑娘在对着瓶口慢慢的 ...