本书结构是自顶向下的,所以请按下列顺序阅读:
1.计算机网络自顶向下–应用层
2.计算机网络自顶向下–运输层
3.计算机网络自顶向下–网络层
4.计算机网络自顶向下–链路层
运输层
概述和运输层服务
运输层协议为运行在不同主机的应用程序之间提供逻辑通信,从应用角度看,运行不同进程的主机好像直接相连。其实他们可能分散在世界各地,通过路由器各种链路连接。
运输层将应用程序接收到的报文转换成运输层分组(报文段)。将应用层报文划分为小块,为每块加上运输层首部以生成报文段
运输层和网络层的关系
网络层提供了主机之间的逻辑通信,运输层为不同主机的进程提供逻辑通信。以东西海岸两个家庭互相写信为例子,这个例子很经典,大家可以多体会体会:
进程 = 堂兄弟姐妹
主机 = 家庭
运输层协议 = Ann 和 Bill(孩子里收信发信的大哥大姐),分发到兄弟姐妹中,将信从一个人(进程)送到另一个人
网络层协议 = 邮政服务,将信从一个家庭(主机)传给另一个家庭
运输层协议只工作在主机(端)中 = Ann 和 Bill 只在家工作,将信拿到门口的邮箱
多种运输层协议,多种服务模型 = Ann 和 Bill 外出,Susan 和 Harvey来收信发信,结果年龄太小,收发邮件次数少,也老丢信
运输层服务受制于网络层服务 = 邮政大哥若三天来一次,Ann和Bill就不可能两天收发一次信
运输层协议能为应用程序提供可靠数据传输服务 = 若邮政大哥把信弄脏弄丢弄混,Ann 和 Bill 可以清理整理信件,或者让对方下次重新发一次
运输层协议能确保应用程序报文不受入侵读取 = 尽管邮政大哥可能被别人骗或者强行看了信件,Ann 和 Bill 也能规定加密方式对信的内容进行加解密(还记得冒险小虎队书吗哈哈哈),弟弟妹妹们只需要看信的内容
多路分解 = Bill 和 Ann从邮递大哥拿到信件,看收信人名字(端口号),然后分别交到他们手上
多路复用 = Bill 和 Ann从弟弟妹妹手里拿到信件,帮他们装填信封写上信息
网络运输层概述
RFC文档等也将TCP的运输层分组称为报文段,UDP分组称为数据报,本书将TCP和UDP分组统称为报文段,网络层分组称为数据报(看情况)
多路复用与多路分解
将由网络层提供的主机之间的交付服务—>延伸—>为应用程序提供正确的应用级进程之间的交付服务
计算机同时运行多个进程,运输层需要将从网络层接收到的数据定向到这些进程中的一个
一个进程由一个或多个(单进程多线程)套接字socket,每个socket有唯一标识符,运输层报文段中有端口号(用来指示socket),运输层接收报文段检查字段后将报文段定位到该socket,该工作称为多路分解
反之,从Socket中(应用层和运输层接口)收集数据块,并为每个数据块装填字段生成运输层报文段,传递到网络层的工作称为多路复用
运输层报文段有源端口号和目的端口号。端口号为16bit的数,065535,其中01023是周知端口号,受限制,留给HTTP、FTP之类的应用层协议使用。开发应用程序时,必须为其分配一个端口号
无连接的多路复用和多路分解
一个UDP socket由(目的IP地址,目的端口号)标识。注意是用来标识,不是只有这两个相关字段,还有源端口号、源IP地址字段。源端口号和目的端口号按传递方向反转
面向连接的多路复用和多路分解
一个TCP socket由(源IP地址,源端口号,目的IP地址,目的端口号)来标识
安全性:端口扫描
如果发现一台主机正在运行具有已知安全缺陷的应用程序,比如在端口1434上监听的一台SQL服务器遭受缓存溢出,使得一个远程用户能在易受攻击的主机上执行任意代码
比如前段时间(2017.5)的勒索比特币的病毒,就是利用微软给打印机开放的一个端口的漏洞,导致很多政府部门、学校计算机瘫痪
Web服务器与TCP
考虑一台运行在80端口上的Apache Web服务器,当客户们的浏览器向该服务器发送报文段时,所有报文段的目的端口都将是80。该服务器能够根据源地址IP和源端口号区分来自不同客户的报文段
socket与进程之间并非总是一一对应的关系,可以为每个新的客户连接创建具有新连接socket的新线程(这些线程都属于同一个进程,相当于socket和线程一一对应),也就是多个socket对应到一个进程
无连接运输:UDP
概述
UDP只是做了运输层能够做的最少工作,除了复用/分解功能及少量差错检测外,几乎没有对IP增加别的东西
UDP从应用进程拿到数据块,附加上用于多路复用分解的IP地址和端口号字段,和另外两个小字段,将形成的报文段交给网络层。网络层再将其封装到IP数据包,尽力交给对方主机。
UDP发送报文段之前,没有跟接收方运输层建立连接,所以它是无连接的。
DNS是使用UDP的应用层协议,当DNS应用程序想要进行一次查询时,先构造一个DNS报文交给UDP,UDP为此报文添加首部字段,将形成的报文段给网络层,网络层将此UDP报文封装进一个IP数据报中,将其发送给一个DNS服务器。查询主机的DNS应用则等待响应。
为何有些应用更适合UDP
实时应用要求最小发送速率,不希望过分延迟报文段传送,且能容忍一些数据丢失,TCP服务模型并不适合(当然了,一个程序可以由很多部分,一些部分用TCP更适合)
无需建立连接。因此DNS运行在UDP之上,否则DNS会慢得多。HTTP使用TCP是因为传输文本数据必须可靠,即使要承受其时延
无连接状态。TCP需要维护连接状态,各种缓存和参数。所以某些应用运行在UDP上可以支持更多活跃用户
分组首部开销小,TCP报文段一般20字节,UDP8字节首部开销
应用
流式多媒体、因特网电话(TCP越来越多)、网络管理、路由选择协议RIP、DNS
如果不限制UDP,每个人都启动高比特率视频而不拥塞控制,路由器会有大量分组溢出,以至于几乎没有UDP分组能顺利到达目的,也会大大减小TCP的速率,因此需要拥塞控制
UDP应用可以实现可靠数据传输(注意是应用实现的,UDP本身不能实现),比如增加确认与重传机制。
UDP报文段结构
源端口号、目的端口号、长度、检验和(用来端到端差错检测),每个8bit
应用数据(报文)
UDP检验和(差错检测,网络层中会说)
可靠数据传输原理
构造可靠数据传输协议
经完全可靠信道的可靠数据传输 rdt1.0
有限状态机
有完全可靠的信道,接收方就不需要提供任何反馈信息给发送方
经具有比特差错信道的可靠数据传输 rdt2.0
自动重传请求ARQ协议
差错检测。需要利用额外的比特(组原中海明码、奇偶校验码之类)
接收方反馈。肯定确认ACK(收到了),否定确认NAK(请再传一遍),接收方没收到确认就不能发送(停等协议)
考虑ACK、NAK分组受损的可能性,在数据分组中添加新字段,分组序号0和1(向前取模)
重传。接收方收到有差错分组
经具有比特差错的丢包信道的可靠数据传输 rdt3.0
发送方负责检测和回复丢包工作,对每一个分组维护一个定时器。如果在一个时间段内没收到ACK,则判定为丢包,重传分组,这也引入了冗余数据分组的可能性(时延很大的情况)
流水线可靠数据传输协议
rdt3.0的问题核心在于,它是一个停等协议,实际上大部分时间是在等待,效率极低,解决方法:不适用停等方式运行,允许发送方发送多个分组无需等待确认,称为流水线
流水线对可靠数据传输协议的影响
增加分组序号范围,每个分组需要唯一序号
协议发送方和接收方必须缓存多个分组,发送方最少能缓存已发送但未确认的分组,接收方也可能需要缓存已正确接收的分组
解决流水线差错回复的两种方法:回退N步GBN,选择重传SR
回退N步GBN(滑动窗口协议)
分组序列被基序号(最早未确认分组序号)、下一个序号(下一个要发的分组序号),以及长度为N的窗口(包括未确认和未发送)分为四部分,分别是已确认、未确认(已发送但未确认)、未发送、不可用。其中窗口长度N为未确认分组数量的上限。可以想象分组不断被确认,窗口不断右移的场景
GBN发送方需相应三种事件
上层的调用。发送方首先检查窗口是否已满,若未满则继续发送,直到未确认分组充满窗口;若窗口已满,发送方将数据返回上层,说明窗口满了,上层可能过会再试。实际上发送方更可能缓存这些数据,或者使用同步机制(如信号量)使上层仅在窗口不满的时候才调用
收到一个ACK。累计确认,表明接收方已正确接收到序号为n及之前的所有分组,接收方窗口右移一个分组长度
超时事件。若超时,发送方重传发送窗口里已发送但未确认的分组(这就是回退N步。假设窗口长度20,前10个未确认,后10个未发送,当得知第2个分组发送超时的时候,回退到2重发2-10)
GBN接收方
如果一个序号为n的分组被正确接收到并且按序,则接收方为分组n发送一个ACK,并将该分组交给上层。
否则丢弃该分组(失序或损坏),并且为最近按序接收的分组重发ACK(发送方收到后会回退到失序的分组开始发送)。这样接收方不需要缓存任何失序分组。唯一需要维护的就是下一个分组的序号,用来对比接收到的分组是否有序
若分组k已接收并交付,则所有<k的分组也已经交付,这就是累积确认
选择重传SR
GBN在窗口长度和带宽时延积都很大时,存在性能问题。单个分组差错能引起GBN重传大量分组
SR协议让发送方仅重传它怀疑在接收方出错的分组,从而避免不必要重传
SR发送方事件动作
从上层收到数据。如果序号位于发送方窗口内(没错这个协议的逻辑也用窗口表示,不过要区分GBN),将数据打包并发送,否则缓存或返回上层
超时。每个分组有自己的定时器(单个硬件定时器模拟多个逻辑定时器),超时后只发一个分组
收到ACK。若该序号在窗口内,将该分组标记为已接收(确认)。如果这个序号在窗口最左边,那窗口就可以往右移到最小序号的未确认分组处(若是GBN只移动一个,但SR未确认分组的下一个可能已确认,所以要跨过这些已确认。比如窗口20,1-10中1未确认,其他都已确认,当收到1的ACK时,那窗口就能直接移动11的位置。GBN的话,就必须回退到1重新传1之后的所有分组)
SR接收方事件动作
序号在rcv_base(接收方窗口的基序号,期待但未收到的最小序号,比它小的都收到了)开始的N长度窗口中被正确接收。收到的分组落在接收方窗口内,一个ACK发给发送方(可能有失序的分组,窗口后面有的收到了,前面有的没收到)。若序号等于基序号,则该分组以及以前缓存的序号连续的分组交付给上层,窗口右移(比如窗口有20个,第1个未收到,但是后面9个收到了,若第1个也收到,就直接交付1-10,窗口右移10个)
序号在基序号前的分组正确接收。接收方重新确认并回发ACK。因为发送方和接收方的窗口并不是总一致的(比如一个ACK丢了,发送方重传该分组,要是接收方不确认,那就会一直发这个分组,导致窗口无法移动)
其他情况,忽略分组
若分组序号有限,可能会产生不同步导致严重后果,无法区分这一序号是新的分组还是旧的已经确认的分组。对SR协议而言,窗口长度N必须小于等于序号空间大小的一半
现实中,分组在发送方和接收方可能会被重新排序。由于序号可以被重新使用,因此发送方需要确信任何先前发送的序号都不在网络中。通过假定一个分组在网络中存活时间不会超过某个固定最大时间来做到这一点。在TCP扩展中,最长分组寿命为3分钟。改变使用序号的方法可以完全避免重新排序问题
面向连接的运输:TCP
TCP连接
TCP连接双方都将初始化与之相关的TCP状态变量,连接状态完全保留在两个端系统(主机)中。
TCP协议只在端系统中运行,中间网络元素不会维持TCP连接状态,路由器对TCP连接视而不见,他们看到的只是数据报
全双工服务,点对点服务
三次握手建立连接
客户先发送一个特殊的TCP报文段
服务器用另一个特殊的TCP报文段响应
客户用第三个报文段作为响应,同时可以承载应用层数据
客户进程通过socket(进程之门)传递数据流,数据一旦通过该门,就由客户运行的TCP控制
TCP连接的组成:主机上的收发缓存、变量、socket
TCP报文段结构
源端口号、目的端口号
检验和
32bit的序号字段、32bit的确认号字段
实现可靠数据传输服务
一个报文段的序号是该报文段首字节的字节流编号
主机A填充进报文段的确认号是主机A期望从主机B收到的下一字节的序号
累积确认,TCP只确认该流中至第一个丢失字节为止的字节。如A收到B的0535和9001000,还没收到536~899,则A到B的下一个报文段将在确认号中包括536
16bit接收窗口字段
用于流量控制,指示接收方愿意接收的字节数量
选项字段
发送接收方协商最大报文段长度
高速网络环境下用作窗口调节因子
4bit首部长度字段,指示TCP首部长度
因为选项字段的存在,TCP首部长度是可变的
6bit的标志字段
如ACK比特等
往返时间的估计与超时
TCP采用超时/重传机制处理报文段丢失
估计往返时间
TCP维护一个SampleRTT均值,代入公式更新估计时间
设置和管理重传超时间隔
超时间隔 = 估计RTT + 4*偏差RTT
TCP发送方使用了流水线
可靠数据传输
三种情况
A向B发送一个报文段,序号92,包含8字节,交给IP后开始等待一个来自B的确认号为100的报文段。然而确认报文段丢失,超时,A又重发相同报文段。B收到后对比序号发现已经收到过该报文段的一些字节,于是B的TCP确认后,将报文段重复的字节丢弃
A连续发了两个报文段,92,8和100,20(序号和字节)。B收到两个报文段并发送确认100和200。超时前没有一个确认到达A,超时后A重传92,8的报文,重启定时器。只要第二个报文的ACK在新的超时以前发生到达,第二个报文就不会重传。
和上面一种一样,A发送两个报文段,第一个报文段的确认在网络丢失,但是超时之前收到了第二个报文段的确认报文。因为累积确认机制,A知道B已经收到第二个以及之前的报文,不会重传了
超时间隔加倍
实际大多数情况下,每次TCP重传时会将下次超时间隔设为先前的两倍
当定时器在另外两个事件(收到上层应用数据,收到ACK)中的任意一个启动时,超时间隔又从估计RTT和偏差RTT推算得到。比如一个报文段第一次超时1s,重发,第二次超时就是2s,第三次4s…但是当收到该报文确认时,超时间隔又重置,算是一种拥塞控制
快速重传
发送方挨个发送大量报文段,如果一个丢失,很可能引起一个接一个冗余ACK(即重复的ACK,上面说过,A连续发了序号为1,2,3,4,5的报文,若B没收到2,却收到了3,4,5,则对2之后的报文都发送确认号为2的ACK)。如果TCP发送方接收到对相同数据的3个冗余ACK,它把这当做一种指示,说明这个重复确认报文段后面的报文都丢失了。一旦收到3个冗余ACK,TCP执行快速重传,即在该报文段的定时器过期之前重传丢失的报文段
回退N步 or 选择重传
TCP确认是累积式的,正确接收但失序的报文段是不会被接收方逐个确认的,像GBN风格
但是TCP和GBN有显著区别
许多TCP实现会将正确接收但失序的报文段缓存起来
GBN不仅重传未确认分组,还会重传之后所有分组,TCP只传一个或不传(若其后面的ACK超时前到来)
选择确认,允许接收方有选择地确认失序报文段,而不是累积确认最后一个正确接收的有序报文段。该机制与选择重传机制结合(跳过重传已确认报文段),TCP看起来像SR
TCP的差错恢复机制是GBN协议和SR协议的混合体
流量控制
一条TCP连接每一侧主机都设置了缓存,当TCP连接收到正确有序的字节后,将数据放入缓存,应用从缓存中读取。若应用较忙,发送方发送太快太多,可能会造成缓存溢出
流量控制是一个速度匹配服务,发送方发送速率与接收方程序读取速率相匹配
TCP发送方因为IP网络的拥塞而降速(超时间隔加倍),属于拥塞控制,不属于流量控制,虽然都是降速
TCP让发送方维护一个称为接收窗口rwnd的变量,用于给发送方指示接收方还有多少可用缓存。因为是全双工通信,双方都维护接收窗口变量
B把rwnd值放入给A的报文段接收窗口字段中,通知A自己还有多少缓存空间,A控制未确认的数据量小于rwnd
当B的rwnd为0时,A继续发送只有一个字节数据的报文段,B确认,缓存开始清空,确认报文里包含一个非0的rwnd值(B的rwnd为0时,A收到后就不发的话,那B就一直空了,A就一直不发了)
UDP无流量控制,缓存溢出就溢出了
TCP连接管理(必考哟)
TCP建立连接(三次握手),假设客户主机一进程想与服务器一进程建立一条连接:
客户TCP向服务器TCP发送一个特殊TCP报文段(不含应用层数据),报文段首部标志位SYN置为1(因此该特殊报文段也叫SYN报文段)。客户随机选择一个初始序号client_isn放在序号字段中。该报文段封装在IP数据报中,发送给服务器
服务器收到SYN报文段,为该TCP分配TCP缓存和变量,然后向客户TCP发送允许连接的报文段(不含应用层数据)。报文段首部中,SYN置为1,确认号字段置为初始序号client_isn+1,服务器选择自己的初始序号server_isn。该报文段也叫SYNACK报文段
客户收到SYNACK报文段后,客户给该TCP连接分配缓存和变量。然后客户给服务器发送第三个报文段,对SYNACK报文段进行了确认,确认号置为server_isn+1,SYN字段置为0,序号为client_isn+1。这第三个报文段可以负载客户的应用层数据。建立TCP连接后,之后每个报文段SYN都为0
为何要三次?类比攀岩者和保护者(位于攀岩者下方,处理攀岩者的安全绳索)
TCP结束连接(四次挥手),假设客户想关闭连接
客户TCP向服务器发送一个终止报文段,首部标志位FIN置1
服务器收到后,向客户会送一个确认报文段
服务器再发送它自己的终止报文段,FIN置为1
客户收到后,向服务器发送确认报文段
关闭连接后,客户端所有资源释放(包括变量、缓存、端口号等等)
安全性:SYN泛洪攻击
攻击者发送大量TCP SYN报文段,而不完成第三次握手,服务器不断为这些半开连接分配资源,导致服务器连接资源消耗殆尽
SYN cookie防御系统,服务器收到SYN报文段先生成一个cookie(根据IP地址、端口号字段,和该服务器唯一知道的一个散列函数),而不分配资源。若客户返回ACK,则跟cookie对比,对上才分配资源
连接不匹配
若主机收到一个TCP报文段,目的端口是80,但是主机80不接受连接,则会返回一个报文段,RST标志位置1,告诉主机没有该报文段的socket,不要再发
若主机收到一个UDP分组,目的端口与当前UDPsocket不匹配,则发送一个特殊ICMP数据报(网络层里讲)
拥塞控制原理
丢包一般是当前网络拥塞时由于路由器缓存溢出引起,因此分组重传能作为网络拥塞的征兆,但分组重传无法解决网络拥塞
拥塞原因与代价
情况一:两个发送方和一台无穷大缓存的路由器
容量为R的共享式输出链路上传输
当发送速率超过R/2时,路由器的排队分组就会无限增加,源和目的平均时延变成无穷大
情况二:两个发送方和一台有限缓存的路由器
分组到达一个已满的缓存时会被丢弃,发送方必须执行重传以补偿因为缓存溢出丢弃的分组
发送方遇到高时延时,进行的不必要重传引起路由器转发不必要的分组副本
情况三:4个发送方和具有优先缓存的多台路由器及多条路径
当一个分组沿一条路径被丢弃时,每个上游路由器用于转发该分组到丢弃该分组使用的传输容量被浪费
拥塞控制方法
端到端拥塞控制
网络层没有为运输层拥塞控制提供反馈信息,端系统通过对网络行为的观察(分组丢失与时延)来推断
TCP报文段的丢失(超时/3次冗余确认)认为网络拥塞,TCP相应减小窗口长度
RTT增加—>拥塞程度增加
网络辅助的拥塞控制
路由器向发送方提供关于网络拥塞状态的显式反馈信息,有两种方式
路由器发给发送方一个通知,采用阻塞分组形式(也就是告诉说:『我拥塞了』)
路由器标记从发送方流向接收方分组的某个字段,来指示拥塞产生。接收方看到后,再向发送方通知网络拥塞
网络辅助拥塞控制例子
ATM(异步传递方式)
面向虚电路VC的方法来处理分组交换,从源到目的地路径上每台交换机都维护有关源到目的地VC的状态
这种逐个VC的状态允许交换机跟踪各个发送方的行为(如平均传输速率),采取拥塞控制动作(如当交换机拥塞时,向发送方发送显式信令,减少它的速率
ABR(可用比特率服务)
一种弹性数据传输服务
当网络轻载时,AR服务充分利用空闲可用带宽;拥塞时,将传输速率抑制为预定好的最小传输速率
ATM ABR服务的拥塞控制框架
基于速率,发送方明确计算出它能所发送的最大速率,并据此进行相应调整
TCP拥塞控制
TCP必须用端到端拥塞控制,因为IP层不向端系统提供显式网络拥塞反馈
TCP让每一个发送方根据感知到的拥塞程度限制其发送速率
如何限制其发送速率?
维护拥塞窗口cwnd值(注意流量控制使用接收窗口rwnd值)
发送方未确认数据量 <= min { cwnd , rwnd }
如何感知它到目的地路径存在拥塞?
丢包:超时或收到3个冗余ACK
TCP是自计时的,使用确认来触发增大它的拥塞窗口长度(及其传输速率)
TCP发送方如何确定发送速率?
一个丢失的报文意味着拥塞,当丢失报文段时应当降低速率(当前速度不能正常交付,得慢点)
先前未确认报文段的确认到达时,增加发送方速率(当前速度能够正常交付,说明可以再快点)
带宽探测(试探):TCP发送方增加速率,丢包,从该速率后退,再往前探测…(因为拥塞情况是波动的,得尽力保持在最高速率)
TCP拥塞控制算法(TCP Reno)
慢启动
一开始cwnd只设为一个MSS的较小值(这就是『慢』,不过瞬间指数级加速)
收到一个确认,cwnd增加一个MSS ——> 每过一个RTT,cwnd翻番,发送速率翻倍(1,2,4…每次发的也多一个)
丢包(拥塞)结束慢启动
将ssthresh(慢启动阈值)设置为cwnd/2,cwnd置为1重新开始慢启动
当cwnd=ssthresh时,结束慢启动,TCP转移到拥塞避免模式
收到3个冗余ACK,执行快速重传,进入快速回复模式
拥塞避免
进入拥塞避免状态,cwnd值是上次遇到拥塞时的一半
每个RTT只将cwnd值增加一个MSS/cwnd字节
丢包,结束拥塞避免
超时:ssthresh更新为cwnd的一半,cwnd置为1个MSS,返回慢启动状态
3冗余ACK:ssthresh更新为cwnd的一半,cwnd值减半,进入快速恢复状态
快速恢复
快速恢复中,每个冗余ACK,cwnd 增加一个MSS,当丢失报文的ACK到达时,TCP降低cwnd,进入拥塞避免状态
超时:同慢启动和拥塞避免
TCP拥塞控制回顾
TCP拥塞控制常被称为加性增(+1*MSS),乘性减(cwnd/2)拥塞控制,产生锯齿状行为(发送速率折线图)
TCP Vegas算法
对TCP吞吐量的宏观描述
在一个RTT内,TCP发送数据的速率是cwnd与当前RTT的函数
一条连接的平均吞吐量 = (0.75*W)/RTT ,其中W是窗口长度字节
TCP拥塞控制公平性
公平性和UDP
许多多媒体应用不想传输速率被遏制,即使网络非常拥塞,所以在UDP上运行。这些应用以更定速率将其数据注入网络,宁愿丢包,也不愿将发送速率将至『公平』级别且不丢包
TCP角度来看,UDP是不公平的,UDP源可能会压制TCP流量
公平性和并行TCP连接
除了UDP,我们也没办法阻止TCP应用使用多个并行连接。当一个应用使用并行连接,会占用一条拥塞链路中较大比例的带宽
当前新的运输层协议
数据包拥塞控制协议:相当于有拥塞控制的UDP,与TCP兼容
流控制传输协议
TCP友好速率控制协议(是一种拥塞控制协议):为了平滑锯齿状行为,维护一种长期发送速率