srs代码学习(7)--消息接收过程

本文详细介绍了SRS服务器在处理RTMP消息时如何减少内存开销,通过SrsStSocket、SrsFastBuffer、SrsChunkStream、SrsCommonMessage和SrsSharedPtrMessage五个类实现数据的高效转发。在SrsSharedPtrMessage中使用引用计数避免了数据复制,仅通过copy()函数创建新的引用。整个流程包括st_read的底层读取、数据解析、chunksteam的处理以及消息的转发,确保了消息的正确性和效率。分析表明,服务器基本原样转发接收到的数据,转码操作可以在压力较低时在此处进行,或者将任务转发到专门的转码服务器。
摘要由CSDN通过智能技术生成

通过前几篇的分析。我们发现,如果一份数据推上来的话,有点时候需要转发多路出去,比如forward,多路forward,或者多个edge的出现,这时候的消息如果每一路都复制一份的话,内存开销会非常的打。那么srs怎么处理呢?

首先看看消息的结构,为了能正确的解析消息,srs吧接受消息的过程分成了大概五个类来执行,从下往上依次是

SrsStSocket

SrsFastBuffer

SrsChunkStream

SrsCommonMessage

SrsSharedPtrMessage

这五个类的数据拷贝已有依次,就是从fastbufet里会拷贝到chunkstream里面,然后到这层数据指针一直应用到SrsSharedPtrMessage类里,在SrsSharedPtrMessage中会增加conut变量,来做数据引用,而且SrsSharedPtrMessage类隐藏了构造和拷贝函数,只能通过copy()函数来新建一个引用。这样多路发送只需要一份数据就可以了。下面我们看具体的流程。



流程图上可以看出,最底层的是st_thread的库函数st_read,读取的数据经过srssocket的简单转手,就会提交个bufff库,看看buf的grow()函数的实现

int SrsFastBuffer::grow(ISrsBufferReader* reader, int required_size)
{
    int ret = ERROR_SUCCESS;

    // already got required size of bytes.
    if (end - p >= required_size) {
        return ret;
    }

    // must be positive.
    srs_assert(required_size > 0);

    // the free space of buffer, 
    //      buffer = consumed_bytes + exists_bytes + free_space.
    int nb_free_space = (int)(buffer + nb_buffer - end);
    // resize the space when no left space.
    if (nb_free_space < required_size) {
        // the bytes already in buffer
        int nb_exists_bytes = (int)(end - p);
        srs_assert(nb_exists_bytes >= 0);
        srs_verbose("move fast buffer %d bytes", nb_exists_bytes);

        // reset or move to get more space.
        if (!nb_exists_bytes) {
            // reset when buffer is empty.
            p = end = buffer;
            srs_verbose("all consumed, reset fast buffer");
        } else {
            // move the left bytes to start of buffer.
            srs_assert(nb_exists_bytes < nb_buffer);
            buffer = (char*)memmove(buffer, p, nb_exists_bytes);
            p = buffer;
            end = p + nb_exists_bytes;
        }
        
        // check whether enough free space in buffer.
        nb_free_space = (int)(buffer + nb_buffer - end);
        if (nb_free_space < required_size) {
            ret = ERROR_READER_BUFFER_OVERFLOW;
            srs_error("buffer overflow, required=%d, max=%d, left=%d, ret=%d", 
                required_size, nb_buffer, nb_free_space, ret);
            return ret;
        }
    }

    // buffer is ok, read required size of bytes.
    while (end - p < required_size) {
        ssize_t nread;
        if ((ret = reader->read(end, nb_free_space, &nread)) != ERROR_SUCCESS) {
            return ret;
        }
        
#ifdef SRS_PERF_MERGED_READ
        /**
        * to improve read performance, merge some packets then read,
        * when it on and read small bytes, we sleep to wait more data.,
        * that is, we merge some data to read together.
        * @see https://github.com/ossrs/srs/issues/241
        */
        if (merged_read && _handler) {
            _handler->on_read(nread);
        }
#endif
        
        // we just move the ptr to next.
        srs_assert((int)nread > 0);
        end += nread;
        nb_free_space -= nread;
    }
    
    return ret;
}

buff类里会有一个缓冲区buffer,长度为128k

#define SRS_DEFAULT_RECV_BUFFER_SIZE 131072
有两个移动指针p和end.来标志里面的数据。那么如果这个空间不够怎么办,有两个办法,一个是数据大小nb_exists_bytes,如果为0,那么表示数据都用完了,reset指针。如果还有数据,那么只好重新分配内存大小 buffer = (char*)memmove(buffer, p, nb_exists_bytes);

在获取到数据后,提供两个接口,让外部类来读取数据


char SrsFastBuffer::read_1byte()
{
    srs_assert(end - p >= 1);
    return *p++;
}

char* SrsFastBuffer::read_slice(int size)
{
    srs_assert(size >= 0);
    srs_assert(end - p >= size);
    srs_assert(p + size >= buffer);
    
    char* ptr = p;
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值