传输层是整个TCP/IP协议栈核心之一,位于网络层之上,应用层之下。利用网络层的服务,为上层应用层提供服务。与网络层类似,传输层也拥有面向连接的服务与无连接的服务两种,用途在于提供高效的可靠的,性价比高的数据传输
传输层的作用在于为运行在不同主机上的应用进程提供逻辑通信(这里的逻辑通信指的是在传输层角度看来两个进程间直接进行通信,实际上还是通过下层提供的服务,不都安将数据向下层传递并在对方处向上传递后才实现通信),简言之,传输层提供的是进程到进程间的通信
完成传输层任务的硬件或软件
网络层运行在由承运商操作的路由器上,因此用户无法真正控制网络层。对于丢包,高延迟等问题只能选择被动接受。传输层架设在网络层之上,允许用户控制服务质量
传输层原语独立于网络层原语,而网络层原语会因为网络的不同而不同。传输层的原语在向应用层传输的原语可以屏蔽掉这些不同,只提供标准,统一的原语。
原语:计算机进程的控制通常由原语完成。所谓原语,一般是指由若干条指令组成的程序段,用来实现某个特定功能,在执行过程中不可被中断。在操作系统中,某些被进程调用的操作,如队列操作、对信号量的操作、检查启动外设操作等,一旦开始执行,就不能被中断,否则就会出现操作错误,造成系统混乱。所以,这些操作都要用原语来实现 原语是操作系统核心(不是由进程,而是由一组程序模块组成)的一个组成部分,并且常驻内存,通常在管态下执行。原语一旦开始执行,就要连续执行完,不允许中断
传输层和网络层的作用范围不同,网络层负责把数据从源机送达到目的机(主机到主机 Host to Host)。传输层负责把数据送达到具体的应用进程或端口(End to End 端到端,end point端点即套接字socket和某个具体的应用程序绑定)
传输层处理的协议数据单元是TPDU(Transport Protocol Data Unit)即数据段(segment),是从传输实体发到对应端传输实体的信息
TPDU作为数据(载荷)被封装在分组(packet)中,通过网络层进行传输交换
在发送报文的主机一端完成,从多个套接字接收来自多个进程的报文,根据套接字获取对应报文的IP地址和目标端口,并将目标端口传给传输层进行报文段的封装,IP地址传给网络层进行数据报的封装。这些头部信息用于进行以后的解封装
在位于接收报文一端的主机处完成,根据报文段头部信息中的端口号与数据报中提供的IP地址将接收到的报文段传送给正确的套接字
这里比较难以理解的一点是,传输层直接与套接字相联系,而传输层上的报文段只包含了源端口与目标端口,并没有主机IP地址信息,那么IP地址是如何传送给相应套接字进行匹配的? 事实上,只需要认清TCP/IP中的各层结构是人们认为规定的标准结构,是存在于虚拟环境下的结构(至少对于传输层和网络层是如此)通过套接字编程就可以看出,无论是报文,报文段还是数据报都是通过参数向下一层逐层传递的,所以虽然IP地址在从网络层向传输层传递之前就已经被取出,其也可以通过参数的形式向传输层传递并最终传递给套接字进行套接字的匹配或更新。
UDP套接字用二元组标识(目标IP地址、目标端口号),即通过目标IP地址与目标端口号可以唯一标识一个UDP Socket,这就说明源IP或源端口不同但目标IP与端口一致的两个请求会指向同一个套接字
无连接多路解复用的过程一般是:
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 |
(User Dataprogram Protocol)
UDP是一个无连接的传输层协议,UDP传输的是数据段(报文段),无需建立连接(UDP发送端和接收端之间不进行握手,每个UDP报文都被单独的处理),不提供数据的可靠传输。很多网络应用,例如DNS都采用了UDP。UDP传输的是UDP数据段
UDP提供的是尽力而为的服务:
使用UDP的原因:
因为以上的原因,UDP常被应用于实时流媒体播放,DNS或SNMP
如果想要在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校验和计算方式是将IP伪头部,UDP头部和数据按照二进制每行16位的格式排列,然后对这些排列好的数据进行补码相加求和(注意:当数字相加时,在最高位的进位要回卷,再加到结果上),再对得到的结果进行求反码,最终得到的结果就是校验和
这里需要注意UDP伪头部是在计算校验和时临时与UDP数据报拼接到一起,二者临时相连只是为了计算校验和
接收方在接收到数据段后利用其中的校验和以及其他部分数据经过计算最终得到的结果每个位应该全部为1,如果出现0,证明传输过程中发生错误。(这里应该注意即便正确,也有可能是多次比特翻转导致最终结果错等于校验和,不代表一定不出错)
在计算校验和的过程中使用了属于网络层的IP地址,这破坏了分层原则
UDP提供端点标识,端到端的数据传输,不提供差错检测和可靠传输,但简洁高效
RDT是网络中最重要的问题之一,其所要解决的是“如何利用下层提供的并不可靠的服务,为其上层提供可靠服务”,下层可能出现的问题包括比特反转带来的传输数据错误,或是由于网络原因导致的报文段乱序,以及报文段丢失等问题
要解决上文所属的可能出现的错误,首先可以被应用的就是停等协议,即在对方报文段到达之前,不进行下一步操作。要解决可能由于比特翻转带来的数据传输错误,需要发送方在报文段头部加入检错码(校验和),而接收方在接收后利用校验和对报文段进行检验,随后通过发送确认报文段的方式,将校验结果返回给发送方,结果可以使用两种:
上文所述方法解决了在报文段内容出错时的情况,但没有考虑到,作为重要结果因素的校验结果报文段(ACK/NAK)也有可能出错,一种情况下发生比特翻转导致接收报文段的一方无法识别报文段内容,也无法理解校验结果,一种情况下,ACK/NAK直接丢失,由于停等协议本身的限制条件,对方不做反应或没有接收到对方反应的情况下,双方会陷入死锁状态。而如果只是单纯在没有接收到响应报文段的情况下采取直接重传的措施,则可能导致报文段的重复发送
为了解决上述问题,需要做出如下改进,为发送方发送的报文段加入序号机制,表明每个分组的序号,加入计时器机制(发送方等待一段合理的时间,一般是一次数据发送到确认分组返回的几倍的时间),在一段时间未收到响应后,对上一个分组进行重传。如果ACK/NAK出错导致不可读,则重传该分组(考虑最坏情况),接收方如果收到重复分组,则直接丢弃该分组并重传上一个分组的确认。(链路层timeout时间是确定的,传输层的timeout时间是自适应的,会根据网络情况进行调整)
无NAK协议指仅使用ACK表明分组接收状态的协议,此时接收方每次只发送最后一个正常接收到的分组,对于错误分组,则直接返回其上一个分组的分组的确认,而发送方在接到确认分组后,就会发送其确认分组对应的数据分组的下一个分组,实现数据的重传。
这样处理使得确认信息减少一半,使得协议更加简单,并且可以一次性传送多个分组,为下文做准备
上文所述的方式可以工作,也就是常见的停等协议,但这种方式的效率十分低下(链路容量比较大时,一次发一个PDU 的不能够充分利用链路的传输能力)
1 Gbps的链路,15 ms端-端传播延时,分组大小为1kB,求其利用率
U_{sender}表示当前信道的利用率,即该信道一段时间内用于数据传输的比例,可见效率极低,而其瓶颈在于网络协议限制了物理资源的利用,即由于停等协议导致数据无法发送
为解决上述存在的利用率低的情况,可以使用流水线协议的方式发送数据,允许发送方在未得到对方确认的情况下一次发送多个分组
GBN:回退N步协议
SR:选择性重传协议
简言之,GBN与SR都能一次发送多个未经确认的分组,提高线路的利用率,但GBN接收窗口尺寸为1,只能顺序接收分组,如果乱序,则整个发送窗口内都要被重传,SR接收窗口尺寸大于1,不需要顺序接收分组,每个分组确认单独发送,但只有目标序号分组被确认后,接收窗口才会滑动。GBN只需要维护一个倒数计时器,记录最先发送的未确认分组的时间,在时间结束后,重传所有发送窗口中的分组,SR需要维护所有已发送但未确认分组的倒数计时器,并在指定计时器结束后,重传特定的分组
区别 | GBN | SR |
---|---|---|
优点 | 简单,所需资源少(接收方一个缓存单元) | 出错时,重传一个代价小 |
缺点 | 一旦出错,回退N步代价大 | 复杂,所需要资源多(接收方多个缓存单元) |
适用范围 | 出错率低:比较适合GBN,出错非常罕见,没有必要用复杂的SR,为罕见的事件做日常的准备和复杂处理 | 链路容量大(延迟大、带宽大):比较适合SR而不是GBN,一点出错代价太大 |
窗口尺寸 | $2^n-1$ | $2^{n-1}$ |
(Transmission Control Protocol)
是专门为了在不可靠的网络上提供可靠的端到端的字节流而设计的(应用进程到进程)。TCP必须动态地适应不同的拓扑、带宽、延迟、分组大小和其它的参数,并且当有错误的时候,能够足够健壮
支持TCP的机器都有一个TCP实体,或者是用户进程,或者是操作系统内核。都可以管理TCP流跟IP层接口,TCP是一个全双工的协议,在同一个连接下,数据可以双向传输,双方均可以作为发送方或接收方,并且这种发送和接收可以同时进行。采用管道化(流水线)技术进行报文段的发送,即可以在未经接收方确认的情况下发送多个报文段。发送方与接收方都有缓存空间,发送方的缓存空间是为了方便数据出错或计时器结束时进行报文段的重发,接收方的缓存空间是为了处理接收的数据,对数据进行排序。
TCP发送的数据是可靠的,有序的字节流,但会根据情况对完整的报文进行切分,报文的还原工作完全由应用层负责,同时负责区分报文的边界
TCP软件决定数据段的大小,有两个因素限制了数据段的长度:
标明了一个连接的两个端点,是通信五元组中的两个重要元素,用来跟踪同一时间内通过网络的不同会话。一般每个端口对应一个应用程序
报文段首字节的在字节流的编号,标识了该报文段中数据的首字节在整段报文中的顺序,其中最特殊的是初始序列号ISNs(initial sequence numbers ):随机产生的
期望从另一方收到的下一个字节的序号(32位),在TCP中为了保证可靠传输,采用了肯定确认重传技术,确认号就是用于肯定确认重传
单位32位(4字节),含义与IP的段头长度完全一致
即图中灰色部分,现在也开始逐步使用(进行拥塞控制等)
URG,ACK,PSH,RST,SYN,FIN共6部分,每部分1byte,称作控制比特
愿意接收的字节数量,为了避免收方被大量涌入数据所淹没,TCP实体进行了流控(Flow Control)。通常使用可变长的滑动窗口来完成流控。所以第十二个字段用16位来表示窗口尺寸
告诉对方可以发送的数据字节数(从确认字节号开始(决定于接收方)
与UDP中的校验和是一样的,唯一区别在于协议位置的编号不一样
和URG数据段配合使用,指明了紧急数据
提供了一种增加基本头没有包含内容的方法
TCP提供的是面向连接的服务,TCP数据段的传输是在TCP连接上进行的。而TCP连接是三次握手建立的,三次连接主要是为了使双方互相同意连接的建立,并且互相同意连接的参数(双方的起始序号)
在经过三次握手后,就成功建立了TCP连接,任何采用TCP的应用在正式传输数据前都会先建立这条连接。
三次握手建立TCP连接也被称为同步。这个过程中双方交换的最重要参数就是初始序列号,初始序列号可以用来跟踪后续交换的每一个字节
建立TCP连接的双方没有主从之分,它们可以相互收发数据,也就是说TCP数据段的传输是全双工的
三次握手连接可能导致一些安全问题,例如著名的:SYN泛洪导致DoS攻击
服务器通过大量的代理服务器,向被攻击的机器不断发送大量(泛洪)的第一次握手信息SYN,被攻击机器在收到第一次握手信息后会回发第二次握手信息,并且等待接收第三次握手信息,但是由于发送的第一次握手信息使用了伪造的IP地址,所以被攻击的机器永远无法收到第三次握手信息,这让被攻击者挂起很多进程在等待,最终因为资源耗尽而瘫痪
经过三次握手建立TCP连接之后,就可以开始进行数据的传输,在数据传输完后,就需要释放掉这条TCP连接
由于决定何时两边都释放这个问题具有一定难度,它极易形成两军队问题
两军队攻击敌人,单独出击必败,两军出击必胜,如何战胜敌人?最好的方法就是相互通信决定攻击时间,但一方发出消息后无法确定对方是否成功收到消息,因此对方会发出确认消息,由此双方会不断互发确认消息,无法结束,即最后信息的发送者,永远无法知道这个信息是否到达
在数据的接收端拥有缓冲区用于存储未被取走的报文数据,应用层中的各个应用进程负责取走各自的数据,传输层负责取出数据放入缓冲区,但应用取走缓冲数据并不是实时的,其可能在需要时才取走数据,这就导致了缓冲区数据有溢出的可能
TCP传输采用了基本的肯定确认重传技术,TCP以数据段形式传输数据,一个数据段包含很多字节,相当于批量传输。为避免大量数据淹没接收方,采用流控技术。利用到了数据段当中的一个字段窗口尺寸(Window Size)。
可以看到整个流程中发送方首先向接收方传输了一个数据段,这个数据段大小2K,SEQ为0,表示从0开始填充字节
接收方大小为4K,此时接收方为空,接收方在成功接收数据段后剩余2K空余位置,然后向发送方回发确认,确认中包括了ACK=2048表示成功接收到了2048以前的字段,期望接受从2048往后的字段,以及WIN=2048表示接收方还剩余2K位空余位置,下次传输数据的大小不能超过2K
发送方在接收到确认后会继续发送剩余数据,可以看到,发送方在下次发送时,会根据确认调整传输数据大小以及初始序列号(SEQ)
在第二次传输数据完成后,接收方被占满,没有了空余位置,接收方收到确认后就会开始等待,等接收方重新获得空余位置并再次返回确认后再继续发送数据,知道所有数据全部传输完成
当窗口数为 0 时,发送者不能正常发送数据段,除非:
发送者不需要马上发送应用程序产生的数据,接收者也不需要马上发送应答(当收到数据的时候)
考虑一个指向某交互式编辑器(远程)的TELNET 连接,该编辑器对用户的每次击键都作出响应,在最坏的情况下:
接收端可以推迟500ms发送确认分组和窗口更新窗口,以便可以免费搭载在处理后的回显分组内(free ride)
Nagle算法在很多TCP上实现,但是有些时候最好禁用,比如:当一个X-Windows应用在互联网运行的时候,鼠标的移动事件必须发送给远程计算机,把这些移动事件收集起来一批一批发送出去,使得鼠标的移动极不连贯
另一个使TCP性能退化的问题是傻瓜窗口综合症(silly window syndrome problem):当有大块数据被传递给发送端TCP实体,但接收端的交互式应用每次只读取一个字节的时候,就会出现问题:
发送方每向接收方发送一个连接数据段,就会占满整个接收方的空间,然后接收方就会返回一个剩余大小为0的确认,导致发送方等待一段时间,直到新的确认返回后继续发送消息,但只要一发送数据段就会占满,导致数据无法从输入端传递到输出端。
Clark解决方案 :阻止接收方发送只有1个字节的窗口更新, 相反,它必须等待一段时间,当有了一定数量的空间之后再 告诉发送方,接收方可以可以维护一个内部缓冲,且阻塞上层应用的READ 请求,直到它有大块的数据提供
网络的吞吐量与通信子网负荷(即通信子网中正在传输的分组数)有着密切的关系。当通信子网负荷比较小时,网络的吞吐量(分组数/秒)随网络负荷(每个节点中分组的平均数)的增加而线性增加。当网络负荷增加到某一值后,若网络吞吐量反而下降,则表征网络中出现了拥塞现象(由于此时排队延时趋于无穷,快速增大)。在一个出现拥塞现象的网络中,到达某个节点的分组将会遇到无缓冲区可用的情况,从而使这些分组不得不由前一节点重传,或者需要由源节点或源端系统重传。当拥塞比较严重时,通信子网中相当多的传输能力和节点缓冲器都用于这种无谓的重传,从而使通信子网的有效吞吐量下降。由此引起恶性循环,使通信子网的局部甚至全部处于死锁状态,最终导致网络有效吞吐量接近为零。
拥塞的表现有:
多个分组短时间内同时到达路由器同一个端口尝试输出时,路由器需要建立输出队列控制分组通过端口的输出顺序,同时,需要将暂时不能输出的分组缓存在路由器的缓存空间中,如果分组数量超过缓存空间大小,则超出部分的分组会被路由器丢弃,同时由于排队输出的原因导致拥塞产生。
适当增加路由器缓存空间大小可以一定程度上减轻这种情况,但一味的增加缓存空间大小,只会导致拥塞情况更加严重,过多的分组堆积在路由器处,不能在规定时间到达接收方触发确认返回,导致计时器结束后分组被重传,可实际上,此时的原分组只是阻塞在链路上,并不是丢失,一次次引发重传使得网络资源被浪费,并且进一步加重了网络的拥塞
过多的分组同时通过输出线路进行传输,逼近传输链路的带宽极限,导致排队时延陡增,整条线路产生拥塞
如果路由器处理器处理排队,更新路由表等操作时,速度跟不上高速链路,就会发生网络拥塞
发送端与接收端通过信元(cell)进行数据传输
TCP进行拥塞控制的机制是端到端的拥塞控制机制,即路由器并不向主机反馈有关拥塞的信息,这样可以减轻路由器的压力,并且符合TCP/IP架构所遵循的网络核心简单的原则。端系统根据自身得到的信息,判断网络拥塞情况,进而采取动作
虽然网络层也试图管理拥塞,但是,大多数繁重的任务是由 TCP来完成的,因为针对拥塞的真正解决方案是减慢数据率,所以TCP遵循分组守恒即当有一个老的分组离开之后才允许新的分组注入网络。 TCP希望通过动态维护窗口大小来实现这个目标
所有的互联网TCP算法都假定超时是由拥塞引起的,并且通过监视超时的情况来判断是否出现问题
另外,网络中还存在轻微拥塞的概念,端系统在收到三个冗余的ACK(一个正常ACK后收到三个冗余ACK)的情况下,会判定网络处于轻微拥塞的状态
如图中(a)快速的网络向小容量的接收方传输数据(接收者容量问题),(b)慢速的网络向大容量的接受方传输数据(网络容量问题)
互联网解决方案应该是认识到两个潜在的问题的:网络容量,接收者容量,然后单独地处理这两个问题
为此要保证发送者发送的数据字节数是两个窗口中小的那个窗口数,这样就既不会因为接收者窗口大小导致拥塞,也不会因为网络容量大小导致拥塞,实际上,接收窗口反映了网络中的流量控制问题,拥塞窗口反映了网络中的拥塞控制问题。所以在实际控制中需要采取联合控制的方法,发送端控制发送但是未确认的量同时也不能够超过接收窗口,满足流量控制要求
(决定拥塞窗口的大小)
因此在达到接收窗口大小或超时前,慢启动算法下的拥塞窗口大小都是以指数形式增长的,特点是启动初值很低,但是增长速度很快
使用窗口window尺寸W和RTT来描述
如果K个TCP会话分享一个链路带宽为R的瓶颈,每一个会话的有效带宽为R/K
2个竞争的TCP会话:始终遵循加性增加,斜率为1, 吞吐量增加。乘性减,吞吐量比例减少
TCP采用了肯定确认重传技术来保证每一个字节的可靠传输,为解决数据段丢失问题,每发一个数据段都会启动一个重传定时器,它是最重要的定时器之一
它的时间设置需要非常多的考量,如果时间设置过长,就会导致等待时间过长,如果设置过短,可能引发频繁的超时和重传
重传定时器所设置的时间一般比较保守,都比较长,如果单纯依靠重传定时器完成数据的重传,会导致在网络较差的环境下延时较长,快速重传通关检测重复的ACK判定报文段的丢失并进行重传
发送方通常发送大量的报文段,如果其中的报文段丢失,通常会引发接收方发送多个重复的ACK
以上图为例,当50-59部分的报文段丢失后,后续报文段到达接收方,接收方发现不是所期待的分组,所以仍然发送前一个报文段的确认,即对49之前字节的报文段确认并再次请求50字节及以后的报文段,发送方在接收到正常的确认报文段后又接收到了三个重复的ACK,此时发送方会直接重发所请求的报文段,而不再等待重传计时器(三次重复已经说明该报文段有大概率丢失了)
用来避免如下的死锁( deadlock )发生
当接收方发送一个窗口数为0的确认后,发送方开始启动一个持续定时器
此时,如果接收方在计时器限定时间范围内空出空间,并成功发送新确认到发送方,持续计时器结束并继续数据传输
假设持续计时器时间为0新的确认还没有到达,此时发送方就会发送一个探测数据段,里边没有任何数据内容,单纯引发接收方重新发送一个确认,以解决死锁问题
用来检查连接是否存活,当一个连接空闲的时间超过保活定时器的时间,该连接将被杀掉。
还有在关闭时刻处于TIMED WAIT状态中使用的定时器:运行两倍的最大分组生存时间,以确保连接关闭之后,该连接上的所有分组都完全消失。
性能 | TCP | UDP |
---|---|---|
可靠性 | T | F |
传输延迟 | 不确定 | 网络延迟 |
拥塞控制 | T | F |