大概两年前,我就在想写一套按照自己架构的属于自己的通用通信类库。
从一年半前,开始着手编写,刚开始,不得其门,参考了很多开源通信框架,但是其应用实现场景,与自己想要的或需要抽象表达的,总差了一些方向。
这其中修修改改,大概有三到四个月的时间,曾经变动了几次大的设计架构,总算做出了一个版本。
但是这个版本,离自己想要的还是差一点火候,但是缺在哪里自己又不清楚,后来意识到了,缺的是定位,想大而全了,多了一些自己可能用不到的设计,把主要关注点偏离掉了。
大概一年前,用这个版本,正式开始在第一个项目中使用,通用通信类库或框架,无非就是把常见的通信方式,包括:串口、TCP、UDP打包成通用端口,达成线程调度一致,接口模型一致。但是使用过程中,出现过几次纠结,第一次是异步通信数据会话,转变成同步等待的过程,比如我们向远程发送了一个执行请求,会返回一个结果,但是这个结果其实是在异步线程上回来的,请求的同步线程,这会儿可能就是一个信号量的等待,第一版的设计中,我把这个过程放在了通信层,但是这遇到了问题,如果是会话连接上回来的数据包不是当前请求的呢,或回来的数据包一次解析不完呢,这同步等待的过程就出现了问题,尤其是大数据量的会话调用上。完蛋了,在连接上偶尔出现的一个连包或粘包现象,造成了后续数据包的解析错误,解包成功率降低。
那就思考如何解决吧,其实为了实现这种同步处理,曾经在会话连接上做了大量的心思和操作,比如把发送队列引入消息过滤器,在消息过滤器返回时,优先从队列头弹出数据作为同步数据匹配。但是这种还是有点撞运气的嫌疑,如果运气不好,就比较可悲了。那就再分析,业务调用的封包、拆包都是属于消息层的事,好了把这种同步过程,放到消息层,突然一下子问题得到解决了,因为消息层会有数据包的包头、包尾等,也就是包协议,有了完整的包协议,要解决就简单了,还是换了一种思路,问题得到迎刃而解。
大概半年前,这个版本已陆续在一些项目中开始大量使用,此时发现,在利用其做一些复杂的设备交互协议时,又遇到了新问题,没有一个统一的设备协议开发接口,也就是一个通用的附属协议栈,利用协议栈可以与通信层更低的耦合和关注,开发人员只需要去实现协议栈接口即可,通信调度层自动实现调度。为了实现这个通信栈,势必要求对通信框架的所有调用的具体执行都是需要回掉回来执行,因为总会有具体的协议转换的地方,但是具体的协议的传输就不关心了。好在利用C#语言,提供了大量的动态注入机制,利用这种机制,只需要实现基本的接口,包括数据请求、应答、消息封包、消息解包等几个接口,所有的通信逻辑相关的,都是利用接口实现逻辑,而我们的协议栈,则附属到接口上,好了每种协议栈在实现时,先去实现几个基本的接口,有了这个协议栈内部就完全不关心通信的调用逻辑了。
解决了大的问题,基本上已经实现了层与层的隔离了,也就像是TCP/IP协议栈那么明确的隔离了。但是小的问题出现了,为了实现消息应答,因为担心数据包连包等情况的出现,曾经的应答层,出现了大量的链表等,到了业务层通知调用时,也需要在链表中遍历应答数据,这可不是好主意,我要的是一个协议栈,栈的目的是,数据来了就能自动弹入、弹出,数据的入、出都是明确的数据实体,好了引入消息分包缓冲消息队列吧,这个队列的目的是无休止的去对数据包进行分割解析,直到剩下的缓冲区数据中构不成一个完整的数据包为止,分割的数据按照处理顺序,分别压入消息队列,由消息队列将应答数据泵入业务层,实现业务处理。
通过几次优化组合,实现了代码的简洁、高效,为项目所用,代码量最后一版比第一版的量少了一半,但是功能却更明确了。
总结几次代码的打磨,第一是要明确自己做的框架类库的应用场合或环境,这是大定位,第二,要有足够的技术储备,比如连基本的TCP会话连接管理、线程池构造、消息构造都没搞懂,其他的就谈不上了,功课要做足。第三,业务场景中应用,实践是检验真理的唯一标准嘛。有了这几块,就是时间的打磨了,要耐得住寂寞,不能轻易的放弃。