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\n") return; } if (recvlen == -1) { printf("uehandle_recv_pack() ret -1,errno=%d"\n, 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)\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, 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\n", 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; } } }

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM