标签:size 就是 str 限制 链接 toolbar 情况下 拷贝 进制
1数据分包在TCP收数据的时候可能会有一个很大的包,这时候很有可能一次就收不完,
或者说一次收到多个数据包,
二进制分包常用的就是size+body的方式;
为了避免服务器被恶意的攻击, 一般会规定一个包的大小上线,
如果超过了,立刻关闭该链接. 比如两个字节表示长度就是64kb
第一次就是提交一个请求
2数据分包实现思路
1先设置一块内存,并设置大小是可以读取的内存数据,如果完成了这个请求
就会返回实际读到的大小,处理完后继续投递请求;
2 发送投递请求的默认大小为8192(1024*8) 也可以是4k就是命令的大小
如果要收收的数据包 小与 8k,可能有两种情况
1 收到的数据大小 大于等于 一个包的数据大小(就是说你可能收到了两个数据包)
处理完整的包,将剩下的不足一个包的数据移动到下一次投递,跳过那部分内容即可。
这就是粘包
2收到的数据只有一个字节,不足header长度,保留继续 投递recv.
3 如果要收的数据>8k 那么肯定第一次收不完,所以,要重新分配一个数据包
大小的内存来保存这个数据包,投递请求的时候,根据这个大小来投递,不多收。
因为已经知道这个大的数据包有多大,收完后才重新投递。
首先设置这个包的最大上限 如果超过了这个上限 就认定是恶意 数据包 直接断开连接 //左移16位 就是两个字节最大 然后-1 //0000000000000001 1000000000000000 -1 1111111111111111 //转换成16进制0xFFFF 十进制 61440+3840+240+15 65535 #define MAX_PAG_SIZE ((1<<16) - 1) //包的上限制 #define MAX_RECV_SIZE 2048 //一个包的大小 struct io_package { WSAOVERLAPPED overlapped; //重叠结构用于IOCP WSABUF wsabuffer; //收数据的缓冲区 int opt; // 标记一下我们当前的请求的类型; //事件类型 int accpet_sock; int recv; //当前io_data 缓冲了多少数据 unsigned char* long_pkg; //处理大的数据包的情况 > MAX_RECV_SIZE char pkg[MAX_RECV_SIZE]; //投递百分之90游戏命令的大小 };
3现在我规定 前面两个字节是大小 如果只收到一个字节就要继续recv
io_data->recv += dwTrans; //当前读取到数据的总大小 if(io_data->recv < 1) //收到的数据小于一个字节那就退出 因为包长都获取不到 { 这里继续recv 在原来缓冲区的上面 内存地址要+io_data->recv }else{ //获取 包的长度 pkg_size = (pkg[0] | (pkg[1]<<8)); } return pkg_size ;
4 然后就获取到了包的长度啦 然后就要判断这个长度是不是符合我们的规定
if (pkg_size >= MAX_PAG_SIZE ){ //大于64k // 异常的数据包 关闭连接 printf("数据异常........\n"); close_session(s); free(io_data); break; }
5 这里就会出现几种情况
1就是数据包是50个字节,但是你收到70个字节这样就是TCP粘包
TCP粘包是指发送的若干个数据包到接收方因为,流式传输没有边界,
也就是你后面的包会粘在你前面包的尾部,但是无法区分
首先处理粘包的情况,因为包头两个字节是长度,所以很好解决
if (io_data->recv >= pkg_size){ //如果收到的数据大于等于一个包长 //这里表示 至少有一个完整的包了 //在这里可以直接处理第一个包, 因为有pkg_size 就是这个包的长度 //........这里放处理代码 //处理粘包 if(io_data->recv > pkg_size){ //如果收到的数据是大于一个 //直接移动内存 //把这个包 后面的粘包移动到最前面, 然后长度是 收到的长度 减去 第一个包长 memmove(io_data->pkg,io_data->pkg + pkg_size,io_data->recv - pkg_size) } //处理完这个包了 io_data->recv -= pkg_size; //这里就要继续recv 这时候注意 缓冲区的地址 要加上收到的长度, 因为他可能是一个粘包数据,他也可能是0 io_data->wsabuffer.buf = io_data->pkg + io_data->recv; io_data->wsabuffer.len = MAX_RECV_SIZE - io_data->recv; }
2数据包没有收完
io_data->wsabuffer.buf = recv_buffer + io_data->recv; //剩下的长度就是总长度-去收到的长度 io_data->wsabuffer.len = pkg_size - io_data->recv; 继续recv
3数据包的长度 大于一个包的长度
//这个指针 默认是指着 普通大小包的缓存 unsigned char* recv_buffer = io_data->pkg; if (pkg_size > MAX_RECV_SIZE){ if (pkg_size > MAX_RECV_SIZE){ //数据包的长度 大于当前缓冲区的长度 //需要准备更多的缓冲区 来接收这个剩余包 if (io_data->long_pkg == NULL){ //动态申请这么多的内存 io_data->long_pkg = malloc(pkg_size); //把已经读到的数据拷贝到 申请的内存里面 memcpy(io_data->long_pkg, io_data->pkg, io_data->recv); } //指向新的内存 recv_buffer = io_data->long_pkg; } 继续recv 这里也是一样的 在原理的缓冲区上加上你收到的长度 io_data->wsabuffer.buf = recv_buffer + io_data->recv; //这里就是你接下来 要收取的长度 也就是包长减去 收到的数据 io_data->wsabuffer.len = pkg_size - io_data->recv;
3Json协议的分包
1 使用\r\n来区分一个数据包, 然后如果你的命令里带了\r\n 那就要base64编码
使用json的目的就是为了,脚本语言的使用。
2首先要获得包长 就要获得\r\n
//总收到的长度 + 当前收到的长度 io_data->recved += dwTrans; //比较 if (recv < 2){ //没两个字节不存在\r\n return -1; } *pkg_size = 0; for (int i = 0; i < recved - 1; i++){ if (pkg_data[i] == '\r' && pkg_data[i + 1] == '\n') { //找到了结尾符 获得长度 因为是从0开始 //还有\r在\n前面一个字节 *pkg_size = (i+2); return 0; } } 这种情况下要判断 收到的数据是不是大于 规定的长度 大于就要关闭连接 if (io_data->recved >= ((1 << 16) - 1)){ //超出规定 直接关掉 close_session(s); free(io_data); break; } //如果当前的收到的长度 大于 普通的长度 if (io_data->recved >= io_data->max_pkg_len){ //重新申请一段内存 int alloc_len = io_data->recved * 2; //如果这个长度大于了 65535 那么默认给他65535 alloc_len = (alloc_len > ((1 << 16) - 1)) ? ((1 << 16) - 1) : alloc_len; if (io_data->long_pkg == NULL){ io_data->long_pkg = malloc(alloc_len); memcpy(io_data->long_pkg, io_data->pkg, io_data->recved); } else{ //释放原来的内存 重新申请内存 io_data->long_pkg = realloc(io_data->long_pkg, alloc_len); } io_data->max_pkg_len = alloc_len; } //如果返回不为0 就是没有\r\n结尾 要就要继续接收 io_data->wsabuffer.buf = buf + io_data->recved; io_data->wsabuffer.len = io_data->max_pkg_len return; 如果到这就表示 已经有一个完整的包了 //需要判断是不是粘包了 if (io_data->recved > pkg_sizes){ //这里表示后面还有一个包 memmove(pkg_da, (io_data->pkg + pkg_sizes), io_data->recved - pkg_sizes); } --总长度减去 一个包长 io_data->recved -= pkg_sizes; 如果为0 就表示所有的包都处理完了
标签:size 就是 str 限制 链接 toolbar 情况下 拷贝 进制
原文地址:http://blog.51cto.com/12158490/2062111