传输层
传输层概述
传输层是整个TCP/IP协议栈核心之一,位于网络层之上,应用层之下。利用网络层的服务,为上层应用层提供服务。与网络层类似,传输层也拥有面向连接的服务与无连接的服务两种,用途在于提供高效的可靠的,性价比高的数据传输
传输层的作用在于为运行在不同主机上的应用进程提供逻辑通信(这里的逻辑通信指的是在传输层角度看来两个进程间直接进行通信,实际上还是通过下层提供的服务,不都安将数据向下层传递并在对方处向上传递后才实现通信),简言之,传输层提供的是进程到进程间的通信
传输实体
完成传输层任务的硬件或软件
- 传输实体可以在操作系统的内核
- 可以以一个链接库的形式绑定到网络应用中
- 可以以一个独立的用户进程运行
- 甚至可以实现在网络接口卡(网卡)上
传输层与网络层的区别
网络层运行在由承运商操作的路由器上,因此用户无法真正控制网络层。对于丢包,高延迟等问题只能选择被动接受。传输层架设在网络层之上,允许用户控制服务质量
传输层原语独立于网络层原语,而网络层原语会因为网络的不同而不同。传输层的原语在向应用层传输的原语可以屏蔽掉这些不同,只提供标准,统一的原语。
原语:计算机进程的控制通常由原语完成。所谓原语,一般是指由若干条指令组成的程序段,用来实现某个特定功能,在执行过程中不可被中断。在操作系统中,某些被进程调用的操作,如队列操作、对信号量的操作、检查启动外设操作等,一旦开始执行,就不能被中断,否则就会出现操作错误,造成系统混乱。所以,这些操作都要用原语来实现 原语是操作系统核心(不是由进程,而是由一组程序模块组成)的一个组成部分,并且常驻内存,通常在管态下执行。原语一旦开始执行,就要连续执行完,不允许中断
传输层和网络层的作用范围不同,网络层负责把数据从源机送达到目的机(主机到主机 Host to Host)。传输层负责把数据送达到具体的应用进程或端口(End to End 端到端,end point端点即套接字socket和某个具体的应用程序绑定)
数据段TPDU
传输层处理的协议数据单元是TPDU(Transport Protocol Data Unit)即数据段(segment),是从传输实体发到对应端传输实体的信息
TPDU作为数据(载荷)被封装在分组(packet)中,通过网络层进行传输交换
常用协议
- 用户数据报协议(UDP:User Datagram Protocol)
- 传输控制协议(TCP:Transmission Control Protocol)
多路复用与解复用
多路复用
在发送报文的主机一端完成,从多个套接字接收来自多个进程的报文,根据套接字获取对应报文的IP地址和目标端口,并将目标端口传给传输层进行报文段的封装,IP地址传给网络层进行数据报的封装。这些头部信息用于进行以后的解封装
多路解复用
在位于接收报文一端的主机处完成,根据报文段头部信息中的端口号与数据报中提供的IP地址将接收到的报文段传送给正确的套接字
这里比较难以理解的一点是,传输层直接与套接字相联系,而传输层上的报文段只包含了源端口与目标端口,并没有主机IP地址信息,那么IP地址是如何传送给相应套接字进行匹配的? 事实上,只需要认清TCP/IP中的各层结构是人们认为规定的标准结构,是存在于虚拟环境下的结构(至少对于传输层和网络层是如此)通过套接字编程就可以看出,无论是报文,报文段还是数据报都是通过参数向下一层逐层传递的,所以虽然IP地址在从网络层向传输层传递之前就已经被取出,其也可以通过参数的形式向传输层传递并最终传递给套接字进行套接字的匹配或更新。
无连接(UDP)多路解复用
UDP套接字用二元组标识(目标IP地址、目标端口号),即通过目标IP地址与目标端口号可以唯一标识一个UDP Socket,这就说明源IP或源端口不同但目标IP与端口一致的两个请求会指向同一个套接字
无连接多路解复用的过程一般是:
- 主机收到UDP报文段
- 检查报文段的目的端口(由于已经传送到目的主机上,所以目的端口唯一标识一个套接字)
- 存在与该端口对应的套接字则将该报文段传送给套接字
- 不存在则创建套接字并传给其报文段
面向连接(TCP)的多路解复用
TCP套接字:四元组本地标识:源IP地址,源端口号,目的IP地址,目的端口号
TCP套接字由四元组唯一标识,也就是说四元组中哪怕只有一个参数不相同,都会指向不同的套接字。
这使得刚才在UDP多路解复用中出现的不同源端由于目的相同使用同一个套接字的情况不会出现,Web服务器对每个连接客户端有不同的套接字
通信模型
端点
端点就是所说的套接字(Socket),一个套接字包括;两个内容:IP地址和端口号。可以写成(IP,Port)。
通信模型-通信五元组
通信五元组由源端点,目的端点和协议组成,其中源端点和目的端点包含IP地址和端口,协议可以是TCP或UDP
一些已分配的知名端口
Port | Protocol | Use |
---|---|---|
21 | FTP | File Transfer |
23 | Telnet | Remote login |
25 | SMTP | |
69 | TFTP | Trivial file transfer protocol |
79 | Finger | Looking information about a user |
80 | HTTP | World Wide Web |
110 | POP-3 | Remote email access |
119 | NNTP | USENET news |
用户数据报协议 UDP
(User Dataprogram Protocol)
UDP是一个无连接的传输层协议,UDP传输的是数据段(报文段),无需建立连接(UDP发送端和接收端之间不进行握手,每个UDP报文都被单独的处理),不提供数据的可靠传输。很多网络应用,例如DNS都采用了UDP。UDP传输的是UDP数据段
UDP提供的是尽力而为的服务:
- 可能出现数据段的丢失
- 可能由于网络原因导致应用进程的报文段乱序
使用UDP的原因:
- 不建立连接(降低延时)
- 简单,在发送端和接收端都没有连接状态
- 报文段头部小(TCP20字节,UDP8字节)减小开销,存放更多数据
- 没有拥塞控制和流量控制,UDP可以尽可能快的发送报文段
因为以上的原因,UDP常被应用于实时流媒体播放,DNS或SNMP
如果想要在UDP上进行可靠的数据传输,只能从应用层入手,在应用层增加相应的检测机制以及差错恢复
UDP数据段
数据段包括总长为64bits,共4部分,每部分16bits的数据段头和数据两个部分。
数据段头结构
第三个字段数据段长度表示包括段头和数据的总长度,UDP中校验和可能存在也可能不存在,不存在时校验和长度设为0.
UDP数据段头最重要的内容就是前两个字段源端口和目的端口,二者长度均为16bits,能表示的最大长度是65536,也就是能表示的端口数量是65536个,范围从0~65535。
端口定义
端口号 | - |
---|---|
小于等于1023(知名端口) | 用于公共应用(保留,全局分配,用于标准服务器),只能用于特权用户,比如UNIX的root用户启动标准80服务。由IANA分配,目前已经使用700多个 |
1024~49151 | 用户端口/非特权用户端口,可以通过IANA注册(例如BT使用了6881-6887的端口) |
大于等于49152 | 动态端口,私人端口(其中包括自由端口:free port,由本地分配,并且动态随机生成的端口号,访问网站时操作系统会随机产生一个自由端口用于访问) |
UDP校验和
UDP校验和计算方式是将IP伪头部,UDP头部和数据按照二进制每行16位的格式排列,然后对这些排列好的数据进行补码相加求和(注意:当数字相加时,在最高位的进位要回卷,再加到结果上),再对得到的结果进行求反码,最终得到的结果就是校验和
TCP/UDP伪头部(pseudo header)
这里需要注意UDP伪头部是在计算校验和时临时与UDP数据报拼接到一起,二者临时相连只是为了计算校验和
接收方在接收到数据段后利用其中的校验和以及其他部分数据经过计算最终得到的结果每个位应该全部为1,如果出现0,证明传输过程中发生错误。(这里应该注意即便正确,也有可能是多次比特翻转导致最终结果错等于校验和,不代表一定不出错)
在计算校验和的过程中使用了属于网络层的IP地址,这破坏了分层原则
UDP提供端点标识,端到端的数据传输,不提供差错检测和可靠传输,但简洁高效
可靠数据传输原理RDT
RDT是网络中最重要的问题之一,其所要解决的是“如何利用下层提供的并不可靠的服务,为其上层提供可靠服务”,下层可能出现的问题包括比特反转带来的传输数据错误,或是由于网络原因导致的报文段乱序,以及报文段丢失等问题
停等协议
要解决上文所属的可能出现的错误,首先可以被应用的就是停等协议,即在对方报文段到达之前,不进行下一步操作。要解决可能由于比特翻转带来的数据传输错误,需要发送方在报文段头部加入检错码(校验和),而接收方在接收后利用校验和对报文段进行检验,随后通过发送确认报文段的方式,将校验结果返回给发送方,结果可以使用两种:
- 确认(ACK):接收方显式地告诉发送方分组已被正确接收
- 否定确认(NAK): 接收方显式地告诉发送方分组发生了差错,发送方收到NAK后,发送方重传分组
上文所述方法解决了在报文段内容出错时的情况,但没有考虑到,作为重要结果因素的校验结果报文段(ACK/NAK)也有可能出错,一种情况下发生比特翻转导致接收报文段的一方无法识别报文段内容,也无法理解校验结果,一种情况下,ACK/NAK直接丢失,由于停等协议本身的限制条件,对方不做反应或没有接收到对方反应的情况下,双方会陷入死锁状态。而如果只是单纯在没有接收到响应报文段的情况下采取直接重传的措施,则可能导致报文段的重复发送
为了解决上述问题,需要做出如下改进,为发送方发送的报文段加入序号机制,表明每个分组的序号,加入计时器机制(发送方等待一段合理的时间,一般是一次数据发送到确认分组返回的几倍的时间),在一段时间未收到响应后,对上一个分组进行重传。如果ACK/NAK出错导致不可读,则重传该分组(考虑最坏情况),接收方如果收到重复分组,则直接丢弃该分组并重传上一个分组的确认。(链路层timeout时间是确定的,传输层的timeout时间是自适应的,会根据网络情况进行调整)
无NAK协议指仅使用ACK表明分组接收状态的协议,此时接收方每次只发送最后一个正常接收到的分组,对于错误分组,则直接返回其上一个分组的分组的确认,而发送方在接到确认分组后,就会发送其确认分组对应的数据分组的下一个分组,实现数据的重传。
这样处理使得确认信息减少一半,使得协议更加简单,并且可以一次性传送多个分组,为下文做准备
上文所述的方式可以工作,也就是常见的停等协议,但这种方式的效率十分低下(链路容量比较大时,一次发一个PDU 的不能够充分利用链路的传输能力)
例题:
1 Gbps的链路,15 ms端-端传播延时,分组大小为1kB,求其利用率
U_{sender}表示当前信道的利用率,即该信道一段时间内用于数据传输的比例,可见效率极低,而其瓶颈在于网络协议限制了物理资源的利用,即由于停等协议导致数据无法发送
为解决上述存在的利用率低的情况,可以使用流水线协议的方式发送数据,允许发送方在未得到对方确认的情况下一次发送多个分组
- 必须增加序号的范围:用多个bit表示分组的序号
- 在发送方/接收方要有缓冲区
- 发送方缓冲:未得到确认,可能需要重传;
- 接收方缓存:上层用户取用数据的速率≠接收到的数据速率;接收到的数据可能乱序,排序交付(可靠)
滑动窗口协议
发送缓冲区
- 形式:内存中的一个区域,落入缓冲区的分组可以发送
- 功能:用于存放已发送,但是没有得到确认的分组
- 必要性:需要重发时可用
- 发送缓冲区的大小:一次最多可以发送多少个未经确认的分组
- 停止等待协议=1
- 流水线协议>1,合理的值,不能很大,链路利用率不能够超100%
- 发送缓冲区中的分组
- 未发送的:落入发送缓冲区的分组,可以连续发送出去;
- 已经发送出去的、等待对方确认的分组:发送缓冲区的分组只有得到确认才能删除
发送窗口
- 概念:发送缓冲区内容的一个范围,那些已发送但是未经确认分组的序号构成的空间
- 发送窗口的最大值<=发送缓冲区的值
- 开始状态:没有发送任何一个分组
- 后沿=前沿
- 之间为发送窗口的尺寸=0
- 每发送一个分组,前沿前移一个单位
- 发送窗口前沿移动的极限:不能够超过发送缓冲区
接收窗口/接收缓冲区
- 接收窗口=接收缓冲区,二者范围一致
- 接收窗口用于控制哪些分组可以接收;
- 只有收到的分组序号落入接收窗口内才允许接收
- 若序号在接收窗口之外,则丢弃
- 接收窗口尺寸Wr=1,则只能顺序接收;
- 接收窗口尺寸Wr>1,则可以乱序接收
- 但提交给上层的分组,要按序
正常情况下两个窗口之间的动作
- 发送窗口
- 有新的分组落入发送缓冲区(或原来发送缓冲区中就存在未发送的分组),则进行分组的发送,并且若发送缓冲区未满则将缓冲区前沿向前移动
- 接收到目标序号的分组的确认(不是所有已发送分组的确认都可以,必须是未确认分组中的第一个),则缓冲区后沿向前滑动,使更多分组落入缓冲区中
- 接收窗口
- 收到分组,且落在发送缓冲区范围内,则接收分组
- 若收到的是目标序号的分组,则返回确认给发送方
异常情况下GBN的窗口动作
GBN:回退N步协议
- 发送窗口
- 有新的分组落入发送缓冲区(或原来发送缓冲区中就存在未发送的分组),则进行分组的发送,并且若发送缓冲区未满则将缓冲区前沿向前移动
- 倒数计时器在时间到后,仍未收到目标序号的分组确认,则将当前缓冲区中的所有分组按序重发
- 收到重复的旧分组确认,则窗口不进行滑动,没有新分组在缓冲区中能够发送,进而诱发计时器
- 接收窗口
- 收到乱序窗口,没有在接收窗口范围内,则抛弃
- 然后发送旧分组的确认
异常情况下SR的窗口动作
SR:选择性重传协议
- 发送窗口
- 有新的分组落入发送缓冲区(或原来发送缓冲区中就存在未发送的分组),则进行分组的发送,并且若发送缓冲区未满则将缓冲区前沿向前移动
- 倒数计时器在时间到后,仍未收到目标序号的分组确认,则将当前缓冲区中的所有分组按序重发
- 收到乱序的分组确认,则窗口不进行滑动,没有新分组在缓冲区中能够发送,进而诱发计时器
- 接收窗口
- 收到乱序分组,落入到接收窗口范围内,接收
- 发送该分组的确认,单独确认
简言之,GBN与SR都能一次发送多个未经确认的分组,提高线路的利用率,但GBN接收窗口尺寸为1,只能顺序接收分组,如果乱序,则整个发送窗口内都要被重传,SR接收窗口尺寸大于1,不需要顺序接收分组,每个分组确认单独发送,但只有目标序号分组被确认后,接收窗口才会滑动。GBN只需要维护一个倒数计时器,记录最先发送的未确认分组的时间,在时间结束后,重传所有发送窗口中的分组,SR需要维护所有已发送但未确认分组的倒数计时器,并在指定计时器结束后,重传特定的分组
对比GBN与SR
区别 | GBN | SR |
---|---|---|
优点 | 简单,所需资源少(接收方一个缓存单元) | 出错时,重传一个代价小 |
缺点 | 一旦出错,回退N步代价大 | 复杂,所需要资源多(接收方多个缓存单元) |
适用范围 | 出错率低:比较适合GBN,出错非常罕见,没有必要用复杂的SR,为罕见的事件做日常的准备和复杂处理 | 链路容量大(延迟大、带宽大):比较适合SR而不是GBN,一点出错代价太大 |
窗口尺寸 | $2^n-1$ | $2^{n-1}$ |
TCP数据段
TCP-传输控制协议
(Transmission Control Protocol)
是专门为了在不可靠的网络上提供可靠的端到端的字节流而设计的(应用进程到进程)。TCP必须动态地适应不同的拓扑、带宽、延迟、分组大小和其它的参数,并且当有错误的时候,能够足够健壮
支持TCP的机器都有一个TCP实体,或者是用户进程,或者是操作系统内核。都可以管理TCP流跟IP层接口,TCP是一个全双工的协议,在同一个连接下,数据可以双向传输,双方均可以作为发送方或接收方,并且这种发送和接收可以同时进行。采用管道化(流水线)技术进行报文段的发送,即可以在未经接收方确认的情况下发送多个报文段。发送方与接收方都有缓存空间,发送方的缓存空间是为了方便数据出错或计时器结束时进行报文段的重发,接收方的缓存空间是为了处理接收的数据,对数据进行排序。
TCP发送的数据是可靠的,有序的字节流,但会根据情况对完整的报文进行切分,报文的还原工作完全由应用层负责,同时负责区分报文的边界
- 发方:封装
- TCP实体接收本地进程的用户数据流,将其分割成不过64kB的分片(实践中,通常分割成1460字节,以通过以太网传输)
- 收方:解封装
- 当包含TCP数据段的报文到达某台机器的时候,被提交给传输实体,传输实体将其重构出原始的字节流
- TCP连接上的每个字节都有它自己独有的32位序列号
- 收发双方的TCP实体以数据段的形式交换数据
- 一个数据段包括20字节的头部(不包括可选项)和数据域(0或更多字节)
TCP软件决定数据段的大小,有两个因素限制了数据段的长度:
- TCP数据段必须适合IP的65515(65535B-20B)的载荷限制(65535B是IP分组最大长度,20是IP分组头部固定长度,65515就是IP分组净载荷)
- 每个TCP数据段必须适合于下层网络的 MTU (如,1500 字节以太网载荷大小)
- TCP使用的基本协议具有动态窗口大小的滑动窗口协议 (sliding window protocol)
TPDU TCP数据段
源端口和目的端口字段
标明了一个连接的两个端点,是通信五元组中的两个重要元素,用来跟踪同一时间内通过网络的不同会话。一般每个端口对应一个应用程序
序列号 – 字节号 (32 位)
报文段首字节的在字节流的编号,标识了该报文段中数据的首字节在整段报文中的顺序,其中最特殊的是初始序列号ISNs(initial sequence numbers ):随机产生的
确认号
期望从另一方收到的下一个字节的序号(32位),在TCP中为了保证可靠传输,采用了肯定确认重传技术,确认号就是用于肯定确认重传
TCP段头长度
单位32位(4字节),含义与IP的段头长度完全一致
保留域/字段
即图中灰色部分,现在也开始逐步使用(进行拥塞控制等)
控制比特
URG,ACK,PSH,RST,SYN,FIN共6部分,每部分1byte,称作控制比特
- URG:当紧急指针使用的时候,URG被置为1。紧急指针是一个对于当前序列号的字节偏移量,标明紧急数据从哪里开始
- 当URG=1时,表明有紧急数据,必须首先处理
- 与紧急指针配合使用
- 收方收到这样的数据后,马上处理,处理完后恢复正常操作
- 即使win=0,也可以发送这样的紧急数据段
- ACK:1表示确认号有效,0表示确认号无效
- PSH:表示这是带有PUSH标志的数据接收方收到这样的数据,应该立刻送到上层,而不需要缓存它
- RST:被用来重置一个已经混乱的连接
- SYN 用在连接建立的过程
- SYN=1,ACK=0:连接请求
- SYN=1,ACK=1:连接接受
- FIN:被用来释放连接,它表示发送方已经没有数据要传输了,但是可以继续接收数据
窗口尺寸
愿意接收的字节数量,为了避免收方被大量涌入数据所淹没,TCP实体进行了流控(Flow Control)。通常使用可变长的滑动窗口来完成流控。所以第十二个字段用16位来表示窗口尺寸
告诉对方可以发送的数据字节数(从确认字节号开始(决定于接收方)
校验和
与UDP中的校验和是一样的,唯一区别在于协议位置的编号不一样
紧急指针
和URG数据段配合使用,指明了紧急数据
选项域(可选)
提供了一种增加基本头没有包含内容的方法
TCP三次握手建立连接
TCP提供的是面向连接的服务,TCP数据段的传输是在TCP连接上进行的。而TCP连接是三次握手建立的,三次连接主要是为了使双方互相同意连接的建立,并且互相同意连接的参数(双方的起始序号)
TCP连接的建立
- 一方(server)被动地等待一个进来的连接请求
- 另一方(the client)通过发送连接请求,设置一些参数(第一次握手)
- 服务器方回发确认应答(第二次握手)
- 应答到达请求方,请求方最后确认,连接建立(第三次握手)
在经过三次握手后,就成功建立了TCP连接,任何采用TCP的应用在正式传输数据前都会先建立这条连接。
三次握手建立TCP连接也被称为同步。这个过程中双方交换的最重要参数就是初始序列号,初始序列号可以用来跟踪后续交换的每一个字节
建立TCP连接的双方没有主从之分,它们可以相互收发数据,也就是说TCP数据段的传输是全双工的
DoS攻击
三次握手连接可能导致一些安全问题,例如著名的:SYN泛洪导致DoS攻击
服务器通过大量的代理服务器,向被攻击的机器不断发送大量(泛洪)的第一次握手信息SYN,被攻击机器在收到第一次握手信息后会回发第二次握手信息,并且等待接收第三次握手信息,但是由于发送的第一次握手信息使用了伪造的IP地址,所以被攻击的机器永远无法收到第三次握手信息,这让被攻击者挂起很多进程在等待,最终因为资源耗尽而瘫痪
TCP连接释放
经过三次握手建立TCP连接之后,就可以开始进行数据的传输,在数据传输完后,就需要释放掉这条TCP连接
- 任何一方在没有数据要传送的时候,都可以发送一个FIN置位了的 TCP 数据段
- 当FIN被确认的时候,该方向的连接被关闭
- 当双向连接都关闭了的时候,连接释放
由于决定何时两边都释放这个问题具有一定难度,它极易形成两军队问题
两军队攻击敌人,单独出击必败,两军出击必胜,如何战胜敌人?最好的方法就是相互通信决定攻击时间,但一方发出消息后无法确定对方是否成功收到消息,因此对方会发出确认消息,由此双方会不断互发确认消息,无法结束,即最后信息的发送者,永远无法知道这个信息是否到达
- 为了避免两军队(two-army)问题,使用定时器:
- 如果一方发送了FIN数据段出去却在一个设定的时间没有收到应答,释放连接。另一方最终会注意到连接的对方已经不在了,超时后连接释放
- 理论上讲,如果初始DR的和重传都丢了,协议失败
- 发送者将放弃发送且释放连接,但是,另外一端却不知道这些情况,仍然处于活跃的状态,这种情形导致半开放连接(half-open)
- 杀死半开放连接的方式
- 如果在一定的时间内,没有TPDUs到达的话,连接自动释放
- 如果这样,传输实体在发送一个TPDU的时候必须启动定时 器,定时器超期,将发动一个哑TPDU(dummy TPDU),以免被断掉
三次握手正常释放连接
TCP四次挥手终止会话
最后的确认TPDU丢失
TCP传输策略-TCP流量控制
在数据的接收端拥有缓冲区用于存储未被取走的报文数据,应用层中的各个应用进程负责取走各自的数据,传输层负责取出数据放入缓冲区,但应用取走缓冲数据并不是实时的,其可能在需要时才取走数据,这就导致了缓冲区数据有溢出的可能
TCP传输采用了基本的肯定确认重传技术,TCP以数据段形式传输数据,一个数据段包含很多字节,相当于批量传输。为避免大量数据淹没接收方,采用流控技术。利用到了数据段当中的一个字段窗口尺寸(Window Size)。
可以看到整个流程中发送方首先向接收方传输了一个数据段,这个数据段大小2K,SEQ为0,表示从0开始填充字节
接收方大小为4K,此时接收方为空,接收方在成功接收数据段后剩余2K空余位置,然后向发送方回发确认,确认中包括了ACK=2048表示成功接收到了2048以前的字段,期望接受从2048往后的字段,以及WIN=2048表示接收方还剩余2K位空余位置,下次传输数据的大小不能超过2K
发送方在接收到确认后会继续发送剩余数据,可以看到,发送方在下次发送时,会根据确认调整传输数据大小以及初始序列号(SEQ)
在第二次传输数据完成后,接收方被占满,没有了空余位置,接收方收到确认后就会开始等待,等接收方重新获得空余位置并再次返回确认后再继续发送数据,知道所有数据全部传输完成
当窗口数为 0 时,发送者不能正常发送数据段,除非:
- Urgent数据。比如,用户想杀掉远端机器上的进程的时候,可以发送数据
- 发送者可以发送一个字节的数据段,以便让接收者再次发送期待接收的字节号和窗口数(避免死锁)
发送者不需要马上发送应用程序产生的数据,接收者也不需要马上发送应答(当收到数据的时候)
考虑一个指向某交互式编辑器(远程)的TELNET 连接,该编辑器对用户的每次击键都作出响应,在最坏的情况下:
- 当用户敲入一个字符的时候,被送到传输实体,创建一个21字节的数据段,在传到网络层,变成了41字节的IP分组
- 接收方(运行着编辑器的远端机)收到这个信息后,会立刻发送一个40字节的确认分组(20字节的TCP段头和20字节的IP头)
- 随后,当编辑器读取出这个字节,TCP实体发送一个窗口更新,这个分组也是40字节
- 最后,当编辑器处理了这个字符,它发送一个41字节的分组作为该字符的回显
- 总共累计起来,对于每个敲入的字符,需要至少 162 字节的带宽(还没有考虑到链路层的开销),发送4个数据段
优化接收端
接收端可以推迟500ms发送确认分组和窗口更新窗口,以便可以免费搭载在处理后的回显分组内(free ride)
优化发送端
Nagle算法
- 当数据以一次一字节的速度到达的时候,只发送第一个字节,然后将后续的字节缓存起来,直到发出的字节得到确认
- 将缓存起来的字节在一个数据段中发出,再继续缓存,直到发出的数据得到确认
Nagle算法在很多TCP上实现,但是有些时候最好禁用,比如:当一个X-Windows应用在互联网运行的时候,鼠标的移动事件必须发送给远程计算机,把这些移动事件收集起来一批一批发送出去,使得鼠标的移动极不连贯
傻瓜窗口综合症
另一个使TCP性能退化的问题是傻瓜窗口综合症(silly window syndrome problem):当有大块数据被传递给发送端TCP实体,但接收端的交互式应用每次只读取一个字节的时候,就会出现问题:
发送方每向接收方发送一个连接数据段,就会占满整个接收方的空间,然后接收方就会返回一个剩余大小为0的确认,导致发送方等待一段时间,直到新的确认返回后继续发送消息,但只要一发送数据段就会占满,导致数据无法从输入端传递到输出端。
解决方法
Clark解决方案 :阻止接收方发送只有1个字节的窗口更新, 相反,它必须等待一段时间,当有了一定数量的空间之后再 告诉发送方,接收方可以可以维护一个内部缓冲,且阻塞上层应用的READ 请求,直到它有大块的数据提供
总述:
- 发送方
- 尽量不发送数据含量小的数据段
- 缓存应用层的数据,达到一定量再发送
- 接收方
- 不请求对方发送短数据段(window size)
- 延迟窗口变更信息,使接收缓冲区足够大
拥塞控制原理
网络的吞吐量与通信子网负荷(即通信子网中正在传输的分组数)有着密切的关系。当通信子网负荷比较小时,网络的吞吐量(分组数/秒)随网络负荷(每个节点中分组的平均数)的增加而线性增加。当网络负荷增加到某一值后,若网络吞吐量反而下降,则表征网络中出现了拥塞现象(由于此时排队延时趋于无穷,快速增大)。在一个出现拥塞现象的网络中,到达某个节点的分组将会遇到无缓冲区可用的情况,从而使这些分组不得不由前一节点重传,或者需要由源节点或源端系统重传。当拥塞比较严重时,通信子网中相当多的传输能力和节点缓冲器都用于这种无谓的重传,从而使通信子网的有效吞吐量下降。由此引起恶性循环,使通信子网的局部甚至全部处于死锁状态,最终导致网络有效吞吐量接近为零。
拥塞的表现有:
- 分组丢失(路由器缓冲区溢出)
- 分组经历比较长的延迟(在路由器的队列中排队)
引起拥塞的主要原因
1. 路由器缓冲区容量有限
多个分组短时间内同时到达路由器同一个端口尝试输出时,路由器需要建立输出队列控制分组通过端口的输出顺序,同时,需要将暂时不能输出的分组缓存在路由器的缓存空间中,如果分组数量超过缓存空间大小,则超出部分的分组会被路由器丢弃,同时由于排队输出的原因导致拥塞产生。
适当增加路由器缓存空间大小可以一定程度上减轻这种情况,但一味的增加缓存空间大小,只会导致拥塞情况更加严重,过多的分组堆积在路由器处,不能在规定时间到达接收方触发确认返回,导致计时器结束后分组被重传,可实际上,此时的原分组只是阻塞在链路上,并不是丢失,一次次引发重传使得网络资源被浪费,并且进一步加重了网络的拥塞
2. 传输线路的带宽有限
过多的分组同时通过输出线路进行传输,逼近传输链路的带宽极限,导致排队时延陡增,整条线路产生拥塞
3. 路由器的处理能力有限
如果路由器处理器处理排队,更新路由表等操作时,速度跟不上高速链路,就会发生网络拥塞
常见的拥塞控制方法
1. 端到端的拥塞控制
- 没有来自网络的显式反馈
- 端系统根据延迟和丢失事件推断是否有拥塞
- 是TCP采用的方法
2. 网络辅助的拥塞控制
- 路由器提供给端系统以反馈信息
- 单个bit置位,显示有拥塞(SNA, DECbit, TCP/IP ECN, ATM)
- 显式提供发送端可以采用的速率
ATM ABR拥塞控制方法
发送端与接收端通过信元(cell)进行数据传输
- 数据信元(data cell)负责进行数据的传输
- 资源管理信元(RM cell)由发送端发送,在数据信元间间隔插入
- RM信元上设有网络辅助比特,这些辅助比特由链路中的交换机进行修改
- NI bit:no increase in rate (轻微拥塞)速率不要增加了
- CI bit:congestion indication 拥塞指示
- 发送端发送的RM信元被接收端返回, 接收端不做任何改变
- 在RM信元中还设有两个字节的ER (explicit rate)字段,处于拥塞的交换机可能会降低信元中ER字段的值(若比当前字段值还拥塞则降低该值,若持平或由于该值的拥塞情况则不修改),所以该值可以表征当前链路中最低的可支持速率
TCP拥塞控制
TCP进行拥塞控制的机制是端到端的拥塞控制机制,即路由器并不向主机反馈有关拥塞的信息,这样可以减轻路由器的压力,并且符合TCP/IP架构所遵循的网络核心简单的原则。端系统根据自身得到的信息,判断网络拥塞情况,进而采取动作
虽然网络层也试图管理拥塞,但是,大多数繁重的任务是由 TCP来完成的,因为针对拥塞的真正解决方案是减慢数据率,所以TCP遵循分组守恒即当有一个老的分组离开之后才允许新的分组注入网络。 TCP希望通过动态维护窗口大小来实现这个目标
TCP拥塞检测(Congestion detection)
所有的互联网TCP算法都假定超时是由拥塞引起的,并且通过监视超时的情况来判断是否出现问题
另外,网络中还存在轻微拥塞的概念,端系统在收到三个冗余的ACK(一个正常ACK后收到三个冗余ACK)的情况下,会判定网络处于轻微拥塞的状态
拥塞控制 (Congestion prevention)
- 当一个连接建立的时候,双方选择一个合适的窗口大小,接收方根据自己的缓冲区大小来指定窗口的大小。
- 如果发送者遵守此窗口大小的限制,则接收端不会出现缓冲区溢出的问题,但可能由于网络内部的拥塞而发生问题(网络内部的瓶颈)
如图中(a)快速的网络向小容量的接收方传输数据(接收者容量问题),(b)慢速的网络向大容量的接受方传输数据(网络容量问题)
互联网解决方案应该是认识到两个潜在的问题的:网络容量,接收者容量,然后单独地处理这两个问题
- 接收窗口 大小反映了目前窗口的容量 (容易控制)
- 拥塞窗口 大小反映了网络目前的容量(难于控制)
为此要保证发送者发送的数据字节数是两个窗口中小的那个窗口数,这样就既不会因为接收者窗口大小导致拥塞,也不会因为网络容量大小导致拥塞,实际上,接收窗口反映了网络中的流量控制问题,拥塞窗口反映了网络中的拥塞控制问题。所以在实际控制中需要采取联合控制的方法,发送端控制发送但是未确认的量同时也不能够超过接收窗口,满足流量控制要求
TCP拥塞控制策略
1. 慢启动算法Slow Start
(决定拥塞窗口的大小)
- 当连接建立的时候,发送者用当前使用的最大数据段长度(MSS)初始化拥塞窗口(设置CongWin=1MSS),然后发送一个最大的数据段 速率:rate≈frac{CongWin}{RTT}bytes/sec 例如:MSS = 1460bytes 并且 RTT = 200 msec 则初始速率= 58.4kbps
- 如果在定时器超期之前收到确认,则将拥塞窗口翻倍,然后发送两个数据段……直至超时(或达到接收方窗口的大小)
- 确定出拥塞窗口的大小
因此在达到接收窗口大小或超时前,慢启动算法下的拥塞窗口大小都是以指数形式增长的,特点是启动初值很低,但是增长速度很快
2. AIMD 加性增,乘性减
- 乘性减
- 丢失事件后将CongWin降为1,将CongWin/2作为阈值,进入慢启动阶段(倍增直到CongWin/2)
- 加性增
- 当CongWin>阈值时,一个RTT内如没有发生丢失事件,将CongWin加1MSS进行探测,逐渐逼近当前网络容量
- 收到三个冗余ACK的情况下,表示网络进入轻微拥塞,但还具有一定的段传输能力
- CongWin减半
- 窗口(缓冲区大小)之后线性增长
- 超时时间发生后,表示网络进入拥塞状态
- CongWin被设置成1 MSS,进入SS(慢启动)阶段,之后窗口指数增长
- 增长到一个阈值(上次发生拥塞的窗口的一半)时,再线性增加
注意:
- 线性增长,可以将越来越粗放的窗口尝试力度变小,以获得更准确的拥塞窗口值
- TCP慢启动算法就是这样不断超时,不断重启,尝试出的拥塞控制窗口值也随着网络状况的变化而变化,达到拥塞控制的目的
- 如果收到一个ICMP抑制分组( ICMP source quench)并被送给TCP传输实体 ,则这个事件被当作超时对待
TCP拥塞控制状态
- 当CongWin < Threshold(阈值), 发送端处于慢启动阶段(slow-start), 窗口指数性增长
- 当CongWin > Threshold, 发送端处于拥塞避免阶段(congestion-avoidance), 窗口线性增长
- 当收到三个重复的ACKs (triple duplicate ACK),Threshold设置成CongWin/2,CongWin=Threshold 3
- 当超时事件发生时timeout, Threshold=CongWin/2,CongWin=1 MSS,进入SS阶段
TCP平均吞吐量
使用窗口window尺寸W和RTT来描述
- 忽略慢启动阶段,假设发送端总有数据传输
- W:发生丢失事件时的窗口尺寸(单位:字节)
- 平均窗口尺寸:3/4W
- 平均吞吐量:1RTT时间内吞吐3/4W avg TCP thruput=frac{frac{3}{4}W}{RTT}bytes/sec=frac{3}{4}*frac{W}{RTT}bytes/sec
TCP的公平性
公平性目标
如果K个TCP会话分享一个链路带宽为R的瓶颈,每一个会话的有效带宽为R/K
TCP是公平的原因
2个竞争的TCP会话:始终遵循加性增加,斜率为1, 吞吐量增加。乘性减,吞吐量比例减少
- UDP不是公平性的,其尽其所能向网络中传输数据,所以其对TCP具有侵略性
- TCP的公平性针对的是连接间的,而不是端口间或应用间的,例如两个应用,其中一个建立九条TCP连接,一个建立一条TCP连接,最后占据九条的分走网络带宽的9/10,占据一条的只能分走1/10
TCP定时器
重传定时器
TCP采用了肯定确认重传技术来保证每一个字节的可靠传输,为解决数据段丢失问题,每发一个数据段都会启动一个重传定时器,它是最重要的定时器之一
它的时间设置需要非常多的考量,如果时间设置过长,就会导致等待时间过长,如果设置过短,可能引发频繁的超时和重传
快速重传
重传定时器所设置的时间一般比较保守,都比较长,如果单纯依靠重传定时器完成数据的重传,会导致在网络较差的环境下延时较长,快速重传通关检测重复的ACK判定报文段的丢失并进行重传
发送方通常发送大量的报文段,如果其中的报文段丢失,通常会引发接收方发送多个重复的ACK
以上图为例,当50-59部分的报文段丢失后,后续报文段到达接收方,接收方发现不是所期待的分组,所以仍然发送前一个报文段的确认,即对49之前字节的报文段确认并再次请求50字节及以后的报文段,发送方在接收到正常的确认报文段后又接收到了三个重复的ACK,此时发送方会直接重发所请求的报文段,而不再等待重传计时器(三次重复已经说明该报文段有大概率丢失了)
持续定时器
用来避免如下的死锁( deadlock )发生
死锁
- 接收方发送了一个窗口数为零的确认(窗口更新),告诉发送方等待。
- 稍后,接收方空出了缓冲,发送更新窗口的数据段,但是,很不幸,该分组中途丢失
- 现在,收发双方都在等待对方发送数据段过来,但永远等不到,死锁产生。
利用持续定时器解决死锁问题
当接收方发送一个窗口数为0的确认后,发送方开始启动一个持续定时器
此时,如果接收方在计时器限定时间范围内空出空间,并成功发送新确认到发送方,持续计时器结束并继续数据传输
假设持续计时器时间为0新的确认还没有到达,此时发送方就会发送一个探测数据段,里边没有任何数据内容,单纯引发接收方重新发送一个确认,以解决死锁问题
保活定时器
用来检查连接是否存活,当一个连接空闲的时间超过保活定时器的时间,该连接将被杀掉。
还有在关闭时刻处于TIMED WAIT状态中使用的定时器:运行两倍的最大分组生存时间,以确保连接关闭之后,该连接上的所有分组都完全消失。
性能 | TCP | UDP |
---|---|---|
可靠性 | T | F |
传输延迟 | 不确定 | 网络延迟 |
拥塞控制 | T | F |
TCP
- 可靠传输方式
- 可让应用程序简单化,程序员可以不必进行错误检查、修正等工作
UDP
- 为了降低对计算机资源的需求(DNS)
- 应用程序本身已提供数据完整性的检查机制,勿须依赖传输层的协议来保证
- 应用程序传输的并非关键性的数据(路由器周期性的路由信息交换)
- 一对多方式,必须使用UDP(TCP限于一对一的传送)(视频传播)