在学习的过程中,相信大家都有过“学了就忘“这种经历,又特别是TCP/IP知识点密集的通信协议,所以在此总结一下自己学到的皮毛,希望对自己对大家也有所帮助。
这篇博客主要讲运输层TCP和UDP的东西,IP层以后有时间再来补充。
TCP
TCP的全称叫传输控制协议(Transmission Control Protocol),这个协议的目的就是为网络数据提供可靠的运输服务。
tcp5个特点:
- 面向连接。应用程序在使用TCP协议之前,必须先建立TCP连接(三次握手)。数据传输完毕后,必须释放连接。
2)每一条TCP连接只能有两个端点。
3)提供可靠交付的服务。通过TCP连接传送的数据,无差错、不丢失、不重复、并且按序到达。(其实我们很多web服务也是要求可靠的,所以服务在应用层的设计是不是可以参考TCP的协议的设计?)
4)全双工通信。通信双方任何时候都能发送数据。
这里面有个重点就是两端都设有发送缓存和接收缓存(在内核中),用来临时存放双向通信的数据。在发送时,应用进程把数据传输给内核中的发送缓存,然后就去做其他事情(典型的异步),然后内核在合适的时候把数据发送出去。接收时,内核把收到的数据放入接收缓存,应用进程在合适的时候读取缓存中的数据。(这两个缓存非常重要,在之后各种IO模型都会在这两个缓存上搞事情)
5)面向字节流。“流”是指流入到进程或从进程流出的字节序列。虽然应用进程和TCP的交互式一次一个大小不等的数据块,但TCP把应用进程传过来的数据看成一连串的无结构的字节流(看成是连续的)。
tcp面向流的概念图
TCP并不关心应用进程一次把多长的报文发送到TCP的缓存中,而是根据对方给出的窗口值和当前网络拥塞的程度来决定一个报文段应包含多少个字节--如果应用进程传送到TCP缓存的数据块太长,TCP就划分短一些再传送;如果应用进程一次只发过来一个字节,TCP也可以累积有足够多的字节后再构成报文段发送出去。
可靠传输的工作原理】
说TCP是可靠的,那什么样的条件才算可靠的呢?
理想的可靠传输条件有两个特点:
传输信道不产生差错。
不管多快的发送速度,接收方总能来得及处理收到的数据。
我们不可能达到理想条件,但我们可以尽可能达到。
要达到第一点的方法是,当出现差错的时候,就让发送方重发出现差错的数据;
达到第二点的方法是,接收方来不及处理数据时,及时告诉发送方适当降低下发送速度。
第一点的方法怎么实现呢?先介绍简单的停止、等待协议。
停止、等待协议(做个了解,为后面做铺垫)]
“停止、等待”就是发送完一个分组(数据单元的统称,在IP称作数据表,在TCP层称作报文段)就停止发送,等待对方的确认。在收到确认后再发送下一个分组。
这种协议会遇到三种情况。
情况1.无差错情况]
A发送分组M1,发完就暂停发送,等待B确认(ack)。B收到了M1就向A发送确认。以此类推。这种情况不会出现什么问题。
情况2.出现差错]
如图上M1分组的情况--M1分组发送给B的时候,B检测M1出了差错,就丢弃了M1,然就就什么也不做了(不通知A收到有差错的分组);或者M1在传输过程中丢失了,这时B当然什么都不知道。在这两种情况下B是不会给A发送任何消息包括确认消息。
遇到这种情况,A采用的办法叫超时重传: A只要超过了一段时间仍然没有收到确认的话,就认为刚才发送的分组丢失了,因而重传上次发送过的分组。
所以每发完一个分组就为该分组设置一个超时计时器,如果在时间内收到了确认,就撤销该超时计时器。
这里面有三个要点:
1. A需要暂时保留已发送分组的副本。收到确认后清除副本。
2. 分组和分组的确认进行编号,这样才能知道哪些分组收到了确认。
3. 从上图中就可以重传时间要比平均往返更长一些。(具体长多少,要确定这个值比较麻烦,必须要根据当时的网络情况具体分析)
情况3. 确认丢失和确认迟到]
这个字面上就很好理解,就是比如B收到了M1分组,但B发送的确认丢失了或者迟到了。 因此A在超时计时器到期后就要重传M1。这时B又收到之前那个M1了!B应该怎么做?(确认丢失)
1. 丢弃这个重复M1。
2. 然后给A发送M1的确认。这个确认必须发送,不能说之前发送过就不发送了。
这里面有个细节,就是当B再次收到M1时,它是知道这是重复的!
确认迟到--因为网络拥塞等情况,B发给A的M1确认可能迟到。B的确认迟到,就意味着A等待确认后超时!如果超时后,会再次发送M(B收到这个重复M1后,直接丢弃,如上面确认丢失的做法),如果A按时收到了B相应的确认,那那个迟到确认就重复收到了,A收到这个重复确认怎么办?很简单,也是收下后直接丢弃!
像上述停止、等待这类可靠的传输协议,我们通常称为自动重传请求ARQ(Automatic Repeat reQuest)
缺点:]
停止、等待协议有个特点,从无差错下面那个图可以看出来,是线性的!发送完一个数据必须等待确认收到后才发送第二个,可以看成像程序上的同步。 我们可以来算这样做的信道利用率。TD是A发送数据需要的时间,RTT是数据在信道中的往返时间,TA是B发送确认的时间(发送确认时间非常短)。因为只有TD那个时间才是发送的有用数据,所以利用率公式为:
U = TD/(TD+RTT+TA)
从这个公式就可以看出,当信道特别长(一定特别长,都跨国了),发送功率特别大(科技发展,发送速度必然越来越快)的时候,我们的信道利用率就特别低!也就是说这个信道多数时候是闲置的。
那我们让A连续发送过个分组,不必每发完一个分组就停下来等待确认呢?类似于我们web开发说的异步服务。
当然有,下面介绍类似这种异步的协议--连续ARQ协议,和再后面的大BOSS滑动窗口协议。
连续ARQ协议]
其实该协议很简单,就是有一个发送窗口,一个窗口内的5(数字可以改)个分组一起发送出去。然后接收方采用累积确认的方式,即接收方不必对收到的分组逐个发送确认,而是收到几个分组后,对按序到达的最后一个分组发送确认。
如图:
(a)图先发送12345编好号的分组,在收到接收方发送的最后一个分组确认(即第5个分组的确认)后,(b)图向前移动5个分组,再一次性发送678910分组数据。
根据这个简单的介绍,连续ARQ协议在这种情况下会出现个大问题:
累积确认有优点也有缺点。优点是:容易实现,即使确认丢失也不必重传。但缺点是
不能向发送方反映出接收方已经正确收到的所有分组的信息。
例如,如果发送方发送了前5个分组,而中间的第3个分组丢失了。这时接收方只能对前两个分组发出确认。发送方无法知道后面三个分组的下落,而只好把后面的三个分组都再重传一次。这就叫做Go-back-N(回退N),表示需要再退回来重传已发送过的Ⅳ个分组。可见当通信线路质量不好时,连续ARQ协议会带来负面的影响。
在深入讨论TCP的可靠传输问题之前,先了解TCP的报文段首部的格式。
上一大段巴拉巴拉,就是说累积确认有缺陷。在解决这个缺陷之前,我们先来看看TCP的首部格式!
TCP报文段首部格式】
TCP传输的数据单元是报文段。一个报文段由TCP头部和报文段数据部分组成。TCP的全部功能都体现在它首部中各个字段的作用。头部图如下:
源端口号和目的端口号都好理解。
序号(seq number): 占4字节,范围是02的32次方-1(04294967295)。序号增加到4294967295后,下一个序号又回到0。在TCP传输的字节流中的中每一个字节都按顺序编号。首部中的序号字段值是指本报文段所发送的数据的第一个字节的序号。
例如一段报文的序号字段的值是301,携带的数据共100个字节,那下一个报文段的数据序号就是401。(这个序号非常重要,在后面重组报文的时候用)
序号一共就4294967296个,会用完吗?在一般的情况下,可保证当序号重复使用时,旧的序号数据早已经通过网络到达终点了。
确认号(ack):是期望收到对方下一个报文段的第一个数据字节的序号。
例如:B正确收到了A发送过来的一个报文段,其序号值是501,数据长度是200字节(序号501~700),这表明B期望收到A的下一个数据号是701,于是B在发送给A的确认报文段中把确认号设置为701。
头部长度(数据偏移): 头部长度的单位是32位(4字节),即如果这个字段的值位0110(6),那头部的长度就是24个字节,固定头部是20个字节,那选项部分就只有4个字节。 由于头部字段占4位,即最长的头部长度为4 * 16 = 60字节,即选项最多40字节。
保留:保留为今后使用,但目前应置为0。
接下来的6个控制位是说明报文段性质用的。
紧急URG:用于处理紧急报文段。也就是这个报文段优先级高,最先处理。要发紧急报文段就把这个位置为1。后面会介绍如何处理紧急报文段。
确认ACK:是经过确认过的报文段。比如建立连接后的所有传送的报文段都必须把ACK置为1。
推送PSH:推送操作很少使用,可以作为了解。(这个跟我业务上说的push不一样哈)
复位RST:当RST=1时,表明TCP连接中出现严重差错,必须释放连接,再重新建立连接。RST置1还用来拒绝一个非法的报文段或拒绝打开一个连接。
同步SYN:在连接建立时用来同步序号。一般跟ACK配合使用。
当SYN=1而ACK=0时,表明这是一个连接请求报文段。若对方同意建立连接,则在响应的报文段中使SYN=1和ACK=1。
因此,SYN置为1就表示这是一个连接请求或连接接受报文。后面的文章会介绍TCP连接的建立和TCP连接的释放。
FIN: 表示发送端已经达到数据末尾,也就是说双方的数据传送完成,没有数据可以传送了,发送FIN标志位的TCP数据包后,连接将被断开。这个标志的数据包也经常被用于进行端口扫描。
窗口:是TCP流量控制的一个手段(还记得我们之前说实现可靠性的第二个条件吗?控制发送速度!就是通过窗口来控制发送速度的!)。
这的窗口是指发送本报文段己方的接收窗口(还有发送窗口,但这是指接收窗口),占2字节,所以值是的范围是0~2的16次方-1间的整数。
窗口值告诉对方(重点!是告诉对方):从本报文段的确认号算起,自己目前允许对方发送的数据量(还能容纳多少字节数据),这样对方就可以控制发送速度。有点绕哈,举例说明:
如果我发送的这个报文段的确认号是701,窗口值是1000,就表明从701号算起(序号必须是>=701),我还能接收1000个字节数据(接收缓存空间)。
窗口值就是对方设置发送窗口的依据。
校验和:由发送端填充,接收端对TCP报文段执行CRC算法,以检验TCP报文段在传输过程中是否有损坏。
这的校验不仅包括头部,也包括数据部分。是可靠传输的重要保障!
紧急指针:指出本报文段中的紧急数据的字节数。它指出了紧急数据的末尾在报文段中的位置(这不是很理解,没有关系,在后文会有介绍)!紧急指针仅在URG=1时才有意义。
头部选项:
典型的头部选项结构如下图:
选项的第一个kind说明选项的类型。第二个length指定该选项的总长度,该长度包括kind和length所占的两个字节。第三个info是选项的具体信息。
有的TCP选项只有kind字段。常见的TCP选项有7种:
kind=0:表示结束选项
kind=1:空选项,没有特殊含义
kind=2:最大报文段长度(MSS)选项。TCP模块通常将MSS设置为MTU(IP层的最大传输单元,最大值1500)-40(20TCP头部字节+20IP头部字节)。所以MSS最大值是1500-40=1460字节。MSS的默认值是536字节。
kind=3:窗口扩大选项。防止窗口值不够用。如果头部中窗口值是N,窗口扩大因子是M,则,窗口的最大值是2的(N+M)次方 - 1。
窗口扩大选项可以在双方初始建立TCP连接时进行协商。如果某一端实现了窗口扩大,当他不在需要扩大其窗口时,将该项值设置成0即可。
kind=4:选择性确认(SACK)选项。我们前面在停止、等待协议说到会出现重复发送分组或者重复发送确认的情况。SACK就是为改善这种情况发明的技术,它使TCP通信端只重新发送丢失的TCP报文段。
kind=5:SACK实际工作选项。该选项的参数告诉发送方端已经接收到并缓存的是不连续的数据块(有数据丢失),从而让发送端可以根据此检查并重新发送丢失的数据块。
选择确认我们后面还会介绍。
kind=8:时间戳选项。该选项主要有两个功能:
1. 用来计算往返时间RTT。发送方在发送报文段时把当前时钟的时间值放入时间戳值字段,接收方在确认该报文段时,把时间戳字段值复制到时间戳回送回答字段!
2. 处理TCP序号超过2的32次方的情况!称为防止序号绕回(PAWS)。序号只有32位,当使用高速网络,TCP连接的数据传送中很可能,序号很可能被重复使用!那就是加个时间戳不就区别开了!