最近在研究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