《计算机网络》--TCP/UDP协议
推荐大家去看这本谢仁希老师的计算机网络,内容讲的很清楚很好,细细的看可以收获很多。
文章目录
1. 传输层
1. 传输层作用
传输层向他上面的应用提供通信服务,属于面向通信部分的最高层,也是用户功能中的最底层。
从IP层来说,通信的两端是两个主机,IP数据包首部明确标志了两个主机的地址。但是两个主机之间的通信这种说法还不构清除,因为真正通信的实体是在主机中的进程,是这个主机间的进程和另一个主机间的进程在交换数据,因此严格的将,两个主机之间的通信就是两个主机中的应用进程互相通信,IP协议虽然能把分组送往目的主机,但是这个分组还停留在主机的网络层而没有交付给主机的应用进程,从传输层的角度来说,通信的真正断点并不是主机,而是主机中的进程,也就是说端到端的通信就是主机之间的通信。
如图:两个运输层之间有一个双向粗箭头,写明“运输层提供应用进程间的逻辑通信”。“逻辑通信”的意思是:从应用层来看,只要把应用层报文交给下面的运输层,运输层就可以把这报文传送到对方的运输层(那怕双方相距很远,例如几千公里),好像这种通信就是沿水平方向直接传送数据。但事实上这两个运输层之间并没有一条水平方向的物理连接。数据的传送是沿着图中的虚线方向(经过多个层次)传送的。“逻辑通信”的意思是“好像是这样通信,但事实上并非真的这样通信”。
网络层是为主机之间提供逻辑通信,而运输层为应用进程之间提供端到端的逻辑通信。运输层还要对收到的报文进行差错检测。在网络层,IP数据报首部中的检验和字段,只检验首部是否出现差错而不检查数据部分。
2. TCP和UDP协议
① 用户数据报协议UDP
UDP在传送数据之前不需要先建立连接。远地主机的运输层在收到UDP报文后,不需要给出任何确认。虽然UDP不提供可靠交付,但在某些情况下UDP却是一种最有效的工作方式。TCP则提供面向连接的服务。在传送数据之前必须先建立连接,数据传送结束后要释放连接
② 传输控制协议TCP
TCP不提供广播或多播服务。由于TCP要提供可靠的、面向连接的运输服务,因此不可避免地增加了许多的开销,如确认、流量控制、计时器以及连接管理等。这不仅使协议数据单元的首部增大很多,还要占用许多的处理机资源。
传输层的端口:
应用层所有的应用进程都可以通过运输层再传送到IP层(网络层),这就是复用。运输层从IP层收到数据后必须交付指明的应用进程。这就是分用。显然,给应用层的每个应用进程赋予一个非常明确的标志是至关重要的。
两个计算机中的进程要互相通信,不仅必须知道对方的IP地址(为了找到对方的计算机),而且还要知道对方的端口号(为了找到对方计算机中的应用进程)。
3. UDP协议的特点
用户数据报协议UDP只在IP的数据报服务之上增加了很少一点的功能,这就是复用和分用的功能以及差错检测的功能。UDP的主要特点是:
① UDP是无连接的,即发送数据之前不需要建立连接(当然,发送数据结束时也没有连接可释放),因此减少了开销和发送数据之前的时延。
② UDP使用尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的连接状态表(这里面有许多参数)。
③ UDP是面向报文的。发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付IP层。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。这就是说,应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文,如图所示。在接收方的UDP,对IP层交上来的UDP用户数据报,在去除首部后就原封不动地交付上层的应用进程。也就是说,UDP一次交付一个完整的报文。因此,应用程序必须选择合适大小的报文。若报文太长,UDP把它交给IP层后,IP层在传送时可能要进行分片,这会降低IP层的效率。反之,若报文太短,UDP把它交给IP层后,会使IP数据报的首部的相对长度太大,这也降低了IP层的效率。
④ UDP没有拥塞控制,因此网络出现的拥塞不会使源主机的发送速率降低。这对某些实时应用是很重要的。很多的实时应用(如IP电话、实时视频会议等)要求源主机以恒定的速率发送数据,并且允许在网络发生拥塞时丢失一些数据,但却不允许数据有太大的时延。UDP正好适合这种要求。
⑤ UDP支持一对一、一对多、多对一和多对多的交互通信。
⑥ UDP的首部开销小,只有8个字节,比TCP的20个字节的首部要短。
4. UDP协议的首部格式
用户数据报UDP有两个字段:数据字段和首部字段。首部字段很简单,只有8个字节 ,由四个字段组成,每个字段的长度都是两个字节。各字段意义如下:
(1) 源端口 源端口号。在需要对方回信时选用。不需要时可用全0。
(2) 目的端口 目的端口号。这在终点交付报文时必须要使用到。
(3) 长度 UDP用户数据报的长度,其最小值是8(仅有首部)。
(4) 检验和 检测UDP用户数据报在传输中是否有错。有错就丢弃。
UDP用户数据报首部中检验和的计算方法有些特殊。在计算检验和时,要在UDP用户数据报之前增加12个字节的伪首部。所谓“伪首部”是因为这种伪首部并不是UDP用户数据报真正的首部。只是在计算检验和时,临时添加在UDP用户数据报前面,得到一个临时的UDP用户数据报。检验和就是按照这个临时的UDP用户数据报来计算的。伪首部既不向下传送也不向上递交,而仅仅是为了计算检验和。
UDP计算检验和的方法和计算IP数据报首部检验和的方法相似。但不同的是:IP数据报的检验和只检验IP数据报的首部,但UDP的检验和是把首部和数据部分一起都检验。
当运输层从IP层收到UDP数据报时,就根据首部中的目的端口,把UDP数据报通过相应的端口,上交最后的终点——应用进程。
5. TCP协议的特点
(1) TCP是面向连接的运输层协议。这就是说,应用程序在使用TCP协议之前,必须先建立TCP连接。在传送数据完毕后,必须释放已经建立的TCP连接。
(2) 每一条TCP连接只能有两个端点(endpoint),每一条TCP连接只能是点对点的(一对一)。
(3) TCP提供可靠交付的服务。通过TCP连接传送的数据,无差错、不丢失、不重复、并且按序到达。
(4) TCP提供全双工通信。TCP允许通信双方的应用进程在任何时候都能发送数据。TCP连接的两端都设有发送缓存和接收缓存,用来临时存放双向通信的数据。在发送时,应用程序在把数据传送给TCP的缓存后,就可以做自己的事,而TCP在合适的时候把数据发送出去。在接收时,TCP把收到的数据放入缓存,上层的应用进程在合适的时候读取缓存中的数据。
6. TCP可靠传输的原理
TCP发送的报文段是交给IP层传送的。但IP层只能提供尽最大努力服务,也就是说,TCP下面的网络所提供的是不可靠的传输。因此,TCP必须采用适当的措施才能使得两个运输层之间的通信变得可靠。
理想的传输条件有以下两个特点:(1) 传输信道不产生差错。(2) 不管发送方以多快的速度发送数据,接收方总是来得及处理收到的数据。
在这样的理想传输条件下,不需要采取任何措施就能够实现可靠传输。然而实际的网络都不具备以上两个理想条件**。但我们可以使用一些可靠传输协议,当出现差错时让发送方重传出现差错的数据,同时在接收方来不及处理收到的数据时,及时告诉发送方适当降低发送数据的速度。**这样一来,本来是不可靠的传输信道就能够实现可靠传输了。
6.1 停止等待协议
**全双工通信的双方既是发送方也是接收方。**下面为了讨论问题的方便,我们仅考虑A发送数据而B接收数据并发送确认。因此A叫做发送方,而B叫做接收方。因为这里是讨论可靠传输的原理,因此把传送的数据单元都称为分组,而并不考虑数据是在哪一个层次上传送的 。“停止等待”就是每发送完一个分组就停止发送,等待对方的确认。在收到确认后再发送下一个分组。
① 无差错情况:
图(a)为最简单的无差错情况。A发送分组M1,发完就暂停发送,等待B的确认。B收到了M1就向A发送确认。A在收到了对M1的确认后,就再发送下一个分组M2。同样,在收到B对M2的确认后,再发送M3。
② 出现差错情况:
图(b) 是分组在传输过程中出现差错的情况。B接收M1时检测出了差错,就丢弃M1,其他什么也不做(不通知A收到有差错的分组)。也可能是M1在传输过程中丢失了,这时B当然什么都不知道。在这两种情况下,B都不会发送任何信息。可靠传输协议是这样设计的:A只要超过了一段时间仍然没有收到确认,就认为刚才发送的分组丢失了,因而重传前面发送过的分组。这就叫做超时重传。要实现超时重传,就要在每发送完一个分组设置一个超时计时器。如果在超时计时器到期之前收到了对方的确认,就撤销已设置的超时计时器。A为每一个已发送的分组都设置了一个超时计时器。但A只要在超时计时器到期之前收到了相应的确认,就撤销该超时计时器。
③ 确认都是和确认迟到:
(a) 图确认丢失:B所发送的对M1的确认丢失了。A在设定的超时重传时间内没有收到确认,但并无法知道是自己发送的分组出错、丢失,或者是B发送的确认丢失了。因此A在超时计时器到期后就要重传M1。现在应注意B的动作。假定B又收到了重传的分组M1。这时应采取两个行动:
第一,丢弃这个重复的分组M1,不向上层交付。第二,向 A 发送确认。不能认为已经发送过确认就不再发送,因为A之所以重传M1就表示A没有收到对M1的确认。
(b) 图确认迟到:传输过程中没有出现差错,但B对分组M1的确认迟到了。A会收到重复的确认。对重复的确认的处理很简单:收下后就丢弃。B仍然会收到重复的M1,并且同样要丢弃重复的M1,并重传确认分组。
像上述的这种可靠传输协议常称为指导工重传ARQ协议。意思是重传的请求是自动进行的。接收方不需要请求发送方重传某个出错的分组。
6.2 连续ARQ协议
发送方维持的发送窗口,它的意义是:位于发送窗口内的5个分组都可连续发送出去,而不需要等待对方的确认。这样,信道利用率就提高了。
连续ARQ协议规定,发送方每收到一个确认,就把发送窗口向前滑动一个分组的位置。图(b)表示发送方收到了对第1个分组的确认,于是把发送窗口向前移动一个分组的位置。如果原来已经发送了前5个分组,那么现在就可以发送窗口内的第6个分组了。接收方一般都是采用累积确认的方式。这就是说,接收方不必对收到的分组逐个发送确认,而是在收到几个分组后,对按序到达的最后一个分组发送确认,这就表示:到这个分组为止的所有分组都已正确收到了。
累积确认有优点也有缺点。优点是:容易实现,即使确认丢失也不必重传。但缺点是不能向发送方反映出接收方已经正确收到的所有分组的信息。例如,如果发送方发送了前5个分组,而中间的第3个分组丢失了。这时接收方只能对前两个分组发出确认。发送方无法知道后面三个分组的下落,而只好把后面的三个分组都再重传一次。这就叫做回退N帧,表示需要再退回来重传已发送过的N个分组。
7. TCP报文段的首部格式
TCP虽然是面向字节流的,但TCP传送的数据单元却是报文段。一个TCP报文段分为首部和数据两部分,而TCP的全部功能都体现在它首部中各字段的作用。因此,只有弄清TCP首部各字段的作用才能掌握TCP的工作原理。下面就讨论TCP报文段的首部格式。
(1) 源端口和目的端口 各占2个字节,分别写入源端口号和目的端口号。
(2) 序号 占4字节。序号范围是[0, 2^32 - 1],共2^32 个序号。序号增加到2^32 - 1后,下一个序号就又回到0。TCP是面向字节流的。**在一个TCP连接中传送的字节流中的每一个字节都按顺序编号。**整个要传送的字节流的起始序号必须在连接建立时设置。首部中的序号字段值则指的是本报文段所发送的数据的第一个字节的序号。例如,一报文段的序号字段值是301,而携带的数据共有100字节。这就表明:本报文段的数据的第一个字节的序号是301,最后一个字节的序号是400。显然,下一个报文段(如果还有的话)的数据序号应当从401开始,即下一个报文段的序号字段值应为401。这个字段的名称也叫做“报文段序号”。
(3) 确认号 占4字节,是期望收到对方下一个报文段的第一个数据字节的序号。例如,B正确收到了A发送过来的一个报文段,其序号字段值是501,而数据长度是200字节(序号501~700),这表明B正确收到了A发送的到序号700为止的数据。因此,B期望收到A的下一个数据序号是701,于是B在发送给A的确认报文段中把确认号置为701。请注意,现在的确认号不是501,也不是700,而是701。
总之,应当记住:若确认号 = N,则表明:到序号N - 1为止的所有数据都已正确收到。
(4) 同步SYN
在连接建立时用来同步序号。当SYN = 1而ACK= 0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使SYN = 1和ACK = 1。因此,SYN置为1就表示这是一个连接请求或连接接受报文。关于连接的建立和释放,在本章的5.9节还要进行详细讨论。
(5) 终止FIN
用来释放一个连接。当FIN = 1时,表明此报文段的发送方的数据已发送完毕,并要求释放运输连接。
(6) 确认ACK
仅当ACK = 1时确认号字段才有效。当ACK = 0时,确认号无效。TCP规定,在连接建立后所有传送的报文段都必须把ACK置1。
(7) 窗口 占2字节。窗口值是[0, 2^16 - 1]之间的整数。窗口指的是发送本报文段的一方的接收窗口(而不是自己的发送窗口)。窗口值告诉对方:从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。总之,窗口值作为接收方让发送方设置其发送窗口的依据。
8. TCP可靠传输的实现
现假定A收到了B 发来的确认报文段,其中窗口是20(字节),而确认号是31(这表明B期望收到的下一个序号是31,而序号30为止的数据已经收到了)。根据这两个数据,A就构造出自己的发送窗口,其位置如图所示。
根据B给出的窗口值,A构造出自己的发送窗口:
我们先讨论发送方A的发送窗口。发送窗口表示:在没有收到B的确认的情况下,A可以连续把窗口内的数据都发送出去。凡是已经发送过的数据,在未收到确认之前都必须暂时保留,以便在超时重传时使用。
发送窗口里面的序号表示允许发送的序号。显然,窗口越大,发送方就可以在收到对方确认之前连续发送更多的数据,因而可能获得更高的传输效率。但接收方必须来得及处理这些收到的数据。
发送窗口后沿的后面部分表示已发送且已收到了确认。这些数据显然不需要再保留了。而发送窗口前沿的前面部分表示不允许发送的,因为接收方都没有为这部分数据保留临时存放的缓存空间。
发送窗口的位置由窗口前沿和后沿的位置共同确定。发送窗口后沿的变化情况有两种可能,即不动(没有收到新的确认)和前移(收到了新的确认)。发送窗口后沿不可能向后移动,因为不能撤销掉已收到的确认。发送窗口前沿通常是不断向前移动,但也有可能不动。这对应于两种情况:一是没有收到新的确认,对方通知的窗口大小也不变;二是收到了新的确认但对方通知的窗口缩小了,使得发送窗口前沿正好不动。
现在假定A发送了序号为31~41的数据。这时,发送窗口位置并未改变(图5-16),但发送窗口内靠后面有11个字节(灰色小方框表示)表示已发送但未收到确认。而发送窗口内靠前面的9个字节(42~50)是允许发送但尚未发送的。
从以上所述可以看出,要描述一个发送窗口的状态需要三个指针:P1,P2和P3(图5-16)。指针都指向字节的序号。这三个指针指向的几个部分的意义如下:小于P1的是已发送并已收到确认的部分,而大于P3的是不允许发送的部分。P3 - P1 = A的发送窗口(又称为通知窗口)P2 - P1 = 已发送但尚未收到确认的字节数P3 - P2 = 允许发送但尚未发送的字节数(又称为可用窗口或有效窗口)
再看一下B的接收窗口。B的接收窗口大小是20。在接收窗口外面,到30号为止的数据是已经发送过确认,并且已经交付主机了。因此在B可以不再保留这些数据。接收窗口内的序号(31~50)是允许接收的。在图5-16中,B收到了序号为32和33的数据。这些数据没有按序到达,因为序号为31的数据没有收到(也许丢失了,也许滞留在网络中的某处)。请注意,B只能对按序收到的数据中的最高序号给出确认,因此B发送的确认报文段中的确认号仍然是31(即期望收到的序号),而不能是32或33。
现在假定B收到了序号为31的数据,并把序号为31~33的数据交付主机,然后B删除这些数据。接着把接收窗口向前移动3个序号,如下图,同时给A发送确认,其中窗口值仍为20,但确认号是34。这表明B已经收到了到序号33为止的数据。我们注意到,B还收到了序号为37, 38和40的数据,但这些都没有按序到达,只能先暂存在接收窗口中。A收到B的确认后,就可以把发送窗口向前滑动3个序号,但指针P2不动。可以看出,现在A的可用窗口增大了,可发送的序号范围是42~53。
A在继续发送完序号42~53的数据后,指针P2向前移动和P3重合。发送窗口内的序号都已用完,但还没有再收到确认(图)。由于A的发送窗口已满,可用窗口已减小到零,因此必须停止发送。请注意,存在下面这种可能性,就是发送窗口内所有的数据都已正确到达B,B也早已发出了确认。但不幸的是,所有这些确认都滞留在网络中。在没有收到B的确认时,A不能猜测:“或许B收到了吧!”为了保证可靠传输,A只能认为B还没有收到这些数据。于是,A在经过一段时间后(由超时计时器控制)就重传这部分数据,重新设置超时计时器,直到收到B的确认为止。如果A收到确认号落在发送窗口内,那么A就可以使发送窗口继续向前滑动,并发送新的数据。
9. 流量控制
一般说来,我们总是希望数据传输得更快一些。但如果发送方把数据发送得过快,接收方就可能来不及接收,这就会造成数据的丢失。所谓流量控制(flow control)就是让发送方的发送速率不要太快,要让接收方来得及接收。
利用滑动窗口机制可以很方便地在TCP连接上实现对发送方的流量控制。
设A向B发送数据。在连接建立时,B告诉了A:“我的接收窗口rwnd = 400” 。因此,发送方的发送窗口不能超过接收方给出的接收窗口的数值。请注意,TCP的窗口单位是字节,不是报文段。TCP连接建立时的窗口协商过程在图中没有显示出来。再设每一个报文段为100字节长,而数据报文段序号的初始值设为1(见图中第一个箭头上面的序号seq = 1。图中右边的注释可帮助理解整个的过程)。请注意,图中箭头上面大写ACK表示首部中的确认位ACK,小写ack表示确认字段的值。
我们应注意,接收方的主机B进行了三次流量控制。第一次把窗口减小到rwnd =300,第二次又减到rwnd = 100,最后减到rwnd = 0,即不允许发送方再发送数据了。这种使发送方暂停发送的状态将持续到主机B重新发出一个新的窗口值为止。我们还应注意到,B向A发送的三个报文段都设置了ACK = 1,只有在ACK = 1时确认号字段才有意义。
10. TCP的拥塞控制
在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏。这种情况就叫做拥塞(congestion)。可以把出现网络拥塞的条件写成如下的关系式:
拥塞控制的四种算法,即慢开始(slow-start)、拥塞避免(congestion avoidance)、快重传(fast retransmit)和快恢复(fast recovery)。
为了集中精力讨论拥塞控制,我们假定:
(1) 数据是单方向传送,而另一个方向只传送确认。
(2) 接收方总是有足够大的缓存空间,因而发送窗口的大小由网络的拥塞程度来决定。
发送方维持一个叫做拥塞窗口 cwnd (congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送方让自己的发送窗口等于拥塞窗口。以后我们就知道,如果再考虑到接收方的接收能力,那么发送窗口还可能小于拥塞窗口。发送方控制拥塞窗口的原则是:只要网络没有出现拥塞,拥塞窗口就再增大一些,以便把更多的分组发送出去。但只要网络出现拥塞,拥塞窗口就减小一些,以减少注入到网络中的分组数。
① 慢开始算法:
慢开始算法的思路是这样的。当主机开始发送数据时,如果立即把大量数据字节注入到网络,那么就有可能引起网络拥塞,因为现在并不清楚网络的负荷情况。经验证明,较好的方法是先探测一下,即由小到大逐渐增大发送窗口,也就是说,由小到大逐渐增大拥塞窗口数值。通常在刚刚开始发送报文段时,先把拥塞窗口cwnd设置为一个最大报文段MSS的数值[插图]。而在每收到一个对新的报文段的确认后,把拥塞窗口增加至多一个MSS的数值。用这样的方法逐步增大发送方的拥塞窗口cwnd,可以使分组注入到网络的速率更加合理。
在一开始发送方先设置cwnd = 1,发送第一个报文段M1,接收方收到后确认M1。发送方收到对M1的确认后,把cwnd从1增大到2,于是发送方接着发送M2和M3两个报文段。接收方收到后发回对M2和M3的确认。发送方每收到一个对新报文段的确认(重传的不算在内)就使发送方的拥塞窗口加1,因此发送方在收到两个确认后,cwnd就从2增大到4,并可发送M4~M7共4个报文段(见图5-24)。因此使用慢开始算法后,每经过一个传输轮次,拥塞窗口cwnd就加倍。
慢开始的“慢”并不是指cwnd的增长速率慢,而是指在TCP开始发送报文段时先设置cwnd = 1,使得发送方在开始时只发送一个报文段(目的是试探一下网络的拥塞情况),然后再逐渐增大cwnd。这当然比按照大的cwnd一下子把许多报文段突然注入到网络中要“慢得多”。这对防止网络出现拥塞是一个非常有力的措施。
② 拥塞避免算法:
拥塞避免算法的思路是让拥塞窗口cwnd缓慢地增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1[插图],而不是加倍。这样,拥塞窗口cwnd 按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。
为了防止拥塞窗口cwnd增长过大引起网络拥塞,还需要设置一个慢开始门限ssthresh状态变量 。慢开始门限ssthresh的用法如下:当cwnd < ssthresh时,使用慢开始算法。当cwnd > ssthresh时,停止使用慢开始算法而改用拥塞避免算法。当cwnd = ssthresh时,既可使用慢开始算法,也可使用拥塞避免算法。
无论在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有按时收到确认),就要把慢开始门限ssthresh设置为出现拥塞时的发送方窗口值的一半(但不能小于2) 。然后把拥塞窗口cwnd重新设置为1,执行慢开始算法。这样做的目的就是要迅速减少主机发送到网络中的分组数,使得发生拥塞的路由器有足够时间把队列中积压的分组处理完毕。
③ 快重传算法:
快重传算法首先要求接收方每收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方)而不要等待自己发送数据时才进行捎带确认。在图5-26所示的例子中,接收方收到了M1和M2后都分别发出了确认。现假定接收方没有收到M3但接着收到了M4。显然,接收方不能确认M4,因为M4是收到的失序报文段(按照顺序的M3还没有收到)。根据可靠传输原理,接收方可以什么都不做,也可以在适当时机发送一次对M2的确认。但按照快重传算法的规定,接收方应及时发送对 M2的重复确认,这样做可以让发送方及早知道报文段M3没有到达接收方。发送方接着发送M5和M6。接收方收到后,也还要再次发出对M2的重复确认。这样,发送方共收到了接收方的四个对M2的确认,其中后三个都是重复确认。快重传算法规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段M3,而不必继续等待为M3设置的重传计时器到期。由于发送方能尽早重传未被确认的报文段,因此采用快重传后可以使整个网络的吞吐量提高约20%。
④ 快恢复算法:
(1) 当发送方连续收到三个重复确认时,就执行“乘法减小”算法,把慢开始门限ssthresh减半。这是为了预防网络发生拥塞。请注意,接下去不执行慢开始算法。
(2) 由于发送方现在认为网络很可能没有发生拥塞(如果网络发生了严重的拥塞,就不会一连有好几个报文段连续到达接收方,也就不会导致接收方连续发送重复确认),因此与慢开始不同之处是现在不执行慢开始算法(即拥塞窗口cwnd现在不设置为1),而是把cwnd值设置为慢开始门限ssthresh减半后的数值,然后开始执行拥塞避免算法(“加法增大”),使拥塞窗口缓慢地线性增大。
11. TCP连接建立
TCP是面向连接的协议。运输连接是用来传送TCP报文的。TCP运输连接的建立和释放是每一次面向连接的通信中必不可少的过程。因此,运输连接就有三个阶段,即:连接建立、数据传送和连接释放。运输连接的管理就是使运输连接的建立和释放都能正常地进行。
TCP连接的建立采用客户服务器方式。主动发起连接建立的应用进程叫做客户(client),而被动等待连接建立的应用进程叫做服务器(server)。
假定主机A运行的是TCP客户程序,而B运行TCP服务器程序。最初两端的TCP进程都处于CLOSED(关闭)状态。图中在主机下面的方框分别是TCP进程所处的状态。请注意,A主动打开连接,而B被动打开连接。
B的TCP服务器进程先创建传输控制块 TCB[插图],准备接受客户进程的连接请求。然后服务器进程就处于LISTEN(收听)状态,等待客户的连接请求。如有,即作出响应。
A的TCP客户进程也是首先创建传输控制模块TCB,然后向B发出连接请求报文段,这时首部中的同步位SYN = 1,同时选择一个初始序号seq = x。TCP规定,SYN报文段(即SYN = 1的报文段)不能携带数据,但要消耗掉一个序号。这时,TCP客户进程进入SYN-SENT(同步已发送)状态。
B收到连接请求报文段后,如同意建立连接,则向A发送确认。在确认报文段中应把SYN位和ACK位都置1,确认号是ack = x + 1,同时也为自己选择一个初始序号seq = y。请注意,这个报文段也不能携带数据,但同样要消耗掉一个序号。这时TCP服务器进程进入SYN-RCVD(同步收到)状态。
TCP客户进程收到B的确认后,还要向B给出确认。确认报文段的ACK置1,确认号ack = y + 1,而自己的序号seq = x + 1。TCP的标准规定,ACK报文段可以携带数据。但如果不携带数据则不消耗序号,在这种情况下,下一个数据报文段的序号仍是seq = x + 1。这时,TCP连接已经建立,A进入ESTABLISHED(已建立连接)状态。
当B收到A的确认后,也进入ESTABLISHED状态。
为什么A还要发送一次确认呢?这主要是为了防止已失效的连接请求报文段突然又传送到了B,因而产生错误。所谓“已失效的连接请求报文段”是这样产生的。考虑一种正常情况。A发出连接请求,但因连接请求报文丢失而未收到确认。于是A再重传一次连接请求。后来收到了确认,建立了连接。数据传输完毕后,就释放了连接。A共发送了两个连接请求报文段,其中第一个丢失,第二个到达了B。没有“已失效的连接请求报文段”。
现假定出现一种异常情况,即A发出的第一个连接请求报文段并没有丢失,而是在某些网络结点长时间滞留了,以致延误到连接释放以后的某个时间才到达B。本来这是一个早已失效的报文段。但B收到此失效的连接请求报文段后,就误认为是A又发出一次新的连接请求。于是就向A发出确认报文段,同意建立连接。假定不采用三次握手,那么只要B发出确认,新的连接就建立了。
由于现在A并没有发出建立连接的请求,因此不会理睬B的确认,也不会向B发送数据。但B却以为新的运输连接已经建立了,并一直等待A发来数据。B的许多资源就这样白白浪费了。
12. TCP连接的释放
TCP连接释放过程比较复杂,我们仍结合双方状态的改变来阐明连接释放的过程。**数据传输结束后,通信的双方都可释放连接。**现在A和B都处于ESTABLISHED状态 。A的应用进程先向其TCP发出连接释放报文段,并停止再发送数据,主动关闭TCP连接。A把连接释放报文段首部的终止控制位FIN置1,其序号seq =u,它等于前面已传送过的数据的最后一个字节的序号加1。这时A进入FIN-WAIT-1(终止等待1)状态,等待B的确认。请注意,TCP规定,FIN报文段即使不携带数据,它也消耗掉一个序号。
B收到连接释放报文段后即发出确认,确认号是ack = u + 1,而这个报文段自己的序号是v,等于B前面已传送过的数据的最后一个字节的序号加1。然后B就进入CLOSE-WAIT(关闭等待)状态。TCP服务器进程这时应通知高层应用进程,因而从A到B这个方向的连接就释放了,这时的TCP连接处于半关闭(half-close)状态,即A已经没有数据要发送了,但B若发送数据,A仍要接收。也就是说,从B到A这个方向的连接并未关闭,这个状态可能会持续一些时间。
若B已经没有要向A发送的数据,其应用进程就通知TCP释放连接。这时B发出的连接释放报文段必须使FIN = 1。现假定B的序号为w(在半关闭状态B可能又发送了一些数据)。B还必须重复上次已发送过的确认号ack = u + 1。这时B就进入LAST-ACK(最后确认)状态,等待A的确认。
A在收到B的连接释放报文段后,必须对此发出确认。在确认报文段中把ACK置1,确认号ack = w + 1,而自己的序号是seq = u + 1(根据TCP标准,前面发送过的FIN报文段要消耗一个序号)。然后进入到TIME-WAIT(时间等待)状态。请注意,现在TCP连接还没有释放掉。必须经过时间等待计时器(TIME-WAIT timer)设置的时间2MSL后,A才进入到CLOSED状态。
2. 应用层
1. 超文本传输协议HTTP
每个万维网网点都有一个服务器进程,它不断地监听TCP的端口80,以便发现是否有浏览器向它发出连接建立请求。一旦监听到连接建立请求并建立了TCP连接之后,浏览器就向服务器发出浏览某个页面的请求,服务器接着就返回所请求的页面作为响应。最后,TCP连接就被释放了。在浏览器和服务器之间的请求和响应的交互,必须按照规定的格式和遵循一定的规则。这些格式和规则就是超文本传送协议HTTP。
假定图中的用户用鼠标点击了屏幕上的一个可选部分。他使用的链接指向了“清华大学院系设置”的页面,其URL是http://www.tsinghua.edu.cn/chn/yxsz/index.htm。下面我们用HTTP/1.0更具体地说明在用户点击鼠标后所发生的几个事件:
(1) 浏览器分析链接指向页面的URL。
(2) 浏览器向DNS请求解析www.tsinghua.edu.cn的IP地址。
(3) 域名系统DNS解析出清华大学服务器的IP地址为166.111.4.100。
(4) 浏览器与服务器建立TCP连接(在服务器端IP地址是166.111.4.100,端口是80)。
(5) 浏览器发出取文件命令:GET /chn/yxsz/index.htm。
(6) 服务器www.tsinghua.edu.cn给出响应,把文件index.htm发送给浏览器。
(7) 释放TCP连接。
(8) 浏览器显示“清华大学院系设置”文件index.htm中的所有文本。
HTTP使用了面向连接的TCP作为运输层协议,保证了数据的可靠传输。HTTP不必考虑数据在传输过程中被丢弃后又怎样被重传。但是,HTTP协议本身是无连接的。这就是说,虽然HTTP使用了TCP连接,但通信的双方在交换HTTP报文之前不需要先建立HTTP连接。
用户在点击鼠标链接某个万维网文档时,HTTP协议首先要和服务器建立TCP连接。这需要使用三次握手。当三次握手的前两部分完成后(即经过了一个RTT时间后),**万维网客户就把HTTP请求报文作为三次握手的第三个报文的数据发送给万维网服务器。**服务器收到HTTP请求报文后,就把所请求的文档作为响应报文返回给客户。
请求一个万维网文档所需的时间是该文档的传输时间加上两倍往返时间RTT(一个RTT用于连接TCP连接,另一个RTT用于请求和接收万维网文档。这里TCP建立连接的三次握手的第三个报文段中捎带了客户对万维网文档的请求)。
HTTP/1.0的主要缺点,就是每请求一个文档就要有两倍RTT的开销。若一个主页上有很多链接的对象(如图片等)需要依次进行链接,那么每一次链接下载都导致2×RTT的开销。另一种开销就是万维网客户和服务器为每一次建立新的TCP连接都要分配缓存和变量。特别是万维网服务器往往要同时服务于大量客户的请求,所以这种非持续连接会使万维网服务器的负担很重。
HTTP/1.1协议较好地解决了这个问题,它使用了持续连接(persistentconnection)。所谓持续连接就是万维网服务器在发送响应后仍然在一段时间内保持这条连接,使同一个客户(浏览器)和该服务器可以继续在这条连接上传送后续的HTTP请求报文和响应报文。
2. HTTP的报文结构
HTTP有两类报文:
(1) 请求报文——从客户向服务器发送请求报文
(2) 响应报文——从服务器到客户的回答
HTTP请求报文和响应报文都是由三个部分组成。可以看出,这两种报文格式的区别就是开始行不同。
(1) 开始行,用于区分是请求报文还是响应报文。在请求报文中的开始行叫做请求行,而在响应报文中的开始行叫做状态行 。在开始行的三个字段之间都以空格分隔开,最后的“CR”和“LF”分别代表“回车”和“换行”。
(2) 首部行,用来说明浏览器、服务器或报文主体的一些信息。首部可以有好几行,但也可以不使用。在每一个首部行中都有首部字段名和它的值,每一行在结束的地方都要有“回车”和“换行”。整个首部行结束时,还有一空行将首部行和后面的实体主体分开。
(3) 实体主体 ,在请求报文中一般都不用这个字段,而在响应报文中也可能没有这个字段。
状态码(Status-Code)都是三位数字的,分为5大类共33种 。
1xx表示通知信息的,如请求收到了或正在进行处理。
2xx表示成功,如接受或知道了。
3xx表示重定向,如要完成请求还必须采取进一步的行动。
4xx表示客户的差错,如请求中有错误的语法或不能完成。
5xx表示服务器的差错,如服务器失效无法完成请求。