从H264到RTP

1 h264有两种封装 :annexb and mp4

一种是annexb模式,传统模式,有startcode,SPS和PPS是在ES中。
一种是mp4模式,一般mp4 mkv会有,没有startcode,SPS和PPS以及其它信息被封装在container中,每一个frame前面是这个frame的长度

很多解码器只支持annexb这种模式,因此需要将mp4做转换:
在ffmpeg中用h264_mp4toannexb_filter可以做转换
实现:
注册filter
avcbsfc = av_bitstream_filter_init(“h264_mp4toannexb”);
转换bitstream
av_bitstream_filter_filter(AVBitStreamFilterContext *bsfc,
AVCodecContext *avctx, const char *args,
uint8_t **poutbuf, int *poutbuf_size,
const uint8_t *buf, int buf_size, int keyframe)

2 ffmpeg 从mp4上提取H264的nalu

1.获取数据
ffmpeg读取mp4中的H264数据,并不能直接得到NALU,文件中也没有储存0x00000001的分隔符。下面这张图为packet.data中的数据

从图中可以发现,packet中的数据起始处没有分隔符(0x00000001), 也不是0x65、0x67、0x68、0x41等字节,所以可以肯定这不是标准的nalu。其实,前4个字0x000032ce表示的是nalu的长度,从第5个字节开始才是nalu的数据。所以直接将前4个字节替换为0x00000001即可得到标准的nalu数据。

2.获取pps及sps

pps及sps不能从packet获得,而是保存在AVCodecContext的extradata数据域中。如下:

如何从extradata中解析出sps及pps呢?ffmpeg中提供了一个流过滤器”h264_mp4toannexb”完成这项工作,关键代码如下
[cpp] view plaincopyprint?
1.//h264_mp4toannexb_bsf.c
2.static int h264_mp4toannexb_filter(AVBitStreamFilterContext *bsfc,
3. AVCodecContext *avctx, const char *args,
4. uint8_t **poutbuf, int *poutbuf_size,
5. const uint8_t *buf, int buf_size,
6. int keyframe) {
7. H264BSFContext *ctx = bsfc->priv_data;
8. uint8_t unit_type;
9. int32_t nal_size;
10. uint32_t cumul_size = 0;
11. const uint8_t *buf_end = buf + buf_size;
12.
13.
14. /* nothing to filter */
15. if (!avctx->extradata || avctx->extradata_size < 6) {
16. poutbuf = (uint8_t) buf;
17. *poutbuf_size = buf_size;
18. return 0;
19. }
20.
21. //
22. //从extradata中分析出SPS、PPS
23. //
24. /* retrieve sps and pps NAL units from extradata */
25. if (!ctx->extradata_parsed) {
26. uint16_t unit_size;
27. uint64_t total_size = 0;
28. uint8_t *out = NULL, unit_nb, sps_done = 0, sps_seen = 0, pps_seen = 0;
29. const uint8_t *extradata = avctx->extradata+4; //跳过前4个字节
30. static const uint8_t nalu_header[4] = {0, 0, 0, 1};
31.
32.
33. /* retrieve length coded size */
34. ctx->length_size = (*extradata++ & 0x3) + 1; //用于指示表示编码数据长度所需字节数
35. if (ctx->length_size == 3)
36. return AVERROR(EINVAL);
37.
38.
39. /* retrieve sps and pps unit(s) */
40. unit_nb = extradata++ & 0x1f; / number of sps unit(s) */
41. if (!unit_nb) {
42. goto pps;
43. } else {
44. sps_seen = 1;
45. }
46.
47.
48. while (unit_nb–) {
49. void *tmp;
50.
51.
52. unit_size = AV_RB16(extradata);
53. total_size += unit_size+4;
54. if (total_size > INT_MAX - FF_INPUT_BUFFER_PADDING_SIZE ||
55. extradata+2+unit_size > avctx->extradata+avctx->extradata_size) {
56. av_free(out);
57. return AVERROR(EINVAL);
58. }
59. tmp = av_realloc(out, total_size + FF_INPUT_BUFFER_PADDING_SIZE);
60. if (!tmp) {
61. av_free(out);
62. return AVERROR(ENOMEM);
63. }
64. out = tmp;
65. memcpy(out+total_size-unit_size-4, nalu_header, 4);
66. memcpy(out+total_size-unit_size, extradata+2, unit_size);
67. extradata += 2+unit_size;
68.pps:
69. if (!unit_nb && !sps_done++) {
70. unit_nb = extradata++; / number of pps unit(s) */
71. if (unit_nb)
72. pps_seen = 1;
73. }
74. }
75.
76.
77. if(out)
78. memset(out + total_size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
79.
80.
81. if (!sps_seen)
82. av_log(avctx, AV_LOG_WARNING, “Warning: SPS NALU missing or invalid. The resulting stream may not play.\n”);
83. if (!pps_seen)
84. av_log(avctx, AV_LOG_WARNING, “Warning: PPS NALU missing or invalid. The resulting stream may not play.\n”);
85.
86.
87. av_free(avctx->extradata);
88. avctx->extradata = out;
89. avctx->extradata_size = total_size;
90. ctx->first_idr = 1;
91. ctx->extradata_parsed = 1;
92. }
93.
94.
95. *poutbuf_size = 0;
96. *poutbuf = NULL;
97. do {
98. if (buf + ctx->length_size > buf_end)
99. goto fail; //buf为NULL时,以下代码将不再执行
100.
101.
102. //
103. //用于保存数据长度的字节数,是在分析原extradata计算出来的
104. //
105. if (ctx->length_size == 1) {
106. nal_size = buf[0];
107. } else if (ctx->length_size == 2) {
108. nal_size = AV_RB16(buf);
109. } else
110. nal_size = AV_RB32(buf);
111.
112.
113. buf += ctx->length_size;
114. unit_type = *buf & 0x1f;
115.
116.
117. if (buf + nal_size > buf_end || nal_size < 0)
118. goto fail;
119.
120.
121. /* prepend only to the first type 5 NAL unit of an IDR picture */
122. if (ctx->first_idr && unit_type == 5) {
123. //
124. //copy IDR 帧时,需要将sps及pps一同拷贝
125. //
126. if (alloc_and_copy(poutbuf, poutbuf_size,
127. avctx->extradata, avctx->extradata_size,
128. buf, nal_size) < 0)
129. goto fail;
130. ctx->first_idr = 0;
131. } else {
132. //
133. //非IDR帧,没有sps及pps
134. if (alloc_and_copy(poutbuf, poutbuf_size,
135. NULL, 0,
136. buf, nal_size) < 0)
137. goto fail;
138. if (!ctx->first_idr && unit_type == 1)
139. ctx->first_idr = 1;
140. }
141.
142.
143. buf += nal_size;
144. cumul_size += nal_size + ctx->length_size;
145. } while (cumul_size < buf_size);
146.
147.
148. return 1;
149.
150.
151.fail:
152. av_freep(poutbuf);
153. *poutbuf_size = 0;
154. return AVERROR(EINVAL);
155.}

一般情况下,extradata中包含一个sps、一个pps 的nalu, 从上面的代码中容易看出extradata的数据格式。分析后的sps及pps依然储存在extradata域中,并添加了起始符。从代码中还可以看出,上面的函数会将sps、pps及packet中的数据,都copy到poutbuf指示的内存中,如果不需要copy到指定内存,直接给buf参数传入空值即可。

3.使用ffmpeg的流过滤器获取sps及pps
流过滤器”h264_mp4toannexb”, 在av_register_all()函数中会被注册。用法示例如下:
[cpp] view plaincopyprint?
1.int ParseH264ExtraDataInMp4(int stream_id)
2.{
3. uint8_t *dummy = NULL;
4. int dummy_size;
5. AVBitStreamFilterContext* bsfc = av_bitstream_filter_init(“h264_mp4toannexb”);
6.
7.
8. if(bsfc == NULL)
9. {
10. return -1;
11. }
12.
13.
14. av_bitstream_filter_filter(
15. bsfc, format_ctx_->streams[stream_id]->codec, NULL, &dummy, &dummy_size, NULL, 0, 0);
[cpp] view plaincopyprint?
1. av_bitstream_filter_close(bsfc);
2.
3.
4. return 0;
5.}

3 H264TS AnnexB —NAUL

AnnexB格式:NALU数据+开始前缀(00000001或000001,此处注意为甚么是4bit或3bit,后面有描述);针对H.320电话会议
RTP 格式:NALU数据+20个字节的类似的并不符合RTP协议的RTP头。针对IP网络的RTP打包方式
H.264协议只规定了字节流格式,没有规定 RTP 格式, 实际包交换网络中必须按照 RFC3984 将 NALU 数据封装为 RTP 包,而不能使用 JM 的 RTP 格式。
3.1 h264 纵览
NALU:Coded H.264 data is stored or transmitted as a series of packets known as NetworkAbstraction LayerUnits. (NALU单元)
RBSP :A NALU contains a Raw Byte Sequence Payload, a sequence of bytes containingsyntax elements.(原始数据字节流)
SODB:String Of Data Bits (原始数据比特流, 长度不一定是8的倍数,故需要补齐)

  逻辑关系:

SODB + RBSP trailing bits = RBSP
NAL header(1 byte) + RBSP = NALU
Start Code Prefix(3 bytes) + NALU + Start Code Prefix(3 bytes) + NALU + …+ = H.264BitsStream

即: h264的功能分为两层,视频编码层(VCL)和网络提取层(NAL)
VCL数据即被压缩编码后的视频数据序列。在VCL数据要封装到NAL单元中之后,才可以用来传输或存储。NAL单元格式如下图:

Nal头 EBSP Nal头 EBSP Nal头 EBSP

 说明:
1. SODB即编码形成的真实码流(VCL层 ),为了使一个RBSP为整字节数,需要加trailing bits, 具体加的方法可以看JM8.6中的SODBtoRBSP函数.

[cpp] view plain copy
1.void SODBtoRBSP(Bitstream *currStream)
2.{
3. currStream->byte_buf <<= 1;
4. currStream->byte_buf |= 1;
5. currStream->bits_to_go–;
6. currStream->byte_buf <<= currStream->bits_to_go;
7. currStream->streamBuffer[currStream->byte_pos++] = currStream->byte_buf;
8. currStream->bits_to_go = 8;
9. currStream->byte_buf = 0;
10.}

  2. NALU header为一个字节,这8个比特分别对应forbidden_zero_bit, nal_ref_idc, nal_unit_type. NALU的body其实就是RBSP. 由RBSP转NALU是由RBSPtoNALU函数来实现的.

[cpp] view plain copy
1.typedef struct
2.{
3. int startcodeprefix_len; //! 4 for parameter sets and first slice in picture, 3 for everything else (suggested)
4. unsigned len; //! Length of the NAL unit (Excluding the start code, which does not belong to the NALU)
5. unsigned max_size; //! Nal Unit Buffer size
6. int nal_unit_type; //! NALU_TYPE_xxxx
7. int nal_reference_idc; //! NALU_PRIORITY_xxxx
8. int forbidden_bit; //! should be always FALSE
9. byte *buf; //! conjtains the first byte followed by the EBSP
10.} NALU_t;
[cpp] view plain copy
1.int RBSPtoNALU (char *rbsp, NALU_t *nalu, int rbsp_size, int nal_unit_type, int nal_reference_idc,
2. int min_num_bytes, int UseAnnexbLongStartcode)
3.{
4. int len;
5.
6. // 断言,以后要学会用assert进行断言,很重要滴.
7. assert (nalu != NULL);
8. assert (nal_reference_idc <=3 && nal_reference_idc >=0);
9. assert (nal_unit_type > 0 && nal_unit_type <= 10);
10. assert (rbsp_size < MAXRBSPSIZE);
11.
12. // 下面这个是必须的,所以不需要通过参数传进来
13. nalu->forbidden_bit = 0;
14. // 下面两个通过参数传进来
15. nalu->nal_reference_idc = nal_reference_idc;
16. nalu->nal_unit_type = nal_unit_type;
17.
18. // 判断是否在Start Code Prefix前面加Ox00
19. nalu->startcodeprefix_len = UseAnnexbLongStartcode?4:3;
20.
21. // 对nalu->buf[i]进行赋值
22. nalu->buf[0] =
23. nalu->forbidden_bit << 7 |
24. nalu->nal_reference_idc << 5 |
25. nalu->nal_unit_type;
26. memcpy (&nalu->buf[1], rbsp, rbsp_size);
27.
28.// printf (“First Byte %x\n”, nalu->buf[0]);
29.// printf (“RBSPtoNALU: Before: NALU len %d\t RBSP %x %x %x %x\n”, rbsp_size, (unsigned) nalu->buf[1], (unsigned) nalu->buf[2], (unsigned) nalu->buf[3], (unsigned) nalu->buf[4]);
30.
31. len = 1 + (&nalu->buf[1], 0, rbsp_size, min_num_bytes);
32.
33.// printf (“RBSPtoNALU: After : NALU len %d\t EBSP %x %x %x %x\n”, rbsp_size, (unsigned) nalu->buf[1], (unsigned) nalu->buf[2], (unsigned) nalu->buf[3], (unsigned) nalu->buf[4]);
34.// printf (“len %d\n\n”, len);
35. nalu->len = len;
36.
37. return len;
38.}

  3. Start Code Prefix为3个字节. 但是,为了寻址方便,要求数据流在长度上对齐,因此H.264建议在Start Code Prefix前面加若干个0.    
  4. 为了简便起见,上面的逻辑关系图没有考虑"防止竞争"机制.

3.2 NAL的3字节起始码和4字节起始码
4字节起始码本质是(zero_byte + 3字节起始码):
if( next_bits( 24 ) != 0x000001 )
zero_byte f(8)
start_code_prefix_one_3bytes f(24)
}

