几个小兄弟要实现一个网络服务器的功能,老板让他们设计一下协议,于是他们问我,用Socket+Tcp/ip就可以了吧。我觉得问题很大,就找他们聊了一会,以免他们以为设计协议就是选择用udp还是tcp这么简单。
Socket翻译为插座,这个翻译蛮烂的,只能牵强的隐喻一下了。插座一接上就能有电流,相对于网络通讯的数据流。但UDP的数据包模式如何理解?相当于有些电网提供的是直流电吧。。。
UDP是不可靠数据包协议,存在的必要是什么--性能。某些场合关注性能,并且可以接受一定程度的丢包,比如商场、飞机场里的大显示屏,丢了几个数据包大不了就显示几格阻止人类文明进步的马赛克,反正过一会就有新的数据重新显示
TCP是可靠的,但只是系统API层面保证到达,或者不可到达时立刻告知(如:断网、超时)。业务层面就存在几个问题了:1、业务上的数据包会粘在一起到达接收方,这是流模式的Tcp本质决定的。2、断网时等异常时,需要业务层面自行处理这种情况,是要自动重发还是不管了(让用户自己处理,如:网页的手工F5刷新)。3、数据格式如何组织,用文本协议还是二进制协议,这个才是他们老板想叫他们做的工作!
上述3个问题的简单解决办法如下:
1、粘包。网上有不少socket框架已经处理好粘包的问题了,大体就是数据格式中要带有数据包长度的信息,以便程序能正确缓存、截断数据。
2、异常。如果真要业务层面也保障数据包的正确到达,就要自己也实现一个断网尝试重连、数据超时重发机制了,想像一下玩Dota时网络不好的情况,这里有大量的异常处理机制在运用。有一种优化手段是每个数据包都带有编号,接收方发现编号有跳号,则主动提醒服务端重发。
3、协议。
- 简单的就用空格符做间隔,回车符做结尾的文本协议,类似命令行环境下的Dos命令。这种协议的好处是简单易读,实现容易。缺点是效率低,保密性差。如:telnet 8.8.4.4 100、ping 4.4.8.8 -t
- 复杂的要用二进制协议,所有信息已经经过编码(如:0x1表示telnet,0x2表示ping),并适当的组织起来,一般的组织方式是:前N个字节表示数据长度,中间N个字节表示命令,后边则是该命令需要的参数。为了校验错误,还可以考虑在最后加上数据的散列值,如CRC、MD5。
- 商用的系统几乎都采用二进制协议,因此好的Socket框架除了解决Socket的高性能管理外,还会内置有常见协议的支持,如:superSocket(http://www.cnblogs.com/jzywh/),只需要简单的在框架中配上一些参数,再加上自己的一些代码就能实现一个满足业务要求的高性能网络服务器。
因此,几个小兄弟的首要工作是列出业务上需要的所有命令(还包括参数等),然后对命令、参数进行编码并出具编码表,最后才是选一个Socket框架并实现功能。