计算机网络
计算机网络
七层模型(OSI模型)
应用层
- 为应用软件提供网络服务,如文件传输,电子邮件和其他网络软件服务
- 协议示例 HTTP、FTP、SMTP(简单邮件传输协议)
表示层
- 确保一个系统发送的信息能被另一个系统的应用层读取,进行数据的翻译和加密
- 协议示例:ASCII,JPEG,SSL/TSL(安全层协议)
会话层
- 管理和控制应用程序之间的会话,提供数据交换的同步和组织
- 协议实例:SOCKS(代理服务器协议)、RPC
传输层
- 负责提供端到端的数据传输服务,确保数据的完整性和顺序
- 协议示例:TCP、UDP
网络层
- 负责数据包从源到目的地的传输和路由选择,处理数据包在网络中的移动
- 协议示例:IP、ICMP(互联网控制消息协议)
数据链路层
- 负责在相邻节点之间的物理链路上传输数据帧,处理帧的寻址、错误检测和纠正
- 协议示例:Ethernet(以太网)、PPP(点对点协议)
物理层
- 负责在物理媒介上传输原始的比特流,定义了电气、机械、过程和功能标准
- 协议示例:RS-232、V.35、Fiber Optic(光纤)
五层模型只出现在计算机网络学习教学过程中,他是对七层模型和四层模型的一个折中,及综合了OSI和TCP/IP 体系结构的优点,这样既简洁又能将概念阐述清楚,(主要是因为官方的7层模型太过麻烦复杂)因此主要差别是去掉了会话层和表示层,而传输层改为了运输层,因为他们觉得运输名字更贴切。
五层模型
- 应用层
最顶层,提供应用程序与网络通信的接口,如HTTP,SMTP,FTP等协议。 - 传输层
负责端到端的数据传输,提供可靠的数据传输服务,如TCP、UDP协议。 - 网络层
负责在网络中建立和维护节点之间的连接,并选择最佳的路径进行数据传输,如IP协议。 - 数据链路层
负责将数据帧从一个节点传输到相邻节点,提供了一些错误检测和纠正的功能,如以太网协议。 - 物理层
最底层,负责传输原始比特流通过物理介质,如电缆、光纤或无线电波。
应用层
DNS
域名服务器
- 本地域名服务器
- 根域名服务器
- 顶级域名服务器
- 权限域名服务器
递归查询
本地域名服务器代替我们进行DNS查询,查找到IP,或者确认ip不存在
迭代查询
各个级别的域名服务器分工合作,得到最终的域名服务器
当我们在地址栏输入一个网址后的过程