总之:一共有两种起始码:3字节的0x000001和4字节的0x00000001
字节的0x000001只有一种场合下使用,就是一个完整的帧被编为多个slice的时候,包含这些slice的nalu使用3字节起始码。其余场合(在SPS、PPS和7.4.1.2.3规定的Access Unit的首个nalu): 都是4字节的。

3.3 NAL 层
NAL(Network Abstraction Layer)基本上可分两种:1,以有序字节流方式传送的针对H.320的;2,针对IP网络的RTP打包方式的。

3.4 从RBSP 到EBSP —脱壳操作
编码过程中:若遇到连续的两个字节的0,就在后面插入一个字节0x03,以和NAL的开始码(0x000000 或 0x0000001)相区别。

3.5 H264 码流结构

3.6 解析过程实例
1、码流总体结构:
h264的功能分为两层,视频编码层(VCL)和网络提取层(NAL)。H.264 的编码视频序列包括一系列的NAL 单元,每个NAL 单元包含一个RBSP。一个原始的H.264 NALU 单元常由 [StartCode] [NALU Header] [NALU Payload] 三部分组成,其中 Start Code 用于标示这是一个NALU 单元的开始,必须是”00 00 00 01” 或”00 00 01”。

其中RBPS有分为几种类型:

NAL的解码单元的流程如下:

2、 NAL Header:
占一个字节,由三部分组成forbidden_bit(1bit),nal_reference_bit(2bits)(优先级),nal_unit_type(5bits)(类型)。
forbidden_bit:禁止位。
nal_reference_bit:当前NAL的优先级,值越大,该NAL越重要。
nal_unit_type :NAL类型。参见下表

#define NALU_TYPE_SLICE 1

define NALU_TYPE_DPA 2

define NALU_TYPE_DPB 3

define NALU_TYPE_DPC 4

define NALU_TYPE_IDR 5

define NALU_TYPE_SEI 6

define NALU_TYPE_SPS 7

define NALU_TYPE_PPS 8

define NALU_TYPE_AUD 9

define NALU_TYPE_EOSEQ 10

define NALU_TYPE_EOSTREAM 11

define NALU_TYPE_FILL 12

1.typedef struct {
2. //byte 0
3. unsigned char TYPE:5;
4. unsigned char NRI:2;
5. unsigned char F:1;
6.
7.} NALU_HEADER; /*// 1 BYTES */

几个例子:

NALU类型,将其转为二进制数据后,解读顺序为从左往右算,如下:
(1)第1位禁止位,值为1表示语法出错
(2)第2~3位为参考级别
(3)第4~8为是nal单元类型
NAL分为VCL的NAL单元和非VCL的NAL单元。
一个以IDR Access Unit开始的Coded Sequence,由一个或多个IDR Slices组成,每一个都是Intra Coded Slice。然后紧接着就是非IDR Slice或分割Slice。非VCL的NAL单元包括PSP、SEI等,这些参数对解码和显示视频都是很有用的。
1、A、B和C类数据分割
组成Slice的Coded Data被存放在三种数据分割中,A、B和C类数据分割。每种分割包含Coded Slice的一个子集。每种分割被单独的存放在一个NAL单元中,因此可以被单独传输。
A类数据分割:包含Slice的头和每个宏块的头;
B类数据分割:包含Intra和SI Slice宏块的被编码的Residual数据;
C类数据分割:包含P、B和SP Slice宏块的被编码的Residual数据。
如果A类数据分割丢失,很难甚至不可能重构其所在的Slice。三类数据分割在对错误的容忍度方面相比,A最不能容忍错误的出现,而C的容错能力最强。在一个容易出错的环境,提高性能的策略包括:对这三种数据分割应用非均匀错误保护。例如,对A类数据分割使用向前纠错,通过不同的通道传输不同类型的数据分割,或者给A类数据分割选择一个最可靠的通道。
2、SEI
SEI(Supplemental Enhancement Information),补充增强信息,这部分参数可以作为H.264的比特流数据而被传输,每一个SEI信息被封装成一个NAL单元。SEI对于解码器来说可能是有用的,但是对于基本的解码过程来说,并不是必须的。
3、PPS和SPS
Parameter Sets是携带解码参数的NAL Units。发送这些参数采用独立于Coded Slices的方式能够提高效率,因为共用的参数仅仅需要传输一次。Parameter Sets对于正确的解码是非常重要的。在一个有损耗的传输场景,在传输过程中比特流或包可能丢失或损坏。在这种网络环境中,Parameter Sets可以通过高质量的服务来发送,比如向前纠错机制或优先级机制。
Parameter Sets与其之外的句法元素之间的关系如下图所示:

