通过前几篇的分析。我们发现,如果一份数据推上来的话,有点时候需要转发多路出去,比如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;