淘宝开源网络框架tbnet 之packet

来源:互联网 发布:阿里云 华北 华东 华南 编辑:程序博客网 时间:2024/05/18 01:01

 接着上篇博文,在上篇博文当中我们一起简要的分析了下tbnet库中的transport类,该类是整个开源库的总起,接下来,我们就来分析下tbnet库中所使用的一些传输数据包的封装,在tbnet中对于传输包定义的比较开放,如果自己想要借用tbnet库时,可以通过继承tbnet库的packet类来定义自己的包结构,一个传输数据包一般的结构分为两个部分:1)包头;2)包体,而包头主要提供了一些包的基本信息以及路由信息,而包体则是包含用户传输的数据,在tbnet库中,首先定义了一个包头结构,代码如下:

class PacketHeader {public:    uint32_t _chid;         // ͨµÀID    int _pcode;             // Êý¾Ý°üÀàÐÍ    int _dataLen;           // Êý¾Ý°übody³¤¶È(³ýÍ·ÐÅÏ¢Íâ)};

在该结构中,包含了三部分内容:channelID、数据包的编码以及包体的长度,在使用tbnet库中,所有的数据包都会定义包头,接下来看看packet的部分定义,代码如下

class Packet {    friend class PacketQueue;public:...    /*     * ×é×°     *     * @param output: Ä¿±êbuffer     * @return ÊÇ·ñ³É¹¦     */    virtual bool encode(DataBuffer *output) = 0;    /*     * ½â¿ª     *     * @param input: Ô´buffer     * @param header: Êý¾Ý°üheader     * @return ÊÇ·ñ³É¹¦     */    virtual bool decode(DataBuffer *input, PacketHeader *header) = 0;...protected:    PacketHeader _packetHeader; // Êý¾Ý°üµÄÍ·ÐÅÏ¢    int64_t _expireTime;        // µ½ÆÚʱ¼ä    Channel *_channel;    Packet *_next;              // ÓÃÔÚpacketqueueÁ´±í};

在packet类中,我们主需要关注一下其中的两个函数即可,encode函数和decode函数,每个数据包在传输之前都需要经过二进制编码的过程,而在收到数据包时吗,又需要二进制解码过程,所以在packet类中,定义了这两个函数,并且这两函数声明为虚函数,从而使得在继承子类中可以根据需要来自行设计二进制编解码过程,最后来看看其主要的成员变量,包含了一个包头结构,超时时间以及一个Channel指针,这个指针主要的作用就是声明这个数据包的归属问题,接下来,我们来看看在tbnet库中实现的关于httprequestpacke类,这个类是packet的子类,并且主要用于 HTTP协议,首先从该类的成员变量说起,代码如下:

