我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。
这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。
目录
序
凡是两部分交互都要有个协议,双方都遵守协议一切就正常,一方犯错就进行不下去。大到TCP/IP这样的全世界都遵守的协议,小到自己写的两个程序交互,都是如此。
我这里说的“协议”指的是,一个程序要把数据通过二进制或者xml或者json之类传递给另一个程序的情形。
很多人会说“XML、json不就是标准格式吗?怎么会有问题!”,xml和json里面是什么格式?参数名是什么?数据类型是什么?随便给你个xml或json程序都能出结果?
自己写的两个程序可以共享同一套协议代码,两边同时编译一下就OK了。我们自己写东西确实是可以这样做的。只是一旦两个程序分开,一个可以改代码而另一个不能动,那怎么办?
这是很头疼的。
我看过很多代码,大部分在涉及到两个不同程序间的协议交互时缺乏基本的协议性设计(其实就是完全没有考虑),仅当两个程序都恰好都是正确的版本的时候才能正确运行,甚至,程序无法判断什么是“正确的版本”,只能看程序挂不挂、数据出不出来。
以下是一些要点:
版本
说起来好像必须有,其实大部分程序都没有。程序里面要定义自己的版本,做协议传输的时候带上,这样有问题的时候才能针对正确的版本代码去查错。如果程序有好几个部分相对独立,也可以分成几个不同的版本号,不要怕麻烦,该有的信息不要少,不然都是给自己挖坑。比如细分为程序版本和协议版本(显然这两个应该是分开的)。
兼容性
有了版本才能谈论兼容性。兼容性可能包括两方面:二进制的兼容和功能的兼容。二进制的兼容指的是原始数据过来能认得,不至于高版本收到低版本数据直接当垃圾丢了。功能的兼容就是一般的新版本功能扩充,同时还支持旧的版本。
二进制的兼容为什么单独拿出来呢,因为这是需要额外的技术考虑的。比如协议本来是简单的JSON格式,为了减少网络流量,新版本使用了压缩,传输的格式直接就是压缩后的二进制流,那么旧版本将无法识别新版本,只会当成垃圾数据丢掉。
这种情况下新版本如果考虑周全一些可以把压缩数据放在合法的JSON里面,虽然旧版本不会因此就知道如何解压数据,但起码知道自己收到了新版本数据,自己该升级了。
等等……旧版本是如何知道收到的是新版本数据呢?
这就依靠前面说的版本标识了。
版本标识
版本标识应该在制定协议的最初就完成(现实是这么做的人很少)。考虑到二进制兼容的问题,协议的基本载体要想好,如果基于二进制就在最开始定义几个字节来传输版本,如果基于XML或JSON就定义一个节点来记录相关信息。
版本格式要定义清楚,方便程序比对。点分格式人眼易读程序稍微复杂些。
容错
写协议处理最大的不同是需要步步查错,基本上可能说,只要数据是别人丢过来的,每一个字节都可能是错的。
比如:
- 必须的数据没有,程序读到空,访问空就挂了(此种应该报错)
- 数据名字错了,程序没读到,挂了(此种可以考虑兼容一下)
- 数据类型错了,比如json放整数,加不加引号感觉没什么区别,但是程序就挂了(此种可以在接口层直接做类型兼容)
- 数据不协调,比如数据直接给出的后续数据个数和实际传输的不一样
- 数据完全是空的,很多程序真的没处理这种情况
- 数据量极大,压垮了程序,这可能是无意的,也可能是恶意攻击
能检查就检查
一般程序很多错误场景不太可能发生,程序不检查也没啥大问题,出了问题也容易干预。协议问题就不好说了,比如网络服务,一个客户端不停地发错误数据,甚至可能根本没法找到它。
所以,协议处理程序一定不要偷懒,能检查的都检查,能保护的都保护。
(这里是结束)