运输层是整个网络体系结构①中的关键层次之一,它向上层提供通信服务,属于面向通信部分的最高层,同时也是用户功能中的最低层。它表示主机中的进程之间端到端的通信。
运输层两个主要协议:
- 无连接的用户数据报协议UDP(User Datagram Protocol)
- 面向连接的传输控制协议TCP(Transmission Control Protocol)
下图给出了这两种协议在协议栈中的位置:
用户数据报协议UDP
概述
用户数据报协议UDP只在IP的数据服务之上增加了很少一部份功能,就是复/分用的功能以及差错检查的功能。UDP的主要特点:
- 无连接的,即发送数据之前不需要建立连接,减少了开销和发送数据之前的时延。
- 尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的连接状态表。
- 面向报文的,发送方UDP对应用层交下来的报文,既不合并,也不拆分,在添加首部(8字节)后向下交付给IP层。也就是说,UDP一次交付一个完整的报文。因此,应用层必须选择合适大小的报文。若报文太长,IP层在传送时可能要进行分片②,这会降低IP层的效率。反之,若报文太短,会使IP数据报的首部③的相对长度太长,也会降低IP层的效率。
- 没有拥塞控制,因此网络出现拥塞时不会使源主机的发送速率降低。
- 支持一对一、一对多、多对多的交互通信。
- 首部开销小,只有8个字节。
首部格式
- 源端口 源端口号,不需要时可用全0.
- 目的端口 目的端口号,必须使用。
- 长度 数据报长度,最小为8字节(仅有首部)。
- 检验和④ 检测数据报在传输过程中是否出错,有错就丢弃。
基于端口的分用
当运输层从IP层收到UDP数据报时,根据首部中的目的端口,把UDP数据报通过相应的端口,上交到对应的应用进程。如果接收方UDP发现收到的报文中的目的端口号不正确,就丢弃该报文,并通过网际控制报文协议ICMP⑤协议发送端口不可达的差错报文。
传输控制协议TCP
简述
传输控制协议TCP是TCP/IP协议体系中非常复杂的一个协议。TCP最主要的特点:
- 面向连接,应用程序在发送数据前,必须先建立连接。在传送数据完毕后,必须释放已建立的连接。
- 端到端通信,即每个TCP连接只能有两个端点。
- 可靠交付,保证数据无差错,不丢失,不重复,并且按序到达。
- 全双工通信,允许通信双方在任何时候都能收发数据。
- 面向字节流,TCP把应用程序交下来的数据看成是一连串的无结构的字节流。
每一条TCP连接唯一地被通信两端的两个端点所确定,即:
TCP连接 ::= {socket1, socket2,} ::= {(IP1:Port1), (IP2:Port2)}
首部格式
- 源端口和目的端口 各占2个字节。
- 序号 占4个字节。序号的范围是[0, 232 - 1],共232个序号。当序号增加到232 - 1时,下一个序号又重新从0开始编号。在一个TCP连接中传送的字节流中每一个字节都按顺序编号。字节流的起始序号必须在连接建立时设置。
- 确认号 占4个字节,指接收方期望收到的下一个报文段的第一个数据字节的序号。(如确认号=N,则表明序号N-1为止的所有数据都已正确收到)。
- 数据偏移 占4位,指TCP报文段的数据起始处距离TCP报文段的起始位置的距离,即报文段的首部长度。以4字节长的字位计算单位。由于4位二进制数能表示的最大十进制数为15,因此数据偏移能表示的最大值为60个字节,也是TCP首部的最大长度。
- 保留 占6位,目前设置为0。
- 紧急URG 当URG=1时,表明紧急指针有效。它告诉系统此报文中有紧急数据,应当尽快传送,而不要按原来的排队顺序来发送。需要与紧急指针字段配合使用。
- 确认ACK 当ACK=1时,确认号字段有效。TCP规定,在连接建立后所有传送的报文段都必须把ACK置1。
- 推送PSH 当发送方TCP把PSH置1,并立即创建一个报文发送出去。接收方TCP收到PSH=1的报文段,就尽快地交付接收应用程序,而不用等到接收缓冲区填满再向上交付。
- 复位RST 当RST=1时,表明TCP连接中出现严重差错,必须释放连接,然后再重新建立连接。还可以用来拒绝一个非法的报文段或拒绝打开一个连接。
- 同步SYN 在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应该在响应报文段中将SYN和ACK置1。
- 终止FIN 用来释放一个连接。当FIN=1时,表明此报文段的发送方的数据已发送完毕,并要求释放连接。
- 窗口 占2个字节,窗口值是[0, 216 - 1]之间的整数。窗口指的是接收方目前允许对方发送的数据量(以字节为单位)。
- 校验和④ 占2个字节。检验和字段检测的范围包括首部和数据这两部分。
- 紧急指针 占2个字节,仅在URG=1时才有意义。指出本报文段中的紧急数据的字节数(即紧急数据的末尾在报文段中的位置)。需要注意的是,即使窗口为0也可以发送紧急数据。
- 选项 长度可以变,最长可达40个字节。
可靠传输的实现
滑动窗口
TCP的滑动窗口是以字节为单位的。现假定发送方收到了接收方发来的确认报文段,其中窗口是15个字节,而确认号是6。根据这两个数据,发送方就构造出自己的发送窗口,如图所示:
发送窗口表示,在没有收到接收方的确认时,可以连续把窗口内的数据都发送出去。凡是已经发送的数据,在未收到确认前都必须暂时保留,以便超时重传时使用。
发送窗口里面的序号表示允许发送的序号。显然,窗口越大,发送方就可以在收到对方确认之前连续发送更多的数据,因而可能获得更高的传输效率。接收方会把自己的窗口大小放在报文中的窗口字段告知发送方。因此,发送方的窗口大小一定不能超过接收方的窗口大小。另外需要注意的是,发送方的窗口大小还要受到当时网络拥塞程度的影响。
如果接收方收到序号为7和8的数据(未按序到达),但是没有收到序号为6的数据,此时接收方发送的确认报文段中的确认号仍然为6,而不能是7或者8(只能对按序收到的数据中的最高序号给出确认号)。
窗口与缓存的关系
发送方的应用进程把字节流写入TCP的发送缓存,接收方的应用进程从TCP的接收缓存中读取字节流。下图画出了发送双方维持的缓存和窗口。
发送缓存用来暂存:
- 发送应用程序传送给发送方TCP准备发送的数据;
- TCP已发送但未收到确认的数据。
接收缓存用来暂存:
- 按序到达的,但未被接收应用程序读取的数据;
- 未按序到达的数据。
发送窗口是根据接收窗口设置的,但是在同一时刻,发送窗口的大小并不总是等于接收窗口的大小。因为接收方通过网络传输调整后的窗口值需要经历一定的时间滞后。另外,发送方还可能根据当时网络拥塞情况适当减小自己的窗口值。
对于未按序到达的数据,可以通过设置报文首部的选择确认SACK⑥(Selective ACK)选项的方法,只传送缺失的数据而不用重传已经正确到达接收方的数据。
超时重传
TCP发送方在规定的时间内没有收到确认就会重传已发送过的报文段。由于TCP下层是互联网环境,发送的报文段可能会经过不同速率的网络,并且每个IP数据报所选择的路由也可能不同。如果把超时重传的时间设置太小,就会引起很多报文段的不必要的重传,增加网络负荷。如果超时重传时间设置太长,则会使网络的空闲时间变大,降低传输效率。因此,TCP采用一种自适应的算法,根据报文段的往返时间RTT(Round-trip time),来设置超时重传的时间。
流量控制
一般来说,我们希望数据传输速度更快一些。但如果发送方数据发送得过快,接收方可能会来不及接收,就会导致数据丢失(接收缓存溢出)。所以我们需要通过流量控制方式,来限制发送方的发送速率。
利用滑动窗口的机制就可以很方便的在TCP连接上实现对发送方的流量控制。接受方可以在缓存有较大的空闲空间时,适当增大窗口大小。在缓存空闲空间不足时,减小窗口大小。当窗口大小减少到0时,就不允许发送方再发送数据了。此时,发送方会启动一个持续计时器,等待接收方发送非零窗口通知。如果持续计时器设置的时间到期,则发送一个零窗口探测报文,接收方在确认这个探测报文时给出当前的窗口值。如果窗口仍为零,就重置持续计时器的时间。
传输效率
在TCP的实现中,广泛使用Nagle算法来提高传输效率。若发送方应用程序把要发送的数据逐字节送到TCP的发送缓存,则发送方就先把第一个字节先发送出去,把后到达的字节都缓存起来。当发送方收到对第一个字节的确认后,再把缓存中的所有数据组装成一个报文段发送出去,同时缓存后续到达的数据。只有在收到对前一个报文段的确认时,才继续发送下一个报文段。当数据达到较快而网络速率较慢时,使用这种方式可以明显的减少所使用的网络带宽。算法还规定,当数据已到达窗口大小的一半或已达到报文最大长度时,就立即发送一个报文段。这样就可以有效地提高网络的吞吐量。
另外,为了减少传输开销,接收方可以在合适的时候发送确认,也可以在发送数据时捎带确认信息。TCP标准规定,确认延迟的时间不应超过0.5秒。若收到一连串具有最大长度的报文段,则必须每隔一报文段就发送一个确认。
拥塞控制
当网络性能变坏时,我们需要通过一些方法,来防止过多的数据注入到网络中,导致网络中的路由器或者链路过载。
TCP进行拥塞控制的算法有四种:
- 慢开始
- 拥塞避免
- 快重传
- 快恢复
慢开始
因为主机开始发送数据时,并不知道网络的负荷情况。所以在TCP初始化时,会将拥塞窗口的值设置得比较小(不超过2至4个SMSS⑦的数值),避免引起网络发生拥塞。
在执行慢开始算法时,发送方每收到一个对新报文段的确认ACK,就可以把窗口值加一个 min(N, SMSS) 的数值(其中, N 是原先未被确认的、但现在被刚收到的确认报文段所确认的字节数),然后开始下一轮传输。因此使用慢开始算法后,窗口值随传播轮次按指数规律增长。
拥塞避免
为了避免窗口增长过大引起网络拥塞,还需要设置一个慢开始门限值。当窗口值达到慢开始门限值时,就改为执行拥塞避免算法。拥塞算法的思路是让拥塞窗口缓慢增大,即每经过一个往返时间RTT就把发送方的拥塞窗口增加一个MSS的大小。因此拥塞窗口随着传输轮次按线性规律增长。
快重传和快恢复
有时,个别报文段会在网络中丢失,但实际上网络并未发生拥塞。如果发送方迟迟收到不到确认,就会产生超时,误认为网络发生拥塞。这就导致发送方错误的启动慢开始,把拥塞窗口设置为较小的值,降低传输效率。
采用快重传的算法可以让发送方尽早知道发生了个别报文段的丢失。快重传算法首先要求接收方不要等待自己发送数据时才进行捎带确认,而是要立即确认,即使收到失序的报文段也要立即发出对已收到的报文段的重复确认。快重传算法规定,只要发送方连续收到3个重复确认,就知道接收方确实没有收到对应的报文段,因而应该立即进行重传,这样就不会出现超时,发送方也就不会误认为网络出现拥塞。
此时发送方知道只是丢失个别报文,于是不启动慢开始,而是执行快恢复算法,发送方将门限值设置为当前拥塞窗口值的一半,并大大减少拥塞窗口的数值。
发送方窗口值计算
由上文可知,发送方的发送窗口不能超过接收方的窗口值,同时还要受到网络拥塞程度的影响。因此,发送窗口由接收窗口和拥塞窗口中的最小值决定,即:
发送方窗口的上限值 = Min(接收方的窗口值,拥塞窗口值)
连接管理
我们知道,TCP是面向连接的协议,因此连接的建立和释放是每一次面向连接的通信中必不可少的过程。
连接的建立
连接的建立过程叫握手,握手需要客户端和服务器之间交换三个TCP报文段。如图:
一开始服务端进程创建监听套接字,准备接受客户端的连接请求。然后服务端就处于LISTEN状态,等待客户端的连接请求。
客户端进程创建连接套接字,向服务端发送连接请求报文段。这时报文段首部同步位SYN=1,同时选择一个初始序号seq=x。TCP规定,SYN报文段不能携带数据,但是要消耗一个序号。此时,客户端进程进入SYN-SENT状态。
服务端收到请求报文段后,如果同意连接请求,则向客户端发送确认。在确认报文段中,首部同步位SYN=1,确认位ACK=1,确认号是ack=x+1,同时也选择一个序号seq=y。这个报文段也不能携带数据,同样也要消耗掉一个序号。这时服务端进入SYN-RECV状态。、
客户端进程在收到服务端的确认后,需要再给服务端发送一个确认。报文段首部中的ACK=1,确认号是ack=y+1,而自己的序号seq=x+1。这时,连接已经建立,客户端进入ESTABLISHED状态。
服务端进程在收到来自客户端的确认后,也进入ESTABLISHED状态。双方可以开始数据传输。
为什么客户端最后还要回传一次确认呢? 主要是为了防止已失效的连接请求报文段突然又传送到服务端,产生错误。比如客户端发送的连接请求报文因为网络拥塞而未及时收到确认,于是重新发送连接请求报文。之后与服务端成功建立连接,在数据传输完毕之后,就释放连接了。此时如果服务端突然收到之前滞后的连接请求报文,服务端就会认为客户端又发出新的连接请求,于是就向客户端发送确认报文,同意连接建立。假定客户端不对服务端的确认报文给出确认,新的连接就会建立。
连接的释放
数据传输完毕后,通信双方都可以释放连接。假如现在由客户端发起释放连接的请求,如图:
客户端应用进程发送释放连接报文段,并停止发送数据,主动关闭TCP连接。连接释放报文段首部的FIN位置1,其序号seq=x,等于前面已经发送过的数据的最后一个字节的序号加1。此时客户端进入FIN-WAIT1状态,等待服务端确认。TCP规定,FIN报文段需要消耗掉一个序号。
服务端收到释放报文段后立即发出确认,确认号ack=x+1,自己的序号seq=y,等于客户端前面传送过的最后一个字节的序号加1。然后服务端就进入CLOSE-WAIT状态。TCP服务端进程通知应用进程,因此从客户端到服务端这个方向的连接就释放了,这时TCP处于半关闭状态,即客户端已经没有数据要发送了,但是如果服务端发送数据,客户端仍要接收。也就是服务端到客户端方向的连接并未关闭。
客户端收到服务端的确认后,就进入FIN-WAIT2状态,等待服务端发出释放连接的请求。
若服务端已经没有数据要发送,应用进程就通知TCP释放连接。这时服务端发出FIN报文,首部的ACK位置1,确认号是上次已经发送过的确认号ack=x+1,序号seq=z(假如在半关闭状态时服务端又发送了一些数据)。这时服务端就进入LAST-ACK状态,等待客户端确认。
客户端在收到服务端的释放连接报文段后,发出确认。在确认报文段中,将首部的ACK位置1,确认号ack=z+1,序号seq=x+1(根据TCP标准,前面发送过的FIN报文段要消耗一个序号)。然后进入到TIME-WAIT状态。此时TCP连接还没有释放掉。必须等待2MSL⑧的时间后,客户端才进入到CLOSED状态。
为什么要等待2MSL的时间?
第一,为了保证最后的确认报文能够到达服务端。如果该报文丢失,客户端可以在服务端重传FIN+ACK报文段时重传确认,使服务端能正确进入CLOSED状态。
第二,防止已经失效的连接请求报文段出现在本连接中。客户端在发送完最后一个ACK报文段后,再经过2MSL的时间,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样下一个使用同样端点的新连接就不会出现旧连接的请求报文段。
服务端只要收到来自客户端的确认报文段,就可以关闭连接进入CLOSED状态。
除了等待计时器外,TCP还设有一个保活计时器。服务器每收到一次客户端的数据,就重置计时器时间。若在计时器时间到期时,没有收到来自客户端的数据,服务器就会每隔一段时间(75秒)发送一次探测报文段。在一连发送10个探测报文后仍无客户端的响应,服务端就认为客户端出现了故障,主动关闭该连接。
数据在层间的传输
在发送数据时,数据从高层下到低层,然后才到通信链路上传输。
②注:最常用的以太网规定,IP数据字段的最大长度(MTU)为1500字节。
③注:IP数据报首部的固定长度为20字节,可变长度在1字节到40字节不等,取决于所选项目。
④注:UDP/TCP校验和是把首部和数据部分一起校验,但IP数据报的校验和只校验IP数据报的首部。
⑤注:ICMP是互联网的标准协议。ICMP报文作为IP数据报的数据,加上数据报的首部,组成IP数据报发送出去。
⑥注:SACK规范参考www.rfc-editor.org/rfc/rfc2018…