小人拙见,不喜轻喷
这个是我基于socket编程的一个(“自定义”)通信协议的研究,算是从另一个角度去了解“协议”这两个字,由于能力以及水平有限,许多知识并不了解,可能会有很多不正确的地方,请多多包涵。
以下是实现这个“自定义” “协议”过程中的收获或者理解:
------------------------------------------------------------------------------正文------------------------------------------------------------------
说明:
1.本协议“封装”在TCP协议的数据段
2.协议格式: 0 7 15 23 31
协议头(0xAA) | [0] 长度 [1] | 选项 | |
命令(0x04) | (dcmd) 数据 | ||
数据(N byte) | CRC | 协议尾(0x55) |
备注:1.选项高4字节为版本,低4字节保留;
2.长度 = 选项(1 byte) + 命令(1 byte) + 数据(N byte)
3. dcmd包含在数据段中,==0x01:表示心跳包;==0x02:表示发送数据
4.CRC计算包括长度、选项、命令以及数据段
5.对除协议头、协议尾之外的所有数据进行转义,转义字节:0xCC,转义规则:遇到协议头(0xAA)、协议尾
(0x55)以及转义字节(0xCC)均进行转义,在该字节前加转义字节,同时将该字节加12
3.项目源码:https://github.com/Lqinggang/protocol
4.目录结构:
./protocol
├── client
│ ├── client.c ..................... 客户端
│ └── Makefile
├── include
│ ├── crc.h ..................... CRC校验(非本人编码)
│ ├── escape.h ..................... 转义
│ ├── protocol.h .................... 协议
│ └── wrapsock.h .................... 对socket的封装(UNP)
├── lib
│ ├── crc.c ........................ CRC校验源码(非本人编码)
│ ├── escape.c ...................... 转义相关实现
│ ├── Makefile
│ ├── protocol.c .................... 协议相关实现
│ └── wrapsock.c .................... socket的封装
├── Makefile
├── README.md
└── server ............................ 服务器端
├── Makefile
└── server.c
5.C/S实现简介:
客户端:从标准输入获取信息,使用相关协议对其进行“封装”后使用TCP socket将其发往服务器端,同时基于信号机制每30秒向服务器端发送心跳包;
服务器端:基于socket,在使用accept接入一个连接后,使用fork创建一个子进程负责与客户端进行通信(并发?),同时对接收到的数据进行解析,当收到心跳包时,回复客户端心跳包,否则,显示接收到的数据。
部分实现(还没想好怎么写,保留):
1. report,发送数据
/*
* interac: protocol struct
* msg: the data for send
* data: save the generated
* length: length of data
*/
static unsigned char *report(struct interaction interac, const unsigned char *msg, unsigned char *data, size_t *length)
{
int i;
int len = *length;
for(i = 0; i < len; i++) {
/* append (msg) to the data segment
* +1: the first character is REPORT/HEARTBEAT, see generadata() methods
*/
*(interac.data + 1 + i) =*(msg++);
}
*(interac.data + len + 1) = '\0';
unsigned int n = strlen(interac.data) + 2; //+2:option and cmd
interac.length[0] = (n >> (sizeof(byte) * 8) & 0xFF);
interac.length[1] = (n & 0xFF);
interac.crc = cal_crc(interac); //calculate the crc value
return getdata(interac, data, length); //get all message
}
2.getdata,生成封装到TCP数据段的报文,这里就是为什么我觉得“协议”是大家约定好如何实现的东西的原因所在,这只是我的猜想,可能TCP等协议并不是这么实现,保留待深究。
/*
* generate message
* interac: protocol struct
* data: save the generated data
* length: length of data
*/
static unsigned char *getdata(struct interaction interac, unsigned char *data, size_t *length)
{
unsigned char rsc[strlen(interac.data) + 6]; //+6: length[2] + option + cmd + crc
bzero(rsc, sizeof(rsc));
rsc[0] = interac.length[0];
rsc[1] = interac.length[1];
rsc[2] = interac.option;
rsc[3] = interac.cmd;
int i;
int dlen = strlen(interac.data);
for(i = 0; i< dlen; i++) {
*(rsc + 4 + i) = *(interac.data + i); //+4: length[2] + option + cmd
}
/* 0 8 16 24 32
* |--------|--------|--------|--------|
* |length0 |length1 | option | cmd |
* |--------|--------|--------|--------|
* | data: dlen bytes | <- + - crc
* |--------|--------|--------|--------|
*/
*(rsc + dlen + 4) = interac.crc; //+4: length[2] + option + cmd
size_t len = dlen + 5;
/*
* escape for length[2] + option + cmd + data,
* the escaped data is padded to data, the length
* of the data after the escape is passed back
* through len.
* +1: the first character is header, don't need to esacape
*/
escape(rsc, data + 1, &len);
*data = interac.header;
*(data + len + 1) = interac.tail;
*length = len + 2; //+2: header + tail
return data;
}
3. escape,转义
/*
* rscptr: the data need to be escape
* dstptr: data after escape
* length: length that need to be escape
* and return the length after escaping
*/
void escape(const char *rscptr, char *dstptr, size_t *length)
{
volatile unsigned char c;
if(*length > 0) {
size_t i = 0;
int len = 0;
c = *rscptr; //get current character
while(i < *length ) {
len++;
/* need to be escape */
if(*rscptr == header || *rscptr == tail || *rscptr == escapeByte) {
/* characters that need to be escaped plus 1
* and the escape character is inserted before it
*/
(int)c++;
*dstptr++ = escapeByte;
len++;
}
*dstptr = c;
/* get the address of the next character
* and next save character
*/
rscptr++;
dstptr++;
i++;
c = *rscptr;
}
*length = len;
}
}