-
正式的TCP的全部原理很复杂,所以要先介绍几个简单的协议作为正式TCP协议的铺垫
-
停止等待协议
(1) 图示
(2) 原理
1° 发送方每次传输一组数据后,接收方接到后,要发一条确认信息给发送方代表已经收到(两方的信息中都带着__编号__,以确定对应着哪个数据块);一组数据成功发送和确认接收后,才能发送下一组数据
2° 发送方每发送一组数据,它就开始启动__超时定时器__,如果在超时中断到来前还没有收到确认信息,那就__重传__这组数据(所以要暂时保存那些发送出去、但是还没有收到确认消息的数据)
3° 中途可能__出错的环节__有:发送方的数据在路上丢失、接收方的确认消息在路上丢失、虽然都没丢失但是传输超时。无论发生了哪种情况,对于每一组数据来说,必须要经过完整的发送、确认到达过程以后,才能传下一组数据,否则就是超时中断就重传
4° 如果接收方已经接收到分组,但是确认信息没到发送方一侧,那么发送方还会重传这个分组,这时接收方接到这个重复的分组时的操作是:丢弃这个分组 + 发送确认这个分组的信息(因为这代表这个分组确认信息接收方没收到)
5° 如果接收方的确认消息姗姗来迟,那么发送方很可能收到好几次关于同一编号分组的确认消息,此时发送方的操作是:丢弃这个确认消息(因为都已经收到过一遍确认消息了,很可能开始传下一个编号的分组了)
(3) 停止等待协议其实中间还用到了__ARQ(Automatic Repeat reQuest,自动重传请求)。ARQ还在后面的协议中用到了,这里自动重传机制,已经保证了__停止等待协议是可靠传输的
(4) 存在的问题:效率太低
设发送的分组长为L,发送方发送速率为R,则发送方发送分组需要的时间 t1 = L / R;
同理,设确认信息的分组长为l,接受方发送速率为r,则接收方发送确认信息需要的时间 t2 = l / r;
设分组在信道中传递的__往返__时间是RTT;
又因为停止等待协议中,不完整经过一次发送和确认的过程,下个分组永远都不会发,所以其实只有t1的时间在干正事(其实t1中还有一部分时间在传首部信息这种无效信息),所以__信道利用率__特别低
U ≈ t1 / (t1 + RTT + t2)
(5) 有了停止等待协议以后,好消息是这就是一个__可靠传输机制__,坏消息是__效率太低__
-
连续ARQ协议(Go-Back-N)
(1) 刚才的停止等待协议虽然可以可靠传输,但是效率太低;那解决效率太低的方法是:不要每次只传一个然后就傻乎乎的等,而是一次都传一些分组
(2) 图示
(3) 原理
1° 现在发送方不是一次传一个分组了,而是一次传一群分组,它们现在也有序号,并且序号是从小到大的;
2° 图5-13(a)中黑框里面的部分称为__发送窗口__,无论是开始发送,还是超时重传,每次都发送的是发送窗口内的一群分组;
3° 现在接受方的确认机制不是每来一个分组就发一次确认消息,因为现在很可能是一群分组“纷至沓来”,所以采取__累积确认__的方法:对按顺序、无丢失达到的一组分组,对最后一个分组发送确认;
例如现在接收方发现来了序号为1,2,3,4,5的5个分组,那它就向发送方发针对5的确认信息,告诉发送方5之前的都收到了;如果接收方发现来了1,2,4,5的4个分组(3在中途丢了),那么它就针对2发送确认信息,告诉发送方2之前的都收到了
也就是说,现在的确认信息在告诉发送方,序号X和X以前的分组收到了,你不用再重传了;
4° 发送方收到接收方针对X的确认信息以后,它就会__滑动发送窗口__,使得发送窗口从X+1开始,因为X之前的已经收到了,接下来发X+1以后的分组
(4) 现在的好消息是信道利用率提高了(因为一次可以发多个分组不用每个都等),坏消息是如果信道质量有问题中间总是丢分组的话(例如发了1,2,3,4,5收到了1,3,4,5),那么还是要Go-Back-N,从2开始重发,因为2丢了,这样和每次发一次好像也没什么区别
-
TCP报文段的首部格式
(1) 图示
固定首部是20 Byte,可选首部是4*n个Byte,长度由固定首部中的数据偏移一项指定(最大为40 Byte)
(2) 源端口、目的端口
(3) 序号:4 Byte --> 序号范围是 [0, 2^32 - 1]
1° 在TCP连接中传送的字节流中的每一个字节都__按顺序编号__,直到2^32 - 1后又回到0
2° 在TCP__连接建立__时,就要指定字节流的__起始编号__
3° 每个TCP报文段中的序号指的都是当前报文段字节流的__起始编号__
例如当前TCP报文段起始编号为301,而数据部分有100个字节,说明当前携带的是301-400这个100个字节
(4) 确认号
1° 代表期望收到对方下一个报文段的第一个数据字节的序号
例如确认号是701,说明接收方__已经收到了__700和700以前的字节,接下来它希望收到从701开始的字节
2° 也是4 Byte,可以和__序号__部分对应上
(5) 数据偏移
4 Byte,代表TCP首部的总长度,单位是32位字(一个32位字 = 4个 Byte)
因此,TCP首部的总长度最大值为 (2^4 - 1) * 4 = 60 Byte,去掉固定部分的20 Byte,说明可选部分的最大长度是 40 Byte
(7) URG标志
平时都是0,置为1的时候代表:告诉发送方操作系统,本报文段中含有紧急数据,当前报文段不应该参与排队,而是紧急发送;然后发送方TCP会把紧急数据插入到报文段数据部分的__最前面__,后面跟着的还是非紧急数据(到底有多少字节的紧急数据这个由首部中的紧急指针选项指定)
(8) ACK标志
ACK = 1时,__确认号__字段有效;
ACK = 0时,确认号字段无效;
只要连接建立,所有传送的报文段的ACK都必须置为1
(9) PSH标志
平时为0,置为1表示发送方想要告诉接收方,不要缓存填满再上交给进程,而是当前报文段需要被Push,立刻上交给进程
(10) RST标志
平时为0,置为1表示Reset,释放TCP连接然后重新建立连接
(11) SYN标志
平时为0,置为1表示当前报文是一个__连接请求__或__连接接受__报文
(12) FIN标志
平时为0,置为1表示此报文段的发送方的数据已经发送完毕,要求__释放连接__
(13) 窗口
1° 进行TCP连接的双方都有自己的__发送缓存__和__接收缓存__。窗口是当前报文段的发送方,在告诉接收方:我自己的接收缓存窗口有多大
2° 如果当前报文段的确认号是701,窗口是1000,说明当前报文段的发送方想要告诉对方,你可以给我发送701-1700这么多的字节流
3° 窗口字段明确指出了现在__允许对方发送的数据量__,经常是__动态变化__的
(14) 检验和
校验 TCP首部 + TCP数据段 + 伪首部(跟IP相关的部分)
(15) 紧急指针
在URG=1时才有意义,指出紧急数据的字节数,后面的就是普通数据了
-
TCP可靠传输的实现
(1) Go-Back-N和TCP可靠传输已经很像了,主要区别在对那些不按顺序到达的分组的处理
(2) 根据接收方传来的__接收窗口大小__和__网络拥塞情况__,发送方确定当前的发送窗口大小是X
(3) 发送窗口后沿的后面部分代表__已发送+已确认__,所以后沿只能前进/不动,不能后退
(4) 发送窗口前沿的前面部分代表还不允许发送的部分,前沿可能前进(收到了窗口内的确认号,窗口整体前移);也有可能不动(确认号啥也没收到或者收到很久之前的;或者虽然窗口整体前移但是接收方要求窗口大小缩小);但是不能后退,因为有可能产生一些错误;
图5-15中代表,接收方发过确认号31,根据接收方要求的窗口和拥塞情况判断出当前发送窗口大小应该是20
(5) 对发送窗口可以进行进一步细分,可以用__三个指针__说明,如图5-16
P1是发送窗口后沿的指针,P3是发送窗口前沿的指针,而P2是是否已发送字节的分界线:P1——P2之间是已经发送出的字节,但是还没有收到确认信息;P2——P3之间是还没有发送出去的字节,但是它们是允许发送的;P3以后是不允许发送的
(6) TCP的可靠数据传输机制和Go-Back-N的区别在于,对于GBN来说,只要分组没有按顺序到达,那么不按顺序的那些分组直接丢掉;而TCP的接收方有接收窗口,先把不按顺序的字节缓存下来;然后发送方接下来要发哪个字节,是根据发送窗口中的P2走,而在发送方继续发P2——P3之间字节流的过程中,完全有可能5-16中的31号字节到达接收方了,这时接收方可能很给力的发一个确认号为34的TCP报文段,然后发送方就可以愉快的前移发送窗口了,而不用像GBN一样傻乎乎的每次都必须有序,不有序的直接舍弃;
(7) 除非是31号的确认号就是迟迟不来,那窗口位置就是动不了,P2要指向31号位置开始重传了
(8) 缓存与窗口
窗口是缓存的一个部分,除了窗口,发送缓存还包括应用程序写入的未进入窗口的字节;接收窗口还包括应用程序未读入的但是应该按序到达的字节
(9) 说明
1° 缓存和序号空间都是有限的,因此都是__循环使用__的
2° 发送窗口未必和接收方要求的一样大,因为a.网络延迟造成双方不同步 b.还要考虑网络拥塞情况
3° 接收方有__累积确认__的功能,即攒了几个接收分组之后发一条待确认号的TCP报文段;但是攒的时间不能过长,因为会引起发送方不必要的重传