目录
1.网络概念
1.1局域网与广域网
1.2路由器与交换机
1.3IP与端口号
1.4.协议
1.5五元组
1.6协议分层
1.7.OSI七层模型
1.8TCP/IP 五层 (四层) 协议模型
2.网络数据通信基本流程
2.TCP/UDP
2.1TCP
2.2.UDP
3.传输层协议
3.1.UDP协议
3.2.TCP协议
3.2.1确认应答
3.2.2.超时重传
3.2.3.连接管理
3.2.3.1.建立连接
3.2.3.2.断开连接
3.2.4.滑动窗口
3.2.5.流量控制
3.2.6.拥塞控制
3.2.7.延时应答
3.2.8.捎带应答
3.2.9.面向字节流
3.2.10.异常情况处理
4.网络层
4.1IP协议
4.2.地址管理
4.2.1.NAT机制
4.2.2.网段划分
4.3.路由选择
5.数据链路层
5.1以太网
5.2ARP协议
1.网络概念
1.1局域网与广域网
局域网:
Local 即标识了局域⽹是本地,由路由器局部组建的⼀种私有⽹络。
组件局域网的方法:
1.网线直连
2.集线器组建
3.路由器组建
3.交换机组建
广域网:
通过路由器将多个局域网进行连接,就成了广域网
1.2路由器与交换机
路由器:
路由器用于将多台设备进行连接,组成局域网
交换机:
路由器上面的接口是有限的,想要扩大局域网范围就需要交换机增加接口
1.3IP与端口号
IP地址:
IP地址⽤于定位主 机的⽹络地址。
端口号:
端口号用于定位主机中的进程
1.4.协议
网络协议,通信双方对于发送/接受 数据格式的约定 我的数据怎么发,你收到就得怎么解析
就相当于我们中国人与中国人之间进行对话,普通话就是我们之间的协议
网络通信中 协议非常关键 多个主机设备 多个主机都能认同并遵守同一套协议,此时通信才是有意义的
1.5五元组
在TCP/IP协议中,⽤五元组来标识⼀个⽹络通信:
1.
源IP:标识源主机
2.
源端⼝号:标识源主机中该次通信发送数据的进程
3.
⽬的IP:标识⽬的主机
4.
⽬的端⼝号:标识⽬的主机中该次通信接收数据的进程
5.
协议号:标识发送进程和接收进程双⽅约定的数据格式
1.6协议分层
网络通信, 非常复杂 如果我们设计一个协议,完成网络通信中方方面面的问题,
势必会使这个协议非常复杂,非常庞大
所以需要
拆分 把一个大的协议,拆成若干个小的,功能单一的协议了 拆完之后,小的协议,太多了
所以需要
把这些小的协议归类
分层
只有相邻两层协议之间可以进行交互上层协议可以调用下层协议,
下层协议可以给上层提供服务(协议之间的交互,不能夸层进行)
分层的作用:
封装 1.上层协议,不需要了解下层协议的细节~打电话的人,会说话就行了,不需要理解电话机的工作原理 解耦 2.分层之后,灵活的替换其中的某一层,对于整体的工作过程影响很小
1.7.OSI七层模型
这一套分层的体系,只是存在于教科书上 由于体系太复杂了,实际真实的网络分层方式更简化的
理想状态下的分层:
1.8TCP/IP 五层 (四层) 协议模型
为啥又叫四层呢
把数据链路层 和 物理层 看做一个整体了 硬件设备直接相关
物理层:
物理层规定了网络通信中的一些硬件设施符合的要求
网线 wif 光纤 (光猫)
物理层的协议,就是约定这些硬件设施要符合的要求是怎样的,
数据链路层:
数据链路层 完成两个相邻的设备之间如何进行通信的,
通过网线,把电脑连到路由器/交换机,上电脑和路由器/交换机 相邻节点/设备
网络层:
网络层 则是两个任意设备之间如何进行通信这俩设备之间,可能隔着很多的交换机和路由器 考虑通讯的中间过程是怎样的
传输层:
传输层 也是两个任意设备之间的通信,不考虑中间过程,只考虑,起点和终点
应用层:
应用层
就是考虑拿到数据后该怎么做
举个例子:
淘宝, 我要买一台电脑
我则关心电脑买到之后,我要如何使用 ->应用层
卖家只要关心,收件人信息->传输层
物流公司则关心,包裹怎样路径传输的->网络层
快递小哥/货车司机, 考虑的是相邻节点 ->数据链路层
主机:应用层工作过程涉及到 从物理层
通过应用程序满足网络通信的需求
路由器、网络层工作过程涉及到 从物理层
组建局域网用于网络数据包的转发
交换机:工作过程涉及到 从物理层涉及到数据链路层
对路由器接口的扩展,不需要考虑组网问题
2.网络数据通信基本流程
举个例子:
假设我用 QQ给对方发送了一句hello
出自己主机,进行分装部分:
1.应用程序 获取到用戶输入, 构造一个应用层的数据包这个应用层数据包 就会遵守 应用层协议.
应用层协议:往往是开发这个程序的程序员自己定义的
应用层的数据包:往往是“结构化数据”
网络传输的数据,本质上都是"字符串"或者"二进制的 bit 流”
发送数据的时候,把结构化数据 =>字符串 / 二进制 bit 流 序列化
接受数据的时候,字符串/二进制比特流=>结构化数据 反序列化
此处假设这样的协议格式:
发送者的qq号,接受者的q9,消息的时间,消息正文
2.应用程序调用,传输层 提供的接口(API),把数据交给传输层传输层拿到数据之后,构造出"传输层数据包
传输层的协议,主要是两个
TCP UDP
这里调用的是TCP
TCP 数据包 = TCP 报头(header)+ TCP 载荷(payload)
TCP报头:TCP 功能相关的属性 源端口/目的端口 TCP载荷:应用层数据包
3.传输层构造好数据之后,继续调用网络层的 api, 把传输层的数据包交给网络层网络层继续进行处理.
网络层最主要的协议:IP 协议. IP 协议继续对上述数据包进行加工 =>拼上 IP 报头
IP 数据包 = IP 报头 +IP 载荷 (整个传输层的数据包)
IP报头:源IP/目的IP
IP 载荷: (整个传输层的数据包)
IP 协议本身不关心载荷内容是啥. TCP 协议也不关心载荷内容是啥.
协议类型,其实不只是一份数据,有很多层
其中 IP 报头里就会记录当前的传输层使用的是哪个协议 传输层报头中,也会记录应用层使用哪个协议(准确的说是数据交给哪个应用程序) 数据链路层报头中也会记录 网络层使用哪个协议
4.网络层继续调用 数据链路层的 api, 把 IP 数据包交给数据链路层数据链路层中,
核心协议,
"以太网”以太网这个协议,
也会在网络层数据包的基础上进一步加工
以太网数据帧 = 帧头 + 载荷 + 帧尾
网络数据包/帧/报/段
段(segment) TCP 用段
报 (Datagram) UDP用的 包 (packet) IP 用的 帧 (frame) 数据链路层 以太网数据帧
5.以太网继续这样的数据交给硬件设备 (网卡)网卡会把上述二进制数据,最终以 光信号/电信号/电磁波信号 传播出去了
中间部分传输:
假设从主机1 传输到到主机 2
传输的中间过程中,也是涉及到封装分用的.
可能会遇见下面这几种情况
交换机:
1.交换机,只需要封装分用到 数据链路层即可. 主机的数据 =>交换机, 交换机收到之后,
物理层解析,数据链路层解析 (没有网络层了),
重新构造出新的以太网数据帧,发给下一个设备数据链路层中,
得到的以太网数据帧的帧头,信息就足以支持交换机进行下一步工作
交换机是工作在数据链路层 (二层转发)
路由器:
2.主机的数据 =>路由器,路由器收到之后,
物理层,数据链路层,网络层解析 (没有传输层),重新构造出新的网络数据包,
造出以太网数据帧,构造出二进制数据,进行转发
路由器是工作在网络层 (三层转发)
防火墙 / 网关:
也有设备工作在传输层 这样的设备.比如 防火墙/网关封装分用到传输层
到达对方主机,进行解析部分:
1.数据到达接收方的网卡. 光电信号,网卡把光电信号还原成二进制 0101 把二进制数据交给上层数据链路层
物理层数据信号是咋来的,和数据链路层是直接相关的
网线/光纤来的数据,数据链路层 =>以太网
wifi 来的数据,数据链路层 =>802.11 协议
2.数据链路层拿到数据后以以太网协议将报头和尾巴给取出,再将剩下的载荷传输给网络层
以太网的帧头中有专门的数据来存储网络层使用的是哪个协议
3.网络层拿到数据后IP协议将报头给取出,再将剩下的载荷传输给传输层
IP报头中中有专门的数据来存储传输层使用的是哪个协议
4.传输层拿到数据后TCP协议将报头给取出,再将剩下的载荷传输给应用层
传输层报头中,通过目的端口号,告知我们数据交给哪个应用程序
5.QQ 应用程序, 解析应用层数据,拿到关键信息,展示到界面,给出提示
不同的应用程序,自然使用不同的应用层协议来解析
2.TCP/UDP
操作系统提供的一组 api =>socket api(传输层给应用层提供的)
socket的概念:
计算机中的"文件"通常是一个"广义的概念“
文件还能代指一些硬件设备(操作系统管理硬件设备,也是抽象成文件,统一管理的)
网卡=>socket 文件 操作网卡的时候,流程和操作普通文件差不多打开 ->读写 ->关闭
操作网卡,直接操作不好操作. 把操作网卡转换成操作 socket 文件. socket 文件就相当于"网卡的遥控器
传输层 两个核心协议
TCP
UDP
TCP 有连接,可靠传输,面向字节流, 全双工
UDP 无连接,不可靠传输,面向数据报,全双工
1.有连接/无连接(抽象的概念,虚拟的/逻辑上的连接)
有连接:
对于 TCP 来说, TCP 协议中,就保存了对端的信息A 和 B 通信, A 和 B 先建立连接让 A 保存, B 的信息,B 保存 A 的信息(彼此之间知道,谁是和他建立连接的那个)
无连接:
对于 UDP 来说, UDP 协议本身,不保存对方的信息 就是 无连接
但是
可以在你自己的代码中写变量保存对方信息这不是 UDP 的行为
2.可靠传输 vs 不可靠传输
网络上,数据是非常容易出现丢失的情况(丢包) 光信号/电信号,都可能受到外界的干扰 本来是传输 0101,其中有些 bit 位就被修改了 这样乱了的数据就会被识别出来,把这样的数据给丢弃掉
可靠传输:
可靠传输的意思,不是保证数据包 100% 到达,
而是尽可能的提高传输成功的概率如果出现丢包了,能够感知到
不可靠传输:
不可靠传输,只是把数据发了,就不管了
3.面向字节流vs 面向数据报
面向字节流:
面向字节流,读写数据的时候,是以字节为单位
支持任意长度 =>粘包问题
面向数据报:
面向数据报,读写数据的时候,以一个数据报为单位(不是字符) 一次必须读写一个 UDP 数据报,不能是半个
不存在粘包问题=>长度限制
全双工vs 半双工
全双工:
一个通信链路, 支持 双向通信(能读,也能写)
半双工:
一个通信链路, 只支持单向通信~~(要么读,要么写)
2.1TCP
专门给服务器用的 -->ServerSocket 服务器和客户端都会用 -->Socket
1.ServerSocket
构造方法:
方法:
2.socket 构造方法:
方法:
客户端代码演示:
服务器代码演示:
注意点:
1.缓冲区的概念
writer.println(request);
这个操作只是把数据放到"发送缓冲区"中还没有真正写入到网卡里
flush 方法来"冲刷缓冲区 PrintWriter 的行为如果不套壳,是可以直接发送的
2.区分完整请求标记
UDP 就是以 DatagramPacket 作为单位的
TCP 则是 字节为单位实际上一个请求,往往是由多个字节构成的,
暗暗约定,一个请求/响应 使用 \ 作为结束标记对端读的时候,也是读到 \n 就结束(认为是读到一个完整的请求了)
3.对象的生命周期
每个客户端连接,都会创建一个新的每个客户端断开连接,这个对象也就可以不要了
所以在一个请求结束后,就需要手动关闭
3.阻塞问题
一个是要为服务器是要为多个客服端服务
但是:
无法同时等待 accept 和 等待用户请求 等待用户发请求的时候,没法等 accept这个时候,有新的客户端连过来了,也无法接通电话.
如果客户端1 不发请求, 务器就会阻塞在 hasNext 这里
这时候就要引入多线程
2.2.UDP
1.构造方法:相当于打开文件
创建 socket 的时候,就会关联上一个 端口号使用端口号区分主机上不同的应用程序
2.读写操作
注意:我们这里面的参数是DatagramPacket,表示一个完整的 UDP 数据报,
3.关闭文件操作:
DatagramPacket
DatagramPacket是UDP Socket发送和接收的数据报。
1.DatagramPacket 构造⽅法:
2.
DatagramPacket 提供的方法
服务器代码示例:
上述代码分析:
1.socket 对象代表网卡文件 读这个文件等于从网卡收数据,写这个文件等于让网卡发数数据
2.在主循环中,做三件事
服务器程序通常的流程
1.读取请求并解析 2.根据请求,计算响应.(服务器最关键的逻辑)但是此处写的是回显服务器,这个环节相当于省略了 3.把响应返回给客户端
3.读取请求
a.构造 DatagramPacket 对象,
DatagramPacket 就代表 UDP 数据包报头 + 载荷(new 字节数组保存)
b.调用receive
将创建的DatagramPacket 对象,给填写
c.把 udp 数据包载荷取出来, 构造成一个 String
1.获取到字符数组
2.获取到数组有效长度
4.根据请求计算响应
5.
//response.getBytes()是字节数组的起始位置。
//response.getBytes().length是字节数组的长度
//datagramPacket.getSocketAddress()就包含了目的IP和目的端口
6.发送
7.记录日志
客户端代码示例:
127.0.0.1 特殊的 IP 环回 IP 表示当前这个主机, 无论你主机的 IP 真实是啥, 都可以使用 127.0.0.1 代替类似于 this
3.传输层协议
可靠传输(大部分场景下,都会优先使用 TCP)
HTTP, 浏览器/app 访问服务器
UDP:和效率更高 (对于性能要求高, 可靠性要求不高的场景下
机房内部的主机之间通信 机房内部, 网络结构比较简单, 带宽通常很充裕
不太容易出现丢包的情况机房内部,通常对于性能要求是很高的(微服务)
3.1.UDP协议
UDP协议端格式
也可以写成下面这种格式
UDP组成部分分析:
1.载荷:完整的应用层数据包
2.报头:
目的端口和源端口号:就是确定是主机上的哪一个程序
2 个字节,16 bit位一个端口号的取值范围,0->65535实际上,
一般把 1024 以下的端口保留,咱们写代码都是用1024->65535 这个范围的
如果设置 端口号 10w,非法的端口号
长度:UDP的长度(报头+载荷)
上图显示:UDP整个报头的长度是8个字节,
报头又分成4各部分,所以长度有两个字节,
长度属性,也是 2 个字节,表示范围是
0-65535==>64kb
面试表述:
UDP 总长度最大是 64KB
1.UDP 总长度达到 64KB 上限
2.UDP 携带的载荷长度达到 64KB 上限
UDP 报头,只有 8 个字节
8 字节相对于 64KB 来说,非常小的数字
65536-8 =>65528(近似看成 64KB,也是没问题的)
校验和:是防止出现传输过程中的"比特翻转“ 0->1 1->0 光信号,电信号,电磁波收到外界干扰 可能会使高低电平/高低频光信号发生改变
校验和工作原理:
1.发送之前,先计算一个校验和,把真个数据包的数据都代入.把数据和校验和一起发送给对端.
2.接收方收到之后重新计算一下校验和,和收到的校验和进行对比(UDP 发现校验和不一致,就会直接丢弃)
UDP 的校验和使用了 CRC 方式来进行校验(循环冗余校验)
CRC:
把每个字节(除了校验和位置的部分之外),都当做整数, 进行累加.溢出也没关系,继续加最终得到结果
UDP遇到的问题:
64kb太小了,现在数据根本装不下
解决方法:
1.应用层拆包,应用层将数据拆成一个一个小包
工作量是比较大的 写大量的逻辑,实现此处的分包组包功能并且需要进行复杂的验证
如何拆,参考下面的IP协议中的拆包
2.TCP 协议.没有数据包长度的限制
面试:
如果问你UDP如何实现可靠传输 其实就是问下面TCP协议中的可靠传输是如何实现的
3.2.TCP协议
TCP协议格式:
协议组成部分分析:
1.
源/⽬的端⼝号: 表⽰数据是从哪个进程来, 到哪个进程去;
2.
32位序号/32位确认号: 在确认应答中体现
3.
4位TCP报头⻓度: 表⽰该TCP头部有多少个32位bit(有多少个4字节);
所以TCP头部最⼤⻓度是15 * 4 = 60
4.6位标志位:
◦
URG: 紧急指针是否有效
◦
ACK: 确认号是否有效
◦
PSH: 催促标志位:发送方给接收方发了数据中带有这个标志位,接收方就会尽快的这个数据 read 到应用程序中.
◦
RST: 对⽅要求重新建⽴连接; 我们把携带RST标识的称为复位报⽂段
◦
SYN: 请求建⽴连接; 我们把携带SYN标识的称为同步报⽂段
FIN: 通知对⽅, 本端要关闭了, 我们称携带FIN标识的为结束报⽂段
5.16位窗⼝⼤⼩: 在流量控制中有作用
6.
16位校验和: 发送端填充, CRC校验. 接收端校验不通过, 则认为数据有问题.
此处的检验和不光包含 TCP⾸部, 也包含TCP数据部分.
7.
16位紧急指针: 标识哪部分数据是紧急数据
8.
40字节头部:滑动窗口中能使用到
9.数据:应用层数据包
此时URG就会置为1
TCP 正常来说,按照序号顺序,发送和接收 紧急指针,相当于"插队 跳过前面的数据,直接从某个指定的序号来开始read
10.保留6位:UDP 的问题,长度不够.又不能扩展 TCP 的设计者就考虑到这样的问题,TCP 报头中就预留了一些"保留位” (现在先不用,但是占个位子.)
3.2.1确认应答
TCP 的特性:可靠性
网络通信,是非常复杂此处的可靠性,不是说,A 给 B发一个消息,B100% 能收到
而是 A 给 B发了消息之后,尽可能的让 B 收到 A 能够知道 B 是否收到了
这个可靠性主要就靠:确认应答,超时重传
保证可靠性的关键前提是,发送方知道接受方已经收到了我的数据
此时就引入了 标志位中的 ”应答报文“,(ackknownledge)简称:ack ,、
发送方收到 ack就知道我发的数据已到达
确认应答的工作原理:
1.首先:TCP将载荷部分的每一个字节部分进行编号
2.主机A向主机B发送数据
确认序号的含义:
1.<1001 的数据都已经确认收到了
2.接下来你要从 1001 开始给我发送
序号的填法:填写载荷部分第一个字节的序号
确认序号的填法:将受到数据的载荷部分最后一个字节的序号+1,在进行填入
3.数据到达主机B的时候,因为我们此时引入了序号,所以可以利用序号进行排序
TCP 在接收方这里会安排"接收缓冲区"(内存,操作系统内核里通过网卡读到的数据,先放到接收缓冲区中,后续代码里调用 read也是从接受缓冲区来读的
根据序号来排序,序号小的在前面,大的在后面确保前面的数据已经到了,然后 read 才能接触阻塞如果是后面的数据先到,read 继续阻塞,不会读取到数据:
3.2.2.超时重传
超时重传针对 "丢包问题"
为什么会丢包:
数据报经过某个路由器,交换机转发的时候,该路由器/交换机已经非常繁忙了,导致当前需要转发的数据量超出路由器/交换机的转发能力上限
丢包的两种情况:
1.A给B发送的数据丢了
2.B返还给A的ack丢了
丢包造成的影响:
不管是丢包的第一种情况还是第二种情况,因为此时A都是没有收到主机B发来的ack,都会造成主机A进行重发数据,
所以B又会出现两种情况,
接受到一份数据
接收到两份相同的数据
B 收到了两份一样的数据 如果 tcp 不处理,可能会使应用层读到两次一样的数据
去重操作: tcp 会在内部进行去重操作 接受缓冲区 就可以根据序号,在接受缓冲区中找一下如果存在,就直接丢弃如果不存在,才放进去
重传机制:
达到等待时间的上限,还没有收到 ack
A 就认为传输中发生丢包了
1.A ->B 发的数据丢了
2. B -> A 返回的 ack 丢了
引入超时时间,来判定是否丢包
假设A给B发送时据,超出了时间阈值T,还没有接受到B的ack
但是此时会延长这个时间阈值,是动态改变的
但是不会无限的延长,如果超出了一定的次数或者是一定的时间,就会放弃此次传输,进行重传
3.2.3.连接管理
3.2.3.1.建立连接
连接:是一个虚拟的连接,就是让对方都保留自己的信息。
6位标志位:
synchronized 同步:同步这个术语,有多种含义在 加锁 ,理解成“互斥
TCP 中的同步,:
"数据上的同步”A 告诉 B,接下来我要和你建立连接.就需要你把我的关键信息(端口号),
保存好,同时你也把你的信息同步发给我,接受到对方的关键信息后序号发给对方一个ack
建立连接使用三次握手来实现:
三次握手, 一定是客户端主动发起 syn(第一次握手, 一定是客户端开头的)
第一次:A给B发送一个syn
第二次:B给A发送一个syn+ack(这里的 syn 和 ack都是内核负责的.和用户代码无关可以保证是同一时机)
第三次:A给B发送一个ack
为什么要进行合并操作:
合并操作,是有效能够提高传输的效率的 网络传输过程,是要能够进行"封装和分用的
客户端服务器状态分析:
1.CLOSED:
不存在的状态,TCP还没有进行连接
2.LISTEN:
启动服务器,new ServerSoket的时候就会进入这种状态,相当于饭店已经开门做好了,接客的准备
3.ESTABULISHED:
已经建立连接,随时可以发送数据
三次握手的作用:
1.三次握手, 相当于"投石问路” 先初步的探一探网络的通信链路是否通畅.(网络通畅是可靠传输的前提条件)
2.验证通信双方的发送能力和接收能力是否也正常
当B第一次收到A的信息:A的发送功能是可以的
当A收到B的信息:B的接受和发送功能是可以的
当B第二次收到A的信息:A的接受功能是可以的
3.三次握手还可以协商一些信息
TCP要协商最重要的一个事情就是:传输的数据从序号多少开始
1.初始的序号一般不是从0开始
2.连续的两次连接的初始序号一般相差很大
举个例子:
假设有两次连续的连接,第一次的传输的起始序号是100000,
第二次传输的起始序号是800000
1.在第一次传输的时候,有个数据因为延迟,在第一次传输结束,创建第二次连接的时候才达到主机B
2.因为有了之前的协商,知道我的序号从哪开始,知道这个晚来的数据不是这次连接的,所以就可以将这个数据给剔除
3.2.3.2.断开连接
四次挥手流程:
四次挥手, 客户端和服务器, 都可以主动发起 FIN(就看是谁先调用 close)
第一次:A给B发送一个FIN
第二次:B给A发送一个ACK
第三次:B给A发送一个FIN
第次四:A给B发送一个ack
上面的第二次发送和第三次发送是否可以合成为一次?
不能:
ack 是内核控制返回的. 内核收到 FIN,第一时间返回 ack,和你应用程序代码无关
第二个 FIN 则是代码中调用 socket.close 才会触发的(进程结束, 也能触发)
第二个 FIN 的时机和 ACK 的时机很可能不是同一时机
代码上的体现:
当客户端发送第一次FIN的时候,此时就会碰到代码1,
此时就会个客户端发送ACK,
但是服务器还是要执行完代码2,
到达代码3才可以发送第二个FIN
客户端服务器状态分析:
TIME WAIT:谁是主动发起 FIN 的一方,就会进入到 TIME WAIT
CLOSE WAIT: 谁是被动发起 FIN 的一方,就会进入 CLOSE WAIT
四次挥手出现丢包的情况:
正常的情况:
1.如果第一个 FIN 丢了,A 就无法再规定时间内拿到ACK, 于是 A 重新发送 FIN
2.如果是第一个 ACK 丢了,同上
3.如果是第二个 FIN 丟了呢,B 也再次重传就可以了在第二个 FIN 到达 A 之前,A 这里的连接肯定是存在的,就能够及时的处理 ACK
4.但是如果第二个ACK丢了,此时B就会以为自己的FIN没有到达A,进行重发FIN,
所以此时的A不可以一收到FIN发出ACK就进行CLOSED.
此时A需要进行一段时间的休眠,就是为了等待B是否会不会进行重发FIN
异常的情况:
此时A不等了,但是B还是没有收到A的ACK
1.站在 A 的视角, 此时 A 给 B 已经把 FIN 发了很久了,B 也没有进行后续的挥手操作A 就会主动释放连接(也就是把 B 的核心信息删了)
2.B 这边,由于代码逻辑都有 bug, 这里的连接,暂时存在 (还会保存对方的信息,此时也没法进行正常的数据通信了)
休眠的时间:
2*MSL(网络上任何两个节点传输过程中消耗的最大时间) 通常这个时间会配置成 60s
此处 TIME WAIT 等待 2min(这个数值不要背,不同系统可能不一样,可以修改的)
3.2.4.滑动窗口
上面讲了TCP中的确认应答,超时重连,连接管理都是为了TCP的可靠性,
但是代价就是自身的效率下降,
此时滑动窗口就是为了,挽救自身的效率而做的
没有引进滑动窗口的机制:
引入了滑动窗口的机制:
流程:
1.一开始是直接将一组数据进行发送
2.后序,接受到一个ack就发送一条数据(如果等待一组的ack都到达,这样更加消耗时间)
问题:
如果2001比1001的ack先到达是怎么滑动的?
如果是 2001 ack 比 1001 先到窗口直接往后走两个格子就可以了 确认序号的含义: 该序号之前的数据,都确认收到了 2001 ack 能够涵盖 1001 的含义
滑动窗口中的丢包问题
情况一:ack丢失
此时不用管,因为后面的ack就包含了之前的数据已传达
情况二:数据丢失
此时1000这个数据丢失,主机B会发三次你要将1000的数据给我,
此时就只会重发1000这个数据,这种操作叫做 快速重传
快速重传: 只是谁丢了,重传谁,其他已经收到的数据无需重传整个重传过程速度很快的 (滑动窗口下的,超时重传的变种操作)
超时重传:传输的数据量少,没有构成滑动窗口批量传输的形式
快速重传:传输数据量多,形成滑动窗口
3.2.5.流量控制
滑动窗口,窗口越大, 效率就越高,但是不能无限大,太大了会影响到可靠性
接收方的处理能力是有上限的,和你写的代码有关系
举个例子:
1.蓄水池:应用程序,需要从接受缓冲区中, 读取数据就相当于放水
读了一个字节,这个字节就可以从接受缓冲区删除了
2.蓄水的速度:就是发送方发送的速度
3.应用程序读取的速度:看你代码是咋写的了
如果水位已经满了,此时继续蓄水就会冒(丢包)
流量控制就是给发送方踩刹车,让它发的慢点 流量控制可以让接收方,根据自身处理数据的速度,反馈给发送方,限制发送方发送的速度在 ack 中,依赖一个特殊的属性,"窗口大小'
接收方接收缓冲区的剩余空间大小填入到这个属性中 发送方就会按照这个数字来重新设定发送的滑动窗口大小(滑动窗口的大小,是动态变化)
问题:那么此时滑动窗口最大值是64kb?
不是
因为在选项中有一个特殊的属性:窗口扩展因子
窗口大小=16位窗口大小<<窗口扩展因子
流量控制的机制:
1.假设A给B发送1000数据,B给A的应答中包含了自己的窗口大小为3000.
2.那么主机A就一次性发送3000的数据,假设此时应用程序没有读取任何数据
3.那么当数据都到达的时候,B给A的ack中就会告诉我的窗口大小为0
4.那么此时A就不会发信心给B
5.但是一直不给B发数据也不是一个事,此时就引入“窗口数据包”
6.A给B发送一个窗口数据包,只是为了触发ack主动的询问一下消化的怎么样,窗口大小为多少
3.2.6.拥塞控制
流量控制:依据是接受方的处理能力,进行限制
拥塞控制:依据是链路传输层的转发能力,进行限制
拥塞控制的总体思想:
先按照小的窗口(小的速度) 先发着如果发的时候很顺利,不丢包,加大速度出现丢包,减小速度 又不丢包了,继续加大速度 又丢包了,继续减小速度 动态平
问题:窗口的大小到底是谁说的算
谁小谁老大
拥塞控制的机制:
1.首先是指数增长
3.到达一定的值后,换成线性
3.触发到丢包问题,利用乘法减小
4.之后再次进行线性
3.2.7.延时应答
默认情况下,接收方都是在收到 数据报 第一瞬间,就返回 ack
但是可以通过延时返回 ack 的方式来提高效率(不是100%,要看读取数据和传输数据的速度比)
当读取速度比传输的速度快的时候:
那么所有的包都可以延迟应答么?
•
数量限制: 每隔N个包就应答⼀次;
•
时间限制: 超过最⼤延迟时间就应答⼀次;
具体的数量和超时时间, 依操作系统不同也有差异; ⼀般N取2, 超时时间取200ms;
3.2.8.捎带应答
TCP 已经有了延时应答了,基于延时应答,
引入"捎带应答"返回业务数据的时候,顺便把,上次的 ack 给带回去
如果没有延时应答, 返回 ack 的时机和返回响应的时机,就是不同时机
原因:
把两个包合成一个就能起到提高效率的作用 涉及之前的封装,分用
3.2.9.面向字节流
UDP 来说,就不存在这样的问题以 UDP 的数据包为单位读写的,
一个 UDP 数据包承载一个应用层数据包,
每次 receive 得到的结果就是一个完整的应用层数据包
面向字节流,就会引发一个问题==>粘包问题
粘的是"应用层数据包”
通过字节流方式传输,很容易混淆包和包之间的边界
从而接收方无法去区分从哪里到哪里是一个完整的应用层数据包
解决方法:
上述问题,在 tcp 的层次上,无解需要站在应用层解决. 定义好应用层协议,明确包之间的边界
1.约定包和包之间的分隔符(包的结束标记)
echo server 采用的办法.约定 \n 作为结束标记
2.约定包的长度~比如约定每个包开头 4 个字节,表示数据包一共多长
3.2.10.异常情况处理
1.某个进程崩溃了
资源释放正常
进程崩溃,和主动退出,没有本质区别, 进程释放
FIN,触发四次挥手 进程虽然没了,但是 TCP 的连接信息还存在此时四次挥手还是可以正常进行的
2.主机关机了
分为两种情况
主机关机了正常流程的关机本质上还是会先杀死所用的用户进程
情况一:
关机也需要一定的时间,如果一定时间内,四次挥手,挥完了,就和正常一样了
情况二:
没有挥完
假设 B 的 FIN 来的太迟了,导致 A 已经关机完成了,
意味着 B 的 FIN 不会有 ACK B 重传 FIN,也没有 ACK经过几次之后,认为对端出现严重问题,
此时 B 主动放弃连接(B 把保存的 A 上的信息就删掉了)
此时资源还是可以释放的
3.主机掉电
分两种情况
情况一:接受方掉线
1.B发送数据给A,但是A没有给B返回ack
2.就会激发B的重传机制
3.重传到一定次数就会触发“重置TCP连接“
4.B主动给A发送一个”复位报文“(RCT, 表示我们重头开始)
5.如果还是没有接收到ack,那么B只能单方面的释放连接
情况二:发送方掉线
1.B不知道A是什么情况,只能等待A一会
2.等了一会后,B会主动给A传输一个”心脏包“(没有携带载荷数据),为了触发ack
3.如果对方有心跳,继续正常等待如果没心跳, 就只能通过 rst 尝试还是不行,就只能单方面释放连接
发送心脏包的行为-->"保活机制"
问题:TCP 的心跳, 周期太长了,分钟级别的
虽然 TCP 内置了心跳包,实际开发中,通常还是会在应用层重新实现心跳包效果
现在通常希望,秒级,甚至毫秒级,就能发现对端是否,从而触发一些后续的操作
4.网线断开
站在 A 的视角,就是和刚才上面"接收方掉电"是一样的情况 站在 B 的视角,就和刚才上面"发送方掉电"是一样的情况
4.网络层
4.1IP协议
IP协议组成部分分析:
1. 4位版本号:
指定IP协议的版本, 对于IPv4来说, 就是4
2. 4位首部长度
IP头部的⻓度是多少个32bit, 也就是 length * 4 的字节数.
4bit表⽰最 ⼤的数字是15, 因此IP头部最⼤⻓度是60字节
3. 8位服务类类型(TOS)
决定了IP协议的工作状态
8位服务类型(Type Of Service): 3位优先权字段(已经弃⽤), 4位TOS字段, 和1位保留字段(必须置为
0). 4位TOS分别表⽰:
最⼩延时, 最⼤吞吐量, 最⾼可靠性, 最⼩成本
. 这四者相互冲突, 只能选择⼀个.
对于ssh/telnet这样的应⽤程序, 最⼩延时⽐较重要; 对于ftp这样的程序, 最⼤吞吐量⽐较重要
4. 16位总长度(字节数)
一个IP数据包=报头+载荷 的长度
所以一个IP数据包的最大内存就是 64kb
IP协议中内置的拆包操作 ,假设一个TCP数据报,IP协议就会自动拆分成多个,通过多个IP数据报共同传输一个TCP数据包
5.IP协议中的拆分
16位标志:
拆包:将相同第一个IP数据报拆包的时候,分配相同的标识
组包:将相同标识的数据报组合在一起
3位标识:
一位没有用
一位标识是否发生了拆包操作
一位标识这个包是否是最后一个数据报
13位片偏移:
用于标识数据包所在的位置,靠前偏移量较小,靠后偏移量较大
6. 8位生存时间(TTL)
假设此时我发送了一个IP错误的数据报,此时这个数据报就会一直到达不了目的地,但是也不能一直传输下去,TTL就此引入
TTL:
一个数据报在节点中传输的最大限度,单位是次数,进行一次传输,TTL就进行 -1,当TTL减到0 的时候就表示传输失败,此报就会被丢弃。
TTL的初始值范围是0~225可以是64,也可以是128,下面就是128
问题:如果IP正确,但是没有到达目的地TTL就耗尽了怎么办?
六度空间理论(Six Degrees of Separation) 是一个社会学和网络科学中的经典假说,其核心思想是:世界上任意两个陌生人之间,平均最多通过六个中间人就能建立联系。这一理论揭示了人类社会网络的紧密性和“小世界”特性。
虽然六度空间理论没有实质的帮助我们减少TTL的消耗,但是也告诉我们TTL的消耗没有那么高
7. 8位协议
标识传输层使用哪种协议
分用的时候,IP 协议解析 IP 数据报的时候,拿到载荷,
交给上层处理此处 8 位协议编号,就能起到区分效果
类似于:
传输层到应用层 =>端口号 网络层到传输层=>8 位协议 数据链路层到网络层 =>也有一个类似的协议编号的
8. 16位首部校验和
载荷部分是TCP或者是UDP数据包,所以载荷部分它们自己会校验
9. IP地址
IP协议中最主要的部分
ip地址的本质是通过32位的整数来表示的
由于 32位整数不方便阅读,通常会把IP写作 点分十进制 来表示
其实就是用三个点将32的bit位分成四部分,每部分8个bit'位,再将每一部分转换成十进制来表示
4.2.地址管理
上面提到32位的ip地址,最多有42亿多个IP地址,这个地址数量是远远不够的,
有三种解决方法
1.动态分配IP地址
有设备使用就分配,休眠状态下就释放IP地址
2.升级为IPV6
由128个bit位构成,ip地址的个数是一个天文数字,但是ipv4和ipv6是不兼容的,这样的话就需要将跟换所有的设备,这个成本是很大的
3.NAT网络地址转换
现在的主力方法
4.2.1.NAT机制
NAT将IP地址分成两部分
1.公网 / 外网
IP地址是唯一的
下面这个就是公网IP,有许多人公用这一个公网ip
2. 私网 / 内网
在相同局域网中,IP地址是不唯一的,但是在不同局域网中,一个IP地址可能有无数个
下面这个ip就是私网ip,在相同局域网中P地址是不唯一的,但是在不同局域网中,一个IP地址可能有无数个
有下列这几种私网ip:
•
10.*,前8位是⽹络号,共16,777,216个地址
•
172.16.
到172.31.
,前12位是⽹络号,共1,048,576个地址
•
192.168.*,前16位是⽹络号,共65,536个地址
包含在这个范围中的, 都成为私有IP, 其余的则称为全局IP(或公⽹IP);
NAT背景下的网络通信:
1.同一个局域网下,设备A 访问设备B
由于 IP 本身不允许重复,自然不受任何影响,NAT 不起到作用
2.公网设备 A 访问公网设备 B
由于 公网 |P 本身也不重复,也不受到影响,NAT 不起作用
3.不同局域网,设备 A 访问设备 B
不允许的
NAT 机制禁止这样的访问方式
但是也是一种保护机制,这样你的ip就不会被放在大街上一样被随意访问
4.公网设备 A 访问私网设备 B
不允许的
需要特殊手段的 你在你电脑上搭一个能让外网访问的服务器 比如,通过内网穿透,通过 VPNL(点拟私人网络)
内网穿透:
5.局域网设备 A 访问公网设备 B
这里涉及到 “网络地址映射”
1.当这些设备向服务器发送请求的时候,先经过公网ip的路由器,此时在这个路由器中,会创建一个类似于hash表一样的键对值,将私网ip和端口号一一对应并记录下来。
其实端口号NAT设备也可以是随意更改的
2.此时路由器再将这些私网ip改成自己的ip,端口号不变发送给服务器。
3.服务器进行反应的时候,首先发个公网路由器,在根据之前创建的“哈希表“,一一对应进行返回
4.2.2.网段划分
IP地址分为两个部分, ⽹络号和主机号
• ⽹络号: 保证相互连接的两个⽹段具有不同的标识;
• 主机号: 同⼀⽹段内, 主机之间具有相同的⽹络号, 但是必须有不同的主机号;
• 不同的⼦⽹其实就是把⽹络号相同的主机放到⼀起.
• 如果在⼦⽹中新增⼀台主机, 则这台主机的⽹络号和这个⼦⽹的⽹络号⼀致, 但是主机号必须不能和 ⼦⽹中的其他主机重复.
• 有⼀种技术叫做DHCP, 能够⾃动的给⼦⽹内新增主机节点分配IP地址, 避免了⼿动管理IP的不便.
• ⼀般的路由器都带有DHCP功能. 因此路由器也可以看做⼀个DHCP服务器
一个路由器有俩个IP,一个WAN 一个LAN
路由器的功能就是将两个局域网连接在一起
标签一就是WAN
标签二和三都是LAN
子网掩码:
用于区分主机号和网络号
子网掩码的特点:不会出现01交替的现象,1都在左边,0都在右边
例如:
只有后四位是0,其余都是1,
那么前28位都是网络号,后四位是主机号
淘汰的网段划分机制:
每一个类网络号主句好号都是固定的
特殊ip地址:
•
将IP地址中的主机地址全部设为0, 就成为了⽹络号, 代表这个局域⽹;
•
将IP地址中的主机地址全部设为1, 就成为了⼴播地址, ⽤于给同⼀个链路中相互连接的所有主机发 送数据包;
•
127.的IP地址⽤于本机环回(loop back)测试,通常是127.0.0.1
4.3.路由选择
在复杂的⽹络结构中, 找出⼀条通往终点的路线
路由的过程, 是⼀跳⼀跳(Hop by Hop) "问路" 的过程.
所谓 "⼀跳" 就是数据链路层中的⼀个区间. 具体在以太⽹中指从源MAC地址到⽬的MAC地址之间的帧 传输区间.
IP数据包的传输过程也和问路⼀样.
•
当IP数据包, 到达路由器时, 路由器会先查看⽬的IP;
•
路由器决定这个数据包是能直接发送给⽬标主机, 还是需要发送给下⼀个路由器;
•
依次反复, ⼀直到达⽬标IP地址;
那么如何判定当前这个数据包该发送到哪⾥呢? 这个就依靠每个节点内部维护⼀个路由表;
•
路由表可以使⽤route命令查看
•
如果⽬的IP命中了路由表, 就直接转发即可;
•
路由表中的最后⼀⾏,主要由下⼀跳地址和发送接⼝两部分组成,当⽬的地址与路由表中其它⾏都不 匹配时,就按缺省路由条⽬规定的接⼝发送到下⼀跳地址。
5.数据链路层
5.1以太网
以太网=数据链路层+物理层协议
以太网数据帧格式:
•
源地址和⽬的地址是指⽹卡的硬件地址(也叫MAC地址), ⻓度是48位,是在⽹卡出⼚时固化的;
•
帧协议类型字段有三种值,分别对应IP、ARP、RARP;
•
帧末尾是CRC校验码。
MAC地址与IP地址的区别
举个例子:
我要从广东出发去安徽
先进行路线规划(网络层)
广州-长沙-武汉-合肥
1.广州-长沙
目的IP:广州
源IP:合肥
目的MAC:广州
源MAC:长沙
2.长沙-武汉
目的IP:广州
源IP:合肥
目的MAC:长沙
源MAC:武汉
3.武汉-合肥
目的IP:广州
源IP:合肥
目的MAC:武汉
源MAC:合肥
源IP, 目的IP始终是整个通信路线的起点和终点 目的MAC ,源MAC 是关注当前这一段路,从哪到哪
5.2ARP协议
ARP 不是传输业务数据的,而是打辅助的协议
功能是根据 IP 地址, 得到对应的 mac 地址
网络传输过程中,网络这一层,转发是要根据 IP
数据链路层这一层,是要根据 mac
写代码的时候,填写的肯定是 IP 地址,路由器也是根据 IP 地址 查路由表,根据路由表得到接下来从哪个网口转发
但是就往往需要根据下一个节点的ip,找到对应的 mac才能填写以太网数据帧
所以,通过广播地址,向自己相邻的设备发送ARP数据报,询问与他相连的设备的ip和mac(物理地址是什么),再进行一一对应,知道我发的下一个节点是哪个节点