zoukankan      html  css  js  c++  java
  • Socket TCP协议通讯粘包算法

    自己开发了一个股票智能分析软件,功能很强大,需要的点击下面的链接获取:

    https://www.cnblogs.com/bclshuai/p/11380657.html

    1.问题描述

      socket通讯TCP协议虽然是稳定的通讯,但是也会出现丢包的现象,而且会出现一个数据包分几次发送的情况。所以需要用一个缓冲区去缓存数据,并且判断是不是一个完整的包。等接收到一个完整的数据包,然后再去处理解析。

    2.解决方案

    先要定一个特殊字符串,比如&#@!,这样四个字节的协议头,在你的通讯报文中绝对不会出现。然后在丢包之后,通过查找这四个字节的协议头,找到包开始的地方,将数据缺失的包过滤掉。找到这个协议头之后再去解析数据的长度,解析完数据数据长度,在缓存这个长度的包数据进行解析。一般是xml、json等格式的字符串字节流。只要读取指定长度的数据,进行数据解析即可。

     3具体实现代码

    结构定义

    #define MAX_BUFFER_LENGTH        (10*1024)
    #define PROTOCOL_HEAD_LEN        12    //FAD 报文头长度
    
    typedef struct
    {
        char gRecvBuff[TCP_RECV_DATA_LEN];
        HPR_INT32 iBuffLen;
    }BUFFER_INFO;
    
    
    BUFFER_INFO m_stRecvBuf;//一次接收数据的缓存区
    char        m_pDataBuff[MAX_BUFFER_LENGTH];//每次接收的数据都放入这个缓冲区,空间更加,不断的处理接收
    HPR_INT32   m_iDataLen;//总的数据长度
    HPR_INT32   m_iOneFameLen;//一个数据包的数据长度,包括协议头、包长度和包数据

    函数算法实现

    HPR_INT32 CCuItem::ParseData(HPR_ULONG NumberOfBytes)
    {
        HPR_UINT16 iRetVal = HPR_ERROR;
        if( NumberOfBytes<0)
        {
            LOG_ERROR("ParseDataFromUtdu Point is NULL");
            return iRetVal;
        }
        LOG_INFO("Receive the data length is %d",NumberOfBytes);
        int nOneFameLen=0;
        if (NumberOfBytes > 0)//数据大于0
        {
            if ((m_iDataLen + NumberOfBytes) > MAX_BUFFER_LENGTH)//超出缓冲区大小,丢弃
            {
                HPR_ZeroMemory(m_pDataBuff,MAX_BUFFER_LENGTH);
                m_iDataLen = 0;
                m_iOneFameLen=0;
                LOG_ERROR("DataBuff's length is more than MaxBuff!");
                return HPR_ERROR;
            }
            else
            {
                memmove(m_pDataBuff+m_iDataLen, m_stRecvBuf.gRecvBuff,NumberOfBytes);//将数据放入缓冲区
                m_iDataLen+=NumberOfBytes;
            }
            while (m_iDataLen>=HeaderLenth)//数据长度大于协议头长度
            {
                HPR_INT32 msgLen=0;
                HPR_INT32 msgtype = 0;
                HeaderPacket headPack;
                memmove(&headPack,m_pDataBuff,HeaderLenth);//将协议头放入结构体头部
                int msglen = headPack.lenth;//得到包数据长度
                LOG_INFO("The xml length is %d",msgLen);
                if(msgLen<0)
                {
                    LOG_INFO("Parase the Data length is less then zero %d",msgLen);
                    return HPR_ERROR;
                }
                m_iOneFameLen= HeaderLenth +msgLen;//得到协议头和包数据的长度
                LOG_INFO("One Frame length is %d",m_iOneFameLen);
                if (m_pDataBuff[0]!='H'||m_pDataBuff[1]!='K'||m_pDataBuff[2]!='P'||m_pDataBuff[3]!='&')//判断协议头
                {
                    LOG_ERROR("协议头错误,data:%s", m_pDataBuff);
                    //查找协议头
                    int i = 0;
                    for (i = 0; i < m_iDataLen&&i < MAX_BUFFER_LENGTH; i++)//如果前四个字节不是协议头,进入查找
                    {
                        if (m_pDataBuff[i] == 'H')
                        {
                            if ((i+1)>=m_iDataLen)//长度只有1 不够
                            {
                                break;
                            }
                            if (m_pDataBuff[i+1]=='K')
                            {
                                if ((i+2)>=m_iDataLen)//长度只有2 不够
                                {
                                    break;
                                }
                                if (m_pDataBuff[i+2]=='P')
                                {
                                    if ((i + 3) >= m_iDataLen)//长度只有3 不够
                                    {
                                        break;
                                    }
                                    if (m_pDataBuff[i + 3] == '&')
                                    {
                                        break;//找到协议头,跳出循环
                                    }
                                }
                            }
                        }
                    }
                    //将databuff向前移动i位
                    memmove(m_pDataBuff, m_pDataBuff + i, m_iDataLen - i);//删除前面i个脏数据
                    m_iDataLen = m_iDataLen - i;//重新计算缓冲区数据长度
                    HPR_ZeroMemory(m_pDataBuff+ m_iDataLen,MAX_BUFFER_LENGTH- m_iDataLen);//将缓冲区后面的数据置零
                    m_iOneFameLen=0;
                    LOG_ERROR("Pro is wrong!
    ");
                }
                else//头部是协议头,处理数据
                {
                    if (m_iDataLen>=m_iOneFameLen)
                    {
                        ParseRecvMessage(headPack.type,m_pDataBuff+ HeaderLenth,m_iOneFameLen);
                        memmove(m_pDataBuff,m_pDataBuff+m_iOneFameLen,m_iDataLen-m_iOneFameLen);
                        m_iDataLen-=m_iOneFameLen;
                        HPR_ZeroMemory(m_pDataBuff+m_iDataLen,MAX_BUFFER_LENGTH-m_iDataLen);
                    }
                    else
                    {
                        LOG_INFO("The data is Less than oneFrame,keep recieve
    ");
                        break;
                    }
                }
            }    
        }
        return HPR_OK;
    }
    HPR_INT32 CCuItem::ParseData(HPR_ULONG NumberOfBytes)
    {
        HPR_UINT16 iRetVal = HPR_ERROR;
        if( NumberOfBytes<0)
        {
            LOG_ERROR("ParseDataFromUtdu Point is NULL");
            return iRetVal;
        }
        LOG_INFO("Receive the data length is %d",NumberOfBytes);
        int nOneFameLen=0;
        if (NumberOfBytes > 0)//数据大于0
        {
            if ((m_iDataLen + NumberOfBytes) > MAX_BUFFER_LENGTH)//超出缓冲区大小,丢弃
            {
                HPR_ZeroMemory(m_pDataBuff,MAX_BUFFER_LENGTH);
                m_iDataLen = 0;
                m_iOneFameLen=0;
                LOG_ERROR("DataBuff's length is more than MaxBuff!");
                return HPR_ERROR;
            }
            else
            {
                memmove(m_pDataBuff+m_iDataLen, m_stRecvBuf.gRecvBuff,NumberOfBytes);//将数据放入缓冲区
                m_iDataLen+=NumberOfBytes;
            }
            while (m_iDataLen>=HeaderLenth)//数据长度大于协议头长度
            {
                HPR_INT32 msgLen=0;
                HPR_INT32 msgtype = 0;
                HeaderPacket headPack;
                memmove(&headPack,m_pDataBuff,HeaderLenth);//将协议头放入结构体头部
                int msglen = headPack.lenth;//得到包数据长度
                LOG_INFO("The xml length is %d",msgLen);
                if(msgLen<0)
                {
                    LOG_INFO("Parase the Data length is less then zero %d",msgLen);
                    return HPR_ERROR;
                }
                m_iOneFameLen= HeaderLenth +msgLen;//得到协议头和包数据的长度
                LOG_INFO("One Frame length is %d",m_iOneFameLen);
                if (m_pDataBuff[0]!='H'||m_pDataBuff[1]!='K'||m_pDataBuff[2]!='P'||m_pDataBuff[3]!='&')//判断协议头
                {
                    LOG_ERROR("协议头错误,data:%s", m_pDataBuff);
                    //查找协议头
                    int i = 0;
                    for (i = 0; i < m_iDataLen&&i < MAX_BUFFER_LENGTH; i++)//如果前四个字节不是协议头,进入查找
                    {
                        if (m_pDataBuff[i] == 'H')
                        {
                            if ((i+1)>=m_iDataLen)//长度只有1 不够
                            {
                                break;
                            }
                            if (m_pDataBuff[i+1]=='K')
                            {
                                if ((i+2)>=m_iDataLen)//长度只有2 不够
                                {
                                    break;
                                }
                                if (m_pDataBuff[i+2]=='P')
                                {
                                    if ((i + 3) >= m_iDataLen)//长度只有3 不够
                                    {
                                        break;
                                    }
                                    if (m_pDataBuff[i + 3] == '&')
                                    {
                                        break;//找到协议头,跳出循环
                                    }
                                }
                            }
                        }
                    }
                    //将databuff向前移动i位
                    memmove(m_pDataBuff, m_pDataBuff + i, m_iDataLen - i);//删除前面i个脏数据
                    m_iDataLen = m_iDataLen - i;//重新计算缓冲区数据长度
                    HPR_ZeroMemory(m_pDataBuff+ m_iDataLen,MAX_BUFFER_LENGTH- m_iDataLen);//将缓冲区后面的数据置零
                    m_iOneFameLen=0;
                    LOG_ERROR("Pro is wrong!
    ");
                }
                else//头部是协议头,处理数据
                {
                    if (m_iDataLen>=m_iOneFameLen)
                    {
                        ParseRecvMessage(headPack.type,m_pDataBuff+ HeaderLenth,m_iOneFameLen);
                        memmove(m_pDataBuff,m_pDataBuff+m_iOneFameLen,m_iDataLen-m_iOneFameLen);
                        m_iDataLen-=m_iOneFameLen;
                        HPR_ZeroMemory(m_pDataBuff+m_iDataLen,MAX_BUFFER_LENGTH-m_iDataLen);
                    }
                    else
                    {
                        LOG_INFO("The data is Less than oneFrame,keep recieve
    ");
                        break;
                    }
                }
            }    
        }
        return HPR_OK;
    }
  • 相关阅读:
    Elasticsearch聚合 之 Date Histogram聚合
    Elasticsearch聚合 之 Terms
    Elasticsearch分析聚合
    mysql-聚合函数
    flask学习笔记(-操作数据库)
    在VS中调试javascript脚本
    jquery获取设置input值
    jquery后加Dom绑定事件
    Juicer——a fast template engine
    ASP.NET 一般处理程序
  • 原文地址:https://www.cnblogs.com/bclshuai/p/13648546.html
Copyright © 2011-2022 走看看