赞
踩
本文首发于 慕雪的寒舍
以tcpServer的计算器服务为例,实现一个自定义协议
阅读本文之前,请先阅读 tcpServer
本文完整代码详见 Gitee
注意,当下所对tcp的描述都是以简单、方便理解起见,后续会对tcp协议进行深入解读
我们知道,tcp是面向连接的,客户端和服务端要先建立链接,才能开始通信
举个日常生活中的栗子,帮助理解3次握手和4次挥手
假如我们现在需要发送结构化数据,那应该怎么办?
我们知道,tcp是面向字节流的,也就是其能够发送任意数据。也能够发送C语言结构体的二进制数据;
不同平台,对结构体对齐的配置不同,大小端不同,其最终对我们字节流的解析也就不一样。如果采用直接发送结构体数据的方式来通信,适配性极低,我们的客户端和服务端都会被限制在当前的系统环境中运行;
可是,哪怕是同一个系统,其内部对大小端的配置也有可能改变!到时候我们的代码恐怕就无法运行了!
同理,在当初编写C语言通讯录的代码的时候,也不能采用直接将结构体数据写入文件的方式。后续代码升级、环境改变,都可能导致我们存在文件中的数据失效,这肯定是我们不希望看到的情况。
所以,为了解决这个问题,我们就应该将数据进行序列化
之后再发送,客户端接收到信息后,进行反序列化
解析出数据!
所谓序列化,就是将结构化的数据(可以暂时理解为c的结构体)转换成字符串的方式,发送出去
struct date
{
int year;
int month;
int day;
};
比如上面这个日期结构体,我们要想将其序列化,就可以用一个很简单的方式拼接成一个字符串(序列化)
year-month-day
客户端收到这个字符串之后,就可以通过查找分隔符-
的方式,取出三个变量,将其转成int后存放回结构体(反序列化)
这样,我们就算是规定了一个序列化和反序列化的方式,也就是一个简单的协议!
这里还会出现另外一个问题,我要怎么知道我已经读取完毕了一个序列化后的数据呢?
2000-12-10
10000-01-01
如上,假设有一天,我们的年变成了五位数;这时候,服务端要怎么知道自己是否读取完毕了一个完整的序列化数据呢?
这就需要我们做好规定,将前n字节作为标识长度的数据。接收到数据后,先取出前n个字节,读取道此次消息的长度m,再往后读取m个字节的数据,成功取出完整的字符串;
为了区分标识长度的数据和实际需要的序列化内容,我们可以在之中加上分隔符\t
;但这也需要我们确认,传输的数据本身不能带上\t
,否则会产生一系列的问题
10\t2000-12-10\t
11\t10000-01-01\t
以上的这一系列工作,都是协议定制
的一部分!我们给服务端和客户端规定了一个序列化和反序列化的方式,让二者通信规避掉了平台的限制。毕竟任何平台对字符串解码出来的数据都会是相同的!
下面就用一个计算器的服务,来演示一下吧
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。