• 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; } } }

      

  • 相关阅读:
    Storm监控文件夹变化 统计文件单词数量
    Storm默认配置 default.yaml
    Storm集群搭建
    Storm概念
    zookeeper安装
    zookeeper
    zookeeper应用
    zookeeper应用
    zookeeper应用
    HDU 3473 Minimum Sum (划分树求区间第k大带求和)(转)
  • 原文地址:https://www.cnblogs.com/studyofadeerlet/p/7463801.html
Copyright © 2020-2023  润新知