计算机网络学习笔记
本文是在阅读JavaGuide关于计算机网络相关内容时的个人整理;
OSI七层模型
很清晰、理论完整,但是太复杂且不适用,所以更多使用tcp/ip协议
- 应用层:为用户提供了与网络进行交互的接口;
- 表示层:进行一些数据处理,例如加密解密、编码解码、压缩和解压缩
- 会话层:管理应用程序之间的会话
- 传输层:为会话之间的通信提供可靠的数据传输
- 网络层:IP数据包在传输时的路由和寻址
- 链路层:对数据帧进行编码、对传输的误差进行纠正
- 物理层:传输比特流数据


TCP/IP四层模型
应用层
应用层:以报文为数据单元,为用户应用程序提供网络服务接口
常见协议:
- 基于TCP:
- HTTP协议(HTTPs):基于 TCP 协议,是一种用于传输超文本和多媒体内容的协议,为 Web 浏览器与 Web 服务器之间的通信而设计的
- Websocket协议:客户端和服务端可以同时发送或接收,基于 TCP 连接的全双工通信,通过心跳机制来保持 WebSocket 连接的稳定性和活跃性
- SMTP协议(简单邮件传输(发送)协议)、IMAP/POP3协议(负责邮件接收的协议)
- FTP协议:文件传输,使用两条tcp连接,一条用于传输控制信息,一条用于数据传送;不安全,SFTP安全
- SSH协议:通过加密和认证机制实现安全的访问和文件传输等业务;Telnet协议:用于终端登录服务器,明文传输不安全
- 基于UDP
- RTP协议⚠️、WebRTC⚠️
传输层
传输层:提供端到端的通用的数据传输。
TCP协议的三次握手和四次挥手:面向连接的、可靠的
UDP协议:无连接的、尽力的
网络层
网络层:为不同网络之间的主机提供通信服务
封装成IP数据包,解析IP地址,选择合适的路由
常见协议:IP协议(定义数据包的格式、对数据包进行路由和寻址)、ARP协议(网络层地址和链路层地址之间的转换问题)、NAT协议(网络地址转换,内部网到外部网)、ICMP协议⚠️、OSPF协议⚠️、RIP协议⚠️
网络接口层
网络接口层:把IP数据包组装成帧,并在相邻计算机节点之间传输比特流,并提供差错检测和修复等等
HTTP相关
> 以下摁背
HTTP1.0和1.1
- 缓存:见缓存
- 长短连接:HTTP1.0使用短连接,每次HTTP操作都需要建立tcp连接,会导致握手挥手报文大量占据带宽;长连接可以设置超时时间,减少保持tcp连接的资源浪费。
- Host头:域名系统(DNS)允许多个主机名绑定到同一个 IP 地址(服务器)上,所以请求中需要包含域名信息,这样服务端才能理解客户端的需求
1 | GET /home.html HTTP/1.1 |
- 带宽优化:加入
Range头部,可以请求**数据(只能是字节型)**的一部分,支持断点续传,例如下载文件到一半
1 | # 请求:获取一个文件的前 1024 个字节 |
HTTP1.1和2.0
- 多路复用:HTTP/2.0 在同一连接上可以同时传输多个请求和响应
- 二进制帧:HTTP/1.1 使用文本格式的报文,HTTP/2.0 使用二进制帧进行数据传输,减少了传输的数据量和带宽消耗
- 头部压缩:HTTP/1.1 支持
Body压缩,但Header不支持。HTTP/2.0 支持对Header压缩,使用的是HPACK算法 - 服务推送:在客户端请求一个资源时,服务器可以将其他相关资源一并推送给客户端,从而减少了客户端的请求次数和延迟。
HTTP2.0和HTTP3.0
- 基于UDP:新增QUIC(Quick UDP Internet Connections)协议来实现可靠的传输
- 握手合并:QUIC 将 TCP 握手、TLS 握手 (内置 TLS 1.3 加密) 以及 HTTP/3 的连接握手合并,并使用更高效的 QPACK 进行头部压缩。
- 无缝迁移:不再依赖 IP 地址和端口,而是通过 Connection ID 来标识;客户端在使用不同网络时(如 Wi-Fi 和 4G 之间切换)时,连接可以无缝迁移
HTTP和HTTPS
HTTP(明文传输, 没有状态):默认端口80,规范浏览器和服务器端的行为。传输的都是明文,不安全。
无状态(stateless)协议,服务器不维护任何有关客户端过去所发请求的消息,有状态协议会更加复杂,需要维护状态(历史信息),而且如果客户或服务器失效,会产生状态的不一致,解决这种不一致的代价更高。
HTTPS(加密传输, 证书认证):默认端口号是 443。额外使用 SSL/TLS 协议用作加密和安全认证。
先使用非对称加密传输对称加密的密钥,来保证密钥的安全性,即服务器先把它的公钥传输给客户端,客户端生成对称加密的密钥后,使用服务器的公钥传输过去,服务器用私钥解密;之后再进行对称加密的通信。
可能会存在以下使用诈包的攻击手段,所以服务器得进行第三方实名认证,这个就是证书(CA, Certificate Authority)的作用
图源:JavaGuide
HTTP缓存
强制缓存
强制缓存优先于协商缓存进行
HTTP1.0使用Expires,它存储一个过期时间,如果在这个时间内就说明缓存有效,但是依赖于浏览器时间
HTTP1.1使用cache-control字段,有6个字段,常用的是max-age表示缓存的有效时间,例如
Cache-Control: max-age=10
cache-control字段:
- max-age决定客户端资源被缓存多久。
- s-maxage决定代理服务器缓存的时长。
- no-cache表示是强制进行协商缓存。
- no-store是表示禁止任何缓存策略。
- public表示资源即可以被浏览器缓存也可以被代理服务器缓存。
- private表示资源只能被浏览器缓存。
协商缓存
需要向服务器发起请求进行协商,由服务器决定是否使用缓存
- HTTP1.0使用Last-Modified / If-Modified-Since
If-Modified-Since是客户端再次发起该请求时,携带上次请求返回的Last-Modified值,通过此字段值告诉服务器该资源上次请求返回的最后被修改时间。服务器收到该请求,发现请求头含有If-Modified-Since字段,则会根据If-Modified-Since的字段值与该资源在服务器的最后被修改时间做对比,若服务器的资源最后被修改时间大于If-Modified-Since的字段值,则重新返回资源,状态码为200;否则则返回304,代表资源无更新,可继续使用缓存文件;
- HTTP1.1使用Etag / If-None-Match
If-None-Match是客户端再次发起该请求时,携带上次请求返回的唯一标识Etag值,通过此字段值告诉服务器该资源上次请求返回的唯一标识值。服务器收到该请求后,发现该请求头中含有If-None-Match,则会根据If-None-Match的字段值与该资源在服务器的Etag值做对比,一致则返回304,代表资源无更新,继续使用缓存文件;不一致则重新返回资源文件,状态码为200;
常见状态码
- 1xx(临时响应):表示临时响应并需要请求者继续执行操作的状态码。
101:切换协议,切换到更高级的协议,例如HTTP新版
- 2xx(成功):表示成功处理了请求的状态码。
201:请求成功创建一个或多个新的资源
204:请求成功,但没有结果返回(不看重请求结果,只关心是否完成)
206:请求一部分资源,成功响应一部分资源(HTTP1.1断点续传)
- 3xx(重定向):要完成请求,需要进一步操作。通常,这些状态码用来重定向。
301:Moved Permanently,资源被永久重定向。比如网站的网址被更换。
- 4xx(客户端请求错误)
400 Bad Request:发送的 HTTP 请求存在问题。比如请求参数不合法、请求方法错误。
401 Unauthorized:未认证却请求需要认证之后才能访问的资源。
403 Forbidden:直接拒绝 HTTP 请求,不处理。一般用来针对非法请求。
404 Not Found:你请求的资源未在服务端找到。比如你请求某个用户的信息,服务端并没有找到指定的用户。
409 Conflict:表示请求的资源与服务端当前的状态存在冲突,请求无法被处理。
- 5xx(服务器错误)
500 Internal Server Error:服务端出问题了(通常是服务端出 Bug 了)。比如你服务端处理请求的时候突然抛出异常,但是异常并未在服务端被正确处理。
502 Bad Gateway:我们的网关将请求转发到服务端,但是服务端返回的却是一个错误的响应,即网关正常运行,但是网关得到的响应是错误的。
请求头和请求体重要字段
- Content-Type的取值
cookie token session的区别
Cookie 是一种在客户端存储数据的技术,它是由服务器发送给客户端的小型文本文件,存储在客户端的浏览器中,大小限制大致在 4KB 左右。在客户端发送请求时,浏览器会自动将相应的 Cookie 信息发送给服务器,服务器通过读取 Cookie 信息,就可以判断该请求来自哪个客户端。Cookie 可以用于存储用户的登录状态、购物车信息等。
session:每一个客户端与服务端连接,服务端都会为该客户端创建一个 session,并将 session 的唯一标识 sessionId 通过设置 Set-Cookie 头的方式响应给客户端,客户端将 sessionId 存到 cookie 中。
sessionId 是 cookie 和 session 之间的桥梁。
sessionId是无意义的,得服务端存储session,然后比对一下才知道sessionId对应哪个用户;但是Token自带用户的信息(虽然也因此长度比较长),不需要服务端存储什么东西,只要解析一下token就知道这是谁了;
token也是服务端生成传给客户端的,一般放在local storage里面而不是cookie,一方面这是约定成俗的事情,另一方面也是为了安全防护,例如CSRF攻击。
CSRF攻击是因为浏览器发送请求会自动携带cookie,而token不放cookie里,所以天然防御,但是放local storage中也有风险
TCP相关
可靠性保证
- 基于数据块传输:分割成数据块(报文段)再给网络层,网络层也可能进一步分割再形成ip数据包;
- 报文段序列号可以用来重新排序和去重;
- 报文段包含首部和数据,接收后会进行校验和,确保数据没有发生差错;
- 流量控制和拥塞控制
访问网页的全过程
- DNS解析,得到IP地址
- 根据IP地址建立TCP连接
- 在TCP连接上发送HTTP请求报文
- 服务器处理请求,返回HTTP响应
- 解析响应体中HTML代码并渲染
- 对HTML中其他需要的资源再次发起请求
- 可以主动关闭TCP连接,或等待服务器关闭
ARQ协议
- 停止等待ARQ协议(这个没有用到滑动窗口协议)
每发完一个数据包就停止发送,等待对方确认(回复 ACK);如果过了一段时间(超时时间后),还是没有收到 ACK 确认,说明没有发送成功,需要重新发送,直到收到确认后再发下一个数据包。
1.1. 发送的数据包丢了:设置一个定时器,自动重传;
1.2. 确认信息丢了or迟到:发送方没收到确认信息,重传,重复的信息直接丢弃。
- 回退 N 步协议 (Go-Back-N ARQ, GBN)
引入了滑动窗口的概念,位于发送窗口内的分组可以连续发送出去,而不需要等待对方确认。
2.1. 接收方累计确认(对最后一个数据包发送确认),信道利用率高;接收方会设置一个计时器(例如每隔500ms发送一次累计确认)
2.2. 滑动窗口中间某个丢了(发送方收到确认时间超时),需要让发送方把丢失的后续都传一遍,导致宽带浪费。
- 选择重传协议 (Selective Repeat ARQ, SR)
发送方和接收方都有滑动窗口,接收方会缓存乱序到达的包,发送方只重传那个出错的包
需要接收方有较大的缓冲区来存储乱序数据
重传机制
⚠️参考:知乎专栏
流量控制
为了控制发送方发送速率,接收方来得及接收和处理;客户端和服务端各自维护发送窗口和接收窗口
滑动窗口:针对报文段的划分方式,大小是根据接收端处理数据的速度动态调整的;tcp三次握手的时候会协调出窗口的大小
为什么这种机制能做到流量控制?为发送和确认的异步性提供了缓冲(又快又慢)
- 快:不仅得发送,还得确认接受,这样就不用阻塞在等待确认上了,可以先发送多个然后一起等待确认;
- 慢:如果一直不确认累积一定数目,就会不发送;只有确认才能让滑动窗口移动;
图源:JavaGuide
拥塞控制
防止过多的数据注入到网络中,维护一个拥塞窗口cwnd,在实际使用中会选取滑动窗口和拥塞窗口的最小值
慢开始和拥塞避免
tcp协议在一开始发送数据时,会让拥塞窗口从小到大倍数增长,即每经过一个传播轮次RTT,cwnd加倍;
但是后续不可能增大这么快,所以等到达阈值后,会使用每经过一个传播轮次RTT,cwnd+1的方式;
快重传和快恢复
FRR-fast retransmit and recovery
- 如果没有 FRR,当数据包丢失了,TCP 将会使用定时器来要求传输暂停。在暂停的这段时间内,没有新的或复制的数据包被发送。
- 有了 FRR,当接收机接收到一个不按顺序的数据段(例如接收了1 3),它会立即给发送机发送一个重复确认(此时依然正常接收其他数据包,例如接收1 3 4)。如果发送机接收到三个重复确认(如果是1个,可能把网络延迟给误判成丢包了),它会假定确认件指出的数据段丢失了,并立即重传这些丢失的数据段。
- 快重传:指接收方接收到三个重复确认,此时立即重传对应序列的报文段;
- 快恢复:发生快重传时,把阈值ssthresh、拥塞窗口cwnd都缩小到当前cwnd的一半,并切换到拥塞避免算法(每经过一个传播轮次RTT,cwnd+1)
图源:王道考研
三次握手
先总说:1. 客户端发送数据包等待服务端确认,2. 服务端确认的同时,也需要客户端确认能否收到自己的数据;3. 客户端确认能收到服务端的数据
- 第一次握手,发送端首先发送一个带SYN(synchronize)标志的数据包给接收方,第一次的seq序列号是随机产生的x,这样是为了网络安全,如果不是随机产生初始序列号,黑客将会以很容易的方式获取到你与其他主机之间的初始化序列号,并且伪造序列号进行攻击。即
SYN x - 第二次握手,接收端收到后,回传一个带有SYN/ACK(acknowledgement)标志的数据包以示传达确认信息;ACK是为了告诉发送端,发送方到接收方的通道没问题,会携带确认号x+1,用来告诉发送方应该发x+1给我;SYN用来验证接收方到发送方的通道没问题,会随机初始化序列号y,即
SYN y, ACK x+1 - 第三次握手,发送端再回传一个带ACK标志的数据包,代表握手结束,会携带序列号x+1和确认号y+1。若在握手某个过程中某个阶段莫名中断,TCP协议会再次以相同的顺序发送相同的数据包,即
SYN X+1, ACK y+1
四次挥手
客户端发送一个FIN,用来关闭服务端到客户端的数据传送,记序列号seq为x,这并不是初始化的
服务端收到这个FIN,它发回一个ACK,确认序号为x+1,此时seq没有额外区别,但也要占用一个序号,记作seq=y
服务端关闭与客户端的连接,发送一个FIN给客户端,此时seq为y+n,因为第二次挥手后可能又发送一些数据
客户端发回ACK报文确认,并将确认序号设置为y+n+1
问题:为什么不能把服务端发送的 ACK 和 FIN 合并起来,变成三次挥手?
因为服务端收到客户端断开连接的请求时,可能还有一些数据没有发完,这时先回复 ACK,表示接收到了断开连接的请求。等到数据发完之后再发 FIN,断开服务端到客户端的数据传送。







