前言

GB28181规范指定了RTP over TCP采用的协议格式

简单来说,就是TCP传输RTP/RTCP数据的是时候,先传输报文长度(采用两个字节保存发送)

RFC 4571: Framing Real-time Transport Protocol (RTP) and RTP Control Protocol (RTCP) Packets over Connection-Oriented Transport (rfc-editor.org)

jrtplib RTP over TCP实现代码剖析_jrtplib RTP over TCP

jrtplib RTP over TCP实现代码剖析_jrtplib RTP over TCP_02

代码实现

源码rtptcptransmitter.cpp

发送代码

int RTPTCPTransmitter::SendRTPRTCPData(const void *data, size_t len)
{
  if (!m_init)
    return ERR_RTP_TCPTRANS_NOTINIT;

  MAINMUTEX_LOCK
  
  if (!m_created)
  {
    MAINMUTEX_UNLOCK
    return ERR_RTP_TCPTRANS_NOTCREATED;
  }
  if (len > RTPTCPTRANS_MAXPACKSIZE)
  {
    MAINMUTEX_UNLOCK
    return ERR_RTP_TCPTRANS_SPECIFIEDSIZETOOBIG;
  }
  
  std::map<SocketType, SocketData>::iterator it = m_destSockets.begin();
  std::map<SocketType, SocketData>::iterator end = m_destSockets.end();

  vector<SocketType> errSockets;
  int flags = 0;
#ifdef RTP_HAVE_MSG_NOSIGNAL
  flags = MSG_NOSIGNAL;
#endif // RTP_HAVE_MSG_NOSIGNAL

  while (it != end)
  {
    uint8_t lengthBytes[2] = { (uint8_t)((len >> 8)&0xff), (uint8_t)(len&0xff) };
    SocketType sock = it->first;

    if (send(sock,(const char *)lengthBytes,2,flags) < 0 ||
      send(sock,(const char *)data,len,flags) < 0)
      errSockets.push_back(sock);
    ++it;
  }
  
  MAINMUTEX_UNLOCK

  if (errSockets.size() != 0)
  {
    for (size_t i = 0 ; i < errSockets.size() ; i++)
      OnSendError(errSockets[i]);
  }

  // Don't return an error code to avoid the poll thread exiting
  // due to one closed connection for example

  return 0;
}

接收代码

int RTPTCPTransmitter::SocketData::ProcessAvailableBytes(SocketType sock, int availLen, bool &complete, RTPMemoryManager *pMgr)
{
	JRTPLIB_UNUSED(pMgr); // possibly unused

	const int numLengthBuffer = 2;
	if (m_lengthBufferOffset < numLengthBuffer) // first we need to get the length
	{
		assert(m_pDataBuffer == 0);
		int num = numLengthBuffer-m_lengthBufferOffset;
		if (num > availLen)
			num = availLen;

		int r = 0;
		if (num > 0)
		{
			r = (int)recv(sock, (char *)(m_lengthBuffer+m_lengthBufferOffset), num, 0);
			if (r < 0)
				return ERR_RTP_TCPTRANS_ERRORINRECV;
		}

		m_lengthBufferOffset += r;
		availLen -= r;

		assert(m_lengthBufferOffset <= numLengthBuffer);
		if (m_lengthBufferOffset == numLengthBuffer) // we can constuct a length
		{
			int l = 0;
			for (int i = numLengthBuffer-1, shift = 0 ; i >= 0 ; i--, shift += 8)
				l |= ((int)m_lengthBuffer[i]) << shift;

			m_dataLength = l;
			m_dataBufferOffset = 0;

			//cout << "Expecting " << m_dataLength << " bytes" << endl;

			// avoid allocation of length 0
			if (l == 0)
				l = 1;

			// We don't yet know if it's an RTP or RTCP packet, so we'll stick to RTP
			m_pDataBuffer = RTPNew(pMgr, RTPMEM_TYPE_BUFFER_RECEIVEDRTPPACKET) uint8_t[l];
			if (m_pDataBuffer == 0)
				return ERR_RTP_OUTOFMEM;
		}
	}

	if (m_lengthBufferOffset == numLengthBuffer && m_pDataBuffer) // the last one is to make sure we didn't run out of memory
	{
		if (m_dataBufferOffset < m_dataLength)
		{
			int num = m_dataLength-m_dataBufferOffset;
			if (num > availLen)
				num = availLen;

			int r = 0;
			if (num > 0)
			{
				r = (int)recv(sock, (char *)(m_pDataBuffer+m_dataBufferOffset), num, 0);
				if (r < 0)
					return ERR_RTP_TCPTRANS_ERRORINRECV;
			}

			m_dataBufferOffset += r;
			availLen -= r;
		}

		if (m_dataBufferOffset == m_dataLength)
			complete = true;
	}
	return 0;
}

相关代码

while (it != end)
  {
    uint8_t lengthBytes[2] = { (uint8_t)((len >> 8)&0xff), (uint8_t)(len&0xff) };
    SocketType sock = it->first;

    if (send(sock,(const char *)lengthBytes,2,flags) < 0 ||
      send(sock,(const char *)data,len,flags) < 0)
      errSockets.push_back(sock);
    ++it;
  }

代码分析

外层循环,遍历每一个目标主机的套接字准备发送数据,先构建准备发送数据包长度的缓存lengthBytes,先发送两个字节长度的缓存,然后才真正发送真实的数据包,符合RFC4571标准

抓包分析

jrtplib RTP over TCP实现代码剖析_jrtplib RTP over TCP_03

jrtplib RTP over TCP实现代码剖析_jrtplib RTP over TCP_04


0030   20 00 2b 5b 00 00 80 60 0d 99 04 e6 47 07 88 23    .+[...`....G..#

0040   8e b5 00 00 01 ba 44 00 04 04 e4 01 00 03 ff f8   ......D.........

0050   04 cf 80 60 0d 9a 04 e6 55 17 88 23 8e b5 00 00   ...`....U..#....

0060   01 e0 04 bd 80 c0 0a 31 00 03 6d a1 11 00 03 6d   .......1..m....m



参考例子

JRTPLIB/tcptest.cpp at master · j0r1/JRTPLIB · GitHub