上面两篇介绍了库的接口及封装,还有个问题就是数据的处理。
问题一:怎么处理通过回调【-(void) onSocket:(AsyncSocket*)sock didReadData:(NSData*)data withTag:(long)tag】接收的数据
tcp协议面向字节流,无边界,有序的。根据这些特性我们第一步要做的就是划边界,交互的数据可以都是字符串,在每个结尾处给一个特殊的字符表示结束,这样我们就可以向http一样传json了,短链接这样实现是没问题的,但是如果长连接你还得定义字段说明这块数据的意义,而且会增加流量。在长连接中,我们用结构体来给字节流划出边界,比如:
typedef struct
{
unsigned char Account[32];
unsigned char Password[32];
}LoginData;//发给服务器
typde struct
{
uint16_t isSuccess;//0-失败 1成功
}LoginRspose;//服务器处理后的返回
如果我知道一块数据时代码登录数据的,我可以直接强制转换成这个结构体,然后再进行处理。
问题来了,我们(服务器端或客户端)如何知道这块数据是代表登录数据了?在1000个字节里怎么确定这快数据的位子?
有序!这快数据绝对是连续的,我们现在把每一个接收能完整处理的或发生的一个完整的数据集叫一个数据包,我们要传送账号字符串我们就增加了一个char数组,现在要知道位字,需要定义个公用包头来解决以上问题,后面将以包头来解析数据。包头最基本的应该包括:
1,包体大小(根据这个取出数据);
2,包体类别(根据这个判断包的功能,我们采用命令的方式来编写代码,采用一级命令和二级命令);
我们将这个缓冲区定义成一下结构体
typedef struct
{
TCP_Head Head;//数据包头
unsigned char Buffer[SOCKET_TCP_PACKET];//数据缓冲
}TCP_Buffer;
SOCKET_TCP_PACKET宏定义缓冲区大小减去包头大小(SOCKET_TCP_BUFFER-sizeof(TCP_Head))。
包头定义为
typedef struct
{
TCP_infomation Info;
TCP_Eventtype type;
}TCP_Head;
包头里的结构体定义
typedef struct
{
uint8_t version;//使用消息的版本号
uint16_t datalenth;//包体数据长度
}TCP_infomation
根据datalenth取出数据大小
typedef struct
{
uint16_t FistCommond;
uint16_t SecondCommond;
}TCP_type
根据命令来判断出这个数据包的功能,去执行哪一段逻辑代码
比如我们规定 F=0,S= 1为登录命令,把数据填充到LoginData中在拷贝到TCP_Buffer结构体中的Buff中,算出包的到大小赋值给datalenth,发发送给服务端,服务器就可以根据这些字段来处理。
包头是服务端和客户端都必须严格遵守的,不符合这个协议的数据都会被丢弃,当然这个协议是根据你项目的需求定制的,比如要加密或带时间戳都可以在包头扩展数据,逻辑协议就是命令的定义及该命令定对应的数据结构体,这是分为2份,(命令可能一样,但是业务数据结构体可能不一样)比如:以上登录命令都可以是0-1,但是客户端发送登录命令必须遵守LoginData协议,服务器处理完0-1后返回的数据必须遵LoginRspose协议。以上是比较简单的,只说出了定义协议的思路,需要根据项目的需求定义出可以扩展,安全的协议。
下一遍我会讲怎么根据协议解析数据(断包,粘包处理)。这里已经有了处理思路,我们定义协议就是为了处理这些问题。