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

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. codevs 2837 考前复习——01背包

     时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题解  查看运行结果     题目描述 Description Aiden马上要考试了,可他还没怎么复习,于是他 ...

  2. Leave It Behind and Carry On ---- 高一下期末考反思 [补档]

    背景 这个学期的前\(\frac{3}{4}\), 我都是在停课集训中度过的, 先是GDKOI, 再是北京集训, 最后是GDOI, 结果GDOI还没进day3就滚粗了. 学校的内容是考完GDOI后回学 ...

  3. String&&StringBuilder&&StringBuffer

    在java中提供三个操作字符串的类:String,StringBuilder,StringBuffer (1)什么是字符串:多个字符的集合 (2)String 是内容不可变的字符串.(底层使用了一个不 ...

  4. Jackson反序列JSON为实体对象出现:no String-argument constructor/factory method to deserialize from String value的问题

    解决方法: 1.JSON字符串中有转义字符,可以替换,也可以直接toString之后清除转移字符. 参考: https://stackoverflow.com/questions/40986738/s ...

  5. Linux下分区、格式化、自动挂载

    说明:现在硬盘基本没有了IDE,所以基本是从SCSI开始说起,第一块硬盘标示为sda,第二块为sdb,以此类推.那么第一块硬盘的第一个分区为sda1,也是以此类推. 一.硬盘分区 sudo fdisk ...

  6. 利用mkfs.ubifs和ubinize两个工具制作UBI镜像

    转:http://blog.sina.com.cn/s/blog_9452251d01015z9h.html 有了mkfs.ubifs和ubinize两个工具后,就可以制作UBIFS镜像了,具体步骤如 ...

  7. openssl/ssl.h file not found

    sample/le-proxy.c:33:10: fatal error: 'openssl/ssl.h' file not found mac下,在安装某些软件的时候提示如上错误,但是mac已经安装 ...

  8. vertex buffer 数据结构 如何读vb的memory pool

    vertex attribute (declaration)    vertex stream (memory pool) 这两部分 通过attribute 里面对memory的描述把两部分 vbo ...

  9. iframe.contentWindow 属性:关于contentWindow和contentDocument区分

    定义和用法 contentDocument 属性能够以 HTML 对象来返回 iframe 中的文档,可以通过所有标准的 DOM 方法来处理被返回的对象. 语法:frameObject.content ...

  10. 15 Basic ‘ls’ Command Examples in Linux

    FROM: http://www.tecmint.com/15-basic-ls-command-examples-in-linux/ ls command is one of the most fr ...