在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)
{
num = SOCKET_Read(msginfo->msg_socketfd, pinfo->buffer, MAX_BUFFER);
if (num <=0)
{
SOCKET_Close(msginfo->msg_socketfd);
ptl_gossip_stateclear(pinfo);
msginfo->msg_cmd = SOCKCMD_CLOSED;
socket_status = PTLSTATUS_MASTER_IDLE;
break;
}
/* parse data */
ret = ptl_gossip_master_parser(pinfo, num);
printf("\n --- OTHER PROTOCAL --- %d \n", ret);
break;
}
if (msginfo->msg_cmd == SOCKCMD_BEGINSRV)
{
printf("\n +++ MASTER --> SLAVE PTLCMD_M_S_PTLTYPE_ANSWER +++\n");
pinfo->buffer_len = ptl_gossip_makeheader(PTLCMD_M_S_PTLTYPE_ANSWER, pinfo->buffer, 0);
num = SOCKET_Write(msginfo->msg_socketfd, pinfo->buffer, pinfo->buffer_len);
if (num <=0)
{
SOCKET_Close(msginfo->msg_socketfd);
ptl_gossip_stateclear(pinfo);
msginfo->msg_cmd = SOCKCMD_CLOSED;
socket_status = PTLSTATUS_MASTER_IDLE;
break;
}
/* wait for slave send data request , change status */
socket_status = PTLSTATUS_MASTER_ANSWER;
}
break;
case PTLSTATUS_MASTER_ANSWER:
if (msginfo->msg_cmd == SOCKCMD_CLOSE)
{
pinfo->last_msgcmd = SOCKCMD_CLOSE;
break;
}
if (msginfo->msg_cmd == SOCKCMD_RECEIVE)
{
num = SOCKET_Read(msginfo->msg_socketfd, pinfo->buffer, MAX_BUFFER);
if ((num <=0) ||(pinfo->last_msgcmd == SOCKCMD_CLOSE))
{
SOCKET_Close(msginfo->msg_socketfd);
ptl_gossip_stateclear(pinfo);
msginfo->msg_cmd = SOCKCMD_CLOSED;
socket_status = PTLSTATUS_MASTER_IDLE;
break;
}
/* parse data */
ret = ptl_gossip_master_parser(pinfo, num);
if (ret == PROC_NEXT)
{
printf("\n +++ MASTER --> SLAVE FILESYNC_CMD_M_S_DATA_ANSWER +++\n");
pinfo->buffer_len = ptl_gossip_makeheader(FILESYNC_CMD_M_S_DATA_ANSWER, pinfo->buffer, sizeof(ssize_t));
num = SOCKET_Write(msginfo->msg_socketfd, pinfo->buffer, pinfo->buffer_len);
if (num <=0)
{
SOCKET_Close(msginfo->msg_socketfd);
ptl_gossip_stateclear(pinfo);
msginfo->msg_cmd = SOCKCMD_CLOSED;
socket_status = PTLSTATUS_MASTER_IDLE;
break;
}
socket_status = PTLSTATUS_MASTER_RECEIVE;
}
break;
}
break;
case PTLSTATUS_MASTER_PRORECEIVE:
break;
case PTLSTATUS_MASTER_RECEIVE:
if (msginfo->msg_cmd == SOCKCMD_CLOSE)
{
pinfo->last_msgcmd = SOCKCMD_CLOSE;
break;
}
if (msginfo->msg_cmd == SOCKCMD_RECEIVE)
{
num = SOCKET_Read(msginfo->msg_socketfd, pinfo->buffer, MAX_BUFFER);
if ((num <=0) ||(pinfo->last_msgcmd == SOCKCMD_CLOSE))
{
SOCKET_Close(msginfo->msg_socketfd);
ptl_gossip_stateclear(pinfo);
msginfo->msg_cmd = SOCKCMD_CLOSED;
socket_status = PTLSTATUS_MASTER_IDLE;
break;
}
/* parse data */
ret = ptl_gossip_master_parser(pinfo, num);
if (ret == PROC_BACK_IDLE)
{
msginfo->msg_cmd = SOCKCMD_ENDSRV;
printf("\n +++ MASTER --> SLAVE FILESYNC_CMD_M_S_PROC_BREAKOK +++\n");
pinfo->buffer_len = ptl_gossip_makeheader(FILESYNC_CMD_M_S_PROC_BREAKOK, pinfo->buffer, 0);
num = SOCKET_Write(msginfo->msg_socketfd, pinfo->buffer, pinfo->buffer_len);
if (num <=0)
{
SOCKET_Close(msginfo->msg_socketfd);
ptl_gossip_stateclear(pinfo);
msginfo->msg_cmd = SOCKCMD_CLOSED;
socket_status = PTLSTATUS_MASTER_IDLE;
break;
}
ptl_gossip_filestateclear(pinfo);
socket_status = PTLSTATUS_MASTER_IDLE;
break;
}
if (ret == PROC_NEXT)
{
//printf("\n +++ MASTER --> SLAVE FILESYNC_CMD_M_S_BLOCK_ANSWER +++\n");
pinfo->buffer_len = ptl_gossip_makeheader(FILESYNC_CMD_M_S_BLOCK_ANSWER, pinfo->buffer, sizeof(ssize_t));
num = SOCKET_Write(msginfo->msg_socketfd, pinfo->buffer, pinfo->buffer_len);
if (num <=0)
{
SOCKET_Close(msginfo->msg_socketfd);
ptl_gossip_stateclear(pinfo);
msginfo->msg_cmd = SOCKCMD_CLOSED;
socket_status = PTLSTATUS_MASTER_IDLE;
break;
}
if (pinfo->totalsize == pinfo->actsize)
{
printf("\n +++ MASTER --> SLAVE FILESYNC_CMD_M_S_BLOCK_ANSWER +++\n");
if (pinfo->filehd != -1)
{
close(pinfo->filehd);
pinfo->filehd = -1;
}
socket_status = PTLSTATUS_MASTER_RECEIVE;
}
else
{
socket_status = PTLSTATUS_MASTER_RECEIVE;
}
break;
}
}
break;
case PTLSTATUS_MASTER_RECEIVEOK:
break;
default:
break;
}
pinfo->status = socket_status;
return(0);
}
int PTL_Gossip_Slave(SOCKET_MSG_S *msginfo, int index)
{
int socket_status, protocaltype;
int ret , num;
char filepath[MAX_FILEPATH_LEN];
struct stat finfo;
ssize_t filesize;
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;
protocaltype = msginfo->msg_protocaltype;
ret = 0;
switch(socket_status)
{
case PTLSTATUS_SLAVE_IDLE:
if (msginfo->msg_cmd == SOCKCMD_CLOSE)
{
SOCKET_Close(msginfo->msg_socketfd);
ptl_gossip_stateclear(pinfo);
msginfo->msg_cmd = SOCKCMD_CLOSED;
socket_status = PTLSTATUS_SLAVE_IDLE;
break;
}
if (msginfo->msg_cmd == SOCKCMD_BEGINSRV)
{
memset(pinfo->filename, 0, sizeof(char)*MAX_FILEPATH_LEN);
if (NULL == msginfo->msg_ptr)
{
break;
}
memcpy( pinfo->filename , msginfo->msg_ptr, sizeof(char)*msginfo->msg_len);
memset(filepath, 0, sizeof(char)*MAX_FILEPATH_LEN);
strcpy(filepath, g_path);
strcat(filepath, pinfo->filename);
free(msginfo->msg_ptr);
printf("\n +++ filename =%s +++\n", filepath);
ret = access(filepath, 0);
if (ret != 0)
{
printf("\n +++ FILE NO EXIST +++\n");
msginfo->msg_cmd = SOCKCMD_ENDSRV;
ptl_gossip_stateclear(pinfo);
socket_status = PTLSTATUS_SLAVE_IDLE;
break;
}
stat(filepath, &finfo);
pinfo->totalsize = finfo.st_size;
printf("\n +++ file len =%d +++\n", pinfo->totalsize);
printf("\n +++ SLAVE --> MASTER PTLCMD_S_M_PTLTYPE_INFORM +++\n");
memset(pinfo->buffer, 0, sizeof(char)*MAX_BUFFER);
memcpy(pinfo->buffer +sizeof(PROTOCAL_HEAD_S), &protocaltype, sizeof(int));
pinfo->buffer_len = ptl_gossip_makeheader(PTLCMD_S_M_PTLTYPE_INFORM, pinfo->buffer, sizeof(int));
num = SOCKET_Write(msginfo->msg_socketfd, pinfo->buffer, pinfo->buffer_len);
if (num <=0)
{
SOCKET_Close(msginfo->msg_socketfd);
ptl_gossip_stateclear(pinfo);
msginfo->msg_cmd = SOCKCMD_CLOSED;
socket_status = PTLSTATUS_SLAVE_IDLE;
break;
}
socket_status = PTLSTATUS_SLAVE_SERVICE;
}
break;
case PTLSTATUS_SLAVE_SERVICE:
if (msginfo->msg_cmd == SOCKCMD_CLOSE)
{
pinfo->last_msgcmd == SOCKCMD_CLOSE;
printf("\n +++ APPMAIN --> SLAVE MSG = SOCKCMD_CLOSE +++\n");
break;
}
if (msginfo->msg_cmd == SOCKCMD_RECEIVE)
{
num = SOCKET_Read(msginfo->msg_socketfd, pinfo->buffer, MAX_BUFFER);
if (num <=0)
{
SOCKET_Close(msginfo->msg_socketfd);
ptl_gossip_stateclear(pinfo);
msginfo->msg_cmd = SOCKCMD_CLOSED;
socket_status = PTLSTATUS_SLAVE_IDLE;
break;
}
if (pinfo->last_msgcmd == SOCKCMD_CLOSE)
{
printf("\n +++ SLAVE CLOSE SOCKET +++\n");
SOCKET_Close(msginfo->msg_socketfd);
ptl_gossip_stateclear(pinfo);
msginfo->msg_cmd = SOCKCMD_CLOSED;
socket_status = PTLSTATUS_SLAVE_IDLE;
break;
}
ret = ptl_gossip_slave_parser(pinfo, num);
if (ret == PROC_NEXT)
{
printf("\n +++ SLAVE --> MASTER FILESYNC_CMD_S_M_DATA_REQUEST +++\n");
memset(pinfo->buffer, 0, sizeof(char)*MAX_BUFFER);
memcpy(pinfo->buffer +sizeof(PROTOCAL_HEAD_S), &pinfo->totalsize, sizeof(ssize_t));
memcpy(pinfo->buffer +sizeof(PROTOCAL_HEAD_S) + sizeof(ssize_t), pinfo->filename, strlen(pinfo->filename));
pinfo->buffer_len = ptl_gossip_makeheader(FILESYNC_CMD_S_M_DATA_REQUEST, pinfo->buffer, sizeof(ssize_t) + strlen(pinfo->filename));
num = SOCKET_Write(msginfo->msg_socketfd, pinfo->buffer, pinfo->buffer_len);
if (num <=0)
{
SOCKET_Close(msginfo->msg_socketfd);
ptl_gossip_stateclear(pinfo);
msginfo->msg_cmd = SOCKCMD_CLOSED;
socket_status = PTLSTATUS_SLAVE_IDLE;
break;
}
socket_status = PTLSTATUS_SLAVE_REQUEST;
}
break;
}
break;
case PTLSTATUS_SLAVE_REQUEST:
if (msginfo->msg_cmd == SOCKCMD_CLOSE)
{
pinfo->last_msgcmd = SOCKCMD_CLOSE;
printf("\n +++ APPMAIN --> SLAVE MSG = SOCKCMD_CLOSE +++\n");
break;
}
if (msginfo->msg_cmd == SOCKCMD_RECEIVE)
{
num = SOCKET_Read(msginfo->msg_socketfd, pinfo->buffer, MAX_BUFFER);
if (num <=0)
{
SOCKET_Close(msginfo->msg_socketfd);
ptl_gossip_stateclear(pinfo);
msginfo->msg_cmd = SOCKCMD_CLOSED;
socket_status = PTLSTATUS_SLAVE_IDLE;
break;
}
if (pinfo->last_msgcmd == SOCKCMD_CLOSE)
{
pinfo->last_msgcmd = 0;
printf("\n +++ SLAVE CLOSE SOCKET +++\n");
SOCKET_Close(msginfo->msg_socketfd);
ptl_gossip_stateclear(pinfo);
msginfo->msg_cmd = SOCKCMD_CLOSED;
socket_status = PTLSTATUS_SLAVE_IDLE;
break;
}
ret = ptl_gossip_slave_parser(pinfo, num);
if (ret == PROC_BACK_IDLE)
{
/* slave receive PTLCMD_PROCO_BREAKOK */
msginfo->msg_cmd = SOCKCMD_ENDSRV;
ptl_gossip_filestateclear(pinfo);
socket_status = PTLSTATUS_SLAVE_IDLE;
break;
}
if (ret == PROC_NEXT)
{
printf("\n +++ pinfo->totalsize =%d pinfo->actsize =%d +++\n", pinfo->totalsize , pinfo->actsize);
if (pinfo->totalsize == pinfo->actsize)
{
printf("\n +++ SLAVE --> MASTER FILESYNC_CMD_S_M_PROC_BREAK +++\n");
pinfo->buffer_len = ptl_gossip_makeheader(FILESYNC_CMD_S_M_PROC_BREAK, pinfo->buffer, 0);
num = SOCKET_Write(msginfo->msg_socketfd, pinfo->buffer, pinfo->buffer_len);
if (num <=0)
{
SOCKET_Close(msginfo->msg_socketfd);
ptl_gossip_stateclear(pinfo);
msginfo->msg_cmd = SOCKCMD_CLOSED;
socket_status = PTLSTATUS_SLAVE_IDLE;
break;
}
/* close file handle */
if (pinfo->filehd != -1)
{
close(pinfo->filehd);
pinfo->filehd = -1;
}
socket_status = PTLSTATUS_SLAVE_REQUEST;
break;
}
else
{
if (pinfo->filehd == -1)
{
memset(filepath, 0, sizeof(char)*MAX_FILEPATH_LEN);
strcpy(filepath, g_path);
strcat(filepath, pinfo->filename);
pinfo->filehd = open(filepath, O_RDONLY);
}
printf("\n +++ SLAVE --> MASTER FILESYNC_CMD_S_M_BLOCK_SEND +++\n");
pinfo->blocksize = 1024;
lseek(pinfo->filehd, pinfo->actsize, SEEK_SET);
num = read(pinfo->filehd, pinfo->buffer + sizeof(PROTOCAL_HEAD_S), pinfo->blocksize);
pinfo->lastsize = num;
pinfo->buffer_len = ptl_gossip_makeheader(FILESYNC_CMD_S_M_BLOCK_SEND, pinfo->buffer, num);
num = SOCKET_Write(msginfo->msg_socketfd, pinfo->buffer, pinfo->buffer_len);
if (num <=0)
{
SOCKET_Close(msginfo->msg_socketfd);
ptl_gossip_stateclear(pinfo);
msginfo->msg_cmd = SOCKCMD_CLOSED;
socket_status = PTLSTATUS_SLAVE_IDLE;
break;
}
//socket_status = PTLSTATUS_SLAVE_PROSEND;
socket_status = PTLSTATUS_SLAVE_SEND;
}
}
}
break;
case PTLSTATUS_SLAVE_PROSEND:
break;
case PTLSTATUS_SLAVE_SEND:
if (msginfo->msg_cmd == SOCKCMD_CLOSE)
{
printf("\n +++ APPMAIN --> SLAVE MSG = SOCKCMD_CLOSE +++\n");
pinfo->last_msgcmd = SOCKCMD_CLOSE;
break;
}
if (msginfo->msg_cmd == SOCKCMD_RECEIVE)
{
num = SOCKET_Read(msginfo->msg_socketfd, pinfo->buffer, MAX_BUFFER);
if (num <=0)
{
SOCKET_Close(msginfo->msg_socketfd);
ptl_gossip_stateclear(pinfo);
msginfo->msg_cmd = SOCKCMD_CLOSED;
socket_status = PTLSTATUS_SLAVE_IDLE;
break;
}
if (pinfo->last_msgcmd == SOCKCMD_CLOSE)
{
pinfo->last_msgcmd = 0;
printf("\n +++ SLAVE CLOSE SOCKET +++\n");
SOCKET_Close(msginfo->msg_socketfd);
ptl_gossip_stateclear(pinfo);
msginfo->msg_cmd = SOCKCMD_CLOSED;
socket_status = PTLSTATUS_SLAVE_IDLE;
break;
}
ret = ptl_gossip_slave_parser(pinfo, num);
if (ret == PROC_BACK_IDLE)
{
msginfo->msg_cmd = SOCKCMD_ENDSRV;
socket_status = PTLSTATUS_SLAVE_IDLE;
break;
}
if (ret == PROC_NEXT)
{
if (pinfo->totalsize == pinfo->actsize)
{
printf("\n +++ SLAVE --> MASTER FILESYNC_CMD_S_M_PROC_BREAK +++\n");
pinfo->buffer_len = ptl_gossip_makeheader(FILESYNC_CMD_S_M_PROC_BREAK, pinfo->buffer, 0);
num = SOCKET_Write(msginfo->msg_socketfd, pinfo->buffer, pinfo->buffer_len);
if (num <=0)
{
SOCKET_Close(msginfo->msg_socketfd);
ptl_gossip_stateclear(pinfo);
msginfo->msg_cmd = SOCKCMD_CLOSED;
socket_status = PTLSTATUS_SLAVE_IDLE;
break;
}
/* close file handle */
if (pinfo->filehd != -1)
{
close(pinfo->filehd);
pinfo->filehd = -1;
}
socket_status = PTLSTATUS_SLAVE_SEND;
break;
}
else
{
//printf("\n +++ SLAVE --> MASTER FILESYNC_CMD_S_M_BLOCK_SEND +++\n");
lseek(pinfo->filehd, pinfo->actsize, SEEK_SET);
num = read(pinfo->filehd, pinfo->buffer + sizeof(PROTOCAL_HEAD_S), pinfo->blocksize);
pinfo->lastsize = num;
pinfo->buffer_len = ptl_gossip_makeheader(FILESYNC_CMD_S_M_BLOCK_SEND, pinfo->buffer, num);
num = SOCKET_Write(msginfo->msg_socketfd, pinfo->buffer, pinfo->buffer_len);
if (num <=0)
{
SOCKET_Close(msginfo->msg_socketfd);
ptl_gossip_stateclear(pinfo);
msginfo->msg_cmd = SOCKCMD_CLOSED;
socket_status = PTLSTATUS_SLAVE_IDLE;
break;
}
socket_status = PTLSTATUS_SLAVE_SEND;
break;
}
}
}
break;
case PTLSTATUS_SLAVE_SENDOK:
break;
default:
break;
}
pinfo->status = socket_status;
return(0);
}
通過進行模板化的設計,大大提高開發效率。