网络的五层协议(自顶向下)
-
应用层
应用层常用的协议:HTTP(WEB), SMTP(邮件),FTP,DNS
应用层可以控制使用哪一种运输层协议,还可以控制部分运输层协议的参数
HTTP:基于TCP连接的一种应用层协议
HTTP的三个版本:
HTTP 1.0: 无状态:浏览器与服务器的每次请求/响应对均需要进行一次TCP连接 无连接:服务器不对用户进行跟踪,不保存用户信息 (可以借助session或cookie来保存用户信息) 队头阻塞的问题:HTTP1.0中,当前一个请求的响应没有到达的时,后续请求不发送,造成阻塞 HTTP 1.1: 实现长连接(即持续连接),报文中增加Keep-Alive可以保持HTTP的持续连接 管道化:实现假并行传输 把客户端的请求队列迁移到服务端.服务端仍然必须按照客户端的请求顺序依次发送响应的结果 管道化实现真并行传输的方法:创建多个TCP连接同时传输 缓存处理: http缓存请求相应头: Cache-Control:请求/响应头,控制http缓存最高指令 Cache-Control的常用值: no-store:所有内容均不缓存 no-cache:缓存,但使用缓存之前判断资源是否为最新 max-age=x(秒): 请求缓存后的X秒不再发起请求,优先级大于Expires s-maxage=x(秒):代理服务器请求源站缓存后的X秒不再发起请求,只对CDN缓存有效 public 客户端和代理服务器(CDN)都可缓存 private 只有客户端可以缓存 Expires:响应头,服务器提供,代表资源过期时间,优先级小于max-age Last-Modified:响应头,服务器提供,代表资源最新修改时间,与if-Modified-Since进行对比 if-Modified-Since:请求头,客户端提供,代表资源最新修改时间.其值由服务器在传输资源同时给予,与Last-Modified进行对比 Etag:响应头,服务器提供,资源标识,与if-None-Match进行对比,优先级大于Last-Modified if-None-Match:请求头,浏览器提供,缓存资源标识.与Etag进行对比,可以判断资源是否修改过
参考:HTTP缓存处理机制详解
用户操作对Expires/Cache-Control与Last-Modified/Etag是否生效的影响:
HTTP 2.0: 二进制分帧:在应用层和传输层之间增加了一个"二进制分层帧",提高传输性能 二进制分帧可以将所有传输的信息分割为更小的消息和帧,并对它们采用二进制格式的编码 在HTTP1.x版本中的首部封装到headers帧,request body封装到data帧 多路复用:实现并行传输,在一个TCP连接中可以进行任意数量的HTTP请求,基于"二进制分帧" 首部压缩:在客户端与服务端之间使用"首部表"储存之前发送的首部键值对. 在之后请求首部不改变时,自动沿用之前发送的首部,不需要进行额外的开销. 如果首部发生变化,只需要将新建或修改的首部添加至headers帧里 并行双向字节流的请求和响应:HTTP2.0最重要的增强 客户端和服务器可以把HTTP消息分解为互不依赖的帧,然后乱序发送,最后再在另一端把它们重新组合起来. 在这个过程中,同一个链接上可以有多个不同方向的数据流同时进行传输,客户端与服务端都可以一边接收响应,一边发送请求 服务器推送:服务器可以向客户端推送额外的资源 HTTP 3.0(QUIC协议):(使用UDP代替TCP连接) 0-RTT:最大优势 客户端中可以缓存当前会话的上下文.当重新恢复会话的时候,只需要将客户端的缓存传递给服务器,验证通过后就可以继续进行传输 多路复用: 基于UDP,一个链接上的多个stream之间没有依赖.当发生丢包时,只需要重发丢失的包,不需要重传整个链接 通过ID识别链接: QUIC通过ID识别链接,而非IP.所以当网络环境发生变化时(比如移动设备),可以迅速恢复连接. packet加密 向前纠错: 发送冗余的校验数据包,当非校验数据包丢失时,可以根据其余的数据计算出丢失的数据,不需要进行重传.
HTTP请求报文格式:
HTTP响应报文格式:
Web缓存器(代理服务器):代表初始web服务器来满足客户端的HTTP请求,即在自己的磁盘中保存最近请求过的对象副本SMTP:电子邮件的主要应用层协议
使用TCP进行可靠数据传输服务
SMTP一般不使用中间邮件服务器发送邮件.在一次连接尝试失败后,邮件报文会储存在原服务器的报文队列中,等待一定时间后重新发送
邮件访问协议:由于SMTP是一种"推协议",接受邮件的用户从自己的邮件服务器中获取邮件时需要用到"邮件访问协议"
常用的邮件访问协议:POP3, IMAP
DNS:域名解析服务器:主机名(域名)到IP地址的转换
DNS运行在UDP之上.
DNS服务器的种类:
根DNS服务器:提供顶级DNS服务器的IP地址
顶级DNS服务器:解析顶级域名(比如com,org,net或国家顶级域名:cn,uk,fr)
权威DNS服务器:记录各种组织机构的DNS记录
本地DNS服务器:每个ISP都要有一个本地DNS服务器.
各种DNS服务器的交互(迭代查询):
各种DNS服务器的交互(递归查询):
CDN:内容分发网
CDN的操作:(以视频网站为例)
(1) 截获用户的URL请求,分析其中需要的资源
(2) 确定此时适合该用户的CND服务器集群
(3) 将用户的请求重定向至该服务器集群
- 运输层
TCP与UDP的比较:
TCP:面向连接服务,可靠数据传输,拥塞控制
面向链接服务:传输时先进行与服务器的连接(三次握手)
可靠数据传输:安全的传输数据.无差错,按顺序
拥塞控制:TCP连接通过调节发送端发送进网络的流量速率,,防止某一条连接用过多的流量淹没通信主机之间的链路以及交换设备
UDP:无连接,没有拥塞控制,不保证发送的报文到达接受进程,不保证到达时报文的顺序
多路复用与多路分解:
网络层提供的交付服务时主机到主机的服务(根据ip), 运输层需要将这种交付服务延伸到进程到进程(根据端口号)
无连接(UDP)的多路复用:
UDP的套接字是一个二元组.只包含目的地的IP地址和端口号,
所以不同的程序发送到相同的客户进程时,由于目的地的IP和port相同, 会通过相同的套接字
TCP的套接字时一个四元组,包含源IP地址和端口号, 以及目的IP地址和端口号
所以不同程序发送到同一个客户进程时, 由于源不同, 会被分配至不同的套接字
UDP的报文结构:
(UDP报文的长度为首部长度+数据长度)
可靠数据传输协议(rdt):
rdt1.0:
假设信道可靠.
发送方等待上层数据传来, 传来后发送给接收方
接收方等待发送方的数据, 接收到数据后传给上层数据
rdt1.0:
rdt2.0:
考虑到错误的发生,在接收端增加了ACK和NAK.
当接收端接收到数据检验无误后, 向发送端发送一个ACK.检验发现数据有错误时, 发送一个NAK
发送端接收到ACK后, 返回"等待上层传输数据"的状态, 接收到NAK后, 重传数据
rdt2.0:
rdt2.1:
考虑到接收端的相应ACK/NAK报文也有可能出错,增加"发送的分组的序号"(分组的序号仅在0和1之间交替)
在发送的报文中增加表示该报文的序号的字段, 接收端接收到报文后, 回复的ACK或NAK中也增加该序号字段
rdt2.1_sender:
rdt2.1_receiver:
rdt2.2:
因为rdt2.1新增报文序号后, ACK与NAK两个回复字段已经出现冗余
所以在rdt2.2中, 删除NAK字段, receiver仅用ACK+报文序号来表示确认或否认的效果
rdt2.2:
rdt3.0:
考虑到网络中的丢包/超时影响, 在sender中增加一个倒计数定时器
在每个分组发送时, 为该分组启动一个定时器.
在定时器超时时,如果还没有接收到receiver的回应, sender重新发送该报文
rdt3.0的发送方:
流水线可靠数据传输协议:
rdt3.0虽然可以达到可靠数据传输的目的, 但是由于他是一个停等协议(sender每发送一个数据包后都需要等待receiver的回复或等待超时), 导致性能很差.
流水线的可靠数据传输协议:
以不停等的方式运行.sender可以发送多个分组而不需要等待receiver的回复
协议的改进:
增加序号的范围, 从而匹配多个传输中的分组
sender和receiver需要缓存自己接收到的分组
对分组丢失,损坏,超时的处理方法:
1. 回退N步(滑动窗口协议)(GBN)
允许发送方发送多个分组而不需要等待确认, 但所有未确认的分组数不能超过某个最大允许数N
将"base"定义为最近的一个未被确认的分组序号, "nextseqnum"定义为最小的一个未发送且待发送的分组序号, 则发送方的所有分组被分为四个部分
所有"已发送未被确认的分组"和"发送且未被确认的分组"的总和, 被称为"窗口长度".
滑动窗口协议中, sender需要响应三种类型的事件:
1. 上层的调用.
在上层调用rdt_send()时, sender需要检查发送窗口是否已满,
如果未满, 则可以立即发送. 如果已满, 则添加至缓存或使用同步机制阻塞该方法
2. 接收ACK
sender需要对序号为n的分组采用累积确认的方式, 表明接收方已经正确接收到序号为n的及以前的所有分组
5. 超时.
在定时器超时后, sender需要重传所有发送过且未被确认的分组
滑动窗口协议中, receiver的动作:
当一个序号为n的分组被正确接收到, 则返回一个序号为n的ACK.
其他所有情况, 均丢弃该分组, 且返回一个最近成功接受的分组的ack(满足累积确认的方法)
示例,运行中的滑动窗口协议(窗口大小为4):
receiver:
- 在接受分组0和1后, 没有接收到分组2, 后续所有接收到的分组均丢弃且返回ACK1(因为最后一个正确接收到的是分组1)
- 在接收到分组2后, 恢复正常
sender:
- 开始的窗口是[0,1,2,3]
- 在接受到ACK0和ACK1后, 此时的窗口是[2,3,4,5], 且分组4和分组5已经发送
- 分组2计时器超时, 重传当前所有已发送且未确认的分组,即分组2,3,4,5
- 恢复正常(在此后多次接收到receiver传来的ACK1, 但由于不符合累积确认原则, 不对其做出相应)
滑动窗口协议的扩展FSM描述
2. 选择重传(SR)
在滑动窗口协议中, 由于receiver对分组"一个出错, 后续全部丢弃", sender对分组"ACK一个出错, 重传所有发送且未响应的分组"
这造成了资源的浪费, 导致性能变慢
选择重传协议可以让发送方仅重传出错(丢失或受损)的分组
receiver:
- receiver会接受并ACK所有正确接收到且序号在[rcv_base-N, rcv_base+N-1]的分组(即接收方的窗口和之前N个序号的分组)
- 在receiver没有按序接收到分组时, 虽然回复ACK, 但并不会把该分组向上层交付,
而是将其缓存,等待该组之前所有丢失的分组都被收到后, 一起交付
(即使收到的分组不在receiver的窗口内, 仍然需要回复一个ACK, 向sender声明自己收到该分组)
sender:
- 从上层接受数据分组后, 检查该分组的序号是否在窗口内. 在窗口内则直接发送, 不在窗口内则缓存等待
- 定时器超时后, 重传该分组
- 收到ACK:如果收到的ACK序号在窗口内, 则将该序号代表的分组标记为已确认.
如果标记的分组是窗口的最小序号(即send_base), 则移动窗口直至send_base代表的分组为"未确认"
每次移动窗口后, 将新进入窗口的"未发送分组"发送.
选择重传需要注意的事项:
窗口大小不能超过序号空间大小的一半:
例如:当序号空间为8时, 窗口大小不能超过4(最大为4)
选择重传中sender和receiver看到的窗口:
实例:运行中的选择重传(窗口为4)
TCP:面向连接的运输
一个TCP/IP首部的长度:通常为40字节
TCP的连接包括:两台主机的缓存,变量,套接字.
TCP报文的格式:
对TCP报文中Seq和ACK的认识:
假设有两台主机:客户机(主机A)和服务器(主机B), 现在客户机的起始序号时42, 服务器的起始序号是79.
也就是说, 客户机下一个发送的报文序号是42, 服务器下一个发送的报文序号是79.
客户机在等待服务器发来的序号是79的报文, 服务器在等待客户机发送的序号是43的报文
现在A对B发送数据:一个字符"C"
1. 客户机发送data="c", 此时的TCP首部中Seq=42, ACK=79. 代表该条报文的序号为42, 客户机在等待的报文序号为79.
2. 服务器收到并回传data="c", 此时TCP首部中Seq=79, ACK=43. 代表该条报文的序号为79, 服务器已经接收到序号为42的报文,所以现在等待序号为43的报文
3. 客户机收到回显的消息, 回传一个"确认收到"的信息. 此时Seq=43, ACK=80
TCP的快速重传:
一旦收到三个冗余ACK, 立即重传
TCP连接管理(三次握手/四次挥手):
三次握手:
客户机发送TCP连接之前, 首先随机化选择一个自己的起始编号:client_isn
- 第一次握手:客户机向服务器发送一个TCP报文(只有首部, 不包含数据).该报文SYN=1(正常为0), seq=client_isn
- 第二次握手:服务器接收到客户机发送的TCP报文后, 随机选择一个自己的起始编号:server_isn
服务器向客户机发送一个TCP报文(不含数据).该报文SYN=1, seq=server_isn, ack=client_isn+1(代表自身等待接受序号为client_isn+1的报文)
- 第三次握手:客户机接收到服务器发来的报文之后, 对服务器"允许链接"的报文进行确认.
客户机向服务器发送TCP报文(此时的报文中可以包含数据). 该报文SYN=0(因为已经建立起连接), seq=client_isn+1, ack=server_isn+1
四次挥手:
服务器和客户机都可以发起终止连接的请求.
- 第一次挥手:当主机a做出关闭连接的指令之后, a将会对b发送一个特殊的TCP报文. 该报文FIN=1
- 第二次挥手:主机b接收到报文后, 发送一个ACK回复.
- 第三次挥手:在第二次挥手之后, b对a发送一个终止报文段. 该报文段FIN=1. 发送后b的连接关闭
- 第四次挥手:a接收到b的终止报文段后, 关闭TCP连接
三次握手:
四次挥手:
SYN攻击:
对服务器不断的发起TCP连接的第一次握手, 且完成三次握手的第三步.
TCP拥塞控制:
拥塞窗口:cwnd. 用来表示一个TCP发送方能向网路中发送流量的速率的最大值
慢启动阈值:ssthresh. 用来表示慢启动结束的时刻
TCP连接中网络拥塞的原因:超时或出现三个冗余ACK
超时:网路中过于拥堵, 导致时延变高. 发生超时
三个冗余ACK: 路由器缓存溢出, 导致出现数据报被丢弃
TCP的拥塞控制算法:
- 慢启动:TCP连接开始时, cwnd设置为一个较小值(比如1)
在此之后, 每次接收到ACK回复, cwnd的值就*2
直到cwnd超过ssthresh的值时, 代表慢启动结束
- 拥塞避免:在cwnd超过ssthresh的值后, 每次收到回复, cwnd的值+1
直到发生拥塞
发生拥塞后, 将ssthresh的值设置为当前(拥塞发生时)cwnd值的一半.
如果引起拥塞的原因是超时, 则将cwnd的值设为1, 重新进入慢启动
如果引起拥塞的原因是三个冗余ACK, 则进入快速恢复(因为可能存在"没有出现网络拥塞"但"出现丢包"的情况)
- 快速恢复:在丢包事件发生后, 快速恢复传输速度的算法
当出现三个冗余ACK(即丢包)导致重新设置cwnd时, 将cwnd设置为ssthresh或ssthresh+3(而非设置为1), 直接进入拥塞避免
ssthresh+3的原因是:当丢包原因是网络出错时, 因为出现三个冗余ACK, 所以网络上不是堆积报文而是减少了三个报文.
设置为ssthresh+3可以使总传输的报文数不变
(网络层, 链路层, 物理层暂不涉及)
-
网络层
-
链路层
-
物理层