librtmp 源码分析笔记 ReadN
無限緬懷雷神,R.I.P?
https://blog.csdn.net/leixiaohua1020/article/details/15814587
為了能更好的使用librtmp,特將librtmp源碼的個人分析記錄下來,方便日后查看回顧。
rtmp是基于tcp的,無論協(xié)議寫的如何天花亂墜,本質還是發(fā)包和收包,最本質的是二進制數據的交換,所以先查看下發(fā)送、接受陣營的兩位苦工,ReadN,WriteN函數
略去對http方式、加密相關代碼的分析(其實是看不懂),只看最基礎最重要的tcp收發(fā)流程。
ReadN
ReadN函數顧名思義,就是從socket中讀取N個字節(jié)出來,如果不足N個字節(jié),那么我就阻塞等,如果超過N個字節(jié),我也只取前N個字節(jié)。
? ? 此處首先查看當前的socketbuffer中有沒有數據,如果沒有,那么嘗試接受并填充socketbuffer,此處調用了RTMPSockBuf_Fill,這其實才是真正接收數據的地方,后文分析。
}nRead = ((n < avail) ? n : avail);n是還想要讀取的字節(jié)數,avail是buffer中已有的字節(jié)數,nRead就是將要拷貝到輸出地址的字節(jié)數
if (nRead > 0){memcpy(ptr, r->m_sb.sb_start, nRead);r->m_sb.sb_start += nRead;r->m_sb.sb_size -= nRead;nBytes = nRead;r->m_nBytesIn += nRead;拷貝出去后,緩沖區(qū)中nRead字節(jié)的數據已被消費完,做出相應的調整
if (r->m_bSendCounter&& r->m_nBytesIn > r->m_nBytesInSent + r->m_nClientBW / 2)SendBytesReceived(r);}/*RTMP_Log(RTMP_LOGDEBUG, "%s: %d bytes\n", __FUNCTION__, nBytes); */ #ifdef _DEBUGfwrite(ptr, 1, nBytes, netstackdump_read); #endifif (nBytes == 0){RTMP_Log(RTMP_LOGDEBUG, "%s, RTMP socket closed by peer", __FUNCTION__);/*goto again; */RTMP_Close(r);break;}if (r->Link.protocol & RTMP_FEATURE_HTTP)r->m_resplen -= nBytes;#ifdef CRYPTOif (r->Link.rc4keyIn){RC4_encrypt(r->Link.rc4keyIn, nBytes, ptr);} #endifn -= nBytes;ptr += nBytes;因為已經讀了nBytes字節(jié)的數據了,那么接下來還需要讀的數據長度需要減去nBytes,輸出地址的指針也需要相應的移動nBytes
}return nOriginalSize - n;返回已讀取的字節(jié)數,不發(fā)生異常情況,都是返回nOriginalSize,即函數參數中的n,代表圓滿完成了讀取n個字節(jié)的任務
接下來看下更下層的苦工RTMPSockBuf_Fill
int RTMPSockBuf_Fill(RTMPSockBuf *sb) {int nBytes;if (!sb->sb_size)sb->sb_start = sb->sb_buf;這句意思就是,如果buffer里面的未消費的字節(jié)是0,那么趕緊把buffer的內容起始指針還原為buffer的指針
while (1){nBytes = sizeof(sb->sb_buf) - sb->sb_size - (sb->sb_start - sb->sb_buf);nBytes是指當前buffer能讀取的最大字節(jié)數,
#if defined(CRYPTO) && !defined(NO_SSL)if (sb->sb_ssl){nBytes = TLS_read(sb->sb_ssl, sb->sb_start + sb->sb_size, nBytes);}else #endif{nBytes = recv(sb->sb_socket, sb->sb_start + sb->sb_size, nBytes, 0);此處才真正調用了系統(tǒng)調用recv接收數據
}if (nBytes != -1){sb->sb_size += nBytes;}else{int sockerr = GetSockError();RTMP_Log(RTMP_LOGDEBUG, "%s, recv returned %d. GetSockError(): %d (%s)",__FUNCTION__, nBytes, sockerr, strerror(sockerr));if (sockerr == EINTR && !RTMP_ctrlC)continue;if (sockerr == EWOULDBLOCK || sockerr == EAGAIN){sb->sb_timedout = TRUE;nBytes = 0;}}break;}return nBytes; }另附上RTMPSockBuf結構體
typedef struct RTMPSockBuf{int sb_socket;int sb_size; /* number of unprocessed bytes in buffer */char *sb_start; /* pointer into sb_pBuffer of next byte to process */char sb_buf[RTMP_BUFFER_CACHE_SIZE]; /* data read from socket */int sb_timedout;void *sb_ssl;} RTMPSockBuf;其中,
/* needs to fit largest number of bytes recv() may return */ #define RTMP_BUFFER_CACHE_SIZE (16*1024)總結一下,ReadN就是想從當前socket中盡可能讀取N字節(jié),其中會調用RTMPSockBuf_Fill不停的接收數據存入buffer,然后消費buffer,直到讀夠了N字節(jié)。
整體流程還是比較簡潔清晰的,其實這兒的ReadN可以應用到任何同步阻塞讀寫的tcp socket應用中。最重要的還是這個RTMpSockBuf的設計思想,簡單卻又實用,畢竟很多使用到librtmp的著名應用最底層靠的還是它。
總結
以上是生活随笔為你收集整理的librtmp 源码分析笔记 ReadN的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LaTeX 语法教程
- 下一篇: svn合并分支到主干,工具操作