• zlMediaKit 10 http相关


    HttpRequestSplitter.h

    HttpRequestSplitter

    在这里插入图片描述

    结构

        ssize_t _content_len = 0;
        size_t _remain_data_size = 0;
        toolkit::BufferLikeString _remain_data;
    
    • 1
    • 2
    • 3

    input

    上次还有剩余的数据,就把这次的数据和上次的数据接上。

    分包

        const char *ptr = data;
        if(!_remain_data.empty()){
            _remain_data.append(data,len);
            data = ptr = _remain_data.data();
            len = _remain_data.size();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    解析包头

    当 _content_len != 0 || _remain_data_size <= 0 || onSearchPacketTail==nullptr(未找到包尾)

    onRecvHeader需要子类实现

        const char *index = nullptr;
        _remain_data_size = len;
        while (_content_len == 0 && _remain_data_size > 0 && (index = onSearchPacketTail(ptr,_remain_data_size)) != nullptr) {
            if (index == ptr) {
                break;
            }
            if (index < ptr || index > ptr + _remain_data_size) {
                throw std::out_of_range("上层分包逻辑异常");
            }
            //_content_len == 0,这是请求头
            const char *header_ptr = ptr;
            ssize_t header_size = index - ptr;
            ptr = index;
            _remain_data_size = len - (ptr - data);
            _content_len = onRecvHeader(header_ptr, header_size);
        }
    onRecvHeader返回:
    请求头后的content长度, 
    <0 : 代表后面所有数据都是content,此时后面的content将分段通过onRecvContent函数回调出去 
    0 : 代表为后面数据还是请求头, 
    >0 : 代表后面数据为固定长度content,此时将缓存content并等到所有content接收完毕一次性通过onRecvContent函数回调出去
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
        if(_remain_data_size <= 0){
            //没有剩余数据,清空缓存
            _remain_data.clear();
            return;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    缓存不完整的http头,返回

        if(_content_len == 0){
            //尚未找到http头,缓存定位到剩余数据部分
            _remain_data.assign(ptr,_remain_data_size);
            return;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    goto跳转

    onRecvContent会根据场景不同处理固定长度和非固定长度的包

    onRecvHeader要求有能分辨固定长度包和非固定长度包的能力

        //已经找到http头了
        if(_content_len > 0){
            //数据按照固定长度content处理
            if(_remain_data_size < (size_t)_content_len){
                //数据不够,缓存定位到剩余数据部分
                _remain_data.assign(ptr, _remain_data_size);
                return;
            }
            //收到content数据,并且接受content完毕
            onRecvContent(ptr,_content_len);
    
            _remain_data_size -= _content_len;
            ptr += _content_len;
            //content处理完毕,后面数据当做请求头处理
            _content_len = 0;
    
            if(_remain_data_size > 0){
                //还有数据没有处理完毕
                _remain_data.assign(ptr,_remain_data_size);
                data = ptr = (char *)_remain_data.data();
                len = _remain_data.size();
                goto splitPacket;
            }
            _remain_data.clear();
            return;
        }
    
        //_content_len < 0;数据按照不固定长度content处理
        onRecvContent(ptr,_remain_data_size);//消费掉所有剩余数据
        _remain_data.clear();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    onRecvHeader/onRecvContent/onSearchPacketTail

    onRecvHeader

    /**
     * 收到请求头
     * @param data 请求头数据
     * @param len 请求头长度
     *
     * @return 请求头后的content长度,
     *  <0 : 代表后面所有数据都是content,此时后面的content将分段通过onRecvContent函数回调出去
     *  0 : 代表为后面数据还是请求头,
     *  >0 : 代表后面数据为固定长度content,此时将缓存content并等到所有content接收完毕一次性通过onRecvContent函数回调出去
     */
    virtual ssize_t onRecvHeader(const char *data,size_t len) = 0;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    onRecvContent

    /**
     * 收到content分片或全部数据
     * onRecvHeader函数返回>0,则为全部数据
     * @param data content分片或全部数据
     * @param len 数据长度
     */
    virtual void onRecvContent(const char *data,size_t len) {};
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    onSearchPacketTail

    /**
     * 判断数据中是否有包尾
     * @param data 数据指针
     * @param len 数据长度
     * @return nullptr代表未找到包位,否则返回包尾指针
     */
    virtual const char *onSearchPacketTail(const char *data, size_t len);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    setContentLen

    /**
     * 设置content len
     */
    void setContentLen(ssize_t content_len);
    
    • 1
    • 2
    • 3
    • 4

    onSearchPacketTail

    const char *HttpRequestSplitter::onSearchPacketTail(const char *data,size_t len) {
        auto pos = strstr(data,"\r\n\r\n"); //http包的末尾很好判断,两个空行
        if(pos == nullptr){
            return nullptr;
        }
        return  pos + 4;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    总结

    • 如何分包的,HttpRequestSplitter::input,包头和包体怎么缓存处理的。

    • http 根据**“\r\n\r\n”**判断包头包体是否结束

  • 相关阅读:
    Baklib帮助中心:自助服务指南
    【一步一步了解Java系列】:认识异常类
    MVCC:多版本并发控制案例分析(二)
    【简洁】【皮肤美化】博客园页面美化 主文章加宽
    区间DP及其拓展
    Java开发:多线程编程
    C++Qt开发——动画框架、状态机框架
    网络安全(6)
    Windows自带虚拟机的使用方法
    最大子序和
  • 原文地址:https://blog.csdn.net/qq_41565920/article/details/127720454