最近在研究CrtmpServer http部分,记录一些基本的流程,以备查阅。
首先,打开配置脚本CrtmpServer.lua ,确认脚本中有以下内容,如果没有需要加上。
{ name="samplefactory", description="asdsadasdsa", protocol="dynamiclinklibrary", aliases= { "httpOutboundTest" }, acceptors = { { ip="0.0.0.0", port=8989, protocol="httpEchoProtocol" }, { ip="0.0.0.0", port=8988, protocol="echoProtocol" } } --validateHandshake=true, --default=true, },
在浏览器地址栏中输入http://127.0.0.1:8989/httpEchoProtocol/TestHttp,跟踪CrtmpServer堆栈,调用堆栈如下图所示。
图1 CrtmpServer接收Http调用堆栈
下面逐步分析流程:
1.TCPCarrier::OnEvent(select_event &event)
Carrier层要么是udp,要么是tcp,http协议传输层采用的是http,所以是接收到Tcp链接。
CrtmpServer运行后通过Register 将Http,Tcp协议注册到一起。代码如下:
vector<uint64_t> ProtocolFactory::ResolveProtocolChain(string name) { vector<uint64_t> result; if (name == "echoProtocol") { ADD_VECTOR_END(result, PT_TCP); ADD_VECTOR_END(result, PT_ECHO_PROTOCOL); } else if (name == "httpEchoProtocol") { ADD_VECTOR_END(result, PT_TCP); ADD_VECTOR_END(result, PT_INBOUND_HTTP); ADD_VECTOR_END(result, PT_ECHO_PROTOCOL); } else if (name == "httpDownload") { ADD_VECTOR_END(result, PT_TCP); ADD_VECTOR_END(result, PT_OUTBOUND_HTTP); ADD_VECTOR_END(result, PT_HTTP_DOWNLOAD_PROTOCOL); } else { ASSERT("This protocol stack should not land here"); } return result; }
这段代码的调用流程如下图所示:
图2 httpEchoProtocol流程
tcp 从tcp链接缓存中读取数据代码如下,读取的数据放在pInputBuffer.
bool TCPCarrier::OnEvent(select_event &event) { int32_t readAmount = 0; int32_t writeAmount = 0; //3. Do the I/O switch (event.type) { case SET_READ: { IOBuffer *pInputBuffer = _pProtocol->GetInputBuffer(); assert(pInputBuffer != NULL); if (!pInputBuffer->ReadFromTCPFd(_inboundFd, _recvBufferSize, readAmount)) { FATAL("Unable to read data. %s:%hu -> %s:%hu", STR(_farIp), _farPort, STR(_nearIp), _nearPort); return false; } _rx += readAmount; return _pProtocol->SignalInputData(readAmount); }
2. 看下tcp procotol signalInputData代码
bool TCPProtocol::SignalInputData(int32_t recvAmount) { _decodedBytesCount += recvAmount; return _pNearProtocol->SignalInputData(_inputBuffer); }
_inputBuffer 中的内容如下,httpEchoProtocol/TestHttp 即是在浏览器地址上输入的http地址。
GET /httpEchoProtocol/TestHttp HTTP/1.1 Accept: text/html, application/xhtml+xml, image/jxr, */* Accept-Language: zh-CN User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393 Accept-Encoding: gzip, deflate Host: 127.0.0.1:8989 Connection: Keep-Alive
3. _pNearProtocol 实际上是BaseHTTPProtocol. _pNearProtocol->SignalInputData实际调用是BaseHTTPProtocol::SignalInputData。
SignalInputData 调用 HandleFixedLengthContent 方法,该方法会将http内容传给具体EchoProtocol,方法内容如下:
bool BaseHTTPProtocol::HandleFixedLengthContent(IOBuffer &buffer) { //1. Compute the chunk size that we areg going to read //which is how many bytes we have available, but no more than _contentLength uint32_t chunkSize = GETAVAILABLEBYTESCOUNT(buffer); assert(_sessionDecodedBytesCount <= _contentLength); uint32_t remaining = _contentLength - _sessionDecodedBytesCount; chunkSize = chunkSize > remaining ? remaining : chunkSize; //2. Update the session decoded bytes count and decoded bytes count _sessionDecodedBytesCount += chunkSize; _decodedBytesCount += chunkSize; //3. Make the copy and ignore the chunk size _inputBuffer.ReadFromBuffer(GETIBPOINTER(buffer), chunkSize); buffer.Ignore(chunkSize); //3. Call the near protocol if (!_pNearProtocol->SignalInputData(_inputBuffer)) { FATAL("Unable to call the next protocol in stack"); return false; } //4. reset the state if necessary if (TransferCompleted()) { _headers.Reset(); _contentLength = 0; _chunkedContent = false; _lastChunk = false; _state = HTTP_STATE_HEADERS; _sessionDecodedBytesCount = 0; } //5. we are done return true; }
_pNearProtocol 实际是EchoProtocol