实时传输协议(RTP)是网络通信中用于传输实时数据的一种标准协议,广泛应用于音频、视频等多媒体数据的传输。本篇文章将详细探讨RTP如何通过TCP和UDP两种不同的传输层协议来发送和接收数据。 我们了解RTP的基本概念。RTP是一种面向数据包的协议,它设计的目标是在不可靠的网络环境中尽可能地提供高质量的实时服务。RTP通常与RTCP(实时传输控制协议)一起使用,RTCP负责监控传输质量、提供错误恢复和同步信息。 **TCP(传输控制协议)** 是一种面向连接的、可靠的传输协议,它通过确认机制、重传策略和流量控制确保数据的无损传输。在RTP与TCP结合使用时,TCP提供了一种保证数据到达的机制,适合对丢包敏感的应用场景。然而,TCP的这些特性也意味着更高的延迟和更大的开销,可能会影响实时性。 当使用TCP发送RTP时,RTP数据包被封装在TCP段中。由于TCP的顺序保证,接收端可以按照正确的顺序重组RTP数据包,但这也可能导致接收端必须等待丢失的包重传,从而增加延迟。此外,TCP的拥塞控制可能导致数据流的突然减慢或暂停,这对实时应用来说可能是不利的。 **UDP(用户数据报协议)** 是一种无连接、不可靠的传输协议,它不保证数据包的顺序到达,也不处理重传或拥塞控制。尽管这可能导致数据包丢失,但UDP的低开销和快速传输使其非常适合实时应用,如语音通话和在线游戏。RTP通常首选UDP作为传输层协议,因为其低延迟和高效性。 在RTP与UDP结合时,RTP数据包直接作为UDP的数据负载发送,无需TCP的额外开销。接收端需要自己处理可能的包乱序和丢失,通常通过实现一些错误检测和恢复机制,如前向纠错(FEC)或自适应重传。 在具体实现RTP发送和接收时,开发者需要考虑以下关键点: 1. **会话建立**:RTP通常使用SDP(会话描述协议)来协商传输参数,包括IP地址、端口号、编码类型等。 2. **时间戳同步**:RTP数据包包含一个时间戳,用于同步接收端的播放。 3. **序列号**:每个RTP包都有一个序列号,用于检测丢包和重新排序乱序包。 4. **错误处理**:使用UDP时,接收端可能需要实现重传机制或FEC来应对丢包。 5. **多播与单播**:RTP支持多播和单播,多播可让一个源同时发送数据到多个接收者,降低服务器压力。 在"vs2015Project"这个压缩包文件中,可能包含了一个使用Visual Studio 2015开发的示例项目,演示了如何在C++环境中实现RTP的TCP和UDP发送及接收。通过分析和学习该项目,开发者可以深入理解这两种传输方式的差异和应用场景,并为自己的实时通信应用选择合适的传输方案
#include "Rtp.h"
Rtp::Rtp(UINT mtu, int comType) :Udp(mtu, comType)
{
m_SequenceNumber = 0;
if (comType == UDP_COM)
{
m_nRtpHeadSize = RTP_HEADER_SIZE;
}
else
{
m_nRtpHeadSize = RTP_HEADER_SIZE + 4;
}
}
Rtp::~Rtp()
{
}
int Rtp::Read(BYTE* pBuffer, UINT16 bufferSize, INT8* pPayloadType, UINT16* pSequenceNumber, INT32* pTimeStamp, INT32* pSsrc, UINT nTimeOut)
{
int iRead;
UINT8 payloadType;
UINT16 sequenceNumber;
INT32 timeStamp;
INT32 ssrc;
m_RtpPacket.ExtendBuffer(bufferSize+m_nRtpHeadSize);
m_RtpPacket.ClearData();
iRead = Udp::Read(m_RtpPacket.m_pBuffer, m_RtpPacket.m_nBufferSize, nTimeOut);
if (iRead < 12)
return -1;
if (!ParseRtpHeader(m_RtpPacket.m_pBuffer, &payloadType, &sequenceNumber, &timeStamp, &ssrc))
return -1;
m_RtpPacket.m_nDataSize = iRead;
memcpy(pBuffer, m_RtpPacket.m_pBuffer + m_nRtpHeadSize, iRead - m_nRtpHeadSize);
if (pPayloadType)
*pPayloadType = payloadType;
if (pSequenceNumber)
*pSequenceNumber = sequenceNumber;
if (pTimeStamp)
*pTimeStamp = timeStamp;
if (pSsrc)
*pSsrc = ssrc;
return iRead - m_nRtpHeadSize;
}
int Rtp::Write(PBYTE pBuffer, UINT16 bufferSize, INT8 payloadType, INT32 timeStamp, INT32 ssrc, BOOL marker, UINT nTimeOut)
{
int iWrite;
CreateRtpPacket(pBuffer, bufferSize, payloadType, timeStamp, ssrc, marker);
iWrite = Udp::Write(m_RtpPacket.m_pBuffer, m_RtpPacket.m_nDataSize, nTimeOut);
if (iWrite < m_nRtpHeadSize)
return -1;
return iWrite - m_nRtpHeadSize;
}
void Rtp::CreateRtpPacket(PBYTE pData, UINT16 dataSize, UINT8 nPayloadType, INT32 timeStamp, INT32 nSSRC, BOOL marker)
{
m_RtpPacket.ExtendBuffer(m_nRtpHeadSize + dataSize);
m_RtpPacket.ClearData();
if (m_nComType == TCP_COM)
{
m_RtpPacket.m_pBuffer[0] = 0x24;//'$';
m_RtpPacket.m_pBuffer[1] = 0;
short *intlvd_ch = (short *)&m_RtpPacket.m_pBuffer[2];
*intlvd_ch = htons((short)(dataSize + 12));
m_RtpPacket.m_pBuffer[4] = 0x80;
m_RtpPacket.m_pBuffer[5] = (marker?0x80:0x00)|nPayloadType;
m_RtpPacket.m_pBuffer[6] = ( m_SequenceNumber >> 8 )&0xff;
m_RtpPacket.m_pBuffer[7] = m_SequenceNumber&0xff;
m_RtpPacket.m_pBuffer[8] = (BYTE)( timeStamp >> 24 )&0xff;
m_RtpPacket.m_pBuffer[9] = (BYTE)( timeStamp >> 16 )&0xff;
m_RtpPacket.m_pBuffer[10] = (BYTE)( timeStamp >> 8 )&0xff;
m_RtpPacket.m_pBuffer[11] = (BYTE)timeStamp&0xff;
m_RtpPacket.m_pBuffer[12] = ( nSSRC >> 24 )&0xff;
m_RtpPacket.m_pBuffer[13] = ( nSSRC >> 16 )&0xff;
m_RtpPacket.m_pBuffer[14] = ( nSSRC >> 8 )&0xff;
m_RtpPacket.m_pBuffer[15] = nSSRC&0xff;
}
else if (m_nComType == UDP_COM)
{
m_RtpPacket.m_pBuffer[0] = 0x80;
m_RtpPacket.m_pBuffer[1] = (marker?0x80:0x00)|nPayloadType;
m_RtpPacket.m_pBuffer[2] = ( m_SequenceNumber >> 8 )&0xff;
m_RtpPacket.m_pBuffer[3] = m_SequenceNumber&0xff;
m_RtpPacket.m_pBuffer[4] = (BYTE)( timeStamp >> 24 )&0xff;
m_RtpPacket.m_pBuffer[5] = (BYTE)( timeStamp >> 16 )&0xff;
m_RtpPacket.m_pBuffer[6] = (BYTE)( timeStamp >> 8 )&0xff;
m_RtpPacket.m_pBuffer[7] = (BYTE)timeStamp&0xff;
m_RtpPacket.m_pBuffer[ 8] = ( nSSRC >> 24 )&0xff;
m_RtpPacket.m_pBuffer[ 9] = ( nSSRC >> 16 )&0xff;
m_RtpPacket.m_pBuffer[10] = ( nSSRC >> 8 )&0xff;
m_RtpPacket.m_pBuffer[11] = nSSRC&0xff;
}
m_SequenceNumber++;
m_RtpPacket.m_nDataSize = m_nRtpHeadSize;
m_RtpPacket.AppendData(pData, dataSize);
}
BOOL Rtp::ParseRtpHeader(PBYTE pRtpHeader, UINT8* pPayloadType, UINT16* pSequenceNumber, INT32* pTimeStamp, INT32* pSsrc)
{
if (pRtpHeader[0] != 0x80)
return FALSE;
*pPayloadType = pRtpHeader[1];
*pSequenceNumber = ((WORD)pRtpHeader[2]) << 8 | pRtpHeader[3];
*pTimeStamp = (INT32)pRtpHeader[4] << 24 | (INT32)pRtpHeader[5] << 16 | (INT32)pRtpHeader[6] << 8 | pRtpHeader[7];
*pSsrc = (INT32)pRtpHeader[8] << 24 | (INT32)pRtpHeader[9] << 16 | (INT32)pRtpHeader[10] << 8 | pRtpHeader[11];
return TRUE;
}
下面是头文件
#ifndef __RTP_H__
#define __RTP_H__
#include "Udp.h"
#include "VBuffer.h"
const UINT16 RTP_HEADER_SIZE = 12;
const UINT16 RTP_H263_HEADER_SIZE = 2;
class Rtp : public Udp
{
public:
Rtp(UINT mtu = 1500, int comType = TCP_COM);
virtual ~Rtp();
virtual int Read(BYTE* pBuffer, UINT16 bufferSize,
INT8* pPayloadType, UINT16* pSequenceNumber, INT32* pTimeStamp, INT32* pSsrc, UINT nTimeOut = 500000);
virtual int Write(PBYTE pBuffer, UINT16 bufferSize,
INT8 payloadType = 0, INT32 timeStamp = 0, INT32 ssrc = 0, BOOL marker = FALSE, UINT nTimeOut = 500000);
public:
void CreateRtpPacket(PBYTE pData, UINT16 dataSize, UINT8 nPayloadType, INT32 timeStamp, INT32 nSSRC, BOOL marker = FALSE);
BOOL ParseRtpHeader(PBYTE pRtpHeader, UINT8* pPayloadType, UINT16* pSequenceNumber, INT32* pTimeStamp, INT32* pSsrc);
UINT16 m_SequenceNumber;
Buffer m_RtpPacket;
int m_nRtpHeadSize;
};
#endif //__RTP_H__
1万+

被折叠的 条评论
为什么被折叠?