SPS(Sequence Parameter Set)包含一些通用的参数,比如Profile和Level,比如视频帧的尺寸,参考帧的最大数量等,这些参数对整个Video Sequence或者Programme都是通用的。PPS(Picture Parameter Set)包含一些通用的参数,比如熵编码类型,有效的参考图像的数目和初始化参数等,这些参数可以应用到一个Video Sequence或者一部分编码帧。
一个Parameter Set在开始的时候是不活跃的,直到被激活。一个PPS被预先传到解码器,当在一个Slice Header中涉及到的时候,就会被激活,而且直到一个不同的PPS被激活。对于SPS,当一个PPS涉及到它的时候,就会被激活。对于一个以IDR Access Unit开始的Coded Video Sequence,在整个过程中,一个SPS会一直处于活跃状态。因此,一个SPS可以有效的被IDR Slice激活。
4、帧结束符序列结束符和流结束符
帧结束符 (NALU_TYPE_AUD):访问单元分隔符,表示一个帧数据的结束
序列结束符表明下一个Access Unit是IDR,比如一个新的Coded Video Sequence的开始。
流结束符表明比特流的结束。

3、 ffmpeg解析H264流程分析
这是一段实际的码流

在上面的图片中,共有三个起始码:0x00 0000 01
const uint8_t*ff_h264_decode_nal(H264Context*h, const uint8_t *src,int *dst_length, int*consumed, int length)中分析过程为:
h->nal_ref_idc= src[0] >> 5;
h->nal_unit_type= src[0] & 0x1F;
此处src[0]即为06,写成二进制位0000 0110,则h->nal_ref_idc = 0,h->nal_unit_type = 6
可以判断这个NALU类型为SEI,重要性优先级为0。
src++;src向后移动一个位置,此时src指向图中第一行第五列的数据05
length–;未处理数据长度减1

defineSTARTCODE_TEST \

   if(i + 2 < length && src[i + 1] == 0 && src[i + 2]<= 3){     \
       if(src[i + 2] != 3){                                     \
           /* startcode, so we must bepast the end*/             \
           length =i;                                            \
       }                                                          \
       break;                                                     \
   }

for(i = 0; i + 1 < length; i += 2) {
if(src[i])
continue;
if(i > 0 && src[i - 1] == 0)
i–;
STARTCODE_TEST;
}
上述分析:
1)如果src[i] !=0 ,则继续下一次循环,直到src[i] == 0,即等于下一个起始码的第二个00为止,即图中地址为000001a0h行的第3列,地址为0x00 00 01 a3(十进制为419),此时i为414,因为是从0x00 00 00 05地址开始的,此时则第一次执行continue下面的if语句,而且src[i-1]==0,则i–,此时i=413
2)执行宏定义STARTCODE_TEST,进行起始码检测:src[i+ 1] =src[414]=0,src[i+2]=src[415]=0;
继续执行,src[i+2] != 3,这里是进行竞争检测(前面有讲述,如果在编码的过程中出现连续00 00 00时,将会在第三个00前插入一个03,避免和起始码造成冲突),既然没有发生竞争现象,则表明这个确实是下一个NALU的起始码
3)length是指当前NALU单元长度,这里不包括nalu头信息长度,即一个字节,
length = i =413
后面的SEI解析不再赘述
(2)第二个 0x00 00 00 01:

h->nal_ref_idc= src[0] >> 5;
h->nal_unit_type= src[0] & 0x1F;
此时src[0]是指起始码后面第一个数据67(地址为0x00 00 01a6h),写成二进制为0110 0111,则h->nal_unit_type = 7,h->nal_ref_idc =3
则这一个NALU单元类型为SPS
下面开始分析SPS的长度,这一段数据很巧,包含了竞争检测
依然是下面这段代码:
src++;src向后移动一个位置,此时src指向图中地址为00 0001 a7h的数据
length–;未处理数据长度减1

defineSTARTCODE_TEST \

   if(i + 2 < length && src[i + 1] == 0 && src[i + 2]<= 3){     \
       if(src[i + 2] != 3){                                     \
           /* startcode, so we must bepast the end*/             \
           length =i;                                            \
       }                                                          \
       break;                                                     \
   }

