数据封包解包协议之TCP封包解包

版权声明: https://blog.csdn.net/qq_28710983/article/details/83055134

数据封包协议规定:整个数据包包含2字节长度信息+数据包体。2字节长度信息包含本身着2字节。如:数据体是(abcdefg)7个字节,整体封包就是09abcdefg,总共是9个字节的协议

1、netbus接收到数据后发送到static void on_recv_tcp_data(uv_session* s),数据包含在session管理里面s->recv_buf和s->recved两个属性是这个session收取的数据信息

static void on_recv_tcp_data(uv_session* s){

		unsigned char* pkg_data = (unsigned char*)((s->long_pkg != NULL) ? s->long_pkg : s->recv_buf);

		while (s->recved > 0){
			int pkg_size = 0;
			int head_size = 0;
			if (!tcp_protocol::read_header(pkg_data, s->recved, &pkg_size, &head_size)){
				break;  //如果读不到头,就继续收数据
			}
			if (s->recved < pkg_size){  //数据不够一个数据包,继续收数据
				break;
			}
			//至少收到了一个完整的数据包
			unsigned char* raw_data = pkg_data + head_size;     //数据包的起始内存地址,去掉长度
			on_recv_client_cmd((session*)s, raw_data, pkg_size - head_size);   //处理数据
			if (s->recved > pkg_size){   //如果recved大于pkg_size,除了这一个完整的包外,还有其他数据,
				memmove(pkg_data, pkg_data + pkg_size, s->recved - pkg_size);    //处理一个完整数据包后,把后面的未处理数据移动到头部,下一轮循环处理
			}

			//recved的数据最少要等于pkg_size,所以recved不可能小于0,如果recved小于pkg_size上边一个条件判断就会中断了
			s->recved -= pkg_size;
			if (s->recved == 0 && s->long_pkg != NULL){
				free(s->long_pkg);
				s->long_pkg = NULL;
				s->long_pkg_size = 0;
			}
			//end
		}
	}

2、pkg_data是因为数据包体可能超过协议长度RECV_LEN,所以会启用long_pkg,来处理很大的包
下面是一个死循环用来处理接收到的数据(大包处理具体请看https://blog.csdn.net/qq_28710983/article/details/83045843这边文章)

3、read_header函数根据封包协议解析出数据包长度信息

bool tcp_protocol::read_header(unsigned char* data, int data_len,int* pkg_size, int* out_head_size){
	if (data_len < 2){ 
		//没有办法读取包的大小,前两个自己位一个包的长度
		return false;
	}

	*pkg_size = ((data[0]) | (data[1] << 8));
	*out_head_size = 2;

	return true;
}

TCP数据包有粘包问题,接收的数据可能有以下几种情况

收到一个完整数据包的情况下(假设收到的数据封包为09abcdefg)

1、read_header是用来读取包长度信息的,因为规定2字节是长度信息,所以如果收到的信息没有2字节就没有办法解析出长度,所以跳出循环继续接收数据,否则就根据前2字节解析出pkg_size和head_sizehead_size永远为2字节,这个是规定

2、if (s->recved < pkg_size) 这时解析出的pkg_size是9,s->recved = 9条件通过往下执行

3、前面判断通过说明数据接收完成s->recved = 9,raw_data = pkg_data + head_size ,这里raw_data就是数据包体的内存起始地址,因为前2字节是长度信息,(09abcdefg)用这个为例,假如从100为第一个字母地址,那么数据包的起始地址a就是102,那么raw_data的起始地址就是102

4、前面判断都通过,那么就至少解析到了1整个数据包,就进入 void on_recv_client_cmd(session* s, unsigned char* body, int len)函数处理数据

5、处理完成数据后,判断s->recved > pkg_size除了这个数据包,是否还有其他数据,这里s->recved = 9,pkg_size = 9,条件不通过,往下执行

6、s->recved -= pkg_size  ,  s->recved = 0;

7、while循环条件不成立跳出循环

收到1.5个协议包的情况(09abdefg091234)

1、read_header是用来读取包长度信息的,因为规定2字节是长度信息,所以如果收到的信息没有2字节就没有办法解析出长度,所以跳出循环继续接收数据,否则就根据前2字节解析出pkg_size和head_sizehead_size永远为2字节,这个是规定

2、if (s->recved < pkg_size) 这时解析出的pkg_size是9,s->recved = 15条件通过往下执行

3、前面判断通过说明数据接收完成s->recved = 15,raw_data = pkg_data + head_size ,这里raw_data就是数据包体的内存起始地址,因为前2字节是长度信息,(09abcdefg)用这个为例,假如从100为第一个字母地址,那么数据包的起始地址a就是102,那么raw_data的起始地址就是102

4、进入on_recv_client_cmd处理数据

5、处理完成数据后,判断s->recved > pkg_size除了这个数据包,是否还有其他数据,这里s->recved = 15,pkg_size = 9,条件通过,下面是目前内存分布情况

执行memmove函数,memmove(pkg_data, pkg_data + pkg_size, s->recved - pkg_size)前面一个数据封包已经处理好,下次要处理第二个数据封包,on_recv_ws_data每次都是从s->recv_buf的起始位置开始处理,所以需要把第二个封包移动到recv_buf的起始为位置,第二个数据包的起始地址是recv_buf的起始地址100 再加上第一个数据封包的长度9,所以第二个数据封包的起始地址就是recv_buf + pkg_size ,pkg_data就是recv_buf,所以等于pkg_data + pkg_size,移动数据长度是剩下没有处理的数据的长度s->recved - pkg_size = 15 - 9 = 6,移动6个字节,这样就把第二个数据封包移动到s->recv_buf的起始地址

6、s->recved -= pkg_size , s->recved = 6表示还有6个字节长度没有处理完成,继续while循环,当执行完成read_header函数后解析出第二个数据封包长度为9,但是剩余长度只剩下6,还有3字节没有接收完成,所以到if (s->recved < pkg_size)判断的时候跳出while循环,netbus继续接收数据

猜你喜欢

转载自blog.csdn.net/qq_28710983/article/details/83055134