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[2*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,0,MAX_MSG_LEN);
    
    struct sockaddr_in src_addr;
    socklen_t addrlen = sizeof(struct sockaddr_in);

//代码逻辑流程1
int recvlen = recvfrom(fd, recvbuf, MAX_MSG_LEN, 0, (struct sockaddr *) &src_addr, &addrlen); if(recvlen == 0) { printf("uehandle_recv_pack() receive shutdown command from peer endpoint ") return; } if (recvlen == -1) { printf("uehandle_recv_pack() ret -1,errno=%d" , errno); return; }
//代码逻辑流程2

//代码逻辑流程2.1
if (p_tcp_nl_msg->flag_tcp_NL_proc == 1) { 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 = 0; } uint8_t* buf = recvbuf;
//代码逻辑3
while (1) {
//代码逻辑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) ", 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, 0, 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 = 1; break; } }
//代码逻辑流程3.1
else { printf("recvlen(%u) < sizeof(TcpMsgHead):%u ", recvlen, sizeof(TcpMsgHead)); memset(p_tcp_nl_msg->g_recvbuf, 0, 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 = 1; break; } } }

  

原文地址:https://www.cnblogs.com/studyofadeerlet/p/7463801.html