for(i = 0; i + 1 < length; i += 2) {
if(src[i])
continue;
if(i > 0 && src[i - 1] == 0)
i–;
STARTCODE_TEST;
}
分析:
1)自地址0x00 00 01 a7h开始查找src[i] ==0,此时src[i]的地址为0x00 00 01 b1h,此时src[i]==0,而src[i-1]=80 != 0,此时i = 10 ;
2)执行宏定义STARTCODE_TEST:if(i+2

define AV_TIME_BASE 1000000

1秒 = 1000 毫秒 = 1000000 (6个0)AV_TIME_BASE(微秒) = 10000000(7个0)nano =1000000000(9个)纳秒

DTS和PTS是音视频同步的关键技术,同时也是丢帧策略密切相关。
dts/pts定义 dts: decoding time stamp pts: present time stamp 在ISO/IEC13818-1中制定90k Hz 的时钟,如果编码帧频是30,那么时间戳间隔就该是90000 / 30 = 3000。 在FFMPEG中有三种时间单位:秒、微秒和dts/pts。从dts/pts转化为微秒公式:
dts* AV_TIME_BASE/ denominator
其中AV_TIME_BASE为1,000,000,denominator为90,000

3 名词与理解
RTP负载(RTP payload):通过RTP传输的包中的数据,例如,音频样本或压缩好的视频数据。负载格式与解释不在本文讨论范围。
RTP包(RTP packet):一种数据包,其组成部分有:一个固定RTP报头,一个可能为空的作用源(contributing sources)列表(见下文),以及负载数据。一些下层协议可能要求对RTP包的封装进行定义。一般地,下层协议的一个包包含一个RTP包,但若封装方法允许,也可包含数个RTP包(见章节11)。
RTCP包(RTCP packet):一种控制包,其组成部分有:一个类似RTP包的固定报头,后跟一个结构化的部分,该部分具体元素依不同RTCP包的类型而定。格式的定义见章节6。一般地,多个RTCP包将在一个下层协议的包中以合成RTCP包的形式传输;这依靠RTCP包的固定报头中的长度字段来实现。
端口(Port):“传输协议用来在同一主机中区分不同目的地的一种抽象。TCP/IP协议使用正整数来标识不同端口。”[12] OSI传输层使用的传输选择器(TSEL,the transport selectors)等同于这里的端口。RTP需依靠低层协议提供的多种机制,如“端口”用以多路复用会话中的RTP和RTCP包。
传输地址(Transport address):是网络地址与端口的结合,用来标识一个传输层次的终端,例如一个IP地址与一个UDP端口。包是从源传输地址发送到目的传输地址。
RTP媒体类型(RTP media type):一个RTP媒体类型是一个单独RTP会话所载有的负载类型的集合。RTP配置文件把RTP媒体类型指派给RTP负载类型。
多媒体会话(Multimedia session):在一个参与者公共组中,并发的RTP会话的集合。例如,一个视频会议(为多媒体会话)可能包含一个音频RTP会话和一个视频RTP会话。
RTP会话(RTP session):一群参与者通过RTP进行通信时所产生的关联。一个参与者可能同时参与多个RTP会话。在一个多媒体会话中,除非编码方式把多种媒体多路复用到一个单一数据流中,否则每种媒体都将使用各自的RTCP包,通过单独的RTP会话来传送。通过使用不同的目的传输地址对(一个网络地址加上一对分别用于RTP和RTCP的端口,构成了一个传输地址对)来接收不同的会话,参与者能把多个RTP会话区隔开来。单个RTP会话中的所有参与者,可能共享一个公用目的传输地址对,比如IP多播的情况;也可能各自使用不同的目的传输地址对,比如个体单播网络地址加上一个端口对。对于单播的情况,参与者可能使用相同端口对来收听其他所有参与者,也可能对来其他每个参与者使用不同的端口对来收听。
RTP会话间相互区别的特征,在于每个RTP会话都维护一个用于SSRC标识符的独立完整的空间。RTP会话所包含的参与者组,由能接收SSRC标识符的参与者组成,这些SSRC标识符由RTP(同步源或作用源)或RTCP中的任意参与者传递。例如,考虑下述情况,用单播UDP实现的三方会议,每方都用不同的端口对来收听其他两方。如果收到一方的数据,就只把RTCP反馈发送给那一方,则会议就相当于由三个单独的点到点RTP会话构成;如果收到一方的数据,却把RTCP反馈发送另两方,则会议就是由一个多方(multi-party)RTP会话构成。后者模拟了三方间进行IP多播通信时的行为。
R TP框架允许上述规定发生变化,但一个特定的控制协议或者应用程序在设计时常常对变化作出约束。
同步源(SSRC,Synchronization source):RTP包流的源,用RTP报头中32位数值的SSRC标识符进行标识,使其不依赖于网络地址。一个同步源的所有包构成了相同计时和序列号空间的一部分,这样接收方就可以把一个同步源的包放在一起,来进行重放。举些同步源的例子,像来自同一信号源的包流的发送方,如麦克风、摄影机、RTP混频器(见下文)就是同步源。一个同步源可能随着时间变化而改变其数据格式,如音频编码。SSRC标识符是一个随机选取的值,它在特定的RTP会话中是全局唯一(globally unique)的(见章节8)。参与者并不需要在一个多媒体会议的所有RTP会话中,使用相同的SSRC标识符;SSRC标识符的绑定通过RTCP(见章节6.5.1)。如果参与者在一个RTP会话中生成了多个流,例如来自多个摄影机,则每个摄影机都必须标识成单独的同步源。
作用源(CSRC,Contributing source ):若一个RTP包流的源,对由RTP混频器生成的组合流起了作用,则它就是一个作用源。对特定包的生成起作用的源,其SSRC标识符组成的列表,被混频器插入到包的RTP报头中。这个列表叫做CSRC表。相关应用的例子如,在音频会议中,混频器向所有的说话人(talker)指出,谁的话语(speech)将被组合到即将发出的包中,即便所有的包都包含在同一个(混频器的)SSRC标识符中,也可让听者(接收者)可以清楚谁是当前说话人。  
   终端系统(End system):一种应用程序,它产生发送出的RTP包中内容,或者使用接收到的RTP包中内容。在一个特定的RTP会话中,一个终端系统可以扮演一个或多个同步源角色,但通常是一个。
混频器(Mixer):一种中间系统,它从一个或多个源中接收RTP包,可能改变其数据格式,再按某种方式把这些包组合成一个新的包,然后转发出去。由于多个输入源的计时一般不会同步,所以混频器会对各个流的计时作出调整,并为组合流生成一个新的计时。因此,混频器将被标识成它所产生所有数据包的同步源。
转换器(Translator):一种中间系统,它转发RTP包而不改变各包的同步源标识符。转换器的例子如下:不作混频地转变编码的设备,把多播复制到单播的重复装置,以及防火墙里应用层次的过滤器。
监视器(Monitor):一种应用程序,它接收RTP会话参与者所发送的RTCP包,特别是接收报告(reception report),而且对当前服务质量进行评估,评估结果用于分配监视任务,故障诊断和长期统计。监视器常常被内建到参与会话的应用程序中,但也可以是一个的独立的应用程序——不参加会话、也不发送或接收RTP数据包(因为它们在不同的端口上)。这些被称作第三方监视器。还有一种情况也是可以接受的,第三方监视器只接收但不发送数据包,或者另外地算入到会话中。
   非RTP途径(Non-RTP means):为提供一个可用的服务,可能还需要其他的协议和机制。特别地,对多媒体会议来说,一个控制协议可以发布多播地址,发布加密密钥,协商所用的加密算法,以及为没有预定义负载类型值的格式,建立负载类型值和其所代表的负载格式之间的动态映射。

5 RTP数据传输协议
5.1 RTP固定头中的各字段,RTP头有以下格式:

0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X| CC |M| PT | sequence number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identifier |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| contributing source (CSRC) identifiers |
| …. |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   前12个字节出现在每个RTP包中,仅仅在被混合器插入时,才出现CSRC识别符列表。这些域有以下意义:
   版本(V):2比特          此域定义了RTP的版本。此协议定义的版本是2。(值1被RTP草案版本使用,值0用在最初"vat"语音工具使用的协议中。)
   填充(P):1比特          若填料比特被设置,则此包包含一到多个附加在末端的填充比特,填充比特不算作负载的一部分。填充的最后一个字节指明可以忽略多少个填充比特。填充可能用于某些具有固定长度的加密算法,或者用于在底层数据单元中传输多个RTP包。
   扩展(X):1比特          若设置扩展比特,固定头(仅)后面跟随一个头扩展。
   CSRC计数(CC):4比特    CSRC计数包含了跟在固定头后面CSRC识别符的数目。
  标志(M):1比特   标志的解释由具体协议规定。它用来允许在比特流中标记重要的事件,如帧边界。
  负载类型(PT):7比特     此域定义了负载的格式,由具体应用决定其解释。协议可以规定负载类型码和负载格式之间一个默认的匹配。其他的负载类型码可以通过非RTP方法动态定义。RTP发送端在任意给定时间发出一个单独的RTP负载类型;此域不用来复用不同的媒体流。

序列号(sequence number):16比特 每发送一个RTP数据包,序列号加1,接收端可以据此检测丢包和重建包序列。序列号的初始值是随机的(不可预测),以使即便在源本身不加密时(有时包要通过翻译器,它会这样做),对加密算法泛知的普通文本攻击也会更加困难。
时间戳(timestamp):32比特 时间戳反映了RTP数据包中第一个字节的采样时间。(采样时钟必须来源于一个及时的单调、线性递增时钟,以便允许同步和去除网络引起的数据包抖动(见章节6.4.1)。该时钟的分辨率必须满足理想的同步精度和测量数据包到来时的抖动的需要(一种典型的时钟分辨率不满足情况是每个视频帧仅一个时钟周期)时钟频率依赖于负载数据的格式,并在描述文件(profile)中或者是在负载格式描述中(payload format speci_cation)进行静态描述。也可以通过非RTP方法(non-RTP means)对负载格式动态描述。
如果RTP包是周期性产生的,那么将使用由采样时钟决定的名义上的采样时刻,而不是读取系统时间。例如,对一个固定速率的音频,采样时钟(时间戳时钟)将在每个周期内增加1。如果一个音频从输入设备中读取含有160个采样周期的块,那么对每个块,时间戳的值增加160,而不考虑该块是否用一个包传递或是被丢弃。(Example:sess.SetDefaultTimestampIncrement(160);)
时间戳的初始值应当是随机的,就像序号一样。几个连续的RTP包如果(逻辑上)是同时产生的,如:属于同一个视频帧的RTP包,将有相同的序列号。如果数据并不是以它采样的顺序进行传输,那么连续的RTP包可以包含不是单调递增(或递减)的时间戳(RTP包的序列号仍然是单调变化的)。
不同媒体流的RTP时间戳可能以不同的速率增长。而且会有独立的随机偏移量。因此,虽然这些时间戳足以重构一个单独的流的时序,但直接比较不同的媒体流的时间戳不能有效的进行同步。对于每一个媒体,我们把与采样时刻相关联的RTP时间戳与来自于参考时钟上的时间戳(NTP)相关联(相反,对于每一种媒体,RTP时间戳通过与一个参考时间戳wallclock配对的方法来与采样时刻联系起来。)。参考时钟的时间戳表示了(对应于RTP时间戳的)数据的采样时刻。(即:RTP时间戳可用来实现不同媒体流的同步,NTP时间戳解决了RTP时间戳有随机偏移量的问题。)参考时钟用于同步所有媒体的共同时间(参考时间戳为所有媒体所公用,以此来实现同步)。这一时间戳对(RTP时间戳和NTP时间戳),用于判断RTP时间戳和NTP时间戳的对应关系,以进行媒体流的同步。它们不是在每一个数据包中都被发送,而在发送速率更低的RTCP的SR(发送者报告)中。即RTP时间戳记录当前是第几个采样数据,NTP时间戳记录当前数据包相对于参考时钟(1900年1月1日0点算起,当前时间相对于该时刻所经过的秒数)的绝对时间。
选取采样时间作为RTP时间戳的参考点是因为它可以被传输的终节点获知,而且对所有媒体内容有一个相同的定义,再者是它不受编码延迟或其它数据处理的约束。目的是为了对所有在同一时刻采样的媒体进行同步。
如果传输的数据是存贮好的,而不是实时采样得到的,那么会使用从参考时钟得到的虚的表示时间线(virtual presentation timeline虚拟表示时间表)以确定存贮数据中的每个媒体下一帧或下一个单元应该呈现的时间(还是时刻???)。此种情况下RTP时间戳反映了每一个单元应当回放的时间(时刻???)。这就是说,每个单元的RTP时间戳将会和参考时钟相关。真正的回放将由接收者决定。
一个描述了关于预先录制好的视频的现场音频旁白的例子说明了采用采样时间作为参考点的重要性。在这种情况下,视频会在本地提交给解说员观看,同时会通过RTP包向外界传输。通过RTP传输的一帧视频的“采样时刻”将会被建立,建立的方法是在该帧视频提交给解说员的时刻,将RTP包的时间戳与参考时钟比较算出而建立(即当前的采样序号结合绝对时间得出“采样时刻”)。包含有解说员解说的的音频RTP包,它的采样时刻将会在音频被采样的时刻,通过与同一参考时钟相比较而建立。音频和视频甚至可以被不同的主机传输只要这两个主机的参考时钟通过比如NTP之类的途径实现了同步。接受者能够使用RTCP SR包中的时间戳对从而将音频和视频同步。
SSRC:32比特 用以识别同步源。标识符应该被随机生成,以使在同一个RTP会话期中没有任何两个同步源有相同的SSRC识别符。一个用于创造随机标示符的示例算法在A.6第六章讲述。尽管多个源选择同一个SSRC识别符的概率很低,所有RTP实现工具都必须准备检测和解决冲突。第8章讲述了冲突的可能性及其解决机制。若一个源改变本身的源传输地址,必须选择新的SSRC识别符,以避免被当作一个环路源。
CSRC列表:0到15项,每项32比特 CSRC列表指出了对此包中负载内容的所有贡献源。识别符的数目在CC域中给定。若有贡献源多于15个,仅识别15个。CSRC识别符由混合器插入(see Section 7.1),并列出所有贡献源的SSRC识别符。例如语音包,混合产生新包的所有源的SSRC标识符都被列出,以在接收端处正确指示参与者。

h264与RTP
h.264规范中使用了在NAL层使用了RTP的包头形式,下面对RTP的包头做一下解释:
首先我们来看一下H.264中对于RPT的结构封装如下:
typedef struct
{
unsigned int v; // Version, 2 bits, MUST be 0x2
unsigned int p; // Padding bit, Padding MUST NOT be used
unsigned int x; // Extension, MUST be zero
unsigned int cc; // CSRC count, normally 0 in the absence of RTP mixers
unsigned int m; // Marker bit
unsigned int pt; // 7 bits, Payload Type, dynamically established
unsigned int seq; // RTP sequence number, incremented by one for each sent packet
unsigned int old_seq; // to detect wether packets were lost
unsigned int timestamp; // timestamp, 27 MHz for H.26L
unsigned int ssrc; // Synchronization Source, chosen randomly
byte * payload; // the payload including payload headers
unsigned int paylen; // length of payload in bytes
byte * packet; // complete packet including header and payload
unsigned int packlen; // length of packet, typically paylen+12
} RTPpacket_t;

5.2复用RTP会话
省略第一段
独立的音频和视频流不应该在一个RTP会话中传输然后根据负载的类型或SSRC来解除复用。包含不同RTP媒体类型的交错包,并且这些包使用同一SSRC将会引起下面一系列问题。
1.假设,两个共有相同RTP会话和相同SSRC标识的音频流,其中一个改变了编码,由此需要一个不同的负载类型。这时就没有办法来识别究竟是哪个音频流改变了编码。
2. SSRC标示符是被定义用来确定一个单一的时序和序列号空间。如果媒体的时钟频率不同,那么交错的多种媒体负载类型需要不同的时序空间和不同的序列号空间,由此来指出哪种负载类型发生了数据包丢失。
3. RTCP发送方和接受方报告只能对每一个SSRC标示符表述一种时序和序列号空间,并且不能加载负载类型域。
4. RTP混合器不能够将不兼容的交叉流合并到一种数据流中。

为每一种媒体采用一个不同的SSRC标示符,但是仍然将他们放在一个RTP会话中传输可以避免前两个问题,但无法避免最后两个。
另一方面,复用同一媒体的有联系的源于一个使用不同SSRC标示符的RTP会话中是关于多播会话的规范。上述的问题不适用于:比如说,一个RTP混合器能够合并多种音源,并且同样的待遇适用于所有的音源。在最后两个问题不会发生的其他情况下,同一媒体的数据流复用(这些数据流使用不同的SSRC)也可以是适当的。
5.3对于RTP包头特性说明的修改
已经存在的RTP数据包包头被认为已经满足通常所有应用类型的需要。然而,为了符合ALF设计原则,该包头可以通过修改或作一些定义在特性说明中的增加的方法实现个性定制。同时,仍然允许与特性独立的显示和记录工具产生作用。
标示位和负载类型域携带特性说明信息,但是它们被存储在固定的包头中,因为很多应用程序被预计需要它们,并且可能需要增加另一个32位的字用来存储它们。包含这些域的八位字节可以通过用一个文件来重新定义,从而适应于不同的需求,例如增加和减少标示位。对于每一个标示位,都必须定位于字节的最高位,因此,监视器能够遵守包丢失模式和标示位之间的相互关系。
如果一种特殊的应用需要独立于负载类型的附加功能,那么应用程序运行的文件应该定义附加的固定域,这些固定域应该紧跟在已经存在的固定包头的SSRC域的后面。这些应用程序能够很快很直接的访问这些附加域,而这时那些独立文件的监视器和记录器仍然能够通过解释前12个字节的方法来处理RTP数据包。
如果事实证明一种附加的功能在所有文件中被普遍需求,那时,就需要定义一个新版本的RTP包头。

5.3.1 RTP头扩展
RTP提供扩展机制以允许实现个性化:某些新的与负载格式独立的功能要求的附加信息在RTP数据包头中传输。设计此扩展机制可以使其它没有扩展的交互忽略此头扩展。RTP头扩展的格式如下图所示。 注意特殊负载类型需要的附加信息不能使用这种头扩展,而是应该被存贮在数据包的负载部分中。
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| defined by profile | length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| header extension |
| …. |

若RTP头中的扩展比特位置1,则一个长度可变的头扩展部分被加到RTP固定头之后。头扩展包含16比特的长度域,指示扩展项中32比特字的个数,不包括4个字节扩展头(因此零是有效值)。RTP固定头之后只允许有一个头扩展。为允许多个互操作实现独立生成不同的头扩展,或某种特定实现有多种不同的头扩展,扩展项的前16比特用以识别标识符或参数。这16比特的格式由具体实现的上层协议定义。基本的RTP说明并不定义任何头扩展本身。

4 UDP、TCP、IP、RTP三种协议的总结
1七层协议参考模型 与 TCP/IP协议 实现网络模型 对比:
OSI中的层 功能 TCP/IP协议族
应用层 文件传输,电子邮件,文件服务,虚拟终端 TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet
表示层 数据格式化,代码转换,数据加密 没有协议
会话层 解除或建立与别的接点的联系 没有协议
传输层(运输层) 提供端对端的接口 TCP,UDP
网络层 为数据包选择路由 IP,ICMP,RIP,OSPF,BGP,IGMP
数据链路层 传输有地址的帧以及错误检测功能 SLIP,CSLIP,PPP,ARP,RARP,MTU
物理层 以二进制数据形式在物理媒体上传输数据 ISO2110,IEEE802。IEEE802.2

摘自:http://www.jswl.cn/course/A1013/wljczs/tcpip.htm

2.具有五层协议的体系结构
TCP/IP 是四层的体系结构:应用层、运输层、网际层和网络接口层。但最下面的网络接口层并没有具体内容。如下:

    因此往往采取折中的办法,即综合 OSI 和 TCP/IP 的优点,采用一种只有五层协议的体系结构。(???-??????-这句话是什么意思啊?莫非是因为TCP/IP是事实的标准,所以自然取其前三层协议,而网络接口层没什么玩意,在加上以太网太强悍,又恰巧位于数据链路层和物理层,所以谢老就自己创造了所谓的五层模型,如下图所示?)

主机 1 向主机 2 发送数据 的示意图:

PS:一般的适配器(网卡)都包括了数据链路层和物理层这两层的功能。

3 首先来看看在网络中,一帧以太网数据包的格式

在Linux 操作系统中,当我们想发送数据的时候,我们只需要在上层准备好数据,然后提交给内核协议栈 , 内核协议栈自动添加相应的协议头。
下面我们来看看,每一层添加的协议头具体内容。

3.1 TCP协议

TCP协议是面向连接、保证高可靠性(数据无丢失、数据无失序、数据无错误、数据无重复到达)传输层协议。

3.1.1 TCP头分析

先来分析一下TCP头的格式以及每一个字段的含义:

(1)端口号[16bit]

我们知道,网络实现的是不同主机的进程间通信。在一个操作系统中,有很多进程,当数据到来时要提交给哪个进程进行处理呢?这就需要用到端口号。在TCP头中,有源端口号(Source Port)和目标端口号(Destination Port)。源端口号标识了发送主机的进程,目标端口号标识接受方主机的进程。

(2)序号[32bit]

序号分为发送序号(Sequence Number)和确认序号(Acknowledgment Number)。

发送序号:用来标识从 TCP源端向 TCP目的端发送的数据字节流,它表示在这个报文段中的第一个数据字节的顺序号。如果将字节流看作在两个应用程序间的单向流动,则 TCP用顺序号对每个字节进行计数。序号是 32bit的无符号数,序号到达 2 32- 1后又从 0开始。当建立一个新的连接时, SYN标志变 1,顺序号字段包含由这个主机选择的该连接的初始顺序号 ISN( Initial Sequence Number)。

确认序号:包含发送确认的一端所期望收到的下一个顺序号。因此,确认序号应当是上次已成功收到数据字节顺序号加 1。只有 ACK标志为 1时确认序号字段才有效。 TCP为应用层提供全双工服务,这意味数据能在两个方向上独立地进行传输。因此,连接的每一端必须保持每个方向上的传输数据顺序号。

(3)偏移[4bit]

这里的偏移实际指的是TCP首部的长度,它用来表明TCP首部中32 bit字的数目,通过它可以知道一个TCP包它的用户数据是从哪里开始的。这个字段占4bit,如4bit的值是0101,则说明TCP首部长度是5 * 4 = 20字节。 所以TCP的首部长度最大为15 * 4 = 60字节。然而没有可选字段,正常长度为20字节。

(4)Reserved [6bit]

目前没有使用,它的值都为0

(5)标志[6bit]

在TCP首部中有6个标志比特。他们中的多个可同时被置为1 。

URG 紧急指针(urgent pointer)有效
ACK 确认序号有效
PSH 指示接收方应该尽快将这个报文段交给应用层而不用等待缓冲区装满

RST 一般表示断开一个连接
例如:一个TCP的客户端向一个没有监听的端口的服务器端发起连接,wirshark抓包如下

可以看到host:192.168.63.134向host:192.168.63.132发起连接请求,但是host:192.168.63.132并没有处于监听对应端口的服务器端,这时
host : 192.168.63.132发一个RST置位的TCP包断开连接。

SYN 同步序号用来发起一个连接
FIN 发送端完成发送任务(即断开连接)

(6)窗口大小(window)[16bit]

窗口的大小,表示源方法最多能接受的字节数。。

(7)校验和[16bit]

校验和覆盖了整个的TCP报文段:TCP首部和TCP数据。这是一个强制性的字段,一定是由发端计算和存储,并由收端进行验证。

(8)紧急指针[16bit]

只有当URG标志置为1时紧急指针才有效。紧急指针是一个正的偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。TCP的紧急方式是发送端向另一端发送紧急数据的一种方式。

(9)TCP选项

是可选的,在后面抓包的时候,我们在看看它

3.1.2.重点详解

(1)三次握手建立连接

a.请求端(通常称为客户)发送一个SYN段指明客户打算连接的服务器的端口,以及初始序号(ISN,在这个例子中为1415531521)。这个SYN段为报文段1。

b.服务器发回包含服务器的初始序号的SYN报文段(报文段2)作为应答。同时,将确认序号设置为客户的ISN加1以对客户的SYN报文段进行确认。一个SYN将占用一个序号

c.客户必须将确认序号设置为服务器的ISN加1以对服务器的SYN报文段进行确认(报文段3)

这三个报文段完成连接的建立。这个过程也称为三次握手(three-way handshake)

用wirshark抓包如下:

可以看到三次握手确定了双方间包的序号、最大接受数据的大小(window)以及MSS(Maximum Segment Size)。

MSS = MTU - IP头 - TCP头,MTU表示最大传输单元,我们在IP头分析的时候会讲到,它一般为1500个字节。IP头和TCP 头部带可选选项的时候都是20个字节。这样的话MSS=1500 - 20 -20 = 1460。

MSS限制了TCP包携带数据的大小,它的意思就是当应用层向传输层提交数据通过TCP协议进行传输时,如果应用层的数据>MSS就必须分段,分成多个段,逐个的发过去。

例如:应用层一次性向传输层提交4096个字节数据,这个时候通过wirshark抓包效果如下:

前三次是三次握手的过程,后面三次是传送数据的过程,由于数据大小是4096个字节,所以用了三次进行传递(1448 + 1448 + 1200)。
细心的人会问为什么每次传送的最大数据大小不是1460个字节呢?因为这里的TCP携带可选项,TCP头长度 = 20 + 12(可选选项大小) = 32字节。 这样能传输的最大数据为:1500 - 20 - 32 = 1448个字节。

(2)四次挥手断开连接

a.现在的网络通信都是基于socket实现的,当客户端将自己的socket进行关闭时,内核协议栈会向服务器自动发送一个FIN置位的包,请求断开连接。我们称首先发起断开请求的一方称为主动断开方。

b.服务器端收到请客端的FIN断开请求后,内核协议栈会立即发送一个ACK包作为应答,表示已经收到客户端的请求

c.服务器运行一段时间后,关闭了自己的socket。这个时候内核协议栈会向客户端发送一个FIN置位的包,请求断开连接

d.客户端收到服务端发来的FIN断开请求后,会发送一个ACK做出应答,表示已经收到服务端的请求

用wirshar抓包分析如下:

(3)TCP可靠性的保证

TCP采用一种名为“带重传功能的肯定确认(positive acknowledge with retransmission)”的技术作为提供可靠数据传输服务的基础。这项技术要求接收方收到数据之后向源站回送确认信息ACK。发送方对发出的每个分组都保存一份记录,在发送下一个分组之前等待确认信息。发送方还在送出分组的同时启动一个定时器,并在定时器的定时期满而确认信息还没有到达的情况下,重发刚才发出的分组。图3-5表示带重传功能的肯定确认协议传输数据的情况,图3-6表示分组丢失引起超时和重传。为了避免由于网络延迟引起迟到的确认和重复的确认,协议规定在确认信息中稍带一个分组的序号,使接收方能正确将分组与确认关联起来。
从图 3-5可以看出,虽然网络具有同时进行双向通信的能力,但由于在接到前一个分组的确认信息之前必须推迟下一个分组的发送,简单的肯定确认协议浪费了大量宝贵的网络带宽。为此, TCP使用滑动窗口的机制来提高网络吞吐量,同时解决端到端的流量控制。

(4)滑动窗口技术

滑动窗口技术是简单的带重传的肯定确认机制的一个更复杂的变形,它允许发送方在等待一个确认信息之前可以发送多个分组。如图 3-7所示,发送方要发送一个分组序列,滑动窗口协议在分组序列中放置一个固定长度的窗口,然后将窗口内的所有分组都发送出去;当发送方收到对窗口内第一个分组的确认信息时,它可以向后滑动并发送下一个分组;随着确认的不断到达,窗口也在不断的向后滑动。

3.2 UDP协议

UDP协议也是传输层协议,它是无连接,不保证可靠的传输层协议。它的协议头比较简单,如下:

这里的端口号就不解释了,和TCP的端口号是一样的含义。

Length占用2个字节,标识UDP头的长度。
Checksum : 校验和,包含UDP头和数据部分。

3.3 IP协议

I P是T C P / I P协议族中最为核心的协议。所有的T C P、U D P、I C M P及I G M P数据都以I P数据
报格式传输。它的特点如下:

不可靠(u n r e l i a b l e)的意思是它不能保证 I P数据报能成功地到达目的地。 I P仅提供最好
的传输服务。如果发生某种错误时,如某个路由器暂时用完了缓冲区, I P有一个简单的错误
处理算法:丢弃该数据报,然后发送 I C M P消息报给信源端。任何要求的可靠性必须由上层来
提供(如T C P) 。

无连接(c o n n e c t i o n l e s s)这个术语的意思是I P并不维护任何关于后续数据报的状态信息。
每个数据报的处理是相互独立的。这也说明, I P数据报可以不按发送顺序接收。如果一信源
向相同的信宿发送两个连续的数据报(先是 A,然后是B) ,每个数据报都是独立地进行路由
选择,可能选择不同的路线,因此B可能在A到达之前先到达。

3.3.1 IP 头格式

(1)版本 占4位,指IP协议的版本。通信双方使用的IP协议版本必须一致。目前广泛使用的IP协议版本号为4(即IPv4)。关于IPv6,目前还处于草案阶段。

(2)首部长度 占4位,可表示的最大十进制数值是15。请注意,这个字段所表示数的单位是32位字长(1个32位字长是4字节),因此,当IP的首部长度为1111时(即十进制的15),首部长度就达到60字节。当IP分组的首部长度不是4字节的整数倍时,必须利用最后的填充字段加以填充。因此数据部分永远在4字节的整数倍开始,这样在实现IP协议时较为方便。首部长度限制为60字节的缺点是有时可能不够用。但这样做是希望用户尽量减少开销。最常用的首部长度就是20字节(即首部长度为0101),这时不使用任何选项。

(3)区分服务 占8位,用来获得更好的服务。这个字段在旧标准中叫做服务类型,但实际上一直没有被使用过。1998年IETF把这个字段改名为区分服务DS(Differentiated Services)。只有在使用区分服务时,这个字段才起作用。

(4)总长度 总长度指首部和数据之和的长度,单位为字节。总长度字段为16位,因此数据报的最大长度为216-1=65535字节。
在IP层下面的每一种数据链路层都有自己的帧格式,其中包括帧格式中的数据字段的最大长度,这称为最大传送单元MTU(Maximum Transfer Unit)。当一个数据报封装成链路层的帧时,此数据报的总长度(即首部加上数据部分)一定不能超过下面的数据链路层的MTU值。

(5)标识(identification) 占16位。IP软件在存储器中维持一个计数器,每产生一个数据报,计数器就加1,并将此值赋给标识字段。但这个“标识”并不是序号,因为IP是无连接服务,数据报不存在按序接收的问题。当数据报由于长度超过网络的MTU而必须分片时,这个标识字段的值就被复制到所有的数据报的标识字段中。相同的标识字段的值使分片后的各数据报片最后能正确地重装成为原来的数据报。

(6)标志(flag) 占3位,但目前只有2位有意义。
● 标志字段中的最低位记为MF(More Fragment)。MF=1即表示后面“还有分片”的数据报。MF=0表示这已是若干数据报片中的最后一个
● 标志字段中间的一位记为DF(Don’t Fragment),意思是“不能分片”。只有当DF=0时才允许分片。

(7)片偏移 占13位。片偏移指出:较长的分组在分片后,某片在原分组中的相对位置。也就是说,相对用户数据字段的起点,该片从何处开始。片偏移以8个字节为偏移单位。这就是说,每个分片的长度一定是8字节(64位)的整数倍。

(8)生存时间 占8位,生存时间字段常用的的英文缩写是TTL(Time To Live),表明是数据报在网络中的寿命。由发出数据报的源点设置这个字段。其目的是防止无法交付的数据报无限制地在因特网中兜圈子,因而白白消耗网络资源。最初的设计是以秒作为TTL的单位。每经过一个路由器时,就把TTL减去数据报在路由器消耗掉的一段时间。若数据报在路由器消耗的时间小于1秒,就把TTL值减1。当TTL值为0时,就丢弃这个数据报。

(9)协议 占8位,协议字段指出此数据报携带的数据是使用何种协议,以便使目的主机的IP层知道应将数据部分上交给哪个处理过程。

(10)首部检验和 占16位。这个字段只检验数据报的首部,但不包括数据部分。这是因为数据报每经过一个路由器,路由器都要重新计算一下首部检验和(一些字段,如生存时间、标志、片偏移等都可能发生变化)。不检验数据部分可减少计算的工作量。

(11)源IP地址 占32位。

(12)目的IP地址 占32位。

3.3.2.分片解释

分片指的是需要传送的数据大于最大传输单元(MTU)的时候,就需要分成多个包,然后一个个发送给对方。我们在说TCP的时候,说到MSS很多人不能区分它们。通过下面的图,我想就可以完全区分它们了。

个人觉的如果通过TCP协议传输数据,到IP层的时候,可定不需要分片了。只有在通过UDP协议传送大数据的时候,需要分片。

例如:用UDP协议传送10240个字节数据

可以看到,但数据提交到网络层的时候,由于数据超过了最大传输单元,就分片了。分成多个包通过IP协议发送个对方。每个数据包最大的字节为MTU - IP头 = 1500 - 20 = 1480。
4以太网头

三部分组成 :源MAC Address | 目的 MAC Address | 所使用的协议.

所以在以太网中,数据包的格式有一下几种:

ARP协议是通过IP地址获得对应的MAC地址,称为地址解析协议
RARP协议是通过MAC地址来获得对应的IP地址,称为逆向地址解析协议


一、RTP协议分析
1、 RTP概述
1.1. RTP是什么
RTP全名是Real-time Transport Protocol(实时传输协议)。它是IETF提出的一个标准,对应的RFC文档为RFC3550(RFC1889为其过期版本)。RFC3550不仅定义了RTP,而且定义了配套的相关协议RTCP(Real-time Transport Control Protocol,即实时传输控制协议)。RTP用来为IP网上的语音、图像、传真等多种需要实时传输的多媒体数据提供端到端的实时传输服务。RTP为Internet上端到端的实时传输提供时间信息和流同步,但并不保证服务质量,服务质量由RTCP来提供。
1.2. RTP的应用环境
RTP用于在单播或多播网络中传送实时数据。它们典型的应用场合有如下几个。
(1)简单的多播音频会议。语音通信通过一个多播地址和一对端口来实现。一个用于音频数据(RTP),另一个用于控制包(RTCP)。
(2)音频和视频会议。如果在一次会议中同时使用了音频和视频会议,这两种媒体将分别在不同的RTP会话中传送,每一个会话使用不同的传输地址(IP地址+端口)。如果一个用户同时使用了两个会话,则每个会话对应的RTCP包都使用规范化名字CNAME(Canonical Name)。与会者可以根据RTCP包中的CNAME来获取相关联的音频和视频,然后根据RTCP包中的计时信息(Network time protocol)来实现音频和视频的同步。
(3)翻译器和混合器。翻译器和混合器都是RTP级的中继系统。翻译器用在通过IP多播不能直接到达的用户区,例如发送者和接收者之间存在防火墙。当与会者能接收的音频编码格式不一样,比如有一个与会者通过一条低速链路接入到高速会议,这时就要使用混合器。在进入音频数据格式需要变化的网络前,混合器将来自一个源或多个源的音频包进行重构,并把重构后的多个音频合并,采用另一种音频编码进行编码后,再转发这个新的RTP包。从一个混合器出来的所有数据包要用混合器作为它们的同步源(SSRC,见RTP的封装)来识别,可以通过贡献源列表(CSRC表,见RTP的封装)可以确认谈话者。
1.3. 流媒体
流媒体是指Internet上使用流式传输技术的连续时基媒体。当前在Internet上传输音频和视频等信息主要有两种方式:下载和流式传输两种方式。
下载情况下,用户需要先下载整个媒体文件到本地,然后才能播放媒体文件。在视频直播等应用场合,由于生成整个媒体文件要等直播结束,也就是用户至少要在直播结束后才能看到直播节目,所以用下载方式不能实现直播。
流式传输是实现流媒体的关键技术。使用流式传输可以边下载边观看流媒体节目。由于Internet是基于分组传输的,所以接收端收到的数据包往往有延迟和乱序(流式传输构建在UDP上)。要实现流式传输,就是要从降低延迟和恢复数据包时序入手。在发送端,为降低延迟,往往对传输数据进行预处理(降低质量和高效压缩)。在接收端为了恢复时序,采用了接收缓冲;而为了实现媒体的流畅播放,则采用了播放缓冲。
使用接收缓冲,可以将接收到的数据包缓存起来,然后根据数据包的封装信息(如包序号和时戳等),将乱序的包重新排序,最后将重新排序了的数据包放入播放缓冲播放。
为什么需要播放缓冲呢?容易想到,由于网络不可能很理想,并且对数据包排序需要处理时耗,我们得到排序好的数据包的时间间隔是不等的。如果不用播放缓冲,那么播放节目会很卡,这叫时延抖动。相反,使用播放缓冲,在开始播放时,花费几十秒钟先将播放缓冲填满(例如PPLIVE),可以有效地消除时延抖动,从而在不太损失实时性的前提下实现流媒体的顺畅播放。
到目前为止,Internet 上使用较多的流式视频格式主要有以下三种:RealNetworks 公司的RealMedia ,Apple 公司的QuickTime 以及Microsoft 公司的Advanced Streaming Format (ASF) 。
上面在谈接收缓冲时,说到了流媒体数据包的封装信息(包序号和时戳等),这在后面的RTP封装中会有体现。另外,RealMedia这些流式媒体格式只是编解码有不同,但对于RTP来说,它们都是待封装传输的流媒体数据而没有什么不同。

4、 RTP详解
4.1. RTP的协议层次
4.1.1. 传输层的子层
RTP(实时传输协议),顾名思义它是用来提供实时传输的,因而可以看成是传输层的一个子层。图 1给出了流媒体应用中的一个典型的协议体系结构。图2给出了RTP协议与其他协议之间的关系。

图1 流媒体体系结构

图2 RTP协议与其他协议的关系
RTP、TCP、UDP都属于传输层协议; RTP也可以认为是介于应用层与传输层之间

从图中可以看出,RTP被划分在传输层,它建立在UDP上。同UDP协议一样,为了实现其实时传输功能,RTP也有固定的封装形式。RTP用来为端到端的实时传输提供时间信息和流同步,但并不保证服务质量。服务质量由RTCP来提供。

4.1.2. 应用层的一部分
不少人也把RTP归为应用层的一部分,这是从应用开发者的角度来说的。操作系统中的TCP/IP等协议栈所提供的是我们最常用的服务,而RTP的实现还是要靠开发者自己。因此从开发的角度来说,RTP的实现和应用层协议的实现没不同,所以可将RTP看成应用层协议。
RTP实现者在发送RTP数据时,需先将数据封装成RTP包,而在接收到RTP数据包,需要将数据从RTP包中提取出来。
4.2. RTP的封装
一个协议的封装是为了满足协议的功能需求的。从前面提出的功能需求,可以推测出RTP封装中应该有同步源和时戳等字段,但更为完整的封装是什么样子呢?请看图3。

图 3 RTP的头部格式

版本号(V):2比特,用来标志使用的RTP版本。
填充位(P):1比特,如果该位置位,则该RTP包的尾部就包含附加的填充字节。
扩展位(X):1比特,如果该位置位的话,RTP固定头部后面就跟有一个扩展头部。
CSRC计数器(CC):4比特,含有固定头部后面跟着的CSRC的数目。
标记位(M):1比特,该位的解释由配置文档(Profile)来承担.
载荷类型(PT):7比特,标识了RTP载荷的类型。
序列号(SN):16比特,发送方在每发送完一个RTP包后就将该域的值增加1,接收方可以由该域检测包的丢失及恢复包序列。序列号的初始值是随机的。
时间戳:32比特,记录了该包中数据的第一个字节的采样时刻。在一次会话开始时,时间戳初始化成一个初始值。即使在没有信号发送时,时间戳的数值也要随时间而不断地增加(时间在流逝嘛)。时间戳是去除抖动和实现同步不可缺少的。
同步源标识符(SSRC):32比特,同步源就是指RTP包流的来源。在同一个RTP会话中不能有两个相同的SSRC值。该标识符是随机选取的 RFC1889推荐了MD5随机算法。
贡献源列表(CSRC List):0~15项,每项32比特,用来标志对一个RTP混合器产生的新包有贡献的所有RTP包的源。由混合器将这些有贡献的SSRC标识符插入表中。SSRC标识符都被列出来,以便接收端能正确指出交谈双方的身份。

4.3. RTCP的封装
RTP需要RTCP为其服务质量提供保证,因此下面介绍一下RTCP的相关知识。
RTCP的主要功能是:服务质量的监视与反馈、媒体间的同步,以及多播组中成员的标识。在RTP会话期 间,各参与者周期性地传送RTCP包。RTCP包中含有已发送的数据包的数量、丢失的数据包的数量等统计资料,因此,各参与者可以利用这些信息动态地改变传输速率,甚至改变有效载荷类型。RTP和RTCP配合使用,它们能以有效的反馈和最小的开销使传输效率最佳化,因而特别适合传送网上的实时数据。
从图 1可以看到,RTCP也是用UDP来传送的,但RTCP封装的仅仅是一些控制信息,因而分组很短,所以可以将多个RTCP分组封装在一个UDP包中。RTCP有如下五种分组

表 1 RTCP的5种分组类型
上述五种分组的封装大同小异,下面只讲述SR类型,而其它类型请参考RFC3550。
发送端报告分组SR(Sender Report)用来使发送端以多播方式向所有接收端报告发送情况。SR分组的主要内容有:相应的RTP流的SSRC,RTP流中最新产生的RTP分组的时间戳和NTP,RTP流包含的分组数,RTP流包含的字节数。SR包的封装如图3所示。

图 3 RTCP头部的格式
版本(V):同RTP包头域。
填充(P):同RTP包头域。
接收报告计数器(RC):5比特,该SR包中的接收报告块的数目,可以为零。
包类型(PT):8比特,SR包是200。
长度域(Length):16比特,其中存放的是该SR包以32比特为单位的总长度减一。
同步源(SSRC):SR包发送者的同步源标识符。与对应RTP包中的SSRC一样。
NTP Timestamp(Network time protocol)SR包发送时的绝对时间值。NTP的作用是同步不同的RTP媒体流。
RTP Timestamp:与NTP时间戳对应,与RTP数据包中的RTP时间戳具有相同的单位和随机初始值。
Sender’s packet count:从开始发送包到产生这个SR包这段时间里,发送者发送的RTP数据包的总数. SSRC改变时,这个域清零。
Sender`s octet count:从开始发送包到产生这个SR包这段时间里,发送者发送的净荷数据的总字节数(不包括头部和填充)。发送者改变其SSRC时,这个域要清零。
同步源n的SSRC标识符:该报告块中包含的是从该源接收到的包的统计信息。
丢失率(Fraction Lost):表明从上一个SR或RR包发出以来从同步源n(SSRC_n)来的RTP数据包的丢失率。
累计的包丢失数目:从开始接收到SSRC_n的包到发送SR,从SSRC_n传过来的RTP数据包的丢失总数。
收到的扩展最大序列号:从SSRC_n收到的RTP数据包中最大的序列号,
接收抖动(Interarrival jitter):RTP数据包接受时间的统计方差估计
上次SR时间戳(Last SR,LSR):取最近从SSRC_n收到的SR包中的NTP时间戳的中间32比特。如果目前还没收到SR包,则该域清零。
上次SR以来的延时(Delay since last SR,DLSR):上次从SSRC_n收到SR包到发送本报告的延时。
4.4. RTP的会话过程
当应用程序建立一个RTP会话时,应用程序将确定一对目的传输地址。目的传输地址由一个网络地址和一对端口组成,有两个端口:一个给RTP包,一个给RTCP包,使得RTP/RTCP数据能够正确发送。RTP数据发向偶数的UDP端口,而对应的控制信号RTCP数据发向相邻的奇数UDP端口(偶数的UDP端口+1),这样就构成一个UDP端口对。 RTP的发送过程如下,接收过程则相反。
1)RTP协议从上层接收流媒体信息码流(如H.263),封装成RTP数据包;RTCP从上层接收控制信息,封装成RTCP控制包。
2)RTP将RTP 数据包发往UDP端口对中偶数端口;RTCP将RTCP控制包发往UDP端口对中的接收端口。

5、TCP协议分析
1、 TCP协议简介
TCP,全称Transfer Control Protocol,中文名为传输控制协议,它工作在OSI的传输层,提供面向连接的可靠传输服务。
TCP的工作主要是建立连接,然后从应用层程序中接收数据并进行传输。TCP采用虚电路连接方式进行工作,在发送数据前它需要在发送方和接收方建立一个连接,数据在发送出去后,发送方会等待接收方给出一个确认性的应答,否则发送方将认为此数据丢失,并重新发送此数据。
2、TCP报头
TCP报头总长最小为20个字节,其报头结构如下图(图1)所示;
  比特0           比特15 比特16        比特31
源端口(16) 目的端口(16)
序列号(32)
确认号(32)
TCP偏移量(4) 保留(6) 标志(6) 窗口(16)
校验和(16) 紧急(16)
选项(0或32)
数据(可变)

(图1 TCP报头结构)
源端口:指定了发送端的端口
目的端口:指定了接受端的端口号
序号:指明了段在即将传输的段序列中的位置
确认号:规定成功收到段的序列号,确认序号包含发送确认的一端所期望收到的下一个序号
TCP偏移量:指定了段头的长度。段头的长度取决与段头选项字段中设置的选项
保留:指定了一个保留字段,以备将来使用
标志:SYN、ACK、PSH、RST、URG、FIN
SYN: 表示同步
ACK: 表示确认
PSH: 表示尽快的将数据送往接收进程
RST: 表示复位连接
URG: 表示紧急指针
FIN: 表示发送方完成数据发送
窗口:指定关于发送端能传输的下一段的大小的指令
校验和:校验和包含TCP段头和数据部分,用来校验段头和数据部分的可靠性
紧急:指明段中包含紧急信息,只有当U R G标志置1时紧急指针才有效
选项:指定了公认的段大小,时间戳,选项字段的末端,以及指定了选项字段的边界选项
3、TCP工作原理
TCP连接建立:TCP的连接建立过程又称为TCP三次握手。首先发送方主机向接收方主机发起一个建立连接的同步(SYN)请求;接收方主机在收到这个请求后向送方主机回复一个同步/确认(SYN/ACK)应答;发送方主机收到此包后再向接收方主机发送一个确认(ACK),此时TCP连接成功建立;
TCP连接关闭:发送方主机和目的主机建立TCP连接并完成数据传输后,会发送一个将结束标记置1的数据包,以关闭这个TCP连接,并同时释放该连接占用的缓冲区空间; TCP重置:TCP允许在传输的过程中突然中断连接,这称为TCP重置;
TCP数据排序和确认:TCP是一种可靠传输的协议,它在传输的过程中使用序列号和确认号来跟踪数据的接收情况;
TCP重传:在TCP的传输过程中,如果在重传超时时间内没有收到接收方主机对某数据包的确认回复,发送方主机就认为此数据包丢失,并再次发送这个数据包给接收方,这称为TCP重传;
TCP延迟确认:TCP并不总是在接收到数据后立即对其进行确认,它允许主机在接收数据的同时发送自己的确认信息给对方。
TCP数据保护(校验和):TCP是可靠传输的协议,它提供校验和计算来实现数据在传输过程中的完整性。

6、 UDP协议分析
1、UDP简介
UDP协议是英文UserDatagramProtocol的缩写,即用户数据报协议,主要用来支持那些需要在计算机之间传输数据的网络应用。包括网络视频会议系统在内的众多的客户/服务器模式的网络应用都需要使用UDP协议。UDP协议从问世至今已经被使用了很多年,虽然其最初的光彩已经被一些类似协议所掩盖,但是即使是在今天,UDP仍然不失为一项非常实用和可行的网络传输层协议。
与我们所熟知的TCP(传输控制协议)协议一样,UDP协议直接位于IP(网际协议)协议的顶层。根据OSI(开放系统互连)参考模型,UDP和TCP都属于传输层协议。
UDP协议的主要作用是将网络数据流量压缩成数据报的形式。一个典型的数据报就是一个二进制数据的传输单位。每一个数据报的前8个字节用来包含报头信息,剩余字节则用来包含具体的传输数据。
2、UDP协议结构
UDP报头由4个域组成,其中每个域各占用2个字节,具体如下:
源端口号 、目标端口号 、数据报长度 和校验值 。
UDP协议使用端口号为不同的应用保留其各自的数据传输通道。UDP和TCP协议正是采用这一机制实现对同一时刻内多项应用同时发送和接收数据的支持。数据发送一方(可以是客户端或服务器端)将UDP数据报通过源端口发送出去,而数据接收一方则通过目标端口接收数据。有的网络应用只能使用预先为其预留或注册的静态端口;而另外一些网络应用则可以使用未被注册的动态端口。因为UDP报头使用两个字节存放端口号,所以端口号的有效范围是从0到65535。一般来说,大于49151的端口号都代表动态端口。
数据报的长度是指包括报头和数据部分在内的总的字节数。因为报头的长度是固定的,所以该域主要被用来计算可变长度的数据部分(又称为数据负载)。数据报的最大长度根据操作环境的不同而各异。从理论上说,包含报头在内的数据报的最大长度为65535字节。不过,一些实际应用往往会限制数据报的大小,有时会降低到8192字节。
UDP协议使用报头中的校验值来保证数据的安全。校验值首先在数据发送方通过特殊的算法计算得出,在传递到接收方之后,还需要再重新计算。如果某个数据报在传输过程中被第三方篡改或者由于线路噪音等原因受到损坏,发送和接收方的校验计算值将不会相符,由此UDP协议可以检测是否出错。这与TCP协议是不同的,后者要求必须具有校验值。

7 三种协议对比
IP 包头结构:

TCP 包头结构:
UDP 包头结构:

RTP 包头结构:

RTCP 包头结构:

RTP位于UDP之上,UDP虽然没有TCP那么可靠,并且无法保证实时业务的服务质量,需要RTCP实时监控数据传输和服务质量,但是,由于UDP的传输时延低于TCP,能与视频和音频很好匹配。因此,在实际应用中,RTP/RTCP/UDP用于音频/视频媒体,而TCP用于数据和控制信令的传输。
UDP和TCP协议的主要区别是两者在如何实现信息的可靠传递方面不同。TCP协议中包含了专门的传递保证机制,当数据接收方收到发送方传来的信息时,会自动向发送方发出确认消息;发送方只有在接收到该确认消息之后才继续传送其它信息,否则将一直等待直到收到确认信息为止。
所以TCP必UDP多了建立连接的时间。相对UDP而言,TCP具有更高的安全性和可靠性。TCP协议传输的大小不限制,一旦连接被建立,双方可以按照一定的格式传输大量的数据,而UDP是一个不可靠的协议,大小有限制,每次不能超过64K。
相对于TCP协议,UDP协议的另外一个不同之处在于如何接收突法性的多个数据报。不同于TCP,UDP并不能确保数据的发送和接收顺序。
三者的性能对比见表1。

表1 三种协议的性能对比

协议名称
复杂性 连接建
立时间
可靠性 是否确保数据报发送和接受顺序
实时性
适用范围 是否支持组播

RTP/RTCP 低 少 低 否 是 实时音视
频媒体传输 支持
TCP 高 多 高 是 否 数据和控制信令传输 不支持
UDP 低 少 低 否 否 音视频媒体传输 支持

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值