传输层
传输层的介绍
传输层提供应用进程间的逻辑通信
应用进程间的逻辑通信作何解?
从应用进程的角度看,两台相隔两岸的计算机中的进程,通过传输层,就好似直接连接起来进行交流一般,对于底层的路由、链路都是透明不可见的。
传输层和网络层的联系
和传输层相比,网络层提供主机之间的逻辑通信,区别是细微的,但十分重要。
传输层作为终端特殊的一部分,功能是收集整理应用层的消息(message),加入传输头成为(segment)。
真正负责在网络中传输的是网络层的职责。
举个例子:传输层好比一个村的村长,专门收集/发放村民们的邮箱,而网络层才是真正的邮递员。
网络层的功能会影响传输层的功能,譬如带宽
传输层也可以提供网络层无法提供的额外的功能,譬如可靠传输、加密等。
传输层协议的代表:TCP/UDP
网络层协议的代表:IP
传输层概况
传输层的意义?
IP协议提供网络中每一个主机的逻辑通信,因而每一个主机都至少具有一个IP。
IP协议是一个尽力的服务,意味着提供最大可能的通信,尽管如此,它依然无法担保。如segment的顺序,segment的完整性。
因而传输层拓展了网络层的服务
拓展了哪些服务?
提供了完整性检查的功能
数据传输服务和完整性检查服务是最小的传输层服务,也是UDP仅仅提供的两个服务。因而UDP也是不可靠服务。
而TCP提供了可靠数据传输和阻塞控制的服务。
另外,传输层协议将主机间的通信拓展为主机中进程间的通信,这种拓展称为传输层多路复用和多路分解。
多路复用和多路分解
什么是多路复用和多路分解?作用是什么?
将主机间的通信拓展为主机中进程间的通信,关键就在于如何定位计算机中的进程?
网络层协议(IP)定位计算机,因而传输层协议(TCP/UDP)需要做的就是去定位进程,为此计算机为每一个进程提供一个端口号(port),而应用层和传输层之间通过一个抽象的门(套接字)进行数据的传输,所以传输层协议的作用就是定位套接字。
- 多路分解:将传输层报文段中的数据定位到正确的套接字。
- 多路复用:从不同的套接字收集源主机上的数据块,用头信息封装每个数据块(稍后将用于解复用)以创建段,并将这些段传递到网络工作层。
但是无连接(UDP)的多路复用和多路分解又和有连接(TCP)的不一样,举例说就是门牌号的定位方式不一样。
无连接(UDP)的多路复用和多路分解?
创建UDP套接字的java代码:1
2DatagramSocket mySocket = new DatagramSocket();//随机分配端口号
DatagramSocket mySocket = new DatagramSocket(xxxx);//指定端口号
UDP套接字通过(目标IP,目标端口号)(二元)进行定位,因而两个不同来源的UDP报文可以定位到相同的目的进程。
有连接(TCP)的多路复用和多路分解?
创建TCP套接字的java代码:1
Socket clientSocket = new Socket("hostname",portNumber);
TCP套接字通过(源IP,源端口号,目标IP,目标端口号)(四元)进行定位
套接字有时候和线程是对应的
无连接传输——UDP
UDP所提供的服务是非常极简的,使用UDP进行传输,几乎就等于使用IP进行传输。
传输前也不需要做多余的准备工作,因而说UDP是无连接的。
使用UDP的案例——DNS
为什么还有使用UDP协议,而不是更好的TCP协议的应用?
- 提高实时性:由于TCP具有阻塞控制,因而使用TCP往往存在一定的延迟。如果希望提高实时性,并且可以容忍一些数据的丢失,可以使用UDP。
- 减少时延:如果大量的数据都需要进行三次握手,必然会导致较高的时延。
- 无连接状态:TCP需要维护连接状态(接受/发送缓存,阻塞控制参数,序列号和确认号参数,流量控制参数等)。然而,使用UDP能支持更多的活动。
- 头信息开销小:每一个TCP报文头有20字节,而UDP只有8字节。
UDP报文结构
- checksum:校验UDP报文的数据正确性
- length:报文长度(头+数据)
为什么数据的校验要放在传输层?
这是一个被人提倡的端到端原则(end-end princeple)——由于某些功能必须基于端到端来实现,因而在底层实现这些功能是冗余而无意义的。
可靠数据传输原理
在学习TCP协议前,先学习一些可靠数据传输原理。
一般来说,传输层下面的协议都是不会实现可靠传输的,因为难度较大。所以这里讨论的可靠传输协议建立在底层不可靠的传输上。另外,便于分析,只讨论单向数据传输,双向数据传输实际上并不会增加过多的难度。
构造可靠数据传输协议
接下来将会循序渐进的开始学习:
如果底层是可靠传输?
rdt1.0:完全可靠信道上的可靠数据传输
最简单的情况,底层信道是可靠传输,传输层基本不需要做任何其余的处理。
用有穷状态机(FSM)表示请求端和接收端:
FSM中圆圈表示所处的状态,线段表示触发状态变化的动作。
如果底层信道不可靠,如何确保消息准确无误的被接受到?
rdt2.0:接收方采取确认(ACK)和否认(NAK)的方式回复。
实现确认和否认的功能需要三个额外的协议:
- 差错检查:检查是否出错(使用校验和)
- 接收方反馈:需要约定反馈信息(如0表示NAK,1表示ACK)。
- 重传:什么情况下进行重传(如出错后进行重传)
rdt2.0的FSM如下:
但是,问题来了,如果接收方回复的消息在回传的过程中损坏(仅仅是损坏,之后无法确认)了呢?
有3中可能能够解决的方案:
- 发送方收到损坏的回复,无法判断是ACK还是NAK,这时候定义一个新类型分组(相当于请重复一遍),当接收方收到这个特殊的分组,就会重发一次确认消息。但是这样下来,又需要确认这个特殊的分组会不会又损坏,子子孙孙麻烦不断。
- 增加足够的校验和比特,使得发送发不仅可以检查出错误,还可以恢复错误。
- 发送方把数据分组重新发送一次,但是这样问题是接收方需要判断这个数据分组是一个新的分组还是一个重复的分组(冗余分组)。 了
一般是采用第3个方案,所以问题来到:如何判断这个数据分组是一个新的分组还是一个重复的分组(冗余分组)?
新增一个flag——sequence number(序号)字段:前后两个消息seq一样,表示是重复消息,seq不一样表示是新的消息。
对于等停协议(确认一个后再发送下一个),1bit的seq足矣。
rdt2.1的FSM如下:
另外,当消息损坏,接收方发送NAK来告诉发送方。这一步也可以通过发送之前成功的ACK来告诉发送方最近一次的成功记录,间接操作。这就需要在ACK中加入消息的序号。如此一来,便有了rdt2.2:
接下来,如果消息不是损坏,而是在传输的过程中丢失了呢?(丢包现象)
rdt3.0:采用发送端超时重传机制
配合时间设定需要一个定时器:完成如下功能:
- 发送消息时启用定时器
- 响应定时器中断
- 终止定时器
另外,超时重传的时间长度是一个比较重要的设置因素,首先至少需要一个消息的往返时间+接收端处理消息的时间,而太长又会导致时延增加,之后的章节会进一步讨论。
流水线可靠传输协议
之前提到的rdt3.0协议,从可靠性来说已经足够了,但是rdt是一个等停协议,意思就是要等一个分组确认了之后才发送下一个分组,那么确认一个分组可能需要10ms,但是发送一个分组可能只需要1us,那么这是信道的利用率是非常低的,一个完整的文件在这种协议下可能耗时就会比较久。
改善的方式是采用流水线可靠传输协议
实现流水线协议需要拓展3个方面的技术:
- 要增加seq序号的范围(比如增加seq的位数):管道中的分组都得有一个唯一的标识。
- 实现缓存:发送方需要缓存已发送但未确认的分组,接受方要缓存已经成功确认的分组。
- 丢包,损坏,延时的处理方式,序号的范围和缓存的内容也取决于这个方式,两种常用的方式是:GBN(Go-Back-N)和选择重传(selective repeat)
GBN协议
GBN协议和RDT有什么不同?
序号范围增加后,在序号范围内,发送方可以同时发送N个分组
N是窗口的长度
base表示最早的未被确认的序号
nextseqnum表示最小的可以使用序号
window size是可以使用的序号范围,也叫做窗口
随着协议的运行,窗口在序号空间内移动,因此GBN协议也叫做滑动的窗口协议
为什么N不能是无限呢?
受流量控制和拥塞控制的限制,之后会具体学习。
GBN协议的工作原理?
sender FSM响应动作分析:
- 响应上层调用:当窗口未满时,才创建新的分组并发送。
- 响应确认消息:接收方累积确认的作用——接受序号n的ACK后,确认n之前所有的分组,更新窗口。
- 响应超时:如果出现超时,发送base后所有的分组。计时器是窗口共享的,收到ACK,重启计时。如果没有未确认的分组,终止计时。
- 对于错误的分组不作任何处理,等待超时。
recevier FSM响应动作分析:
- 响应接受:累积确认——如果接受的分组序号是希望的序号n,则回复序号n的ACK。
- 其余情况:重传上一次(n-1)的ACK。
GBN协议问题在于:
- 出现超时,发送base后所有的分组。单个分组的差错会引起大量分组的重传,导致信道中出现大量重复的分组。
- 丢弃所有失序的正确分组很蠢和浪费。
因此有SR协议。
selective repeat协议
SR协议和GBN有什么不同?
发送方选择发送错误或者丢失的分组,接受方选择确认正确接受的分组。
对于发送方和接收方,都会维护各自的窗口。
base确认后移动到下一个未确认的分组处,窗口的移动看起来是跳跃的。
SR协议的工作原理?
sender响应动作分析:
- 响应上层调用:和GBN一致。
- 响应ACK消息:接收方选择确认的作用——ACK分组如果在窗口内,标记为已确认。base确认后窗口移动到下一个未确认的分组处。
- 响应超时:选择重发——计时器是分组独享的。超时重传一个分组。
- 对于错误的分组,和GBN一致。
recevier FSM响应动作分析:
- 响应接受窗口内的分组:选择确认——正确接受的分组序号在窗口内,回复序号n的ACK,并且标记为已确认,并且缓存起来。等base分组被确认后,将base之后所有分组一并交付给上层。
- 响应接受窗口前的分组:如果序号在base分组的前N个,依然需要产生一个当前序号的ACK。
- 其余情况:忽略。
接收方第二步的意义何在?
产生这一步的原因是发送方的窗口和接收方的窗口不同步。
比如发送方的窗口是3,4,5,6;接收方的窗口是7,8,9,10.这种情况是可能的。
除此,还有一种情况是,序号的范围只有0,1,2,3,因而发送方的窗口是0,1,2;接收方的窗口是3,0,1。这种情况下第二步就无法确认重发的序号为0的分组是重复的还是新发的了。
经计算,窗口的长度必须小于、等于序列号的一半
TCP可靠传输协议
TCP的相关概念
- TCP是面向连接的,如何理解“连接”是什么意思?:意味着在TCP数据传输前,先发送某些预备报文,以建立确保数据传输所需的参数。这个连接是抽象的,只存在传输层。
- TCP从其它层接受到的报文并不会立即处理,而是先缓存起来,在合适的时间处理。
- MTU(maximum transmission unit):链路层最大帧长度。受MTU影响,TCP报文也有最大的传输报文长度(maximum segment size(MSS),不包括头)
TCP报文段结构
- 源端口号和目的端口号:多路复用和多路分解。
- 检验和:检查错误
- 4字节的seq和4字节的ack:可靠传输
- 2字节接受窗口:流量控制
- 4位的头长度:指示头的长度(以2字节的字为单位)
- 选项字段:协商MSS和调节窗口
- 6Bit的标志字段:具体在后文根据功能讨论
可靠传输实现
和之前的传输协议一样,TCP也具有seq,ack,超时重传等原理。但是采取的方式又有不同:
seq和ack的工作方式?
- seq:seq是无结构还是有序的,选用报文中数据的第一个字节作为seq。
- ack:接收端返回ack是下一个期望从发送端接受到的下一个字节,即下一个报文的seq
如何确定超时间隔?
超时时间不能太长,也不能太短,至少要大于一个TCP消息的往返时间(RTT,报文段发出到确认的时间)
如何估计往返时间?
在某个任意时刻,记录一个报文发送到接受的样本RTT时间(SampleRTT),然后取指数加权移动平均:
EstimatedRTT = (1 – a) • EstimatedRTT + a • SampleRTT
a的参考值是0.125
除此,还会计算SampleRTT偏离EstimatedRTT的程度:
DevRTT = (1 –b) • DevRTT + b •| SampleRTT – EstimatedRTT |
b的参考值是0.25
如何设置超时间隔?
要求超时时间设置为EstimatedRTT加上一定余量:当SampleRTT波动较大,这个余量较大,当SampleRTT波动较小,这个余量较小。因此DevRTT起作用了:
TimeoutInterval = EstimatedRTT + 4 • DevRTT
TCP响应事件?
TCP传输协议和之前学到的几个协议有着大致相同的原理:
- 从应用层接受数据:封装层报文,并发送,启动计时器。
- 超时:重发,重启计时器。
- 接受ACK:累积确认。
和之前的传输原理相比,TCP长期使用以来积累的微妙改动?
- 虽然是类似GBN的累积确认,但是TCP在超时后,只会重发未确认的最小序号的报文,而不会这之后的报文,大大减少了重发报文的数量。
- 当发生超时的时候,下一次的超时时间会被设置为两倍,而不是之前的结论。这种改动提供了一个简单的 拥塞控制,超时很有可能是因为网络拥塞,而该机制会减少重发的频率来缓解拥塞。
- 快速重传:之前的重传都是建立在超时的基础上的,但是有可能就是超时的间隔时间相对较长。TCP一个巧妙的解决方案是,当接受到三次重复确认的ACK时(冗余ACK),就立即重发,不再等待超时。
TCP是一个GBN还是SR?
从累积确认的方式来看,更像是一个GBN协议,但是像上面提到的一样,也是有区别的。
对TCP提议的一种修改是选择确认,这时候就更像是一个SR协议。
流量控制
流量控制的作用是什么?
接受段的缓存是有限的,为了不让消息溢出,应该控制发送端发送数据的速度,这就是流量控制。
如何进行流量
TCP报文头中有一个RcvWindow变量来记录剩余缓存空间:
- LastByteRead:已读的最后一个字节
- LastByteRcvd:接受到的最后一个字节
rwnd = RcvBuffer – [LastByteRcvd – LastByteRead]
当rwnd为0时,发送端依然会持续发送一个只有一个字节的报文来刷新缓存空间。
UDP并不提供流量控制,因此很容易出现溢出的情况。
TCP连接建立
“握手”过程:
- 发送一个特殊的报文(SYN报文)——不含数据,SYN标记位被置为1,随机初始化一个起始序号(client_isn)。
- SYN报文到达接受方,开始分配缓存和变量,之后回复一个特殊的报文(SYNACK报文)——不含数据,SYN标记位被置为1,随机初始化一个起始序号(server_isn),ack为client_isn+1。
- 接收到SYNACK报文,分配缓存和变量。之后发送另外一个报文段——可以含数据,SYN被置为0,ack为server_isn+1。
握手的过程就是初始化变量的过程。
挥手过程:
- 任意一方A发送特殊报文(FIN报文)——FIN标志位置为1 。
- 另一方B接收到FIN报文,回复ACK报文。A接收到ACK后清理缓存和变量。
- B一段时间后发送FIN报文
- A接受到FIN报文,回复ACK报文。B接收到ACK后清理缓存和变量。
- A等待一段时间,确认ACK报文被接收到后关闭。
客户端连接状态:
服务端连接状态:
为什么挥手需要四次?
因为可以同时开始,但是不能同时结束,因为要等一方的所有报文发送完。
为什么是三次握手而不是两次握手?
为了使初始seq达成一致,需要一方发送,一方确认并发送,一方确认。
为什么需要初始化序列?
为了尽量减少在网络中出现重复的序列号
拥塞控制原理
在实际中,分组的丢失往往是因为网络拥塞,重传只是一种丢失后的补救措施。但是无法从根本上解决网络拥塞的问题。
之前说到的TCP中减少重传的频率是一个简单的减缓网络拥塞的方法。
拥塞原因和开销
为什么会出现网络拥塞呢?
往往是因为信道上的消息太多,就和高速公路上的车太多了一样,用专业术语描述就是:
当吞吐量达到链路容量(也就是带宽)的时候,就会发生网络拥塞。
网络拥塞会有怎样的影响?
场景1:路由器的缓存无限大
最直接的影响就是堵车,也就是所谓的排队时延增加。
场景2:路由器的缓存有限
- 多余的车被丢弃,也就是所谓的丢包。
- 由于超时重传机制,导致传输的分组部分是原始数据,部分是重传数据(传过去不会被丢弃)。
- 再进一步,由于时延过大,有些数据并不是丢弃了,而是往返时间大于超时间隔,这时候导致的重传数据是无用重传数据,传过去也会被丢弃。
场景3:路过多个路由器
路由器好比一个阀,同样的发车速率的情况下,已经路过阀的车流量相比没有路过阀的车流量占比肯定要小。
随着发车速率的增加,已经路过阀的车流量路过第二个阀后将会趋于0,同理,这一部分占比较大的车流量在路过下一个阀之后也会被压榨至0。
这样,所有被第一个路由器处理的占比较大的数据报文,在之后都会被丢弃,因而只是做无用功。
当一个分组沿一条路径被丢弃时,每个上游路由器由于转发改分组而后被丢弃的传输容量就被浪费了。
建议是:优先处理经过了一定数量上游路由器的分组
拥塞控制方法
根据网络层是否为运输层拥塞控制提供显示的帮助来区分控制方法:
- 端到端拥塞控制:网络层不提供支持,端系统对网络行为的观察来推断。
- 网络辅助的拥塞控制:路由器会向端发送拥塞状态的反馈信息。
网络辅助拥塞控制又有两种方式:
- 反馈信息可以由路由器直接发给发送方,采用阻塞分组的形式。
- 路由器标记分组中的某字段来指示拥塞现象,一旦接收方收到这个有拥塞标记的分组,就通知发送方网络发生了拥塞。
后一种形式至少经过一个完整的往返时间。
TCP拥塞控制
TCP如何进行发送速率的控制?
和流量控制中的RcvWindow类似,拥塞控制也有一个控制窗口CongWin,发送方根据这个窗口控制发送的数据量。
所以理论上有:LastByteSent – LastByteAcked < min{cwnd, rwnd}
所以发送方的发送速率大概是():min{cwnd, rwnd}/RTT
TCP如何感知网络拥塞?
通过超时或者3次冗余ACK
TCP改变速率的算法是什么?
TCP拥塞控制算法:特性是
- 馒启动阶段:当接受一个确认ACK时,就增加一个MSS,结果是一个RTT事件内CongWin增加一倍。该阶段的特性是指数增。
- 避免拥塞阶段:当接受一个确认ACK时,CongWin增加一点,期望是在一个RTT时间内增加一个MSS。该阶段的特性是,线性增,乘性减。
- 快速恢复阶段:进入一个中间状态,重发第一个未确认报文,同时CongWin减半。重传后如果接受到新的ACK,就恢复到避免拥塞状态,否则进入慢启动阶段。
三个状态之间的转换关系如下:
超时进入慢启动。
达到阀值进入避免拥塞阶段。
接受3次冗余ACK进入快速恢复。
为什么区别对待超时和冗余ACK?
快速恢复机制是新版TCP Reno协议中开始执行的,为了适应快速重发的机制,之前的老版本TCP Tahoe协议并不具有这个中间阶段,对待超时和3次冗余ACK都会进入慢启动状态。
原因是因为能受到冗余ACK,至少说明网络还是有一定的传输能力的,因此只减少一半。这种方式叫快速恢复,结果是增加了吞吐量。
吞吐量
W是最高窗口长度,当达到W时就会减半,所以平均吞吐量平均是0.75W/RTT。
也可以通过计算得到:
达到10G吞吐量需要满足5,000,000,00个组一个丢包,要求是极高的。
公平性
- 两个同样的TCP连接
虽然是竞争,但是在TCP拥塞算法下,最终会处于一个平衡状态。
- TCP和UDP
由于UDP不具有拥塞控制,所以必然会压榨TCP的传输速率。
- 采用多连接TCP
虽然TCP之间是公平的,但是采用一条TCP的数据传输和采用两条TCP连接的数据传输必然是不公平的。
未来
更多完美的TCP拥塞算法在被逐步研究使用。