TCP粘包处理通用框架--C代码
说明:该文紧接上篇博文“
linux epoll机制对TCP 客户端和服务端的监听C代码通用框架实现
”讲来
(1)TCP粘包处理数据结构设计
#define MAX_MSG_LEN 65535
typedef struct
{
//当flag_in_NL_proc为1时,前面两个字段才有效
unsigned char g_recv_buff[*MAX_MSG_LEN];
int g_recv_len;//前次累积未处理的TCP消息长度
int flag_in_NL_proc;//用于标记前次是否有不完整的TCP消息,1,表示有;0,表示没有;若有,当前待处理的TCP消息=前次累积未处理的TCP消息+当前利用recvfrom()接收的新TCP消息,若为0,则当前待处理的TCP消息即为新接收的TCP消息
}TCP_NL_MSG;
数据结构说明:
每个tcp连接维护一个TCP粘包处理结构体TCP_NL_MSG,代码可以维护一个全局变量map<int, TCP_NL_MSG> g_map_fd_TcpNLMsgStr(TCP socket和对应粘包处理结构体的映射表);
(2)粘包处理代码功能描述
RecvTcpMsg函数内部调用recvfrom接收tcp消息,
该函数功能描述:接收TCP消息(先进行粘包处理,然后根据消息类型进入不同的处理分支)
(3)粘包处理原理阐释
待补充;
(4)粘包处理的代码逻辑:
1 调用recvfrom()对本次epoll监听的socket可读事件进行读取到应用程序缓存curr_buff中;
2 判断该socket对应的TCP粘包处理结构体:p_tcp_nl_msg,判断p_tcp_nl_msg->flag_in_NL_proc标志是否为真:
2.1 若为真,则表明上次有未处理TCP消息缓存,保存在p_tcp_nl_msg->g_recv_buff指针中,长度为p_tcp_nl_msg->g_recv_len,则当前待处理的TCP消息为tcpmsg_tobe_processed = p_tcp_nl_msg->g_recv_buff+curr_buff,当前待处理的TCP消息长度为tcpmsglen_tobe_processed = p_tcp_nl_msg->g_recv_len+curr_buff.len;
然后将p_tcp_nl_msg->flag_in_NL_proc更新为0;
2.2 若为假,则表明上次没有未处理TCP消息缓存,则当前待处理的TCP消息即为tcpmsg_tobe_processed = curr_buff;
3 对tcpmsg_tobe_processed进行while(1)循环处理,循环体中的内容为:
从tcpmsg_tobe_processed中前面sizeof(TcpMsgHead)字节长度的消息为TCP消息头p_head,
首先比较tcpmsglen_tobe_processed 与sizeof(TcpMsgHead):
3.1 若前者小,即tcpmsglen_tobe_processed < sizeof(TcpMsgHead),意味着当前应用层接收的TCP消息长度小于TCP消息头的长度,则本次不解析,将tcpmsg_tobe_processed保存在p_tcp_nl_msg- >g_recv_buff指针中,然后将p_tcp_nl_msg->flag_in_NL_proc更新为1,并退出while循环;
3.2 若后者小,即tcpmsglen_tobe_processed > sizeof(TcpMsgHead), 则可以解析出应用层完整的tcp消息头长度为p_head->len,
再比较p_head->len和tcpmsglen_tobe_processed:
3.2.1 若p_head->len > tcpmsglen_tobe_processed,则表明应用层完整的tcp消息尚未接收完全,处理同3.1,退出while循环;
3.2.2 若p_head->len <= tcpmsglen_tobe_processed,则表明应用层完整的tcp消息已经接收完全,则tcpmsg_tobe_processed中前面p_head->len长度的字节即为来自于对端应用层的一条完整的TCP消息,对该消息调用ProcessTcpMsg()进行业务处理(功能是:根据不同的消息类型(维护在TCP消息头的type字段中)进入不同的业务处理分支,switch...case...);
然后对tcpmsg_tobe_processed从p_head->len字节到tcpmsglen_tobe_processed字节的一段消息更新到tcpmsg_tobe_processed中,即:tcpmsg_tobe_processed=tcpmsg_tobe_processed+p_head->len,将tcpmsglen_tobe_processed更新为:tcpmsglen_tobe_processed -= p_head->len,至此,进入while(1)循环的下一次处理,该分支的循环终止条件为:p_head->len == tcpmsglen_tobe_processed;
(5)粘包处理代码实现:
代码实现如下:
#define MAX_MSG_LEN 65535 map<int, TcpNlMsg> g_map_fd_TcpNLMsg;
void RecvTcpMsg(int fd)
{
map<int, TcpNlMsg>::iterator iter = g_map_fd_TcpNLMsg.find(fd);
if (iter == g_map_fd_TcpNLMsg.end())
{
return;
} TcpNlMsg* p_tcp_nl_msg = &iter->second; uint8_t recvbuf[MAX_MSG_LEN];
memset(recvbuf,,MAX_MSG_LEN); struct sockaddr_in src_addr;
socklen_t addrlen = sizeof(struct sockaddr_in);
//代码逻辑流程1
int recvlen = recvfrom(fd, recvbuf, MAX_MSG_LEN, , (struct sockaddr *) &src_addr, &addrlen); if(recvlen == )
{
printf("uehandle_recv_pack() receive shutdown command from peer endpoint\n") return;
} if (recvlen == -)
{
printf("uehandle_recv_pack() ret -1,errno=%d"\n, errno);
return;
}
//代码逻辑流程2 //代码逻辑流程2.1
if (p_tcp_nl_msg->flag_tcp_NL_proc == )
{
memcpy(p_tcp_nl_msg->g_recvbuf+p_tcp_nl_msg->g_recvlen,recvbuf,recvlen); recvlen += p_tcp_nl_msg->g_recvlen;
memcpy(recvbuf, p_tcp_nl_msg->g_recvbuf, recvlen); p_tcp_nl_msg->flag_tcp_NL_proc = ;
} uint8_t* buf = recvbuf;
//代码逻辑3
while ()
{
//代码逻辑3.2
if (recvlen >= sizeof(TcpMsgHead))
{
TcpMsgHead *head=(TcpMsgHead*)buf;
uint32_t msglen = head->msglen;
int type = head->msgtpe;
//代码逻辑流程3.2.2
if(recvlen >= msglen)
{
ProcessTcpMsg(buf+sizeof(TcpMsgHead), type);//业务处理函数,switch...case语句,根据不同的消息类型进入不同的处理分支 if (recvlen == msglen)//循环终止条件之一:当前待处理TCP消息恰好为一条完整的应用层消息
{
break;
} printf("recvlen(%u) > msglen(%u)\n", recvlen, msglen);
//更新待处理TCP消息缓存和长度,进入下一次while循环
recvlen -= msglen;
buf += msglen;
}
//代码逻辑流程3.2.1
else if(recvlen < msglen)
{
printf("sizeof(TcpMsgHead):%u < recvlen(%u) < msglen(%u)",sizeof(TcpMsgHead), recvlen, msglen); memset(p_tcp_nl_msg->g_recvbuf, , MAX_MSG_LEN);
memcpy(p_tcp_nl_msg->g_recvbuf, buf, recvlen);
p_tcp_nl_msg->g_recvlen = recvlen;
p_tcp_nl_msg->flag_tcp_NL_proc = ; break;
}
}
//代码逻辑流程3.1
else
{
printf("recvlen(%u) < sizeof(TcpMsgHead):%u\n", recvlen, sizeof(TcpMsgHead)); memset(p_tcp_nl_msg->g_recvbuf, , MAX_MSG_LEN);
memcpy(p_tcp_nl_msg->g_recvbuf, buf, recvlen);
p_tcp_nl_msg->g_recvlen = recvlen;
p_tcp_nl_msg->flag_tcp_NL_proc = ; break;
} } }
TCP粘包处理通用框架--C代码的更多相关文章
- TCP粘"包"问题浅析及解决方案Golang代码实现
一.粘"包"问题简介 在socket网络编程中,都是端到端通信,客户端端口+客户端IP+服务端端口+服务端IP+传输协议就组成一个可以唯一可以明确的标识一条连接.在TCP的sock ...
- 6行代码解决golang TCP粘包
转自:https://studygolang.com/articles/12483 什么是TCP粘包问题以及为什么会产生TCP粘包,本文不加讨论.本文使用golang的bufio.Scanner来实现 ...
- TCP网络通讯如何解决分包粘包问题(有模拟代码)
TCP作为常用的网络传输协议,数据流解析是网络应用开发人员永远绕不开的一个问题. TCP数据传输是以无边界的数据流传输形式,所谓无边界是指数据发送端发送的字节数,在数据接收端接受时并不一定等于发送的字 ...
- c#解决TCP“粘包”问题
一:TCP粘包产生的原理 1,TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾.出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能 ...
- TCP粘包/拆包问题
无论是服务端还是客户端,当我们读取或者发送消息的时候,都需要考虑TCP底层的粘包/拆包机制. TCP粘包/拆包 TCP是个"流"协议,所谓流,就是没有界限的一串数据.大家可以想想河 ...
- TCP粘包拆包问题
阿π 专注于网络协议,系统底层,服务器软件 C++博客 | 首页 | 发新随笔 | 发新文章 | | | 管理 Socket粘包问题 这两天看csdn有一些关于socket粘包,socket缓冲区设置 ...
- tcp粘包和拆包的处理方案
随着智能硬件越来越流行,很多后端开发人员都有可能接触到socket编程.而很多情况下,服务器与端上需要保证数据的有序,稳定到达,自然而然就会选择基于tcp/ip协议的socekt开发.开发过程中,经常 ...
- Netty(二)——TCP粘包/拆包
转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7814644.html 前面讲到:Netty(一)--Netty入门程序 主要内容: TCP粘包/拆包的基础知 ...
- TCP 粘包问题浅析及其解决方案
最近一直在做中间件相关的东西,所以接触到的各种协议比较多,总的来说有TCP,UDP,HTTP等各种网络传输协议,因此楼主想先从协议最基本的TCP粘包问题搞起,把计算机网络这部分基础夯实一下. TCP协 ...
随机推荐
- 51 nod 1419 最小公倍数挑战【数论/互质+思维】
1419 最小公倍数挑战 题目来源: CodeForces 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 收藏 关注 几天以前,我学习了最小公倍数.玩得挺久了 ...
- 事务没有提交导致 锁等待Lock wait timeout exceeded异常
异常:Lock wait timeout exceeded; try restarting transaction 解决办法: 执行select * from information_schema.i ...
- Bean的实例化--构造器
1,编写Bean对象 package com.songyan.ico; public class Bean1 { } 2,配置xml <?xml version="1.0" ...
- tiny4412 串口驱动分析一 --- u-boot中的串口驱动
作者:彭东林 邮箱:pengdonglin137@163.com 开发板:tiny4412ADK+S700 4GB Flash 主机:Wind7 64位 虚拟机:Vmware+Ubuntu12_04 ...
- 【MyEcplise】新装系统,新装的myEcplise使用原来的workspace和maven库,项目没错但是引用的包不能正确找到
项目中都是原来的正确的项目,但是有个感叹号 正确方法: 更新之后如果有别的问题,例如JDK版本变成原始版本等等,请在build path中调整. 并且修改 项目的编译JDK版本 右键项目propert ...
- Makefile之文件搜索
Makefile之文件搜索 1.Makefile 文件中的"VPATH"变量 如果没有指明这个变量,make只会在当前目录下查找依赖文件和目标文件: 如果定义了这个变量,make会 ...
- python pip安装lxml失败(转)
今天想要试试beautifulsoup4,安装的时候很顺利,然后就准备安装lxml作为解析器,没想到安装时pip直接给我报了一整页的错误. 解决过程 查看了一下错误提示,其中有如下一段: ****** ...
- ES6里关于类的拓展(一)
大多数面向对象的编程语言都支持类和类继承的特性,而JS却不支持这些特性,只能通过其他方法定义并关联多个相似的对象,这种状态一直延续到了ES5.由于类似的库层出不穷,最终还是在ECMAScript 6中 ...
- 弄清java中的字节与字符
问题 在java中,一个字符等于多少字节? 或者更详细的问:在java中,一个英文字符等于多少字节?一个中文字符等于多少字节? 答案 Java采用unicode来表示字符,java中的一个char是2 ...
- MVC EasyUI 时间格式化
用 return Json(dr, JsonRequestBehavior.AllowGet); 会返回一个json 数据格式,在用 EasyUI 输出表格内容时会遇到时间输出不是我们想要的格式, ...