计算机网络——传输层:TCP与UDP

传输层与网络层一齐构建成了网络协议层次的核心。网络层解决的是计算机与计算机之间的通信问题,而传输层解决的是进程与进程之间的通信问题。传输层架构在网络层提供的服务之上,把数据传递服务从两台计算机之间拓展到两台计算机上的进程之间。因此,在传输层通信除了IP之外还需要有端口号来确认是与目标计算机上那个进程通信。与网络层类似的,传输层也提供了两种服务类型。一是面向连接的TCP传输,另外一种是无连接的UDP服务。所以要了解传输层的细节,就是要了解清楚TCP与UDP的细节。

UDP:用户数据报协议(User Datagram Protocol)为应用程序提供一种无需建立连接就可以发送封装的IP数据报的方法。UDP传输模型可以类比为邮递方式,发送方在发送之前无需建立连接,只需要知道接收方的地址(IP和Port),然后把数据报发送出去后,由网络自行路由,最终能否到达目的地,什么时候到达,发送方是不会得到反馈的。UDP除了提供发送数据包功能之外,几乎没有做任何事情,使用UDP传输的时候应用程序应该自己组织策略应对丢包、乱序和数据出错的情况。

TCP:传输控制协议(Transmission Control Protocol)是为了在不可靠的互联网络上提供可靠的端到端字节流而专门设计的一个传输协议。TCP传输模型可以类比为打电话,通信双方在通信之前必须先建立连接(IP和Port),然后在这个连接上收发数据,通信结束后要释放连接。TCP采用的是全双工连接,表示可以在两个方向上同时传输数据。为了提供可靠的端到端字节流传输,TCP在内部实现了丢包重传、乱序重排、校验数据的功能。此外TCP还实现了一个重要功能——拥塞控制,会通过一些数据来得知当前网络情况,在网络情况不好时降低发送速度,在网络情况优良时提高发送速度,从而让整个网络达到一个高效状态。正因为TCP实现了上述这么多的功能,所以TCP是Internet的主力军,大部分网络应用采用TCP传输数据。

想要真正了解TCP协议,就必须了解TCP应对网络不可靠情况做的策略。而要真正用好UDP,也要在一定程度上自己组织策略应对网络不可靠的情况。所以要了解传输层,用好TCP/UDP的核心就是了解要面对的情况及一些相应的处理方案。

检验和:

因为网络传输存在不可靠性,数据有可能在传输的过程中出现被修改的情况,所以首先在收到一个包后首先要做的是验证这个数据包在传输过程中是否有被修改。要校验数据包是否被修改,我们通常会使用一个16位的字段存储校验和。如何求校验和就是把要发送的数据先按照16位字相加,然后用总和的负数作为校验和。这样在收到数据包时,先取出校验和,再与数据段按照16位字相加,结果为0则是正确的。无论TCP还是UDP,在数据包头中都带有校验和,默认是会计算校验和保证数据正确性的。

超时重传:

为了保证通信的可靠性,发送的每一个数据包都需要被确认,一定时间内没收到确认就重发这一个数据包。举例来说,A给B发送1号数据包,B要把1号包已经成功接收的信号返回给A(通常称为ACK信号)。A需要维护一个计时器来实现超时重传,如果一定时间内都没收到B返回的确认信号,就认为1号数据包已经丢失了,就再重新发送一次,在接收到确认包后就认为1号数据包成功发送,然后取消重传计时器。在这样的情况下,无论是A->B的1号数据包或者是B->A的确认数据包丢了,都会触发重传,从而保证数据包始终能从A到达B。

重复到达:

基于超时重传机制,我们可以确保A发送的数据包能成功到达B(否则一直重传)。但由此又会带来一个新的问题就是,第一次发的1号数据包如果并没有真正丢失,而是从一条很远的线路路由到达,这个时候有可能第二次重传的数据包已经到达了,这个时候B会收到两个(或多个)相同的包,这个就是重复到达问题。如果通信双方能够约定好基于一个确定的递增序号上传输数据包,那么重复到达的问题很容易解决。接收方记录已经接收到的最新序号,后续到达的包如果序号小于等于该标记的都认为是无效的。

建立连接:

 在连接期间,我们统一好序号就能解决重复到达的问题,那么现在就要考虑第一次如何正确建立连接了。因为在未建立连接之前,我们是不会记录序号的,所以对于第一个过来的ConnectionRequest初始序号,我们无法判断是否是重复的。为了解决这个问题,引入了三次握手机制。

释放连接:

建立连接似乎不太容易,而释放连接的情况会更加复杂一点。因为存在 两军对垒问题(Two Generals’ Problem),释放连接也不容易处理好。事实上设计一种协议来完全解决两军对垒问题是不可能的,但对于释放连接我们可以让连接两端独立决定是否释放而避免这个问题。首先我们考虑使用三次握手的方式来释放连接的情况:

