在SOCKET收发数据的过程设计,在日常的开发中会经常遇到,如何提供开发效率,使用模板方式进行开发是比较好的方式。
一、数据包通用格式定义
目的:网络传输的主要任务是将一定长度的数据安全传输到另一端,需要对数据包进行定义。
通用的格式是:包头+数据
包头的格式:起始码+包类型+ 包序列号+ 包CRC + 包长
起始码: 数据的起始,作为数据的同步使用,同时也可以用作协议的版本号来使用,比如V1版本定义的包头如下:
typedef struct packet_header_s
{
unsigned short startcode;
unsigned short packet_type;
unsigned int packet_index;
unsigned int packet_crc;
unsigned int packet_len;
}PACKET_HEADER_S;
可以定义STARTCODE = 0x5548, 下一个版本的定义有可能是这样的
typedef struct packet_header_s
{
unsigned short startcode;
unsigned char packet_type;
unsigned char packet_section;
unsigned int packet_crc;
unsigned int packet_len;
}PACKET_HEADER_S;
定义STARTCODE = 0XF048
这样通过STARTCODE的定义,软件就可以做到兼容,保证不同的版本软件前向兼容
包类型:对数据包进行区分,通过不同的包类型去对应不同的自定义协议,比较好的一种方式,也可以是拆包解包的标示,通过包类型对应真实数据的数据格式;
包序列号:传输的数据有可能很长,一般是采用分片的方式进行传输,包序列号可以认为是数据分段传输的标识。同时适用于多SOCKET进行数据传输,包的传输有可能是乱序的,通过序列号进行数据分辨;
包CRC:对数据进行校验,验证传输的正确性。
包长:此定义后面的数据的长度
二、通用传输协议定义
通常的做法是采用“一问一答”的方式,即发送,对方回应。对方超时没有回应,重复发。当SOCKET连接后,CLIENT首先发命令包,SERVER收到命令包,准备接收环境,回应是否准备好,CLIENT发送数据,SERVER回应接收情况,CLIENT根据回应包,决定是否传输下一个包,还是重新传输上一个包,重复上述过程,直到所有的数据都发送完成,CLIENT 发送下一个命令包,进行新一轮的数据传输。
不同的命令其实就是协议状态的迁移,回应是对命令的回应,同样影响协议状态的迁移,在定义协议的状态时,同时是使用这样的模型。
CLIENT、SERVER端需要记录的内容:
数据传输总长度
已经传输的长度
协议状态
对应SERVER端还需要记录多个SOCKET的情况
每一个SOCKET的状态
举例说明:
文件同步协议定义
typedef struct ptl_syncinfo_s
{
int status;
int last_msgcmd;
ssize_t totalsize;
ssize_t actsize;
ssize_t lastsize;
ssize_t blocksize;
int buffer_len;
int filehd;
char filename[MAX_FILEPATH_LEN];
char buffer[MAX_BUFFER];
}PTL_SYNCINFO_S;
协议状态
typedef enum
{
PTLSTATUS_MASTER_IDLE,
PTLSTATUS_MASTER_ANSWER,
PTLSTATUS_MASTER_PRORECEIVE,
PTLSTATUS_MASTER_RECEIVE,
PTLSTATUS_MASTER_RECEIVEOK,
}PROTOCAL_MASTER_E;
typedef enum
{
PTLSTATUS_SLAVE_IDLE,
PTLSTATUS_SLAVE_SERVICE,
PTLSTATUS_SLAVE_REQUEST,
PTLSTATUS_SLAVE_PROSEND,
PTLSTATUS_SLAVE_SEND,
PTLSTATUS_SLAVE_SENDOK,
}PROTOCAL_SLAVE_E;
命令字
typedef enum
{
FILESYNC_CMD_S_M_DATA_REQUEST = 0x01,
FILESYNC_CMD_M_S_DATA_ANSWER,
FILESYNC_CMD_S_M_SIZE_INFORM,
FILESYNC_CMD_M_S_SIZE_ANSWER,
FILESYNC_CMD_S_M_BLOCK_SEND,
FILESYNC_CMD_M_S_BLOCK_ANSWER,
FILESYNC_CMD_S_M_PROC_BREAK,
FILESYNC_CMD_M_S_PROC_BREAKOK,
}PTL_FILESYNC_CMD_E;
int PTL_Gossip_Master(SOCKET_MSG_S *msginfo, int index)
{
int socket_status;
int ret, num, len;
PTL_SYNCINFO_S *pinfo;
if (NULL == msginfo)
return(-1);
if (index >= SOCK_MAX || index <0)
return(-1);
pinfo = &g_syncinfo[index];
socket_status = pinfo->status;
switch(socket_status)
{
case PTLSTATUS_MASTER_IDLE:
if (msginfo->msg_cmd == SOCKCMD_RECEIVE)
{