    char *_strHeader;       // ±£´æÍ·ÄÚÈݵÄbu    char *_strQuery;        // ²éѯ    bool _isKeepAlive;      // ÊÇ·ñÖ§³Ökeepal    int _method;            // get - 1    PSTR_MAP _headerMap;    // ÆäËûÍ·ÐÅÏ¢µÄ    tbnet::Connection *_connection; // ´æconnecti

在该成员中,主要包含了http协议方面的一些东西,如http包头和http请求结构体,并且还有个connection指针,这个指针的作用主要的作用就是标记这个packet的发送端,这样方便回传response包,接着我们就来看看decode函数,该函数的作用就是解压请求的request包,而在httprequsetpacket里面不会实现encode函数,至于原因想必大家已经很清楚了,因为在http接收端只负责接收请求包,并对请求包进行解压,代码如下:

bool HttpRequestPacket::decode(DataBuffer *input, PacketHeader *header) {    int len = header->_dataLen;    _strHeader = (char*) malloc(len+1);    input->readBytes(_strHeader, len);    _strHeader[len] = '\0';    int line = 0;    int first = 1;    char *p, *name = NULL, *value;    p = value = _strHeader;    while (*p) {        // ÕÒÿһ        if (*p == '\r' && *(p+1) == '\n') {            if (value == p && line > 0) { // header ½áÊ                break;            }            *p = '\0';            // ȥǰ¿Õ            while (*value == ' ') value ++;            if (line > 0) {                if (strcmp(name, "Connection") == 0 && strcasecmp(value, "Keep-Alive") == 0) {                    _isKeepAlive = true;                } else {                    _headerMap[name] = value;                }            } else {                _strQuery = value;            }            value = p + 2;            line ++;            first = 1;        } else if (line == 0 && *p == ' ') { // Ê×            if (_method) {                *p = '\0';            } else if (strncmp(value, "GET ", 4) == 0) {    // ÊÇGET ·½                _method = 1;                value = p + 1;            }        } else if (*p == ':' && first == 1) {            *p = '\0';            name = value;            value = p + 1;            first = 0;        }        p ++;    }    return true;}

这段代码其实也没有什么特殊之处,就是针对发送的请求包按照http请求包的格式进行解压而已,在此就不做过多的分析,接下来,我们再来看看httpresponsepacket类,这个类从名字上就可以看出来跟上面说的httprequestpacket相对应

class HttpResponsePacket : public Packet {public:...    /*     * ×é     */    bool encode(DataBuffer *output);    /*     * ½â     */    bool decode(DataBuffer *input, PacketHeader *header);...    /*     * ÉèÖÃheade     */    void setHeader(const char *name, const char *value);    /*     * ÉèÖÃ×     */    void setStatus(bool status, const char *statusMessage = NULL);    /*     * ÉèÖÃÄÚÈ     */    void setBody(const char *body, int len);    /*     * ÊÇ·ñkeepalive     */    void setKeepAlive(bool keepAlive);private:    bool _status;                   // ·µ»ØµÄ״̬, true => 200, false => 404    char *_statusMessage;           // ״̬    char *_body;                    // ·µ»ØµÄÄÚÈÝ    int _bodyLen;                   // ·µ»ØÄÚÈÝÕÒ³¤¶È    STRING_MAP _headerMap;          // ·µ»ØÆäËûÍ·ÐÅÏ¢    bool _isKeepAlive;              // ÊÇ·ñkeepalive}

这个类里面其实内容跟之前说的差不多,数据成员里面包含了http response包的相关结构,在此不作过多地介绍,下面我们花点时间来看看encode这个函数,代码如下:

bool HttpResponsePacket::encode(DataBuffer *output) {    if (_statusMessage) {        output->writeBytes(_statusMessage, strlen(_statusMessage));        output->writeBytes("\r\n", 2);    } else if (_status) { //HTTP/1.1 200 OK        output->writeBytes(TBNET_HTTP_STATUS_OK, strlen(TBNET_HTTP_STATUS_OK));    } else { // HTTP/1.1 404 Not Found        output->writeBytes(TBNET_HTTP_STATUS_NOTFOUND, strlen(TBNET_HTTP_STATUS_NOTFOUND));    }    //¹Ì¶¨    if (_isKeepAlive) {        output->writeBytes(TBNET_HTTP_KEEP_ALIVE, strlen(TBNET_HTTP_KEEP_ALIVE));    } else {        output->writeBytes(TBNET_HTTP_CONN_CLOSE, strlen(TBNET_HTTP_CONN_CLOSE));    }    if (_headerMap.find("Content-Type") == _headerMap.end()) {        output->writeBytes(TBNET_HTTP_CONTENT_TYPE, strlen(TBNET_HTTP_CONTENT_TYPE));    }    char tmp[64];    int len = sprintf(tmp, TBNET_HTTP_CONTENT_LENGTH, _bodyLen);    output->writeBytes(tmp, len);    // Óû§×Ô¶¨Òå    for (STRING_MAP_ITER it=_headerMap.begin(); it!=_headerMap.end(); it++) {        output->writeBytes(it->first.c_str(), strlen(it->first.c_str()));        output->writeBytes(": ", 2);        output->writeBytes(it->second.c_str(), strlen(it->second.c_str()));        output->writeBytes("\r\n", 2);    }    // ¿Õ    output->writeBytes("\r\n", 2);    // bodyLen    output->writeBytes(_body, _bodyLen);    //assert(_packetHeader._dataLen == output->getDataLen());    return true;}

这段代码个人感觉写的比较的漂亮,基本上是按照http回馈包结构填充的,流程十分的清晰,最终会将反馈包写入到输出队列中,而后触发写可用事件,而将数据包发送出去,整个流程就是这样,在tbnet目录下有个test案例里面就用到了这两个包,有时间的话,可以自己在本机上试试,感觉还是蛮好用的,最后在tbnet中,还有一种数据包结构-controlpacket,这个数据包主要适用于服务器端之间的控制信息的发送,实现方式比较的简单,这里就不展开讨论了,本篇到这里就结束了,谢谢

总结

     tbnet库里的packet类整体上来讲定义的不错,该有的部分都有,并且其扩展性也使很好的,用户可以根据自己的需要直接从packet类中继承即可,自己私下也定义了几个packet结构体,使用起来很方便,推荐大家不妨一试,谢谢,本篇到此结束,下篇,我们将讨论IOComponent这个东西,也使很不错哦,谢谢。

如果需要,请注明转载,谢谢 

0 0
原创粉丝点击