- 输入网址https://www.xxx.xxx.xx
- 不知道ip时(只有域名),就会进行DNS查询,将域名转换为IP地址
- 查询浏览器缓存
- 查询系统缓存 –》hosts文件
- 查询路由表
- 查询本地域名服务器,本地域名服务器代理我们访问其域名服务器
- 查询根域名服务器 得到顶级域名服务器地址
- 查询顶级域名服务器 得到二级域名服务器(权威域名服务器地址)
- 查询权威名称服务器
- 知道ip后,开始TCP三次握手
- 连接建立后开始TLS/SSL握手
- 开始安全通信
HTTP
HTTP(Hyper Transfer Protocol超文本传输协议)
无状态
无需连接(底层需要TCP连接)
半双工
HTTP报文结构
请求
请求行
- 结构是方法、网址、版本号:GETwww.baidu.com HTTP/1.1
- RESTFUL风格方法
- GET
- POST
- GET POST的区别
- get幂等安全,post不幂等不安全。幂等:请求多少次都一样,安全:请求不会更改服务器资源
- get参数有限,明文传输;post参数无限
- get没有请求体
- get请求会被浏览器缓存,post不会
- GET POST的区别
请求头
- 多种字段
- Accept:text/heml,image/* 支持的数据类型
- Host:www.xxx.xxx.xx 想要访问的主机
- Referer:防盗链,浏览器告诉服务器自己从哪个页面而来
- User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0) 浏览器内核
- Cookie
- Connection:Close/Keep-Alive 本次请求后连接是否断开
请求空行
分隔请求头和请求体
请求体
响应
响应行
- HTTP/1.1 200ok
- 状态码
- 200 正常
- 301 资源永久重定向
- 302 资源临时重定向,会将 POST 转化为 GET(默认情况下,重定向请求的方法应该与原始请求的方法相同,但POST不幂等,不安全,对服务器有影响,也是为了防止重复提交)
- 307 同302,但不会更改post为get
- 400 请求的报文有误,是一个笼统的状态码
- 401 需要确认身份
- 403 禁止访问,权限不允许
- 404 资源找不到
- 5xx 服务端错误
响应头
- Set-Cookie 设置Cookie
- Connection: close/Keep-Alive 连接状态
- Location 跳转的网页
- Cache-Control: no-cache 是否设置缓存 替代Expires和Last-Modified
- Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT 上次更改时间
- Content-Type: text/html; charset=GB2312 响应数据的类型
响应空行
分隔响应头和响应体
响应体
HTTP1.0
端口80
特点
- 非持续连接
- 每个请求和响应体都需要一个单独的TCP发送
- 10张图片我们就得TCP连接10次
- 队头阻塞 有一个HTTP请求阻塞,之后的都会阻塞
- 一次响应一次发送 只有等待第一个请求响应后,才能发送第二个请求
- 无状态 不保存任何状态
- 数据明文传输
HTTP1.1
改进点
- 持续连接
- 多个请求和响应可以使用同一个TCP连接
- Connection:Keep-Alive
- 管道网络传输
- 无需等待响应就可以发出第二个
- 没有解决队头阻塞,有一个响应出现丢包,依然会阻塞
- 实际上没有多少浏览器支持了这项功能,而是使用其他方式
- 改进无状态 使用Cookie保存数据
依然存在的问题
- 队头阻塞
- 请求响应头部很大,没有压缩,传输压力大
- 没有请求优先级控制
- 请求只能从客户端发起
- 明文传输
HTTPS
端口443
加了TLS/SSL传输层安全协议
特点
- 对称加密+非对称加密
- 摘要算法,负责校验数据完整性
- 数字证书,验证数据是否安全
SSL握手过程(发生在HTTP三次握手建立后)
- 客户端发起握手:发送ClientHello消息 包含TLS版本、加密套件、第一随机数
- 服务器收到回应:发送ServerHello消息 包含TLS版本、加密套件、第二随机数
- 服务器发送数字证书给客户端:发送Certificate消息(可选) 包含公钥和证书信息
- 服务器端发送ServerKeyExchange消息
- 对于RSA算法,它的公钥在证书内,不需要发送此消息
- 对于DH算法,需要在此消息中协商DH参数
- 服务器端发送ServerHelloDone:表示消息发送完毕
- 客户端认证证书,进行秘钥交换
- 浏览器收到证书,对照信任证书列表,确认是否可信
- 如果可信,RSA算法从证书获得公钥,生成48字节的预主秘钥(第三随机数)用公钥加密,发给服务器,服务器用自己的私钥解密,得到预主秘钥
- 预主秘钥是一个临时的对称秘钥
- 服务器和客户端都使用第一随机数和第二随机数生成对称加密秘钥,用于后续通信
HTTP2.0
优化了之前存在的问题
- 头部压缩 HPACK算法,即客户端和服务器各自维护一个头信息表,这样减少每次发送的头信息的大小
- 二进制化 消息头和消息体不再明文传输,而是二进制格式发送
- 无序发送 同一个连接里面连续的数据包,可能属于不同的回应
- 解决队头阻塞
- 可以并发进行请求,并且可以并发响应,还不用按顺序,因为数据帧有流标识符标记
- 如何解决的?
- 每一个数据包称为一个数据流
- 每一个数据流都有一个编号,客户端发出的数据流编号为奇数, 服务器发出的数据流编号为偶数
- 客户端还可以指定数据流的优先级
- 报文含有流标识符,这样解决无序响应的问题
- 服务器可以主动推送信息
存在的问题
TCP性能出现短板 多个HTTP请求复用一个TCP连接,一旦TCP连接丢包,就会触发TCP的重传机制,这样所有的HTTP请求都必须等待丢包重传
HTTP3.0
基于QUIC协议
本质上QUIC协议是为了取代TCP协议而出现的
QUIC协议依托于UDP协议
整合了TLS1.3、HTTP2.0、TCP的握手、流量控制、拥塞控制
优点
- 连接建立快
- 扛丢包能力强
- 自带加密,多路复用
- 握手
- HTTP2.0需要TCP3次握手+TLS三次握手=6次
- QUIC只需要3次
缺点
- 吃CPU性能
- 兼容性和部署问题
如何使用UDP实现可靠传输?
QUIC
在UDP上层封装一层协议,使用UDP进行丢失重传、流量控制、拥塞控制、加上序列号、超时机制
传输层
UDP
特点
- 无连接
- 尽最大努力交付,但是不一定发送成功
- 面向报文
- 支持一对一,一对多,多对多
UDP首部(8字节)
- 源端口
- 目的端口
- 长度
- 检验和
- 检验需要加伪首部,伪首部只用于校验是否正确,并不会具体发送
- 源IP
- 目的IP
- 全0 1个字节,全是0
- 协议
- 长度
- 检验时,使用首部、伪首部、数据字段检验,三者求和取反码,将结果存入检验和字
- 检验需要加伪首部,伪首部只用于校验是否正确,并不会具体发送
TCP
特点
- 需要建立连接
- 可靠交付
- 面向字节流
- 只支持一对一
首部
20 固定+ 40 可选
可选部分支持安全、时间戳等额外的功能。可选部分的长度是可变的,最多可以有 40 字节
- 源端口
- 目的端口
- 序号 本报文段要发送的第一个字节的字节号,TCP按字节编号
- 确认号 期望收到的下一个字节的编号 确认号为N,代表N-1全部收到
- 数据偏移 就是首部长度
- 保留 全为0,今后使用,目前无实际意义
- URG 紧急,如果为1表示此报文段优先传输 比如Ctrl+c命令
- ACK 确认收到
- PSH 推送,无需等待到缓存慢再发,直接发送
- PST复位,表明要断开连接,重新建立
- SYN 同步,建立连接使用
- SYN 终止:表明发送方已经发送完毕,要求终止连接
- 窗口 接受窗口的大小
- 检验和 同UDP的检验,需要与伪首部、首部、数据一同计算
- 紧急指针
- 选项(可变长度,最长是40字节)
- MSS最大报文段长度 默认536(不包括头)
- 窗口扩大选项
- 时间戳
- 用于计算RTT时间(往返时间)
- 判断序号是否重复使用
如何实现可靠传输?
首部字段:序号和确认号
序号本报文段要发送的第一个字节的字节号,TCP按字节编号
确认号
- 期望收到的下一个字节的编号
- 确认号为N,代表N-1全部收到
连续ARQ协议:规定发送方每收到一个确认请求,就将窗口向前推进一个位置;接收方进行累积确认
累积确认:对按序到达的最后一个分组发送确认
回退N:如果丢了一个包,则需要重新发送丢包及其之后的所有包
流量控制
- 3个窗口+1门限
- 发送窗口
- 接收窗口
- 拥塞窗口
- 慢开始门限
- 滑动窗口协议
- TCP 通信双方均维护一个发送缓存与接收缓存(实现全双工)
- 发送缓存
存放两种数据- 还没发出去的数据
- 已经发出去还没确认的数据
- 接收缓存
- 按序到达,但是尚未读取的
- 未能按序到达的数据
拥塞控制
cwnd
什么是cwnd?
- 用于控制发送方可以发送到网络上未经确认的数据量,以避免网络拥塞。cwnd的大小会影响数据流量的控制和网络吞吐量
- 具体来说,cwnd表示了当前发送方可以发送的数据量大小,单位为字节。发送发发送数据时,会根据cwnd的大小来控制发送的数据量,确保网络中的数据包不会过多导致拥塞
- TCP的拥塞控制算法使用cwnd来动态调整发送数据的速率。
sshresh
什么是sshresh?
- sshresh(Slow Start Threshold) 是TCP拥塞控制算法中的一个参数,用于指示拥塞避免阶段的门限。当TCP进入拥塞避免阶段时,拥塞窗口cwnd的速率会由指数增长变为线性增长,而sshresh就是触发这个阶段转换的门限值
如何判断出现了阻塞?
- 出现了超时重传(快重传)
- 出现了丢包
- 出现了ACK延迟或是重复
拥塞窗口cwnd
发送方维持
拥塞控制,慢开始门限ssthresh
cwnd<ssthresh (慢开始门限) 每经过一个RTT就翻倍cwnd
- cwnd=ssthresh 二者选一
cwnd>ssthresh 拥塞避免 经过一个RTT线性增长cwnd
出现拥塞
- 将ssthresh=cwnd/2
- 将cwnd = 1
出现三个重复的ACK
快重传
收到3个重复的确认信息,就判断当前的网络不好,要求接收方进行快重传;
快重传的意思是:即使是收到了乱序的报文段,也要立即发送确认;并且不要再等待缓存满再发送了,ACK要直接进行发送快恢复
一般与快重传一起使用,会设置拥塞窗口更大一点拥塞窗口设置为当前值cwnd = ssthresh,而不是设置为1
计时器
- 连接计时器
- 重传计时器
- 保活计时器
- FIN_WAIT_2计时器
- TIME_WAIT定时器
三次握手

第一次握手(SYN=1,seq=x)
客户端发送一个TCP的SYN标志为1的包,指明客户端打算连接的服务器的端口,以及初始序号x,保存在包头的序列号字段里
第二次握手(SYN=1,ACK=1,seq=y,ACKnum=x+1)
服务器发回确认包(ACK)应答。即SYN标志位和ACK标志位均为1。服务器端选择自己ISN序列号,放到seq域里,同时将确认序号设置为客户的ISN加1,即X+1。发送完毕后,服务器端进入SYN_RCVD状态
第三次握手(ACK=1,ACKnum=y+1)
客户端再次发送确认包(ACK),SYN标志位0,ACK标志位为1,并且把服务器发来 ACK 的序号字段+1,放在确定字段中发送给对方,并且在数据段放写ISN的+1。
发送完毕后,客户端进入 ESTABLISHED 状态,当服务器端接收到这个包时,也进入 ESTABLISHED 状态,TCP 握手结束
为什么是三次握手而不是两次?
根本原因: 无法确认客户端的接收能力。
如果是两次,你现在发了 SYN 报文想握手,但是这个包滞留在了当前的网络中迟迟没有到达,TCP 以为这是丢了包,于是重传,两次握手建立好了连接。
看似没有问题,但是连接关闭后,如果这个滞留在网路中的包到达了服务端呢?这时候由于是两次握手,服务端只要接收到然后发送相应的数据包,就默认建立连接,但是现在客户端已经断开了。
看到问题的吧,这就带来了连接资源的浪费。
四次挥手

第一次挥手(FIN=1,seq=x)
假设客户端想要关闭连接,客户端发送一个 FIN 标志位置为1的包,表示自己已经没有数据可以发送了,但是仍然可以接受数据。
发送完毕后,客户端进入 FIN_WAIT_1 状态
第二次挥手(ACK=1,ACKnum=x+1)
服务器端确认客户端的 FIN 包,发送一个确认包,表明自己接受到了客户端关闭连接的请求,但还没有准备好关闭连接。
发送完毕后,服务器端进入 CLOSE_WAIT 状态,客户端接收到这个确认包之后,进入 FIN_WAIT_2 状态,等待服务器端关闭连接
第三次挥手(FIN=1,seq=y)
服务器端准备好关闭连接时,向客户端发送结束连接请求,FIN 置为1
发送完毕后,服务器端进入 LAST_ACK 状态,等待来自客户端的最后一个ACK
第四次挥手(ACK=1,ACKnum=y+1)
客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT状态,等待可能出现的要求重传的 ACK 包。
服务器端接收到这个确认包之后,关闭连接,进入 CLOSED 状态。
客户端等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后,在这段时间内如果客户端没有收到服务端的重发请求(没有收到服务器端的 ACK ),那么表示 ACK 成功到达,于是自己也关闭连接,进入 CLOSED 状态,挥手结束;否则客户端重发 ACK)
为什么是四次挥手不是三次?
因为服务端收到FIN,不会立即返回FIN,必须等到服务端所有报文发送完毕后,才能发FIN。因此先发一个ACK表示已经收到客户端的FIN,延迟一段时间才发FIN。这就造成了四次挥手。
如果将ACK和FIN合并为一次,即总共三次挥手会出现什么问题?
等于说服务端将ACK和FIN的发送合并为一次挥手,这个时候长时间的延迟可能会导致客户端误以为FIN没有到达客户端,从而让客户端不断的重发FIN
其他问题
TCP和UDP的区别?
- TCP面向流,可靠传输,需要建立连接,只支持一对一
- UDP面向数据包,尽最大努力交付,无需建立连接,支持一对一、一对多、多对多
TCP的粘包半包问题是什么?为什么会出现?怎么解决?
- 粘包:多个数据包“粘在一起”,形成一个大的数据包
- 半包:半包问题是指接收方收到的数据包不完整,只包含了部分数据。这可能是因为发送方发送的数据包在传输过程中被拆分,或者接收方的缓冲区不足以容纳整个数据包
- 原因:
- TCP是面向流的协议,它的特性就是没有边界;NAGLE算法会将小的TCP请求插到其他请求内,这就导致TCP的请求是毫无边界的
- Nagle算法:将小的数据报(没有达到最大报文段长度MSS)的数据包缓存起来,凑几个一起发送
- 解决:
- 发送方和接收方固定发送数据的大小,当字符长度不够时用空字符弥补,有了固定大小之后就知道每条消息的具体边界了,这样就没有粘包的问题了
- 在 TCP 协议的基础上封装一层自定义数据协议,在自定义数据协议中,包含数据头(存储数据的大小)和 数据的具体内容,这样服务端得到数据之后,通过解析数据头就可以知道数据的具体长度了,也就没有粘包的问题了
- 以特殊的字符结尾,比如以“\n”结尾,这样我们就知道数据的具体边界了,从而避免了粘包问题