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协 ...
随机推荐
- SSOJ 2316 面积【DFS/Flood Fill】
题目描述 编程计算由“1”号围成的下列图形的面积.面积计算方法是统计1号所围成的闭合曲线中点的数目. 如图所示,在10*10的二维数组中,“1”围住了15个点,因此面积为15. 题目大意:对于给定的1 ...
- [LOJ6280]数列分块入门 4
题目大意: 给你一个长度为$n(n\leq50000)$的序列$A$,支持进行以下两种操作: 1.将区间$[l,r]$中所有数加上$c$: 2.询问区间$[l,r]$在模$c+1$意义下的和.思路: ...
- jdbc多种实现方式
1,驱动加载 //注册驱动 //DriverManager.registerDriver(new Driver());此方法被淘汰 Class.forName("com.mysql.jdbc ...
- .xcodeprok cannot be opened because the project file cannot be parsed
用svn更新代码后,打开xcode工程文件出现 xxx..xcodeproj cannot be opened because the project file cannot be parsed. 这 ...
- NAND Flash memory in embedded systems
参考:http://www.design-reuse.com/articles/24503/nand-flash-memory-embedded-systems.html Abstract : Thi ...
- java随机打乱集合顺序
public static void main(String[] args) { List <Integer> intList = new ArrayList<Integer> ...
- CENTOS设置MYSQL字符集
CENTOS设置MYSQL字符集 show variables like 'character_set%'; vim etc/my.cnf 修改成如下: [client] port=3306 sock ...
- VBA 时间相关的函数
DateSerial DateADD Datediff http://www.yiibai.com/vba/vba_datediff_function.html https://www.techont ...
- UVa 816 (BFS求最短路)
/*816 - Abbott's Revenge ---代码完全参考刘汝佳算法入门经典 ---strchr() 用来查找某字符在字符串中首次出现的位置,其原型为:char * strchr (cons ...
- iOS:新浪微博OAuth认证
新浪微博OAuth认证 1.资源的授权 •在互联网行业,比如腾讯.新浪,那用户人群是非常巨大的 •有时候要把某些用户资源共享出去,比如第三方想访问用户的QQ数据.第三方想访问用户的新浪微博数据 • ...