• 一个包的libevent流程


    //一个发包的流程
    第一个包就是客户端的心跳包,现在加了版本的包
    再来看看这个发包打包过程,过程坚持,但理解费劲
    void NGP::OnliveTimer()//客户端心跳,5s发一次
    {
        SendCmd(c2s_on_live, NULL, 0);
    }
    bool NGP::SendCmd(int nCmd, void* pData, int nLen)
    {
        //std::wstring msg = L"发送命令:";
        //msg += boost::lexical_cast<std::wstring>(cmd);
        //m_share->log(msg.c_str());
        //发送获取游戏列表请求
        Protocol rpt = {0};
        rpt.cmd_type = nCmd;
        rpt.content = pData;
        rpt.size    = nLen;
        std::vector<char> buffer;
        buffer.resize(nLen + sizeof(Protocol));//这个长度个人感觉可以-sizeof(void*),这个位置其实是被覆盖的
        int send_len = rpt.to_buffer(&buffer[0], buffer.size());
    
        for (;;)
        { 
            bool b = m_spTcpLink->Send(&buffer[0], send_len);
    
            if (b)
                break;
            std::this_thread::sleep_for(std::chrono::milliseconds(5));
        }
    
        return true;
    }
    //包的格式(这个就是初始包发送的格式)
    -(一个字节的协议)----(四个字节的长度表示后面内容的大小)-----------------(包发的内容)
    struct Protocol
    {
        unsigned char    cmd_type;
        int                size;//content的长度, 不包含本协议头
        void*            content;
    
        int to_buffer(void* buffer, int buffer_len)//挺难理解的
        {
            if(!buffer)
                return -1;
            int total_size = size + sizeof(Protocol) - sizeof(void*);//- sizeof(void*)是因为到时就是从这个指针的位置开始复制的
            if(buffer_len < total_size)//buffer_len是提供的内存大小,total_size是打包后的总大小
                return -1;
    
            Protocol* p = (Protocol*)buffer;//把内存模型看成Protocol模型
            p->cmd_type = this->cmd_type;
            p->size = this->size;
            if (content)
            {
                memcpy(&p->content, content, size);//从指针开始位置复制内容
            }
            return total_size;
        }
    }
    //此包在libevent又进行了一次打包过程
    ----(4个字节,表示后面包的总长度)-(一个字节的协议)----(四个字节的长度表示后面内容的大小)-----------------(包发的内容)
    所以对于心跳包的长度是9个字节
    
    //服务端接受到这个包的流程
    线程的读事件被调用
    /* 读数据流 */
    void Channel::read_datastream(struct bufferevent* bev)
    {
        //bufferevent_read每次会将输入缓冲中的数据读出来,好像最多读4096个字节,那这么大的数据有可能是多个包,还有可能一个包都不到,这是个问题
        size_t len = bufferevent_read(bev, m_buf, sizeof(m_buf));
    /**
     * [测试]:先解锁再加锁,防止此线程在分配内存失败的时候死循环等待时,main_thread的send_data陷入阻塞。
     */    
        evbuffer_unlock(bev->output); 
    
        //auto& in = bev->input;    lock一样
        //auto& out = bev->output;
        m_readStream.Push(m_buf, len);//datastream相当于一个缓冲的作用,这个过程看懂废我好大劲,最后还是在同时的帮助下才了解
        read_pack();
    
        evbuffer_lock(bev->output);
    }
    
    void Push(const void * pBuf, int nLen)
    {
        char* pBuf2 = (char*)pBuf;
        
        m_StreamBuffer.insert(m_StreamBuffer.end(), pBuf2, pBuf2 + nLen);//每次将包插入最后一个位置的地方
    }
    
    /* 读包 */
    void Channel::read_pack()
    {
        if(m_readStream.Size() < 4)//如果其大小小于4就表示读完了,不可能存在小于4个字节的包,有可能是不完整的包
            return;
    
        size_t len = *(size_t*)m_readStream.Peek();//获取前四个字节,表示包内容的长度
        if( m_readStream.Size() < 4 + len)//表示包不符合规则,应该>=,当然也有可能是因为这个包是不完整的包,等下次再解析
            return;
    
        m_event->on_receive_data(m_id, m_readStream.Peek() + 4, len); //对包进行处理
        m_readStream.Pop(4 + len);//每个包逃过下次读取位置
    
        read_pack();//一直读到完为止
    }
    void Pop(int nLen)
    {
        m_head_size += nLen;//将头标记移动
        if(m_head_size > m_buff_len)
        {
            //可以将这个数组当成循环队列进行使用,循环队列还有种方式就是两个游标
            m_StreamBuffer.erase(m_StreamBuffer.begin(), m_StreamBuffer.begin() + m_head_size);//删掉重新预留,个人理解有可能是防止读取错误,具体不是太清楚
            m_head_size = 0;
            m_StreamBuffer.reserve(m_buff_len);
        }
    }

    关于这个最终到达GS还有很多东西要做,暂时分析到这。

  • 相关阅读:
    使用pdf.js快速实现pdf查看器
    简单了解一下pinia的结构
    【Spring】 @ConditionalOnExpression 满足条件才初始化Bean 的奇技淫巧
    【mybatis】mybatis 使用association关联查询的几种方式
    【mysql】关于java、Spring、mybatis、mysql项目中数据库datetime存入取出时间差一秒的问题解决方案
    【java】【ExecutorService】【线程池】java 超时返回,异步线程实现超时响应
    消息重发、重试消费、死信队列
    RocketMQ简单使用(二)批处理、过滤、事务消息
    基于SpringMVC自定义HandlerMapping扩展Handler&注册自己的Servlet
    django REST框架 Djangoninja
  • 原文地址:https://www.cnblogs.com/zzyoucan/p/4077605.html
Copyright © 2020-2023  润新知