目录

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(物理地址是什么),再进行一一对应,知道我发的下一个节点是哪个节点