网络应用系统,模块交互是在所难免的,为了日后系统的升级方便,应该在系统设计之初就把系统的通信协议格式确定下来,这样无论后面模块怎么变化,通讯接口始终保持一致,可以保证新老模块的并存。
从之前BT协议分析的经验,以及对现在所在公司的报文协议进行通用报文和通信协议的设计。
报文格式
模块之间的通信需求千差万别,单一报文格式是无法实现这些通信需求。
BT协议报文开始都是4字节长度,然后是消息体,消息体开始就是1字节msg_type消息类型。对这些通信报文进行抽象可以得到“定长头部+变长数据”的通信报文设计方法。
定长头部可以设计实现一个通用的消息头comhead_t;变长数据,采用灵活的数据组织方式,例如json、xml、protobuff等,这里我们选用json作为我们的基准pack格式,性能有要求的地方可以使用bson。
typedef struct _comhead_t { unsigned int magic_num; ///<标识字段,用于识别报文 unsigned short id; /// unsigned short version; ///<版本 unsigned int reserved; ///<预留字段
unsigned int session_id; ///<接入模块创建Session后设置 char provider[16]; ///<请求模块设置,方便调试&日志输出
unsigned int body_len; /// } comhead_t;
|
变长数据pack,类似字典进行数据组织,支持多种数据类型:INT、String、List、Dict等。字典型变长数据pack,可以很容易通过扩展转变为各种动态语言中的字典VAR,方便实现系统中多语言互交互。
对于上层应用系统应该预先约定pack中一些字段,例如消息类型msg_type字段,错误号err_no字段。
消息类型msg_type的字段,用来表示报文的应用类型,因为不同类型报文的消息状态机不同。
msg_type设定的规则:
1. 全局命名空间
对于msg_type可以在整个系统中采用全局命名空间,例如从1开始设定,每新增一个需求就增加相应的msg_type。
2. 数值空间分段设置
为了上面全局命名空间的msg_type设置,应该按照功能将msg_type空间进行分段设置,例如10000-20000开始的值都是与子系统A交互的消息;20000-30000的值都是与子系统B交互的消息。
通讯协议
通信协议跟上层业务系统就严重相关了,这里只是对常见应用场景进行抽象出一些协议设计中必须的一些阶段。
分析之前逆向分析的协议,可以发现通讯协议无非下面4种消息类型:
1. 握手类消息,进行会话初始化,这个过程常见的工作包括:密钥协商、身份识别。
2. 控制类消息,控制会话的双向或单向状态,例如单向请求暂停,单向请求重启,这类消息一般没有应答报文。
3. 请求应答类消息,请求对方的资源,这类消息都是成对出现的,有请求有应答。
4. 保活类消息,定期或不定期发送Keepalive报文,表示自己的存在,保持长连接,可能附带一些自己的状态信息。
纵观这4类报文,其中控制类消息和请求应答消息,是大部分业务系统设计的时候肯定会考虑的。但是对于handshake和keepalive报文在很多设计中就被忽略了,这里特别提醒一下;-)
密钥交换
出于专业角度,这里对Handshake中的密钥交换进行一些简单的说明,基本上现在常用的密钥交换有两种:
DH密钥交换(Diffie-Hellman)和RSA密钥交换,密钥交换都是基于当前一些不可解的数学问题实现的,例如:离散对数问题、费马小定理、欧拉定理、大整数分解问题等。
这里简单描述一下DH密钥交换算法。
DH 密钥交换算法基于数论中的一个古典难题:离散对数问题。
离散对数问题:若 p 是素数,p 已知,考虑方程 y = gx mod p,给定 g,x 求 y 是简单的,但给定 y,g 求 x,即求 x = logg,py mod p,在计算上是不可行的。
DH 密钥交换算法的描述如下:
已知公开的素数 p 和 p 的本原根 α
1. 用户 A 选择秘密的 Xa
Xa mod p,将其发送给 B。
2. 用户 B 选择秘密的 Xb
Xb mod p,将其发送给 A。
3. A 和 B 分别计算 Ka = (Yb)Xa mod p 和 Kb = (Ya)Xb mod p,就同时得到了共享的密钥 K=Ka=Kb,然后就可以用 K 进行加密传输了。
DH 密钥交换算法的优点在于:双方在通信前不需要知道任何共享的密钥,而是通过公开的 p 和 α 协商出一个密钥来进行加密通信。
参考
阅读(12108) | 评论(1) | 转发(0) |