计算机网络

计算机网络

七层模型(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不存在

迭代查询

各个级别的域名服务器分工合作,得到最终的域名服务器

当我们在地址栏输入一个网址后的过程

image-20240318200907799

  1. 输入网址https://www.xxx.xxx.xx
  2. 不知道ip时(只有域名),就会进行DNS查询,将域名转换为IP地址
    • 查询浏览器缓存
    • 查询系统缓存 –》hosts文件
    • 查询路由表
    • 查询本地域名服务器,本地域名服务器代理我们访问其域名服务器
      • 查询根域名服务器 得到顶级域名服务器地址
      • 查询顶级域名服务器 得到二级域名服务器(权威域名服务器地址)
      • 查询权威名称服务器
  3. 知道ip后,开始TCP三次握手
  4. 连接建立后开始TLS/SSL握手
  5. 开始安全通信

HTTP

HTTP(Hyper Transfer Protocol超文本传输协议)

无状态

无需连接(底层需要TCP连接)

半双工

HTTP报文结构

请求
请求行
  • 结构是方法、网址、版本号:GETwww.baidu.com HTTP/1.1
  • RESTFUL风格方法
    • GET
    • POST
      • GET POST的区别
        1. get幂等安全,post不幂等不安全。幂等:请求多少次都一样,安全:请求不会更改服务器资源
        2. get参数有限,明文传输;post参数无限
        3. get没有请求体
        4. 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三次握手建立后)
  1. 客户端发起握手:发送ClientHello消息 包含TLS版本、加密套件、第一随机数
  2. 服务器收到回应:发送ServerHello消息 包含TLS版本、加密套件、第二随机数
  3. 服务器发送数字证书给客户端:发送Certificate消息(可选) 包含公钥和证书信息
  4. 服务器端发送ServerKeyExchange消息
    • 对于RSA算法,它的公钥在证书内,不需要发送此消息
    • 对于DH算法,需要在此消息中协商DH参数
  5. 服务器端发送ServerHelloDone:表示消息发送完毕
  6. 客户端认证证书,进行秘钥交换
    • 浏览器收到证书,对照信任证书列表,确认是否可信
    • 如果可信,RSA算法从证书获得公钥,生成48字节的预主秘钥(第三随机数)用公钥加密,发给服务器,服务器用自己的私钥解密,得到预主秘钥
    • 预主秘钥是一个临时的对称秘钥
  7. 服务器和客户端都使用第一随机数和第二随机数生成对称加密秘钥,用于后续通信
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 通信双方均维护一个发送缓存与接收缓存(实现全双工)
    • 发送缓存
      存放两种数据
      1. 还没发出去的数据
      2. 已经发出去还没确认的数据
    • 接收缓存
      1. 按序到达,但是尚未读取的
      2. 未能按序到达的数据

拥塞控制

cwnd

什么是cwnd?

  • 用于控制发送方可以发送到网络上未经确认的数据量,以避免网络拥塞。cwnd的大小会影响数据流量的控制和网络吞吐量
  • 具体来说,cwnd表示了当前发送方可以发送的数据量大小,单位为字节。发送发发送数据时,会根据cwnd的大小来控制发送的数据量,确保网络中的数据包不会过多导致拥塞
  • TCP的拥塞控制算法使用cwnd来动态调整发送数据的速率。
sshresh

什么是sshresh?

  • sshresh(Slow Start Threshold) 是TCP拥塞控制算法中的一个参数,用于指示拥塞避免阶段的门限。当TCP进入拥塞避免阶段时,拥塞窗口cwnd的速率会由指数增长变为线性增长,而sshresh就是触发这个阶段转换的门限值
  • 如何判断出现了阻塞?

    • 出现了超时重传(快重传)
    • 出现了丢包
    • 出现了ACK延迟或是重复
  • 拥塞窗口cwnd

    • 发送方维持

    • 拥塞控制,慢开始门限ssthresh

      1. cwnd<ssthresh (慢开始门限) 每经过一个RTT就翻倍cwnd

        1. cwnd=ssthresh 二者选一
      2. cwnd>ssthresh 拥塞避免 经过一个RTT线性增长cwnd

      3. 出现拥塞

        • 将ssthresh=cwnd/2
        • 将cwnd = 1
      4. 出现三个重复的ACK

        • 快重传
          收到3个重复的确认信息,就判断当前的网络不好,要求接收方进行快重传;
          快重传的意思是:即使是收到了乱序的报文段,也要立即发送确认;并且不要再等待缓存满再发送了,ACK要直接进行发送

        • 快恢复
          一般与快重传一起使用,会设置拥塞窗口更大一点

          拥塞窗口设置为当前值cwnd = ssthresh,而不是设置为1

计时器
  • 连接计时器
  • 重传计时器
  • 保活计时器
  • FIN_WAIT_2计时器
  • TIME_WAIT定时器

三次握手

image-20240319180555403

第一次握手(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 以为这是丢了包,于是重传,两次握手建立好了连接。

看似没有问题,但是连接关闭后,如果这个滞留在网路中的包到达了服务端呢?这时候由于是两次握手,服务端只要接收到然后发送相应的数据包,就默认建立连接,但是现在客户端已经断开了。

看到问题的吧,这就带来了连接资源的浪费。

四次挥手

image-20240319182209169

第一次挥手(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”结尾,这样我们就知道数据的具体边界了,从而避免了粘包问题