三次握手释放连接在一般情况下工作得足够好(还有half-open的问题),在大部分情况下可以让通信双方能同时协商好关闭连接。但正如上面提到的,TCP采用的是全双工通信,关闭连接时应该满足单边关闭而另外一方仍然能单工通信的情况。所以TCP采用四次握手的方式来释放连接:

half-open问题:

无论是三次握手还是四次握手都会有half-open的问题,half-open问题指的是这个通信信道的一方已经关闭了而另外一方却不知情,仍然在使用这个信道通信。half-open问题除了会在关闭连接丢包时出现,还会在通信一方突然崩溃的情况下出现。为了解决这个问题我们引入一条主动关闭half-open连接的规则:如果在规定的一段时间内这个信道都没有消息过来,就自动断开。在引入了此规则后又有新的情况需要考虑,如果正常的信道长时间不通信,会触发half-open自动关闭规则而被错误的关闭。为此由引入了一个心跳包规则:在发送消息后要启动一个计时器,在一定的时间内没有新消息要发送的话则发送一个心跳包(也称为哑包),发送的心跳包没有什么有效信息,仅仅是为了避免信道被关闭。

另外我们还要区分另外一种叫half-close的情况。所谓的half-close指的是上面提到的四次握手中,在完成第二次握手后,一个全双工连接变成一个单工连接的情况。即只能B发消息给A,A不能发消息给B。half-close是一个正常的、允许的状态。而half-open则是一个需要处理的异常状态,这两者要区分开来。

滑动窗口:

在讨论完如何建立连接、释放连接后,我们继续来看在已建立的连接上如何高效传输数据。对于已经建立好的连接,如果一次只发送一个数据包,然后没有收到返回前暂停不发,这种通信方式称为停等式(stop-and-wait)协议。对于某些无线链路或者卫星链路会采用停等式协议通信。TCP使用的并不是停等式传输,而是会一次连续发一定数量的包,然后等对方确认收到多少,多少需要重发,再做处理。这样就会使用到一个发送缓冲器,这个缓冲区也称为滑动窗口。发送缓冲区大小是可变的,影响当前发送缓冲区大小(可以发多少东西)的因素有接收方的缓冲区大小和当前的网络情况。如果接收方缓冲区大小足够,当前网络情况也足够好,就可以调大发送缓冲区,使得可以一次性发送更多的数据,反之受到接收方缓冲区或网络情况影响,会调小发送缓冲区,从而控制发送速度。

接收缓冲区大小通信:

上面提到了,如果接收方接收缓冲区不够大(甚至满了),发送方应该调整发送缓冲区大小。那么接收缓冲区当前大小是多少的信息是如何通信的呢?

拥塞控制:

发送缓冲区(滑动窗口)不仅仅受对方的接受缓冲区大小影响,还会受当前网络情况影响。网络情况不好的时候,会降低发送缓冲区大小,从而降低发送速度,网络情况好的时候,会增大发送缓冲区大小,提升发送速度,这就是TCP协议一个重要的功能——拥塞控制。正因为TCP实现了拥塞控制,所以才会成为Internet应用的主力军。要实现拥塞控制,第一个问题是如何知道当前网络情况好不好,关于这个问题TCP简单采用了丢包作为网络情况不好的信号,这个信号虽然不太准确,但在他的整体策略下工作得足够好。当连接建立时,TCP会以指数增长的方式提高发送量,直到遇到第一次丢包,遇到丢包后会把发送量减为当前的一半,然后进入线性递增阶段,即每次发送成功后增加固定速度,直到下一次遇到丢包再发送量减半,再线性递增。具体情况如下:

最大—最小公平性:

上面提到的拥塞控制是基于发送接收双方的,那在整个网络层面应该如何安排流量分配呢?具体的问题就是,怎么在多个传输连接之间分配好带宽。这个问题既要保证公平性,也要考虑充分利用当前的网络资源。最大—最小公平原则就是当前网络路由采用的分配形式,它指的是:如果分配给一个流的带宽在不减少分配给另一个流带宽的前提下无法得到进一步增长,那么就不再给这个流更多带宽。

从上图可以了解最大最小公平规则如何分配带宽。假设R1-R6之间每个链路都有单位1的带宽,从上图可以看出,只有R4、R2、R3达到瓶颈,R1、R5、R6都是还有空余带宽的,但这个时候,无论要给ABCD哪一个连接增加带宽,都需要减少其他连接的带宽,那么就认为当前的带宽分配是合理的,符合最大最小公平性,兼顾了效率与公平。

关于传输层、TCP、UDP的细节暂时记录到这里,虽然这些都是比较底层的细节,但了解这些细节有助于我们运用好网络传输、以及在我们自己写网络程序的时候提供参考思路。最近在做帧同步PVP,也陆续在看计算机网络相关的书籍,后面会总结一些相关的知识点写出来作记录。

Tagged , , , . Bookmark the permalink.

Comments are closed.