基于Socket的通信协议的研究

      小人拙见,不喜轻喷

      这个是我基于socket编程的一个(“自定义”)通信协议的研究,算是从另一个角度去了解“协议”这两个字,由于能力以及水平有限,许多知识并不了解,可能会有很多不正确的地方,请多多包涵。

      以下是实现这个“自定义” “协议”过程中的收获或者理解:

  1. 实现过程中,给我的最大的感受就是这个世界上并不存在“协议”这种东西,或者应该说成“协议”不过是大家约定好如何实现的东西,并没有想象中的那么高深莫测,只不过是“协议”的复杂程度不一样而已;
  2. 经过本次基于TCP socket实现服务器与客户端之间进行通信,也算是对socket知道了一些;
  3. BUG是在所难免的,很不幸的经过这次代码的实现,开启了GDB的大门。

 

            ------------------------------------------------------------------------------正文------------------------------------------------------------------

       说明:

     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;
	}
}

 

你可能感兴趣的:(基于Socket的通信协议的研究)