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

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. quailty's Contest #1 道路修建 EXT(启发式合并)

    题目链接  道路修建 EXT 考虑并查集的启发式合并,合并的时候小的子树的根成为大的子树的根的儿子. 可以证明这样整棵树的深度不会超过$logn$. 两个根合并的时候,产生的新的边的边权为当前的时间. ...

  2. Function——记忆化搜索

    题目描述 对于一个递归函数w(a,b,c) 如果a<=0 or b<=0 or c<=0就返回值1. 如果a>20 or b>20 or c>20就返回w(20,2 ...

  3. python 传不可变对象 or 可变对象

    可更改(mutable)与不可更改(immutable)对象 在 python 中,strings, tuples, 和 numbers 是不可更改的对象,而 list,dict 等则是可以修改的对象 ...

  4. MySQL区间统计SQL

    SELECT elt( INTERVAL ( datediff(END_DATE, CURDATE()), 1, 201, 401, 601 ), '0-200', '200-400', '400-6 ...

  5. django 修改默认的user表和默认的认证系统

    django的功能非常强大,但是自带的user表很多情况下并不满足我们的需求,因此我们需要修改其默认的user表,并且把用username登录改成用email登录 第一步,创建自己的user表,在创建 ...

  6. vim常用操作之复制剪切粘贴,注释取消注释,多行缩进等

    进入vim按下v键,选择要操作的对象 按下y复制,d剪切,p粘贴 >缩进,<取消缩进 按下esc退出操作 按下ctrl+v,选择要操作对象, 按下大写I,写入注释符号,按下d取消注释 按下 ...

  7. div 事件透传

    有些时候,我们会想要把一个 div 层覆盖在另一个 div 层上,要让下层的 div 响应鼠标事件而上层的不响应仅仅只做内容展示. 这种时候,我们就可以用到一个 CSS 属性:pointer-even ...

  8. 解决dubbo问题:forbid consumer(2)

    线下环境经常出现类似这种异常: com.alibaba.dubbo.rpc.RpcException: Forbid consumer 10.0.53.69 access service com.ku ...

  9. [Tips + Javascript] Make a unique array

    To make an array uniqued, we can use Set() from Javascript. const ary = ["a", "b" ...

  10. 使用uncompyle2直接反编译python字节码文件pyo/pyc

    update:在Mac OS X版的September 10, 2014版(5.0.9-1)中发现安装目录中的src.zip已更换位置至WingIDE.app/Contents/Resources/b ...