一个包的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还有很多东西要做,暂时分析到这。

时间: 2024-11-25 11:28:24

一个包的libevent流程的相关文章

一个包的TcpServer流程

上次说到对于那种有内容的包 bool TCPServer::on_receive_data(int channel_id, void* data, int len) { packet pkt; { pkt.data = m_memPool.popPkt(len);//从内存池分配len大小的内存,返回地址,此处为什么使用内存池不太清楚 } memcpy(pkt.data, data, len); pkt.size = len; pkt.channel_id = channel_id; pkt.i

一个包的net到gs流程

再来看看一个包走共享内存的流程 先来看看net进程这块如何处理的 {//用shareData这种类型封装刚才从无锁队列中取到的包 shareData sd; sd.channel_id = pkt.channel_id; sd.data = pkt.data; sd.is_data = pkt.is_data; sd.size = pkt.size; auto hr = m_spShareMemInter->pushA(sd);//将这个包放入共享内存中 } //具体看下是如何放入共享内存的 b

一个包从共享内存到达服务器

一个包到从共享内存到GS流程 上次说到一个包从共享内存池取到一个包之后放入共享队列中 hr = m_spShareMemInter->pushA(sd); 看看GS这边是如何取包的 主线程创建了一个子线程 void GameServer::ProcessThread() { try { ProcessThreadTry(); } catch (...) { DWORD dwErrno = GetLastError(); MessageBox(NULL, L"GameServer::proc

一个包从服务器到达客户端

服务器发包到客户端 以登录包为例 SendCmd(s2c_login, &ret, sizeof(LoginEnum)); end_stat BaseChannel::SendCmd(int nCmd, void* pData, int nLen) { Protocol Ptl; Ptl.cmd_type = nCmd; Ptl.content = pData; Ptl.size = nLen; void* tmpBuffer = g_SendCmdBuffer; int nBufferLen

想:类在一个包提前声明,然后在另一个包描述具体的类内容,但是有问题

想法源于:类可以提前声明,具体类内容可以后面写明: TFrmChangePWTransparent = class; //这里声明其他的类,或一些表里 TFrmChangePWTransparent = class(TForm) end; 构思:一个包写好好多类名,然后在另一包写好具体类的属性方法,但是后来发现这是不可以的 type TProtocolUniITPFrameDataBuilder = class; TProtocolUniITPFrameDataStreamBuilder = c

Android Studio突然不能导入某一个包

在AS给带来方便的同时,也因为AS反应太迅速,就会出现无法自动导入某一个包的问题.在开发的时候不知道按错哪个键,使得ListView无法自动导入. 后来才发现问题的所在.如图:(Settings--->Editor---->General---->Auto Import) 选中android.widget.ListView,点击红色减号即可(“-”),具体不知道AS添加这一功能的作用.

判断一个Activity 判断一个包 是否存在于系统中 的方法

判断一个包是否存在于系统中(来自网络),经过测试,好用: public boolean checkBrowser(String packageName) { if (packageName == null || "".equals(packageName)) return false; try { ApplicationInfo info = getPackageManager().getApplicationInfo( packageName, PackageManager.GET_

shell脚本,一个shell的启动流程。

#一个shell的启动流程 #shell有一些变量,叫做环境变量,这些变量是可以继承的, #比如父shell有$UID,子shell也可以有,而且继承父shell的. #正常我们声明一个变量,a=1,在子shell里,a是空,自己声明的变量不能被继续. 如果我们自己声明变量,想让子shell也可以用 [[email protected] wyb]# cat a.sh #!/bin/bash echo $a #如果我们自己声明变量,想让子shell也可以用,#export 是内置变量,通过它声明的

python高级编程 编写一个包1

#目的是:编写,发行python包可重复过程"""1:是缩短开始真正工作之前所需要的设置时间,也就是提供模板2:提供编写包的标准化方法3:简化测试驱动开发方法的使用4:为发行过程提供帮助5:用于所有包公用模式,描述所有python包之间相同之处和distutils和setuptools如何扮演核心角色6:产生式编程:(在维基百科里面搜索)如何通过基于模板方法对此提供帮助7:包模板创建,设置各种工作所需要的一切8:构建一个开发周期"""#用于所有包