层次结构设计的基本原则
互联网在创立之初需要保证几个问题的解决:
- 保证数据通路畅通
- 可以识别目的计算机
- 可以查询目的计算机的状态(是否在线,是否可以接收数据)
- 检测数据是否错误(传播中的噪音干扰丢包等),及错误如何解决
为了解决诸如此类的这些问题,就需要分层实现不同的功能。并且保证各层相互独立
OSI七层模型
- 应用层 :为特定应用程序提供数据传输服务,例如 HTTP、DNS 等协议。数据单位为报文。
- 传输层 :为进程提供通用数据传输服务。由于应用层协议很多,定义通用的传输层协议就可以支持不断增多的应用层协议。运输层包括两种协议:传输控制协议 TCP,提供面向连接、可靠的数据传输服务,数据单位为报文段;用户数据报协议 UDP,提供无连接、尽最大努力的数据传输服务,数据单位为用户数据报。TCP 主要提供完整性服务,UDP 主要提供及时性服务。
- 网络层 :为主机提供数据传输服务。而传输层协议是为主机中的进程提供数据传输服务。网络层把传输层传递下来的报文段或者用户数据报封装成分组。
- 数据链路层 :网络层针对的还是主机之间的数据传输服务,而主机之间可以有很多链路,链路层协议就是为同一链路的主机提供数据传输服务。数据链路层把网络层传下来的分组封装成帧。
- 物理层 :考虑的是怎样在传输媒体上传输数据比特流,而不是指具体的传输媒体。物理层的作用是尽可能屏蔽传输媒体和通信手段的差异,使数据链路层感觉不到这些差异。
- 表示层 :数据压缩、加密以及数据描述,这使得应用程序不必关心在各台主机中数据内部格式不同的问题。
- 会话层 :管理(建立,维护,重连)通信会话。
OSI 七层模型在制定之后实行起来困难重重,该模型各层之间功能互相有重复,并且该标准制定过久,在其制定出来之前TCP/IP协议已经变得十分流行,相关设计人员缺乏工程经验只靠一些理论经验进行制定标准导致如此。
TCP/IP 四层模型
- 应用层:应用层,表示层,会话层,
- 传输层
- 网络层
- 网络接口层:数据链路层,物理层
计算机网络的性能指标
总时延 = 发送时延 + 排队时延 + 传播时延 + 处理时延
排队时延
分组在路由器的输入队列和输出队列中排队等待的时间,取决于网络当前的通信量,比如你的网络包发送到路由器并不是立马处理的,它可能正在处理其他数据包,等其他的处理完再处理你的。
处理时延
主机或路由器收到分组时进行处理所需要的时间,例如分析首部、从分组中提取数据、进行差错检验或查找适当的路由等。
传输时延
传播时延:主机或路由器传输数据帧所需要的时间。t = l/v
其中 l 表示数据帧的长度,v 表示传输速率。
传播时延
电磁波在信道中传播所需要花费的时间,电磁波传播的速度接近光速。= 传播路径距离/传播速率(bit/s)
传播速率由传输介质所决定。比如铜线,光纤等。
发送时延
网络数据发送时,在本机所停留的时间:发送时延 = 数据长度(bit)/发送速率(bit/s)
发送速率受限于计算机网卡,即一般的千兆网卡,万兆网卡等。
往返时间
RTT(Round-Trip Time 往返时延):表示的是数据报文在端到端通信中来回一次的时间。常用来评估网络质量的一项指标
信道
信道是往一个方向发送信息的媒体,一条通信线路包含一个接收信道和一个发送信道。根据通信线路的不同分为:
- 单工通信信道
- 半双工通信信道
- 全双工通信信道:双方都可以同时接收和发送信息。
多端信道相连如果传输信息不频繁会造成信道的浪费,利用率不高,所有就有了分用-复用的技术,物理层已经实现。分为:
- 频分复用
- 时分复用
- 波分复用
- 码分复用
数据链路层
封装成帧
“帧”是数据链路层的基本单位,发送端在网络层的一段数据前后添加特定的“帧”。接收端根据前后特定标记识别出“帧”。
帧首和尾部是特定的控制字符。
- 帧首:SOH:00000001
- 帧尾:EOT:00000100
透明传输
透明表示一个实际存在的事物看起来好像不存在一样。透明传输主要作用点是转义字符
如果数据部分中出现了EOT或者SOH等数据内容,使数据链路层不分辨错一个帧的办法是,对数据内容进行扫描其中的SOH或者EOT前面加上转义字符ESC,如果有转义字符就再在转义字符前加一个转义字符即ESCESC。
如编程语言中“\”一般为转义字符,‘\n’,'\t'等,所有如果要输出''反斜杠的话,需要在反斜杠前再加一个\
反斜杠即\\
,即声明转义字符这里并不是表示转义字符。
差错检测
物理层如果因为外部因素数据出现了丢失情况,物理层并无法控制,则需要数据链路层在控制。数据链路层检测到错误会直接将数据包丢弃。
奇偶校验码
数据在传输过程中,对应的二进制数字信息各位相加的结果为奇数则最后再添加一位1。同理如果为偶数则最后添加一位0,总之最后添加一位来表示。但很明显有局限性,当其中有两位bit为1的数据被干扰成0,但最后的奇偶校验码却不会因此而该表,但数据还是出错了。
循环冗余校验码CRC
一种根据传输或保存的数据而产生固定位数校验码的方法。发送端具体步骤:
- 选定一个用于校验的多项式G(x),并在数据尾部添加r个0.(r是多项式最高阶)
- 将添加了r个0后的数据进行摸2除法,除以多项式的位串(多项式从高阶到0次方的系数组成的位串)。
- 将最后除后的余数添加到原来数据添加了的r个0的位置。
接受端接受到这个数据后对余数也进行模2除法看最后结果是否为0,如果为0则证明数据是正确的,否则是错误的。
信道分类
广播信道:一对多通信,一个节点发送的数据能够被广播信道上所有的节点接收到。当多个节点都在同一个广播信道上发送数据,会造成数据杂糅,因此需要有专门的控制方法进行协调,避免发生冲突(冲突也叫碰撞)。
主要有两种控制方法进行协调,一个是使用信道复用技术,一是使用** CSMA/CD 协议**。
点对点信道:一对一通信。
因为不会发生碰撞,因此也比较简单,使用 PPP 协议进行控制。
信道复用技术
- 频分复用:频分复用的所有主机在相同的时间占用不同的频率带宽资源。
- 时分复用:时分复用的所有主机在不同的时间占用相同的频率带宽资源。
- 统计时分复用:是对时分复用的一种改进,不固定每个用户在时分复用帧中的位置,只要有数据就集中起来组成统计时分复用帧然后发送。
- 波分复用:光的频分复用。由于光的频率很高,因此习惯上用波长而不是频率来表示所使用的光载波。
- 码分复用:
使用频分复用和时分复用进行通信,在通信的过程中主机会一直占用一部分信道资源。但是由于计算机数据的突发性质,通信过程没必要一直占用信道资源而不让出给其它用户使用,因此这两种方式对信道的利用率都不高。
CSMA/CD
CSMA/CD 表示载波监听多点接入 / 碰撞检测。
- 多点接入 :说明这是总线型网络,许多主机以多点的方式连接到总线上。
- 载波监听 :每个主机都必须不停地监听信道。在发送前,如果监听到信道正在使用,就必须等待。
- 碰撞检测 :在发送中,如果监听到信道已有其它主机正在发送数据,就表示发生了碰撞。虽然每个主机在发送数据之前都已经监听到信道为空闲,但是由于电磁波的传播时延的存在,还是有可能会发生碰撞。
MTU
MTU(Max Transmission Unit):最大传输单元。描述数据链路层数据帧最大的传输大小
MTU受底层物理硬件大小的影响,并且数据帧过大或者过小都会影响传输的效率。以太网一般的MTU为1500字节。如果过大,则前面总结的各种时延就会增加。如果过小,虽然时延会降低,但是发送的频率也会增加也会影响通信的效率。
传输路径上的MTU由链路中MTU的最小值来决定的。
以太网协议
数据链路层常用的一种协议。使用以太网技术可以完成 ==相邻设备== 的数据帧传输。
以太网协议的数据格式:
这里的地址指的都是MAC地址。类型指的是数据帧内部的数据类型。
A-B机器通信运作流程:
A机器通过网卡发出数据帧,数据帧到达路由器,路由器取出前6个字节去匹配MAC表确认输出的网络端口,然后发送数据帧。
网络层
由上图所总结内容,以太网协议中我们知道帧数据可以是ARP请求/应答,也可以是IP数据报。其中IP数据报是由IP头和IP数据报的数据所组成的。
IP首部需要注意的是前5行必须具有,也即必然有4*5=20个字节的数据。
- 版本:IPv4/IPv6
- 首部长度:占 4 位,因此最大值为 15。值为 1 表示的是 1 个 32 位字的长度,也就是 4 字节。因为固定部分长度为 20 字节,因此该值最小为 5。如果可选字段的长度不是 4 字节的整数倍,就用尾部的填充部分来填充。
- 总长度:包括首部长度和数据部分长度。
- TTL(Time To Live):表明一个网络包在网络中最长存活时间,它的存在是为了防止无法交付的数据报在互联网中不断兜圈子,浪费网络带宽。以路由器跳数为单位,当 TTL 为 0 时就丢弃数据报。
- 协议:指出携带的数据应该上交给哪个协议进行处理,例如 ICMP、TCP、UDP 等。
- 首部检验和:因为数据报每经过一个路由器,都要重新计算检验和,因此检验和不包含数据部分可以减少计算的工作量。
- 标志:标记数据报文是否可以进行分片。
- 标识:在数据报长度过长从而发生分片的情况下,相同数据报的不同分片具有相同的标识符。
- 片偏移:当IP数据报被拆分后,用来记录拆分后的某一块帧数据是第几片片偏移的帧数据。
五类地址
路由表
计算机和路由器都是有自己的路由表的。如下为路由表数据格式:
如图A要把一个IP数据报发送给C,如下流程:
- A发出目的地为C的IP数据报,查询自己的路由表发现下一跳是E,然后依据E的ip地址通过ARP缓存表拿到E的mac地址,然后拿到数据链路层填到头部。
- IP数据报到达E之后,查看IP头并对照路由表发现下一跳的地址是F。同样通过ARP缓存表或协议拿到F的mac地址,然后添加数据链路层的首部,目标MAC地址改为路由器F的MAC地址。
- 到达F之后,F拿下IP头一看目的IP地址和自己在一个网段。由于此时只知道目标C的ip内网IP地址,通过ARP协议拿到C的mac地址,通过MAC映射表找到目标端口然后转发。
网络层实现主机之间的通信,而链路层实现具体每段链路之间的通信。因此在通信过程中,IP 数据报的源地址和目的地址始终不变,而 MAC 地址随着链路的改变而改变。
ARP缓存表
IP地址分类
32位的IP地址是由网络号和主机号两部分组成的,同时根据网络号与主机号所占位数的不同把ip地址分为5类:
- A类:8位网络号,24位主机号,8位的第一位为0开头
- B类:16位网络号,16位主机号,16位的前两位为10开头
- C类:24位网络号,8位主机号,24位的前3位为110开头
- D类:特殊用途
- E类:特殊用途
- 主机号全为0的ip地址,表示的是当前网络段,不能分给某一台机器。
- 主机号全为1的ip地址,表示的是广播地址,向当前网络段内所有主机发送消息。
子网掩码:用来快速判断某一个ip属于哪个网络号。
NAT
由于需要ip地址转化,所以增加了网络的复杂度,但是减少了ip地址的浪费程度
3类内网地址:
- 10.0.0.0 ~ 10.255.255.255 千万级别
- 172.16.0.0 ~ 172.31.255.255 百万级别
- 192.168.0.0 ~ 192.168.255.255 万级别
NAT表
路由器分组转发流程
- 从数据报的首部提取目的主机的 IP 地址 D,得到目的网络地址 N。
- 若 N 就是与此路由器直接相连的某个网络地址,则进行直接交付;
- 若路由表中有目的地址为 D 的特定主机路由,则把数据报传送给表中所指明的下一跳路由器;
- 若路由表中有到达网络 N 的路由,则把数据报传送给路由表中所指明的下一跳路由器;
- 若路由表中有一个默认路由,则把数据报传送给路由表中所指明的默认路由器;
- 报告转发分组出错。
ICMP
网际控制报文协议(Internet Control Message Protocol)。作用是报告网络包在传输中的错误信息或者异常情况。是为了更有效地转发 IP 数据报和提高交付成功的机会。它封装在 IP 数据报中,但是不属于高层协议。
ping 命令应用的原理其实就是通过ICMP报文的询问报文类型来作用的。
ICMP协议是作用在IP数据报的数据部分的。对于ICMP的报文来说有两种:
- 差错报告报文
- 询问报文
Traceroute
Traceroute 是 ICMP 的另一个应用,用来跟踪一个分组从源点到终点的路径。
Traceroute 发送的 IP 数据报封装的是无法交付的 UDP 用户数据报,并由目的主机发送终点不可达差错报告报文。
- 源主机向目的主机发送一连串的 IP 数据报。第一个数据报 P1 的生存时间 TTL 设置为 1,当 P1 到达路径上的第一个路由器 R1 时,R1 收下它并把 TTL 减 1,此时 TTL 等于 0,R1 就把 P1 丢弃,并向源主机发送一个 ICMP 时间超过差错报告报文;
- 源主机接着发送第二个数据报 P2,并把 TTL 设置为 2。P2 先到达 R1,R1 收下后把 TTL 减 1 再转发给 R2,R2 收下后也把 TTL 减 1,由于此时 TTL 等于 0,R2 就丢弃 P2,并向源主机发送一个 ICMP 时间超过差错报文。
- 不断执行这样的步骤,直到最后一个数据报刚刚到达目的主机,主机不转发数据报,也不把 TTL 值减 1。但是因为数据报封装的是无法交付的 UDP,因此目的主机要向源主机发送 ICMP 终点不可达差错报告报文。
- 之后源主机知道了到达目的主机所经过的路由器 IP 地址以及到达每个路由器的往返时间。
传输层
可参考:blog.csdn.net/u010425776/…
UDP 和 TCP 的特点
用户数据报协议 UDP(User Datagram Protocol)是无连接的,尽最大可能交付,没有拥塞控制,面向报文(对于应用程序传下来的报文不合并也不拆分,只是添加 UDP 首部),支持一对一、一对多、多对一和多对多的交互通信。
传输控制协议 TCP(Transmission Control Protocol)是面向连接的,提供可靠交付,有流量控制,拥塞控制,提供全双工通信,面向字节流(把应用层传下来的报文看成字节流,把字节流组织成大小不等的数据块),每一条 TCP 连接只能是点对点的(一对一)。
UDP
UDP(User DataGram Protocol):用户数据报协议,对应用层传过来的数据报不合并也不拆分。所以UDP协议的报文长短是由应用层传过来的报文长短决定的。UDP数据报数据和UDP头部共同组成IP数据报的数据。
首部字段只有 8 个字节,包括源端口、目的端口、长度、检验和。12 字节的伪首部是为了计算检验和临时添加的。
- UDP是无连接的,主机间传输数据时并不需要提前建立连接。
- UDP不能保证可靠的交付数据,无法保证数据在网络中是否丢失,即使有丢失也不会感知到。
- UDP是面向报文传输的,不会对报文进行任何的处理,应用层传过来的数据会直接作为UDP数据报的数据。
- UDP没有拥塞控制,无论网络是否拥塞,都会尽力传输。
- UDP首部开销小,共4部分8个字节。
与 TCP 相比,UDP 提供一种不可靠的数据交付,也就是说,UDP 协议不保证数据是否到达目标节点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。UDP 是一种无连接的协议,传输数据之前源端和终端无需建立连接,不对数据报进行检查与修改,无须等待对方的应答,会出现分组丢失、重复、乱序等现象。但是 UDP 具有较好的实时性,工作效率较 TCP 协议高。
TCP
TCP(Transmission Control Protocol):传输控制协议,TCP协议和UDP协议一样位于IP数据报中,由TCP首部和TCP数据报的数据共同组成。它是一种面向连接的、可靠的、基于字节流的传输协议,TCP 协议位于传输层,TCP 协议是 TCP/IP 协议簇中的核心协议,它最大的特点就是提供可靠的数据交付。
TCP 的主要特点有 慢启动、拥塞控制、快速重传、可恢复。
MSS(Max Segment Size): 默认536个byte。
- TCP是面向连接的协议,通信前双方需要建立连接,同时通信结束需要结束连接。
- TCP是双向传递数据的,全双工
- TCP提供可靠的传输服务
- TCP协议提供全双工的通信,通信的两个主机双方都可以进行发送数据接收数据。
- TCP是面向字节流的协议,即应用层传输下来的一整块数据,TCP会取其中部分字节来进行传输,而剩下的字节数据会包装到另一个TCP报文中进行传输。
- TCP首部相比UDP开销大,共5部分20个字节。
- 序号:由于TCP是面向字节流进行传输的,所以序号相当于给需要传输的每个字节流进行了标号。
- 确认号:期待收到的数据报的首字节序号。如果一个TCP报文的确认号为N的话,那就说明N-1序号的报文都已经收到了。
- 数据偏移:表示真实的TCP数据内容距离首部的距离,因为第5部分TCP选项的长度是可选的。占4位,单位是4个字节,数值为0
15。所以TCP首部长度区间为 2060 个字节。 - TCP标记:共6钟字段。表示该TCP报文的类型
- 窗口:窗口表明允许对方发送的数据量。比如接收方确认号是501,窗口是1000,那么接下来接收方501~1500字节的数据都是可以接收的。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。但实际下次到达接受方的数据并不一定按照窗口计算方式来,还和网络中的拥塞程度有关。
- 校验和:和UDP中意义一致。
- 紧急指针:指定紧急数据在报文的位置。当TCP标记位为URG的时候该字段才会启用。
- 确认 ACK :当 ACK=1 时确认号字段有效,否则无效。TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 置 1。
- 同步 SYN :在连接建立时用来同步序号。当 SYN=1,ACK=0 时表示这是一个连接请求报文段。若对方同意建立连接,则响应报文中 SYN=1,ACK=1。
- 终止 FIN :用来释放一个连接,当 FIN=1 时,表示此报文段的发送方的数据已发送完毕,并要求释放连接。
超时定时器
发送方在向接收方发送TCP报文的时候,如果以下3个步骤任何一种情况发生,都会导致发送方TCP报文的超时重发。发送方每发送一段TCP报文都会有对应的超市定时器进行定时
- 发送方已发送,接收方已接收,接收方确认报文丢失
- 发送方已发送,接收方未接受,发送方报文丢失
- 发送方已发送,接收方已接收,接收方确认报文延迟到达
选择重传
TCP的选择重传在作用时,一般是整个TCP报文进行了丢失,里面有很多个字节的数据进行了丢失,那么该TCP报文的序号范围会存储在TCP选项中,对整个序号范围内的报文进行重传。
流量控制
流量控制指让发送方发送速率不要太快。流量控制是使用滑动窗口来控制的。通过控制TCP头部的窗口字段大小,来控制发送方发送速率。
如图当接收方告诉发送方自己的窗口大小为0的时候,发送方便会启动一个坚持定时器。即便后续接收方发给发送方的新的窗口大小的报文丢失,每隔一段时间,发送方都会向接收方发送一个窗口探测报文,看看是否窗口增大了。
拥塞控制
需要注意的是流量控制考虑的是点对点的数据接收的瓶颈。而拥塞控制考虑的是整个网络,是全局性的考虑。如果网络对硬件设备的性能要求大于可用资源就会导致拥塞的发生
判断拥塞的常用方法:报文超时则认为发生了拥塞
拥塞控制的两种算法:
- 慢启动算法:由小到大增加发送数据量。每收到一个确认报文,就把上次发送的报文数量乘2,是指数增长的。只要增长到慢启动阈值就会停止增长。到达这个慢启动阈值会触发下一个算法
- 拥塞避免算法:维护一个拥塞窗口的变量,这个值是大于慢启动阈值的。只要网络不拥塞(报文不超时),就试探着将拥塞窗口调大(每次+1),这是线性增长的。
三次握手
三次握手的目的:
- 同步通信双方初始序列号
- 协商TCP通信参数(MSS,窗口信息,指定校验和算法)
TCP头部标记位:
- 首先 B 处于 LISTEN(监听)状态,等待客户的连接请求。
- A 向 B 发送连接请求报文,SYN=1,ACK=0,选择一个初始的序号 x。
- B 收到连接请求报文,如果同意建立连接,则向 A 发送连接确认报文,SYN=1,ACK=1,确认号为 x+1,同时也选择一个初始的序号 y。
- A 收到 B 的连接确认报文后,还要向 B 发出确认,确认号为 y+1,序号为 x+1。
- B 收到 A 的确认后,连接建立。
问:为什么发送方要发出第三个确认报文?
防止已发送的失效报文传送到接收方,建立错误链接。 如果链接只需要两次通信即建立,那么如果发送方在初次和接收方建立连接时的TCP连接请求报文在网络中流转过长时间的话,会自动重发第二个连接报文,此时接收方先回应了第一个连接报文,那么此时两端就已经建立了连接。但后续的第一次TCP连接报文又到达了接收方会造成错误的多次连接。
四次挥手
以下描述不讨论序号和确认号,因为序号和确认号的规则比较简单。并且不讨论 ACK,因为 ACK 在连接建立之后都为 1。
- A 发送连接释放报文,FIN=1。
- B 收到之后发出确认,此时 TCP 属于半关闭状态,B 能向 A 发送数据但是 A 不能向 B 发送数据。
- 当 B 不再需要连接时,发送连接释放报文,FIN=1。
- A 收到后发出确认,进入 TIME-WAIT 状态,等待 2 MSL(最大报文存活时间)后释放连接。
- B 收到 A 的确认后释放连接。
其中如图等待计时器TIME-WAIT的等待时间为2MSL(Max Segment LifeTime最长报文段寿命):,MSL一般设置为2分钟。
四次挥手的原因
客户端发送了 FIN 连接释放报文之后,服务器收到了这个报文,就进入了 CLOSE-WAIT 状态。这个状态是为了让服务器端发送还未传送完毕的数据,传送完毕之后,服务器会发送 FIN 连接释放报文。
TIME_WAIT
客户端接收到服务器端的 FIN 报文后进入此状态,此时并不是直接进入 CLOSED 状态,还需要等待一个时间计时器设置的时间 2MSL。这么做有两个理由:
确保最后一个确认报文能够到达。如果 B 没收到 A 发送来的确认报文,那么就会重新发送连接释放请求报文,A 等待一段时间就是为了处理这种情况的发生。
等待一段时间是为了让本连接持续时间内所产生的所有报文都从网络中消失,使得下一个新的连接不会出现旧的连接请求报文。
TCP 可靠传输
TCP 使用超时重传来实现可靠传输:如果一个已经发送的报文段在超时时间内没有收到确认,那么就重传这个报文段。
TCP 滑动窗口
窗口是缓存的一部分,用来暂时存放字节流。发送方和接收方各有一个窗口,接收方通过 TCP 报文段中的窗口字段告诉发送方自己的窗口大小,发送方根据这个值和其它信息设置自己的窗口大小。
发送窗口内的字节都允许被发送,接收窗口内的字节都允许被接收。如果发送窗口左部的字节已经发送并且收到了确认,那么就将发送窗口向右滑动一定距离,直到左部第一个字节不是已发送并且已确认的状态;接收窗口的滑动类似,接收窗口左部字节已经发送确认并交付主机,就向右滑动接收窗口。
接收窗口只会对窗口内最后一个按序到达的字节进行确认,例如接收窗口已经收到的字节为 {31, 34, 35},其中 {31} 按序到达,而 {34, 35} 就不是,因此只对字节 31 进行确认。发送方得到一个字节的确认之后,就知道这个字节之前的所有字节都已经被接收。
TCP 流量控制
流量控制是为了控制发送方发送速率,保证接收方来得及接收。
接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。
接收方窗口大小改变了也会单独发送一个最新的窗口大小的报文给发送方,防止该通知报文丢失,此时发送方会启动一个坚持定时器来不断嗅探接收方的窗口大小是否产生了改变。
TCP 拥塞控制
如果网络出现拥塞,分组将会丢失,此时发送方会继续重传,从而导致网络拥塞程度更高。因此当出现拥塞时,应当控制发送方的速率。这一点和流量控制很像,但是出发点不同。流量控制是为了让接收方能来得及接收,而拥塞控制是为了降低整个网络的拥塞程度。
TCP 主要通过四个算法来进行拥塞控制:慢开始、拥塞避免、快重传、快恢复。
发送方需要维护一个叫做拥塞窗口(cwnd)的状态变量,注意拥塞窗口与发送方窗口的区别:拥塞窗口只是一个状态变量,实际决定发送方能发送多少数据的是发送方窗口。
慢开始与拥塞避免
发送的最初执行慢开始,令 cwnd = 1,发送方只能发送 1 个报文段;当收到确认后,将 cwnd 加倍,因此之后发送方能够发送的报文段数量为:2、4、8 ...
注意到慢开始每个轮次都将 cwnd 加倍,这样 cwnd 增长速度将是指数级的,从而使得发送方发送的速度增长速度过快,网络拥塞的可能性也就更高。设置一个慢开始门限 ssthresh,当 cwnd >= ssthresh 时,进入拥塞避免,每个轮次只将 cwnd 加 1。
如果出现了超时,则令 ssthresh = cwnd / 2,然后重新执行慢开始。(如图3
快重传与快恢复
在接收方,要求每次接收到报文段都应该对最后一个已收到的有序报文段进行确认。例如已经接收到 M1 和 M2,此时收到 M4,应当发送对 M2 的确认。
在发送方,如果收到三个重复确认,那么可以知道下一个报文段丢失,此时执行快重传,立即重传下一个报文段。例如收到三个 M2,则 M3 丢失,立即重传 M3。
在这种情况下,只是丢失个别报文段,而不是网络拥塞。因此执行快恢复,令 ssthresh = cwnd / 2 ,cwnd = ssthresh,注意到此时直接进入拥塞避免。
慢开始和快恢复的快慢指的是 cwnd 的设定值,而不是 cwnd 的增长速率。慢开始 cwnd 设定为 1,而快恢复 cwnd 设定为 ssthresh。一个是重新执行慢开始阶段,一个是直接进入拥塞避免状态。(如图5
应用层
在传输层及以下层网络就已经可以保证通信的建立,应用层是为了定义应用间通信的一些规则。其中应用层对接传输层,而传输层常用的两个协议为UDP和TCP。
- UDP:面向多媒体信息分发,使用场景:实时直播,语音,视频等实时信息,上一秒的信息丢失不会影响整个通信,因为下一秒信息的到达会进行填充。
- TCP:可靠信息传输,使用场景:金融交易,可靠通讯,MQ等,不容出现错误的地方。
DNS
DNS(Domain Name System):域名系统,是一个分布式数据库,提供了主机名和 IP 地址之间相互转换的服务。这里的分布式数据库是指,每个站点只保留它自己的那部分数据。域名具有层次结构,从上到下依次为:根域名、顶级域名、二级域名。是使用UDP协议的应用层协议
域名具有层次结构,从上到下依次为:根域名、顶级域名、二级域名。
DNS 可以使用 UDP 或者 TCP 进行传输,使用的端口号都为 53。大多数情况下 DNS 使用 UDP 进行传输,这就要求域名解析器和域名服务器都必须自己处理超时和重传从而保证可靠性。在两种情况下会使用 TCP 进行传输。
- 如果返回的响应超过的 512 字节(UDP 最大只支持 512 字节的数据)。
- 区域传送(区域传送是主域名服务器向辅助域名服务器传送变化的那部分数据)。
DHCP
DHCP(Dynamic Host Configuration Protocol):动态主机配置协议,是一个局域网协议。 是使用UDP协议的应用层协议。提供了即插即用的连网方式,用户不再需要手动配置 IP 地址等信息。
DHCP 工作过程如下:
- 客户端发送 Discover 报文,该报文的目的地址为 255.255.255.255:67,源地址为 0.0.0.0:68,被放入 UDP 中,该报文被广播到同一个子网的所有主机上。如果客户端和 DHCP 服务器不在同一个子网,就需要使用中继代理。
- DHCP 服务器收到 Discover 报文之后,发送 Offer 报文给客户端,该报文包含了客户端所需要的信息。因为客户端可能收到多个 DHCP 服务器提供的信息,因此客户端需要进行选择。
- 如果客户端选择了某个 DHCP 服务器提供的信息,那么就发送 Request 报文给该 DHCP 服务器。
- DHCP 服务器发送 Ack 报文,表示客户端此时可以使用提供给它的信息。
常用端口
HTTP
HTTP(Hyper Text Transfer Protocol):超文本传输协议,一种无状态的,以请求应答方式运行的协议,它使用可扩展的语义和自描述消息格式,是可靠的数据传输协议。
HTTP请求方法
- GET:获取指定的服务端资源
- POST:提交数据到服务端
- DELETE:删除指定的服务端资源,不带验证机制。
- UPDATE:更新指定的服务端资源
- PUT:上传文件,由于自身不带验证机制,任何人都可以上传文件,因此存在安全性问题,一般不使用该方法。
- OPTIONS:查询支持的方法,查询指定的 URL 能够支持的方法。会返回 Allow: GET, POST, HEAD, OPTIONS 这样的内容。
- PATCH:对资源进行部分修改,PUT 也可以用于修改资源,但是只能完全替代原始资源,PATCH 允许部分修改。
- HEAD:获取报文首部。和 GET 方法类似,但是不返回报文实体主体部分。主要用于确认 URL 的有效性以及资源更新的日期时间等。
- TRACE:追踪路径,服务器会将通信路径返回给客户端。发送请求时,在 Max-Forwards 首部字段中填入数值,每经过一个服务器就会减 1,当数值为 0 时就停止传输。通常不会使用 TRACE,并且它容易受到 XST 攻击(Cross-Site Tracing,跨站追踪)。
- CONNECT:要求在与代理服务器通信时建立隧道,使用 SSL(Secure Sockets Layer,安全套接层)和 TLS(Transport Layer Security,传输层安全)协议把通信内容加密后经网络隧道传输。
HTTP 状态码
1XX 信息
- 100 Continue :表明到目前为止都很正常,客户端可以继续发送请求或者忽略这个响应。
2XX 成功
- 200 OK
- 204 No Content :请求已经成功处理,但是返回的响应报文不包含实体的主体部分。一般在只需要从客户端往服务器发送信息,而不需要返回数据时使用。
- 206 Partial Content :表示客户端进行了范围请求,响应报文包含由 Content-Range 指定范围的实体内容。
3XX 重定向
- 301 Moved Permanently :永久性重定向
- 302 Found :临时性重定向
- 303 See Other :和 302 有着相同的功能,但是 303 明确要求客户端应该采用 GET 方法获取资源。
注:虽然 HTTP 协议规定 301、302 状态下重定向时不允许把 POST 方法改成 GET 方法,但是大多数浏览器都会在 301、302 和 303 状态下的重定向把 POST 方法改成 GET 方法。
- 304 Not Modified :如果请求报文首部包含一些条件,例如:If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,如果不满足条件,则服务器会返回 304 状态码。
- 307 Temporary Redirect :临时重定向,与 302 的含义类似,但是 307 要求浏览器不会把重定向请求的 POST 方法改成 GET 方法。
4XX 客户端错误
- 400 Bad Request :请求报文中存在语法错误。
- 401 Unauthorized :该状态码表示发送的请求需要有认证信息(BASIC 认证、DIGEST 认证)。如果之前已进行过一次请求,则表示用户认证失败。
- 403 Forbidden :请求被拒绝。
- 404 Not Found
5XX 服务器错误
- 500 Internal Server Error :服务器正在执行请求时发生错误。
- 503 Service Unavailable :服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。
HTTP 首部
连接管理
短连接与长连接
当浏览器访问一个包含多张图片的 HTML 页面时,除了请求访问的 HTML 页面资源,还会请求图片资源。如果每进行一次 HTTP 通信就要新建一个 TCP 连接,那么开销会很大。
长连接只需要建立一次 TCP 连接就能进行多次 HTTP 通信。
- 从 HTTP/1.1 开始默认是长连接的,如果要断开连接,需要由客户端或者服务器端提出断开,使用 Connection : close;
- 在 HTTP/1.1 之前默认是短连接的,如果需要使用长连接,则使用 Connection : Keep-Alive。
流水线
默认情况下,HTTP 请求是按顺序发出的,下一个请求只有在当前请求收到响应之后才会被发出。由于受到网络延迟和带宽的限制,在下一个请求被发送到服务器之前,可能需要等待很长时间。
流水线是在同一条长连接上连续发出请求,而不用等待响应返回,这样可以减少延迟。
Cookie
HTTP 协议是无状态的,主要是为了让 HTTP 协议尽可能简单,使得它能够处理大量事务。HTTP/1.1 引入 Cookie 来保存状态信息。
Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器之后向同一服务器再次发起请求时被携带上,用于告知服务端两个请求是否来自同一浏览器。由于之后每次请求都会需要携带 Cookie 数据,因此会带来额外的性能开销(尤其是在移动环境下)。
Cookie 曾一度用于客户端数据的存储,因为当时并没有其它合适的存储办法而作为唯一的存储手段,但现在随着现代浏览器开始支持各种各样的存储方式,Cookie 渐渐被淘汰。新的浏览器 API 已经允许开发者直接将数据存储到本地,如使用 Web storage API(本地存储和会话存储)或 IndexedDB。
1. 用途
- 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
- 个性化设置(如用户自定义设置、主题等)
- 浏览器行为跟踪(如跟踪分析用户行为等)
2. 作用域
Domain 标识指定了哪些主机可以接受 Cookie。如果不指定,默认为当前文档的主机(不包含子域名)。如果指定了 Domain,则一般包含子域名。例如,如果设置 Domain=mozilla.org,则 Cookie 也包含在子域名中(如 developer.mozilla.org)。
Path 标识指定了主机下的哪些路径可以接受 Cookie(该 URL 路径必须存在于请求 URL 中)。以字符 %x2F ("/") 作为路径分隔符,子路径也会被匹配。例如,设置 Path=/docs,则以下地址都会匹配:
- /docs
- /docs/Web/
- /docs/Web/HTTP
3. HttpOnly
标记为 HttpOnly 的 Cookie 不能被 JavaScript 脚本调用。跨站脚本攻击 (XSS) 常常使用 JavaScript 的 document.cookie API 窃取用户的 Cookie 信息,因此使用 HttpOnly 标记可以在一定程度上避免 XSS 攻击。
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
Secure
标记为 Secure 的 Cookie 只能通过被 HTTPS 协议加密过的请求发送给服务端。但即便设置了 Secure 标记,敏感信息也不应该通过 Cookie 传输,因为 Cookie 有其固有的不安全性,Secure 标记也无法提供确实的安全保障。
Session
除了可以将用户信息通过 Cookie 存储在用户浏览器中,也可以利用 Session 存储在服务器端,存储在服务器端的信息更加安全。
Session 可以存储在服务器上的文件、数据库或者内存中。也可以将 Session 存储在 Redis 这种内存型数据库中,效率会更高。
- 用户进行登录时,用户提交包含用户名和密码的表单,放入 HTTP 请求报文中;
- 服务器验证该用户名和密码,如果正确则把用户信息存储到 Redis 中,它在 Redis 中的 Key 称为 Session ID;
- 服务器返回的响应报文的 Set-Cookie 首部字段包含了这个 Session ID,客户端收到响应报文之后将该 Cookie 值存入浏览器中;
- 客户端之后对同一个服务器进行请求时会包含该 Cookie 值,服务器收到之后提取出 Session ID,从 Redis 中取出用户信息,继续之前的业务操作。
应该注意 Session ID 的安全性问题,不能让它被恶意攻击者轻易获取,那么就不能产生一个容易被猜到的 Session ID 值。此外,还需要经常重新生成 Session ID。在对安全性要求极高的场景下,例如转账等操作,除了使用 Session 管理用户状态之外,还需要对用户进行重新验证,比如重新输入密码,或者使用短信验证码等方式。
浏览器禁用 Cookie
此时无法使用 Cookie 来保存用户信息,只能使用 Session。除此之外,不能再将 Session ID 存放到 Cookie 中,而是使用 URL 重写技术,将 Session ID 作为 URL 的参数进行传递。
Cookie 与 Session 选择
- Cookie 只能存储 ASCII 码字符串,而 Session 则可以存储任何类型的数据,因此在考虑数据复杂性时首选 Session;
- Cookie 存储在浏览器中,容易被恶意查看。如果非要将一些隐私数据存在 Cookie 中,可以将 Cookie 值进行加密,然后在服务器进行解密;
- 对于大型网站,如果用户所有的信息都存储在 Session 中,那么开销是非常大的,因此不建议将所有的用户信息都存储到 Session 中。
HTTPS
HTTP 有以下安全性问题:
- 使用明文进行通信,内容可能会被窃听;
- 不验证通信方的身份,通信方的身份有可能遭遇伪装;
- 无法证明报文的完整性,报文有可能遭篡改。
HTTPS 并不是新协议,而是让 HTTP 先和 SSL(Secure Sockets Layer)通信,再由 SSL 和 TCP 通信,也就是说 HTTPS 使用了隧道进行通信。通过使用 SSL,HTTPS 具有了加密(防窃听)、认证(防伪装)和完整性保护(防篡改)的功能。
SSL(应用层)安全参数握手:
- 客户端首先生成一个随机数1,支持的协议版本,支持的加密算法(对称的非对称的)告诉服务端(明文不加密状态)
- 服务端生成一个随机数2,提供自己的数字证书,确定一个加密算法(明文不加密状态)
- 客户端确认一下证书是否有效,生成随机数3,并且根据服务器的公钥加密随机数3,并且发送给服务端
- 客户端加密后的数据发送给服务端,服务端根据自己才有的私钥进行解密拿到随机数3,此时两端都有随机数1,2,3。然后双方根据随机数1,2,3即最开始确定的加密算法分别生成对称密钥。此时就可以进行加密传输了。
- 摘要算法:对任意长度的数据生成一个固定长度的串
- 对称加密:通过简单的异或运算就能将数据进行还原
- 非对称加密:需要一些数学运算公式,要比对称加密复杂
数字证书的组成:CA信息,公钥用户信息,公钥,权威机构的签名,有效期
签名步骤:hash(你用于申请证书所提交的明文信息)=信息摘要,CA再使用私钥对信息摘要进行加密,密文就是证书的数字签名。
浏览器拿到公钥数字证书之后,会先对数字证书进行验证,比如CA数字证书颁发机构的判断,这个证书链的根证书地址,一般计算机本地是会有保存的,可以验证该证书链的有效性,然后再把证书中的明文信息取出,及证书中指出的hash算法得到一个摘要值,然后再拿CA所给的公钥对证书中的摘要进行解密,然后两个摘要值进行比对。
加密
对称密钥加密,加密和解密使用同一密钥。
- 优点:运算速度快;
- 缺点:无法安全地将密钥传输给通信方。
非对称密钥加密,又称公开密钥加密,加密和解密使用不同的密钥。公开密钥所有人都可以获得,通信发送方获得接收方的公开密钥之后,就可以使用公开密钥进行加密,接收方收到通信内容后使用私有密钥解密。
非对称密钥除了用来加密,还可以用来进行签名。因为私有密钥无法被其他人获取,因此通信发送方使用其私有密钥进行签名,通信接收方使用发送方的公开密钥对签名进行解密,就能判断这个签名是否正确。
- 优点:可以更安全地将公开密钥传输给通信发送方;
- 缺点:运算速度慢。
HTTPS 采用的加密方式
虽然对称加密的传输效率更高,但是无法安全地将密钥传输给通信方。而非对称密钥加密方式可以保证传输的安全性,因此我们可以利用非对称密钥加密方式将密钥传输给通信方。HTTPS 采用混合的加密机制,正是利用了上面提到的方案:
- 使用非对称密钥加密方式,传输对称密钥加密方式所需要的密钥,从而保证安全性;
- 获取到密钥后,再使用对称密钥加密方式进行通信,从而保证效率。
认证
通过使用 证书 来对通信方进行认证。
数字证书认证机构(CA,Certificate Authority)是客户端与服务器双方都可信赖的第三方机构。
服务器的运营人员向 CA 提出公开密钥的申请,CA 在判明提出申请者的身份之后,会对已申请的公开密钥做数字签名,然后分配这个已签名的公开密钥,并将该公开密钥放入公开密钥证书后绑定在一起。
进行 HTTPS 通信时,服务器会把证书发送给客户端。客户端取得其中的公开密钥之后,先使用数字签名进行验证,如果验证通过,就可以开始通信了。
完整性保护
SSL 提供报文摘要功能来进行完整性保护。
HTTPS 的报文摘要功能之所以安全,是因为它结合了加密和认证这两个操作。试想一下,加密之后的报文,遭到篡改之后,也很难重新计算报文摘要,因为无法轻易获取明文。
HTTPS的缺点
- 因为需要进行加密解密等过程,因此速度会更慢;
- 需要支付证书授权的高额费用。
HTTP 2.0
HTTP/1.x 缺陷,HTTP/1.x 实现简单是以牺牲性能为代价的:
- 客户端需要使用多个连接才能实现并发和缩短延迟;
- 不会压缩请求和响应首部,从而导致不必要的网络流量;
- 不支持有效的资源优先级,致使底层 TCP 连接的利用率低下。
二进制分帧层
HTTP/2.0 将报文分成 HEADERS 帧和 DATA 帧,它们都是二进制格式的。
在通信过程中,只会有一个 TCP 连接存在,它承载了任意数量的双向数据流(Stream)。
- 一个数据流(Stream)都有一个唯一标识符和可选的优先级信息,用于承载双向信息。
- 消息(Message)是与逻辑请求或响应对应的完整的一系列帧。
- 帧(Frame)是最小的通信单位,来自不同数据流的帧可以交错发送,然后再根据每个帧头的数据流标识符重新组装。
服务端推送
HTTP/2.0 在客户端请求一个资源时,会把相关的资源一起发送给客户端,客户端就不需要再次发起请求了。例如客户端请求 page.html 页面,服务端就把 script.js 和 style.css 等与之相关的资源一起发给客户端。
首部压缩
HTTP/1.1 的首部带有大量信息,而且每次都要重复发送。
HTTP/2.0 要求客户端和服务器同时维护和更新一个包含之前见过的首部字段表,从而避免了重复传输。
不仅如此,HTTP/2.0 也使用 Huffman 编码对首部字段进行压缩。
HTTP/1.1 新特性
- 默认是长连接
- 支持流水线
- 支持同时打开多个 TCP 连接
- 支持虚拟主机
- 新增状态码 100
- 支持分块传输编码
- 新增缓存处理指令 max-age
GET 和 POST 比较
GET 用于获取资源,而 POST 用于传输实体主体。GET 的参数是以查询字符串出现在 URL 中,而 POST 的参数存储在实体主体中。
因为 URL 只支持 ASCII 码,因此 GET 的参数中如果存在中文等字符就需要先进行编码。POST 参数支持标准字符集。
安全
安全的 HTTP 方法不会改变服务器状态,也就是说它只是可读的。
GET 方法是安全的,而 POST 却不是,因为 POST 的目的是传送实体主体内容,这个内容可能是用户上传的表单数据,上传成功之后,服务器可能把这个数据存储到数据库中,因此状态也就发生了改变。
- 安全的方法除了 GET 之外还有:HEAD、OPTIONS。
- 不安全的方法除了 POST 之外还有 PUT、DELETE。
幂等性
幂等的 HTTP 方法,同样的请求被执行一次与连续执行多次的效果是一样的,服务器的状态也是一样的。
所有的安全方法也都是幂等的。
在正确实现的条件下,GET,HEAD,PUT 和 DELETE 等方法都是幂等的,而 POST 方法不是。
Web请求页面过程
1. DHCP 配置主机信息
- 假设主机最开始没有 IP 地址以及其它信息,那么就需要先使用 DHCP 来获取。
- 主机生成一个 DHCP 请求报文,并将这个报文放入具有目的端口 67 和源端口 68 的 UDP 报文段中。
- 该报文段则被放入在一个具有广播 IP 目的地址(255.255.255.255) 和源 IP 地址(0.0.0.0)的 IP 数据报中。
- 该数据报则被放置在 MAC 帧中,该帧具有目的地址 FF:FF:FF:FF:FF:FF,将广播到与交换机连接的所有设备。
- 连接在交换机的 DHCP 服务器收到广播帧之后,不断地向上分解得到 IP 数据报、UDP 报文段、DHCP 请求报文,之后生成 DHCP ACK 报文,该报文包含以下信息:IP 地址、DNS 服务器的 IP 地址、默认网关路由器的 IP 地址和子网掩码。该报文被放入 UDP 报文段中,UDP 报文段有被放入 IP 数据报中,最后放入 MAC 帧中。
- 该帧的目的地址是请求主机的 MAC 地址,因为交换机具有自学习能力,之前主机发送了广播帧之后就记录了 MAC 地址到其转发接口的交换表项,因此现在交换机就可以直接知道应该向哪个接口发送该帧。
- 主机收到该帧后,不断分解得到 DHCP 报文。之后就配置它的 IP 地址、子网掩码和 DNS 服务器的 IP 地址,并在其 IP 转发表中安装默认网关。
2. ARP 解析 MAC 地址
- 主机通过浏览器生成一个 TCP 套接字,套接字向 HTTP 服务器发送 HTTP 请求。为了生成该套接字,主机需要知道网站的域名对应的 IP 地址。
- 主机生成一个 DNS 查询报文,该报文具有 53 号端口,因为 DNS 服务器的端口号是 53。
- 该 DNS 查询报文被放入目的地址为 DNS 服务器 IP 地址的 IP 数据报中。
- 该 IP 数据报被放入一个以太网帧中,该帧将发送到网关路由器。
- DHCP 过程只知道网关路由器的 IP 地址,为了获取网关路由器的 MAC 地址,需要使用 ARP 协议。
- 主机生成一个包含目的地址为网关路由器 IP 地址的 ARP 查询报文,将该 ARP 查询报文放入一个具有广播目的地址(FF:FF:FF:FF:FF:FF)的以太网帧中,并向交换机发送该以太网帧,交换机将该帧转发给所有的连接设备,包括网关路由器。
- 网关路由器接收到该帧后,不断向上分解得到 ARP 报文,发现其中的 IP 地址与其接口的 IP 地址匹配,因此就发送一个 ARP 回答报文,包含了它的 MAC 地址,发回给主机。
3. DNS 解析域名
- 知道了网关路由器的 MAC 地址之后,就可以继续 DNS 的解析过程了。
- 网关路由器接收到包含 DNS 查询报文的以太网帧后,抽取出 IP 数据报,并根据转发表决定该 IP 数据报应该转发的路由器。
- 因为路由器具有内部网关协议(RIP、OSPF)和外部网关协议(BGP)这两种路由选择协议,因此路由表中已经配置了网关路由器到达 DNS 服务器的路由表项。
- 到达 DNS 服务器之后,DNS 服务器抽取出 DNS 查询报文,并在 DNS 数据库中查找待解析的域名。
- 找到 DNS 记录之后,发送 DNS 回答报文,将该回答报文放入 UDP 报文段中,然后放入 IP 数据报中,通过路由器反向转发回网关路由器,并经过以太网交换机到达主机。
4. HTTP 请求页面
- 有了 HTTP 服务器的 IP 地址之后,主机就能够生成 TCP 套接字,该套接字将用于向 Web 服务器发送 HTTP GET 报文。
- 在生成 TCP 套接字之前,必须先与 HTTP 服务器进行三次握手来建立连接。生成一个具有目的端口 80 的 TCP SYN 报文段,并向 HTTP 服务器发送该报文段。
- HTTP 服务器收到该报文段之后,生成 TCP SYN ACK 报文段,发回给主机。
- 连接建立之后,浏览器生成 HTTP GET 报文,并交付给 HTTP 服务器。
- HTTP 服务器从 TCP 套接字读取 HTTP GET 报文,生成一个 HTTP 响应报文,将 Web 页面内容放入报文主体中,发回给主机。
- 浏览器收到 HTTP 响应报文后,抽取出 Web 页面内容,之后进行渲染,显示 Web 页面。
IO 模型
对于一个套接字上数据的输入流程:等待数据网卡接到光电等信号,调制解调器转成数字信号,进入到缓冲队列,满了之后写入内存,并向CPU发送一个中断信号,操作系统调用对应的中断处理函数,将内存中的数据写入到socket的接收缓冲区。接收缓冲区写好然后应用程序读取
Unix 有五种 I/O 模型:
- 阻塞式 I/O
- 非阻塞式 I/O
- I/O 复用(select 和 poll)
- 信号驱动式 I/O(SIGIO)
- 异步 I/O(AIO)
同步异步,阻塞非阻塞:
- 同步请求,A调用B,B的处理是同步的,在处理完之前他不会通知A,只有处理完之后才会明确的通知A。
- 异步请求,A调用B,B的处理是异步的,B在接到请求后先告诉A我已经接到请求了,然后异步去处理,处理完之后通过回调等方式再通知A。
同步和异步最大的区别就是被调用方的执行方式和返回时机。同步指的是被调用方做完事情之后再返回,异步指的是被调用方先返回,然后再做事情,做完之后再想办法通知调用方。
- 阻塞请求,A调用B,A一直等着B的返回,别的事情什么也不干。
- 非阻塞请求,A调用B,A不用一直等着B的返回,先去忙别的事情了。
阻塞和非阻塞最大的区别就是在被调用方返回结果之前的这段时间内,调用方是否一直等待。
阻塞、非阻塞和同步、异步的区别
两者针对的对象是不一样的。阻塞、非阻塞说的是调用者,同步、异步说的是被调用者。
同步场景,阻塞和非阻塞情况:
- 阻塞:传统的水壶烧水。在水烧开之前我们一直坐在水壶前面,等着水开。这就是阻塞的。
- 非阻塞:我们是用传统的水壶烧水。在水烧开之前我们先去客厅看电视了,但是水壶不会主动通知我们,需要我们时不时的去厨房看一下水有没有烧开。这就是非阻塞的。
异步场景,阻塞和非阻塞情况:
- 阻塞:带有提醒功能的水壶烧水。在水烧发出提醒之前我们一直做在水壶前面,等着水开。这就是阻塞的。
- 非阻塞:带有提醒功能的水壶烧水。在水烧发出提醒之前我们先去客厅看电视了,等水壶发出声音提醒我们。这就是非阻塞的。
Java中提供的3钟IO模型:同步阻塞IO(BIO)、同步非阻塞IO(NIO)、异步非阻塞IO(AIO)。也是基于操作系统的IO模型所做的封装
- BIO (Blocking I/O):数据的读取写入必须阻塞在一个线程内等待其完成。在等待时间线程什么事都不做
- NIO (New I/O):同时支持阻塞与非阻塞模式,但主要是使用同步非阻塞IO。NIO的做法是叫一个线程不断的轮询每个水壶的状态,看看是否有水壶的状态发生了改变,从而进行下一步的操作。
- AIO ( Asynchronous I/O):为每个水壶上面装了一个开关,水烧开之后,水壶会自动通知我水烧开了。
阻塞使IO:应用进程被阻塞,直到数据从内核缓冲区复制到应用进程缓冲区中才返回。当前线程并不会做任何事情,但不影响其他线程及应用程序的执行,这种模型的 CPU 利用率会比较高。
非阻塞式IO:应用进程执行系统调用之后,内核返回一个错误码。应用进程可以继续执行,但是需要不断的执行系统调用来获知 I/O 是否完成,这种方式称为轮询(polling)。由于 CPU 要处理更多的系统调用,因此这种模型的 CPU 利用率比较低。
IO多路复用:一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;没有文件句柄就绪时会阻塞应用程序,交出cpu。多路是指网络连接,复用指的是同一个线程。
信号驱动IO:等待数据阶段应用进程是非阻塞的。内核在数据到达时向应用进程发送 SIGIO 信号,应用进程收到之后在信号处理程序中将数据从内核复制到应用进程中。相比于同步非阻塞式 I/O 的轮询方式,信号驱动 I/O 的 CPU 利用率更高。
异步IO:应用进程可以继续执行,不会被阻塞,内核会在所有操作完成之后向应用进程发送信号。
异步 I/O 与信号驱动 I/O 的区别在于,异步 I/O 的信号是通知应用进程 I/O 完成,而信号驱动 I/O 的信号是通知应用进程可以开始 I/O。
IO模型的比较
- 同步 I/O:将数据从内核缓冲区复制到应用进程缓冲区的阶段(第二阶段),应用进程会阻塞。
- 异步 I/O:第二阶段应用进程不会阻塞。
非阻塞式 I/O 、信号驱动 I/O 和异步 I/O 在第一阶段不会阻塞。
IO复用
使用 select 或者 poll 等待数据,并且可以等待多个套接字中的任何一个变为可读。这一过程会被阻塞,当某一个套接字可读时返回,之后再使用 recvfrom 把数据从内核复制到进程中。它可以让单个进程具有处理多个 I/O 事件的能力。
如果一个 Web 服务器没有 I/O 复用,那么每一个 Socket 连接都需要创建一个线程去处理。如果同时有几万个连接,那么就需要创建相同数量的线程。相比于多进程和多线程技术,I/O 复用不需要进程线程创建和切换的开销,系统开销更小。
select/poll/epoll 都是 I/O 多路复用的具体实现,select 出现的最早,之后是 poll,再是 epoll。
select/poll/epoll 比较
功能: select 和 poll 的功能基本相同,不过在一些实现细节上有所不同。
- select 会修改描述符,而 poll 不会;
- select 的描述符类型使用数组实现,FD_SETSIZE 大小默认为 1024,因此默认只能监听少于 1024 个描述符。如果要监听更多描述符的话,需要修改 FD_SETSIZE 之后重新编译;而 poll 没有描述符数量的限制;
- poll 提供了更多的事件类型,并且对描述符的重复利用上比 select 高。
- 如果一个线程对某个描述符调用了 select 或者 poll,另一个线程关闭了该描述符,会导致调用结果不确定。
速度
- select 和 poll 速度都比较慢,每次调用都需要将全部描述符从应用进程缓冲区复制到内核缓冲区。
可移植性
- 几乎所有的系统都支持 select,但是只有比较新的系统支持 poll。
epoll 的描述符事件有两种触发模式:LT(level trigger)和 ET(edge trigger)。
- LT:epoll_wait() 检测到描述符事件到达时,将此事件通知进程,进程可以不立即处理该事件,下次调用 epoll_wait() 会再次通知进程。是默认的一种模式,并且同时支持 Blocking 和 No-Blocking。
- ET:通知之后进程必须立即处理事件,下次再调用 epoll_wait() 时不会再得到事件到达的通知。很大程度上减少了 epoll 事件被重复触发的次数,因此效率要比 LT 模式高。只支持 No-Blocking,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。
应用场景
- select:select 的 timeout 参数精度为微秒,而 poll 和 epoll 为毫秒,因此 select 更加适用于实时性要求比较高的场景,比如核反应堆的控制。select 可移植性更好,几乎被所有主流平台所支持。
- poll 没有最大描述符数量的限制,如果平台支持并且对实时性要求不高,应该使用 poll 而不是 select。
- epoll:只需要运行在 Linux 平台上,有大量的描述符需要同时轮询,并且这些连接最好是长连接。需要同时监控小于 1000 个描述符,就没有必要使用 epoll,因为这个应用场景下并不能体现 epoll 的优势。需要监控的描述符状态变化多,而且都是非常短暂的,也没有必要使用 epoll。因为 epoll 中的所有描述符都存储在内核中,造成每次需要对描述符的状态改变都需要通过 epoll_ctl() 进行系统调用,频繁系统调用降低效率。并且 epoll 的描述符存储在内核,不容易调试。