来到火币,领导教育我要进行多方面的总结和分享,自己一直都是比较喜欢总结的人,但是我有一个问题,就是我语句表达不是很清晰,我所掌握的技术可能不是比较高深的,后来想了想,语言表达不清晰是可以锻炼的,掌握技术不是高深的,是给自己的一个记忆,也是给那些初入门者的一个思路。

我在联通华建(曾经单独成立鑫财通一年左右,现在公司名叫梧桐花开)的时候,亲自参与讨论设计实现了前置socket连接中用户交互的升级体验。

业务背景

前置交易服务系统指的是鑫财通部署在券商的交易服务程序和业务处理机程序,实现客户端用户券商账户相关的请求。客户端每次登录都连接交易服务程序,当用户发出请求的时候,客户端向交易服务程序发送数据请求或委托指令,交易服务程序将指令转发到业务处理机程序,业务处理机程序将前置机转发过来的数据解包,按照交易接口规范重新组包,把请求发给交易柜台,然后把柜台返回的结果发给交易服务程序,交易服务程序再将业务处理机返回的结果发送给客户端,网络连接结构如图中红框所示。

鑫财通网络连接服务结构图

因此,以上的条件保证了交易业务的正常请求。在各种加密验证的有效处理下,也保证了整个连接的安全性。

服务网络规则背景

 1、客户端与交易服务程序之间的连接是长连接,业务处理机与券商交易柜台之间的连接虽然为socket连接,但是一次只进行一个业务的请求,可以当成短连接来看待。

2、在同一个客户端和交易服务程序之间的连接中,如果同时发出两个请求,业务处理机会创建两个线程请求券商交易柜台,并且客户端发送请求和接收到的反馈数据都会有一个固定的时间戳,也就是客户端能够区分出来该数据返回对应的是哪次请求。

曾经iOS前置连接设计方案

由于iOS代码是由brew代码修改而来,做了一些简单修改,形成设计如下:

层级名称 | 类名 | 说明 - | :-: | -: 界面交互层 | LTDataInterface | 与界面进行交互,接收界面协议请求或反馈给界面请求数据结果 协议组装层 | HJSTKNetWorkDataInterface | 组包、解包、验证数据有效性以及独有的秘钥交换环节处理 数据链接层 | HJSTKStreamSocket | 调用系统接口进行数据的请求,包括返回链接错误等结果

数据链接层

此层处理的是纯粹的socket数据的发包和收包。

协议组装层发过来的数据在可以发送数据的时候直接发送给服务端,不管任何的协议规则。

对于接收到的数据按照协议组装层的要求需要获取到多少数据以及超时时间,只要没有在固定时间内获取到规定的数据长度,则认为超时,反馈给协议组装层。因为socket的传输数据一般都是两部分,第一部分的长度是固定的,其中就包含后边数据的长度,但是由于此时的数据链接层只管接收不管数据的具体内容,一个数据的接收对于数据链路层来说就是两组数据的返回,所对应的超时时间就要协议组装层调用两次,使得同一个请求有两个互不关联的超时处理。

存在问题: * 一次数据的返回要用两个超时时间,设计上不符合一个数据整体的设计结构。 * 同一时刻只能处理一个任务,使得请求只能串行连接,界面层在请求一个业务时只能锁屏处理。

协议组装层

此层接收到界面交互层的请求处理后,按照协议进行组包。与此同时,该层控制着当重新创建一个链接,需要在业务请求前进行交换秘钥,当获取到交换秘钥数据解析成功之后继续进行后续的业务组包请求。

界面交互层

界面组装层组要就是接收到界面发过来的请求把传递过来的数据和需要传递的数据发送给协议组装层。

##升级优化

考虑代码优化和用户体验,需要对代码进行优化升级,主要考虑以下几个方面。

不锁屏

因为以往执行前置请求时就要进行锁屏处理,以防用户操作出现同一时刻要两个业务请求的处理情景,同时可以进行多个业务请求。

委托请求特殊处理

在不锁屏的情况下,要特别注意的是委托业务的重要之处,因为一个股票在一秒钟的时间内就可能会变化很大,因此要考虑如果有多个数据请求的情况下,要保证委托请求优先送达服务端。

择优轮询

一般每个券商都会有两个前置服务,以防一个前置程序出现问题后不影响用户的相关操作,该内容会另发文章进行描述。

现在设计

在新的前置请求框架结构中,自下而上仍然还是数据链路层、协议处理层和界面交互层三个层次,但是相对功能发生了很大的改变。

数据链路层:

数据链路层负责数据的传递、接收完整性验证、超时管理等操作,完成前置请求数据的发送和数据的接收,并将请求结果返回给协议处理层。

数据链路层将前置协议字段结构和socket链接创建、数据发送、数据接收结合到一起,每次请求数据和接受数据的完成都是一个前置请求协议的完整数据:在接受到发送数据func sendData(data:Data)命令时,将请求数据全部转换加载到待请求数据中,等待后续链接可以发送数时发送给服务端,将请求超时时间分别加入到超时数组中。每当数据可以发送的时候,只要本地存在待发送数据就发送,并将没有发送出去的数据继续保留待下次发送;在接收到数据时,根据协议格式,通过返回数据的前n个字节判断该数据请求该返回的数据长度,在获取数据超过n字节数据时立即根据n字节数据内容判断后续的内容长度m,当数据获取长度达到一个完整返回包长度(长度等于n+m)时,直接通过闭包回调的方式调用协议组装层将整个协议请求数据全部传递过去,否则按照数据请求超时处理。

通过数据链路层与具体业务协议处理产生关联,更加有效的进行超时管理,不是前n个字节为一个超时标准,而是一整个请求数据的发送到返回是一个超时时间来控制,与此同时也保证了每次的请求都是一个完整的,也为本设计中的同一时刻可能等待多条协议响应提供了基本前提。使得代码结构层次更加鲜明,并使得协议组装层对请求数据与反馈数据的管理配对等代码编写更加简洁,增加代码的可读性。

协议组装层:

协议处理层主要调用数据链路层,决定着socket的连接和协议请求的发送,同时也负责请求数据的组包、解包、请求队列的管理,对交易请求择优轮询起着至关重要的作用。协议组装层直接将请求结果返回到协议对应的具体界面,避免过多的逻辑传递。

协议组装层主要控制着请求数据的队列机制,控制一个待请求交易数组,保证了队列的有序控制。接收到协议请求时,如果网络断开或者还没有完成密钥交换请求,则将请求放置待请求队列中,并先完成创建连接和密钥交换流程,等密钥交换完成后,将所有待请求委托数据全部发送给数据链路层,并保证委托数据之外的其它请求数据在上一个非委托数据请求结果回来之后再发下一个请求。而进行委托数据请求时,如果完成了交换秘钥,则直接发送给数组链路层,不进行任何的数组存储。

当接收到链路层数据时,通过时间戳判断该数据返回所对应的请求配置,进而反馈给界面。

界面交互层:

当界面控制器要进行数据请求时,将请求数据以及block回调内容传递给界面交互层相关方法,界面交互层再按照规则将传递过来的数据和当前券商账户相关基本数据组装成QzModel对象,并根据请求协议号的判断对委托等请求给予委托特别标识赋值cmd.isFirstExch 使得协议请求在协议组装层有特殊通道,然后调用协议组装层的send方法使得协议组装层继续后面的处理。

界面交互层直接为界面提供接口,接收页面请求命令,根据业务请求整理出需要传递给服务端的相关数据model,并传递给协议处理层,不进行任何结果的反馈。同时也控制账户的切换,对外提供当前账户相关登录数据的查询功能。