概述
运输层协议为运行在不同主机上的应用进程间提供了逻辑通信功能。
运输层协议是在端系统实现.在发送端,运输层将从发送应用程序进程接收到的报文转换成运输层分组,该分组称为运输层报文段。
实现方式:
将应用报文划分为较小的块,并为每块加上一个运输层首部以生成运输层报文端。
在发送端系统,运输层将这些报文段传递给网络层,网络层将其封装为网络层分组,向目的地发送。
网络路由器仅作用于该数据报的网络层字段。
在接收端,网络层从数据报中提取运输层报文段,将该报文段上交给运输层,运输层处理收到的报文段,使报文段数据为接收应用程序使用.
运输层和网络层的关系
网络层提供了主机间的逻辑通信.
运输层为运行在不同主机上的进程之间提供了逻辑通信.
因特网运输层概述
本书中,将TCP
和UDP
分组统称为报文段,将网络层分组称为数据报。
网络层协议有一个叫IP
,IP
为主机之间提供了逻辑通信。
每台主机至少有一个网络层地址,即Ip
地址。
UDP
和TCP
最基本的责任是,将两个端系统间Ip
的交付服务扩展为运行在端系统上的两个进程之间的交付服务.
进程到进程的数据交付
和差错检查
是两种最低限度的运输层服务。
TCP
为应用提供几种附加服务。
(1). 消息不丢失,消息有序递交
(2). 流量控制
(3). 拥塞控制。
多路复用与多路分解
在接收端,运输层检查这些字段,标识出接收套接字,进而将报文端定向到该套接字。将运输层报文段中的数据交付到正确的套接字的工作称为多路分解。
在源主机从不同套接字中收集数据块,并为每个数据块封装上首部信息,从而生成报文段,然后将其传递到网络层,所有这些工作称为多路复用。
(1). 套接字有唯一标识符
(2). 每个报文段有特殊字段来指示该报文段所要交付到的套接字
源端口号字段,目的端口号字段.
端口号是一个16
比特的数,大小在0~65535
之间。
0~1023
范围的端口号称为周知端口号,是受限制的,指的是它们保留给诸如HTTP,FTP
之类的周知应用层协议来使用.
无连接的多路复用与多路分解
一个UDP
套接字由一个二元组全面标识。该二元组包含一个目的Ip
地址和一个目的端口号。
因此,如两个UDP
报文段有不同的源Ip
地址或源端口号,但有相同的目的Ip
地址和目的端口号,则这两个报文段将通过相同的目的套接字被定向到相同的目的进程。
面向连接的多路复用与多路分解
TCP
套接字由一个四元组(源IP
地址,源端口号,目的IP
地址,目的端口号)来标识。
当一个TCP
报文段从网络到达一台主机时,该主机使用全部4
个值来将报文段定向到相应的套接字。
对tcp
套接字:
(1). 携带SYN
的请求建立连接报文到达目的地时,依据目的Ip
,目的Port
,被定位到服务端相应的监听套接字处理.
(2). 不携带SYN
的报文到达目的地时,需依据源端口号,源主机IP
地址,目的端口号,目的IP
地址来定位到相匹配的套接字处理。
无连接运输:UDP
运输层最低限度必须提供一种复用/分解服务,以便在网络层与正确的应用级进程之间传递数据。
UDP
,除了复用/分解功能
及少量的差错检测
外,它几乎没有对IP
增加别的东西。UDP
下在向目的端发送请求前,没有连接建立过程.相应的,连接关闭时,也没有连接断开过程.
UDP报文段结构
UDP检验和
提供差错检测功能。
一个计算UDP
校验和实例:假定有3
个16
比特的字
(1). 01100110 01100000
(2). 01010101 01010101
(3). 10001111 00001100
a. (1),(2),(3)
求和
01100110 01100000 +
01010101 01010101 =
10111011 10110101
10111011 10110101+
10001111 00001100=
01001010 11000001+
00000000 00000001= // 相加过程最高位的溢出需通过这种方式补偿回来
01001010 11000010
b. 对求和结果执行反码作为udp
校验和
01001010 11000010~=
10110101 00111101
在接收方,全部的4
个16
比特字(包括检验和)加在一起,如该分组没有引入差错,则显然在接收方处该和将是11111111 11111111
.
像许多链路层协议也提供了差错检测那样。因为不能保证源和目的之间的所有链路都提供差错检测。
就是说,也许这些链路中的一条可能使用没有差错检测的协议。即使报文段经链路正确地传输,当报文段存储在某台路由器的内存中时,也可能引入比特差错。
在既无法确保全链路每一段可靠性,又无法确保内存中的差错检测情况下,如端到端数据传输服务要提供差错检测,UDP
必须在端到端基础上在运输层提供差错检测。
当协议较高层次已经提供了这类差错检测时,具体完整链路上两个节点间的链路时,可以在协议下面层次不再提供这类功能.
TCP可靠性原理
网络传输过程中数据包到达目的端时可能已经损坏,即包的内容已经不再是原始内容.
网络传输过程中数据包可能被丢弃,进而无法到达目的端.
网络传输过程中顺序发出去的多个包,后面发的包可能比先前发的包先到达目的端.
在上述情况存在下,Tcp
要保证:
(1). 发送端通过Tcp
发出的包一定可以到达目的端,且不会被损坏.
(2). 发送端通过Tcp
顺序发出的多个包,在目的端按一致的顺序提交给Tcp
上层协议.
Tcp
实现可靠传输的机制:
(1). 检验和
用于检测在一个传输分组中的比特错误。
(2). 定时器
用于在超时未收到对包的Ack
时,重传包。
(3). 序号
用于为从发送方流行接收方的数据分组按顺序编号。
(4). 确认
接收方用来告诉发送方已被正确地接收的数据信息。
(5). 发送窗口,接收窗口
用来提升通信双方吞吐率.也可以用来实现流量控制.
MSS
用来限制Tcp
报文数据部分最大尺寸.
(1). MSS
不包含TCP
及IP
的协议头长度。
(2). MSS
选项只能在初始化连接请求(SYN=1
)使用。
(3). 发送方与接收方的MSS
不一定相等。
TCP连接
TCP
“连接”不是一条像在电路交换网络中的端到端TDM
或FDM
电路,该连接是一条逻辑连接,其共同状态仅保留在两个通信端系统的TCP
程序中。针对已经连接的Tcp
套接字执行数据发送时,数据先写入到套接字的发送缓存.内核自行选择合适时机从发送缓存里取出一块数据,将数据传递到网络层。
TCP报文结构
(1). 源端口号,目的端口号.
(2). 检验和.
(3). 32
比特的序号字段和32
比特的确认号字段.
(4). 16
比特的接收窗口字段,自己作为接收方时允许发送方发送的未被确认的数据尺寸.
(5). 4
比特的首部长度字段,指示了以32
比特的字为单位的TCP
首部长度。由于TCP
选项字段原因,TCP
首部的长度是可变的。
(6). 选项字段,用于对基础Tcp
进行扩展。
(7).6
比特的标志字段。
ACK
比特用于指示确认号字段中的值是有效的。
RST
指示对端不存在对应的套接字.
SYN
用于表示连接建立请求报文.
FIN
用于表示连接断开请求报文.
CWR
,ECE
用于拥塞控制。
序号和确认号
一个报文段的序号是该报文段首字节的字节流编号。
举例:
设主机A
的一个进程想通过一条TCP
连接向主机B
上的一个进程发送一个数据流,主机A
中的TCP
将隐式地对数据流中的每一个字节编号。
设数据流含500000
字节的文件组成,其MSS为1000
字节,数据流的首字节编号是0
.
下图,该TCP
将为数据流构建500
个报文段。给第一个报文段分配序号0
,第二个报文段分配序号1000
.
以此类推,每个序号被填入相应TCP
报文段首部的序号字段中
从主机B
到达的每个报文段中有一个序号用于从B
流向A
的数据,主机A
填充进报文段的确认号是主机A
期望从主机B
收到的下一字节的编号。
假主机A
已收到来自主机B
的编号为0~535
的所有字节,同时它打算发一个报文段给主机B
.
主机A
等待主机B
的数据流中字节536
及之后的所有字节,主机A
会在它发往主机B
的报文段的确认号字段中填上536
。
设主机A
已收到一个来自主机B
的包含字节0~535
的报文段,及另一个包含字节900~1000
的报文段。
由于某种原因,主机A
还没收到字节536~899
,主机A仍在等待字节536
及其后的字节。
因此,A
到B
的下一个报文段将在确认号字段中包含536
,TCP
是累计确认。
估计往返时间
报文段的样本RTT
是从某报文段被发出,到对该报文段的确认被收到之间的到时间量。
多数TCP
的实现仅在某个时刻做一次SampleRTT
测量。
也即,任意时刻,仅为一个已发送的但目前尚未被确认的报文段估计SampleRTT
,从而产生一个接近每个RTT
的新SampleRTT
值。
为了估计一个典型的RTT
,要采取某种对SampleRTT
取平均的方法。TCP
维持一个SampleRTT
均值,一旦获得一个新SampleRTT
,TCP
就会依据下列公式来更新EstimatedRTT
:EstimatedRTT = (1-a)*EstimatedRTT + a*SampleRTT
,a
的推荐值是1/8
.
RTT
偏差DevRTT
用于估算SampleRTT
一般会偏离EstimatedRTT
的程度。
计算公式:DevRTT=(1-b)*DevRTT + b*abs(SampleRTT - EstimatedRTT)
.b
的推荐值为0.25
.
设置和管理重传超时间隔
超时间隔设为EstimatedRTT
加上一定余量.当SampleRTT
波动大时,余量可大些。小时,可小些。
一般用:TimeoutInterval = EstimatedRTT + 4*DevRTT
.
推荐的初始 TimeoutInterval
值为1
秒,出现超时后,加倍。
只要更新了EstimatedRTT
,则按上述公式重新设置。
超时间隔加倍
每当超时事件发生时,TCP
重传具有最小序号的还未被确认的报文段。只是每次TCP
重传时都会将下一次超时间隔设为先前值的两倍。
例:假设当定时器第一次过期时,与最早的未被确认的报文段相关联的TimeoutInterval
是0.75
秒.TCP
会重传该报文段,并把过期时间设为1.5s
.如1.5s
后定时器又过期了,则TCP
将再次重传该报文段,并把过期时间设置为3.0s
。
每当定时器在另两个事件(即收到上层应用数据和收到ACK
)中的任意一个启动时,TimeoutInterval
重新设置为由最近的EstimatedRTT
值与DevRTT
值推算得到。
快速重传
发送方通常可在超时事件发生之前通过注意所谓冗余ACK
来较好检测到丢包情况。冗余ACK
就是再次确认某个报文段的ACK
,而发送方先前已收到对该报文段的确认。
当TCP
接收方收到一个具有这样序号的报文段时,即其序号大于下一个所期望的,按序的报文段,它检测到了数据流中的一个间隔。这个间隔可能是由于在网络中报文段丢失或重新排序造成的。
TCP
不使用否定确认,所有接收方不能向发送方发回一个显式的否定确认,它只是对已经接收到的最后一个按序字节数据进行重复确认。
如果TCP
发送方接收到对相同数据的3
个冗余ACK
,它把这当作一种指示,一旦收到3
个冗余ACK
,TCP
就执行快速重传。对快速重传的TCP
,用下列代码段代替之前ACK
收到事件处理.
event: ACK received, with ACK field value of y
if (y > SendBase)
{
SendBase=y
if (there are currently any not yet acknowledged segments)
start timer
}
else
{
increment number of duplicate ACKs received for y
if (number of duplicate ACKS received for y==3)
resend segment with sequence number y
}
break;
流量控制
一条TCP
连接的每一侧主机都为该连接设置了接收缓存。当该TCP
连接收到正确,按序的字节后,它就将数据放入接收缓存。相关联的应用进程会从该缓存中读取数据。如应用程序读取数据慢,数据到达快,会容易使接收缓存溢出。
TCP
为它的应用提供流量控制服务,以消除发送方使接收方缓存溢出的可能性。发送方的发送速率和接收方应用程序的读取速率匹配。TCP
发送方也可能因为IP
网络的拥塞而被遏制,这种称为拥塞控制。
TCP
通过让发送方维护一个称为接收窗口的变量来提供流量控制。接收窗口用于给发送方一个指示:该接收方还有多少可用的缓存空间。
假设主机A
通过一条TCP
连接向主机B
发送一个大文件。主机B
为该连接分配了一个接收缓存,用RcvBuffer
来表示其大小。
主机B
上的应用进程不时地从该缓存中读数据.定义以下变量:
(1). LastByteRead
:主机B
上的应用进程从缓存读出的数据流的最后一个字节编号.
(2). LastByteRcvd
:从网络中到达的且已放入主机B
接收缓存的数据流的最后一个字节的编号。
TCP
不允许已分配的缓存溢出,必须有LastByteRcvd-LastByteRead<=RcvBuffer
.
接收窗口用rwnd
表示,rwnd=RcvBuffer - [LastByteRcvd-LastByteRead]
.
主机B
通过把当前的rwnd
值放入它发给主机A
的报文段接收窗口字段中,通知主机A
它在该连接的缓存中还有多少可用空间.
开始时,主机B
设定rwnd=RcvBuffer
.
主机A
轮流跟踪两个变量,LastByteSent
和LastByteAcked
.
LastByteSent - LastByteAcked
是主机A
发送到连接中但未被确认的数据量。
通过将未确认的数据量控制在值rwnd
以内,可保证主机A
不会使主机B
的接收缓存溢出。
假设主机B
的接收缓存已经存满,使得rwnd=0
,在将rwnd=0
告知主机A
后,假定主机B
没任何数据要发给主机A
.
由于TCP
仅当在它有数据或有确认要发时,才会发送报文段给A
.
这样此后主机B
的rwnd
变大时,A
收不到通知。
为解决此问题,主机B
的接收窗口为0
时,主机A
继续发送只有一个字节数据的报文段。以便及时收到B
的rwnd
不为0
信息。
TCP连接管理
客户进程先通知客户TCP
,想建立一个与服务器上某个进程之间的连接。
客户中的TCP
按以下与服务器中的TCP
建立连接.
(1). 客户端的TCP
先向服务器端的TCP
发送一个特殊的TCP
报文段.报文段中不包含应用层数据,报文段首部中的一个标志位(SYN
)被置为1
.这个特殊报文段称为SYN
报文段.客户会随机地选择一个初始序号,将此编号放置于该起始的TCP SYN
报文段的序号字段中。该报文段会被封装在一个IP
数据报中,发送给服务器。
(2). 一旦包含TCP SYN
报文段的IP
数据报到达服务器主机,服务器会从该数据报中提取出TCP SYN
报文段,为该TCP
连接分配TCP
缓存和变量,并向该客户TCP
发送允许连接的报文段.这个允许连接的报文段也不含应用层数据,但在报文段首部包含了3
个重要信息。
a. SYN
比特被置为1
.
b. 该TCP
报文段首部的确认号字段被置为client_isn+1
.意思是SYN
消耗了一个序号.
c. 服务器选择自己的初始序号(server_isn
),将其放置到TCP
报文段首部的序号字段中,该允许连接的报文段称为SYN ACK
报文段.
(3). 收到SYN ACK
报文段后,客户也要给该连接分配缓存和变量,客户主机向服务器发送另外一个报文段,这最后一个报文段对服务器的允许连接的报文进行了确认.因为连接已经建立,该报文中SYN
为0
.此报文可在报文段负载中携带客户到服务器的数据。
SYN
报文不携带数据,但会消耗一个序号.
参与一条TCP
连接的两个进程中的任何一个都能终止该连接,连接结束后,主机中的"资源"(即缓存和变量)将被释放。
例:某客户打算关闭连接,客户应用进程发出一个关闭连接命令。这引起客户TCP
向服务器进程发送一个特殊的TCP
报文段。这个特殊的报文段让其首部中的一个标志位即FIN
比特被设置为1
.当服务器接收到该报文段后,就向发送方回送一个确认报文段。服务器发送它自己的终止报文段,其FIN
比特被置为1
.该客户对这个服务器的终止报文段进行确认。此时,两台主机上用于该连接的所有资源都被释放了。
在一个TCP
连接的生命周期内,运行在每台主机中的TCP
协议在各种TCP
状态之间变迁,下图说明了客户TCP
会经历的一系列典型TCP
状态。
(1). 客户TCP
开始时处于CLOSED
状态,客户应用程序发起一个新的TCP
连接。这引起客户中的TCP
向服务器中的TCP
发送一个SYN
报文段。在发送过SYN
报文段后,客户TCP
进入SYN_SENT
状态。
(2). 客户TCP
处在SYN_SENT
状态时,它等待来自服务器TCP
的对客户所发报文段进行确认且SYN
比特被置为1
的一个报文段。收到这样一个报文段后,客户TCP
进入ESTABLISHED
状态。
(3). 当处在ESTABLISHED
状态时,TCP
客户就能发送和接收包含有效载荷数据的TCP
报文段了。
(4). 假设客户应用程序决定关闭该连接(服务器也能选择关闭该连接)这引起客户TCP
发送一个带有FIN
比特被置为1
的TCP
报文段,并进入FIN_WAIT_1
状态。
(5). 处于FIN_WAIT_1
状态时,客户TCP
等待一个来自服务器的带有确认的TCP
报文段。当它收到该报文段时,客户TCP
进入FIN_WAIT_2
状态。
(6). 当处在FIN_WAIT_2
状态时,客户等待来自服务器的FIN
比特被置为1
的另一个报文段.当收到该报文段后,客户TCP
对服务器的报文段进行确认,并进入TIME_WAIT
状态.
FIN_WAIT_2
下收到对端的FIN
后,维持TIME_WAIT
状态的意义:
a. 一旦对对端FIN
的ACK
丢失,可以重传该ACK
.
b. 保证属于此连接的包生命期已经全部结束.
(1). 服务端典型下,创建一个监听套接字并启动监听.此时监听套接字处于LISTEN
状态.
(2). 收到来自客户端的SYN
时,服务端分配套接字资源并发送SYN+ACK
,此时分配的套接字处于SYN RCV
.
(3). 之后收到来自客户端的ACK
,转变为ESTABLISHED
.
(4). 在ESTABLISHED
下,可以接收数据,发送数据.
(5). 此后可以主动断开连接,或被动断开连接.上述客户端分析其主动断开下状态变化.相应的,这里分析服务端被动断开下状态变化.
(6). 收到客户端FIN
时,用ACK
回复并进入CLOSE_WAIT
.
(7). 此后服务端主动发FIN
时,转变为LAST_ACK
.
(8). 当收到相应的ACK
后,进入CLOSED
.
RST报文
考虑一台主机接收到一个TCP
报文段,其端口号或源IP
地址与该主机上进行中的套接字都不匹配的情况。
如一台主机接受了有目的端口80
的一个TCP SYN
分组,但该主机在端口80
不接受连接,则该主机将向源发送一个特殊重置报文段。该TCP
报文段将RST
标志位置为1.它告诉源"我没有那个报文段的套接字,请不要再发送该报文段了".
当一台主机接收一个UDP
分组,它的目的端口与进行中的UDP
套接字不匹配,该主机发送一个特殊的ICMP
数据报.
回顾nmap
端口扫描工具为探索目标主机上一个特定的TCP
端口,如端口6789
.nmap
将对那台主机的目的端口6789
发送一个特殊的TCP SYN
报文段,有3
种可能输出:
(1). 源主机从目标主机接收到一个TCP SYNACK
报文段。意味着目标主机上一个应用程序使用TCP
端口6789
运行,nmap
返回打开.
(2). 源主机从目标主机接收到一个TCP RST
报文段。意味着该SYN
报文段到达了目标主机,但目标主机没有运行一个使用TCP
端口6789
的应用程序。但至少知道,发送的报文段未被防火墙阻挡。
(3). 源什么也没有收到,可能表明,该SYN
报文段被防火墙阻挡.
TCP拥塞控制
TCP
拥塞控制算法
慢启动
一条TCP
连接开始时,cwnd
的值常初始置为一个MSS
的较小值.每次收到一轮发出包的回复,窗口就增大一倍.
(1). 慢启动过程遭遇确认超时
TCP
发送方将cwnd
设置为1
并重新开始慢启动过程,还将ssthresh
设为此时cwnd
的一半.
(2). cwnd
已经达到ssthresh
后,每次收到一轮发出包的确认仅使得cwnd
增加一个MSS
.
(3). 检测到3
个冗余ACK
这时TCP
执行一种快速重传并进入快速恢复状态
拥塞避免
进入拥塞避免状态,cwnd
值大约是上次遇到拥塞时值的一半.
每个RTT
只将cwnd值增加一个MSS
.
拥塞避免阶段若遭遇超时:
cwnd
被设置为1
个MSS
,ssthresh
值被更新为cwnd
值一半.然后按慢启动来增加cwnd
.
拥塞避免阶段若遭遇三个冗余ACK
:
将ssthresh
值记录为cwnd
的值的一半,再将cwnd
值减半.进入快速恢复.