说明:该文紧接上篇博文“

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代码的更多相关文章

  1. TCP粘"包"问题浅析及解决方案Golang代码实现

    一.粘"包"问题简介 在socket网络编程中,都是端到端通信,客户端端口+客户端IP+服务端端口+服务端IP+传输协议就组成一个可以唯一可以明确的标识一条连接.在TCP的sock ...

  2. 6行代码解决golang TCP粘包

    转自:https://studygolang.com/articles/12483 什么是TCP粘包问题以及为什么会产生TCP粘包,本文不加讨论.本文使用golang的bufio.Scanner来实现 ...

  3. TCP网络通讯如何解决分包粘包问题(有模拟代码)

    TCP作为常用的网络传输协议,数据流解析是网络应用开发人员永远绕不开的一个问题. TCP数据传输是以无边界的数据流传输形式,所谓无边界是指数据发送端发送的字节数,在数据接收端接受时并不一定等于发送的字 ...

  4. c#解决TCP“粘包”问题

    一:TCP粘包产生的原理 1,TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾.出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能 ...

  5. TCP粘包/拆包问题

    无论是服务端还是客户端,当我们读取或者发送消息的时候,都需要考虑TCP底层的粘包/拆包机制. TCP粘包/拆包 TCP是个"流"协议,所谓流,就是没有界限的一串数据.大家可以想想河 ...

  6. TCP粘包拆包问题

    阿π 专注于网络协议,系统底层,服务器软件 C++博客 | 首页 | 发新随笔 | 发新文章 | | | 管理 Socket粘包问题 这两天看csdn有一些关于socket粘包,socket缓冲区设置 ...

  7. tcp粘包和拆包的处理方案

    随着智能硬件越来越流行,很多后端开发人员都有可能接触到socket编程.而很多情况下,服务器与端上需要保证数据的有序,稳定到达,自然而然就会选择基于tcp/ip协议的socekt开发.开发过程中,经常 ...

  8. Netty(二)——TCP粘包/拆包

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7814644.html 前面讲到:Netty(一)--Netty入门程序 主要内容: TCP粘包/拆包的基础知 ...

  9. TCP 粘包问题浅析及其解决方案

    最近一直在做中间件相关的东西,所以接触到的各种协议比较多,总的来说有TCP,UDP,HTTP等各种网络传输协议,因此楼主想先从协议最基本的TCP粘包问题搞起,把计算机网络这部分基础夯实一下. TCP协 ...

随机推荐

  1. 洛谷 P3378 【模板】堆

    如题,初始小根堆为空,我们需要支持以下3种操作: 操作1: 1 x 表示将x插入到堆中 操作2: 2 输出该小根堆内的最小数 操作3: 3 删除该小根堆内的最小数 输入输出格式 输入格式: 第一行包含 ...

  2. WORDPRESS改为https部署

    1.确保你已经正确开启了httpd 2.4.6的https配置,并且配置了该网站的虚拟主机,如下所示: <VirtualHost *:443> DocumentRoot "/ap ...

  3. [LOJ6280]数列分块入门 4

    题目大意: 给你一个长度为$n(n\leq50000)$的序列$A$,支持进行以下两种操作:​ 1.将区间$[l,r]$中所有数加上$c$: 2.询问区间$[l,r]$在模$c+1$意义下的和.思路: ...

  4. hdu 1599 find the mincost route 最小环

    题目链接:HDU - 1599 杭州有N个景区,景区之间有一些双向的路来连接,现在8600想找一条旅游路线,这个路线从A点出发并且最后回到A点,假设经过的路线为V1,V2,....VK,V1,那么必须 ...

  5. linux PHP 安装及 GD库安装

    linux GD库安装 GD 安裝 第一部需要做的是先要安裝 GD 到系統內,而安裝 GD 前需要安裝 jpeg-6b, libpng, zlib, freetype.以下是下载网址:GD 2.0.3 ...

  6. Ubuntu 16.04通过网络配置工具NetworkManager设置IP、网关、DNS和查看IP、网关、DNS

    说明: 1.NetworkManager工具是Ubuntu桌面版的GUI设置工具. 2.这个工具推荐直接在GUI上操作,不建议用命令行进行管理,比如Wifi这些配置等. 3.当然,这个工具能带有命令行 ...

  7. 纯 CSS 方式实现 CSS 动画的暂停与播放!

    开本系列,谈谈一些有趣的 CSS 题目,题目类型天马行空,想到什么说什么,不仅为了拓宽一下解决问题的思路,更涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题 ...

  8. Visual Studio 行末回车时运算符两侧自动加空格是怎么设置的

    在工具—>选项->文本编辑器->c#->格式设置->间距后有三个选项 如果已经选中"二元运算符前后插入空格" 而VS又抽风没有这个功能时,可以选中其余 ...

  9. NSOperationQueue 和 NSOperation

    The NSOperationQueue class regulates the execution of a set of NSOperation objects. After being adde ...

  10. 拉格朗日对偶与kkt条件