WQhuanm
网络分层模型及各层功能

网络分层模型及各层功能

网络分层模型

  1. OSI七层模型与TCP/IP4层模型

  2. 报文、数据报、数据包、帧的区别

  3. 数据的封装

网络接口层

  1. 网卡
    • 将接收的电信号转为数字信号,并封装为数据帧。
    • 通过 DMA(直接内存访问) 将数据直接写入内核指定的内存缓冲区(临时存储所有传入的原始网络数据包)
    • 内核的网络协议栈对数据包解封装,直到传输层解封后获得端口信息
      • 如果是SYN包,连接成功后将数据转发到监听目的端口的socket的内核缓冲区
      • 否则,通过数据包4元组找到对应的socket,并把数据转发到对应的内核缓冲区

IP层:对数据包进行路由和寻址

IP报文

  • 源ip/目的ip地址:如果有nat,会修改ip协议的源ip/目的ip地址,否则传输时一直不变
  • IP分片/重组:通过标识字段与片偏移字段来实现
    • tcp数据报一般不会被ip分片,tcp建立连接时会确认传输路径所有结点的ip包最大长度(MTU),保证不会被分片(如果存在有结点的MTU变小,则会返回icmp确认最小的MTU,避免分片)
    • upd数据报过大就会被分片,只有第一个片段有udp头部

常见的IP协议

  1. ICMP:ip传输出错时(网络层错误,无需端口号),用于告知网络包传送过程中产生的错误以及各种控制信息(目标不可达、超时、路由重定向)
  2. NAT(Network Address Translation,网络地址转换)的作用:在不同网络之间转换 IP 地址
  3. ARP(Address Resolution Protocol,地址解析协议):将IP解析为MAC地址

数据包如何在网络中传输与定位:IP与MAC地址

  1. 基本网络设备
    • 交换机:一般与子网内的所有计算机/路由器连接。维护MAC地址表,记录交换机的每个端口连接的计算机设备的MAC地址
    • 路由器:路由器每个端口都有一个独立的MAC地址和IP地址,每个端口指向一个子网。维护路由表,映射ip地址对应哪个端口
    • 计算机:每个计算机设备都有唯一的MAC地址,加入网络时,会分配一个ip地址与相应的子网掩码,并指定其默认网关(子网的路由器对应端口的ip)
  2. 数据包传输流程(MAC头部每到一个结点就需要重新封装,设定当前设备MAC为源MAC,下一个设备为目标MAC)
    • 电脑封装完ip数据包后,通过子网掩码判断目标ip是否同一个子网
      • 是则使用ARP协议获取MAC地址,并通过交换机传输到目标计算机
      • 否则使用ARP协议获取默认网关的MAC地址,通过交换机传输给默认网关去转发
    • 路由器的A端口收到数据包后,查询路由表确认从B端口转发出去,B端口需要更新数据包的目的MAC地址:与目标主机同一子网则获取其MAC,否则是默认网关的MAC

传输层

  • 传输层用于确保进程到进程之间的数据交互(端到端),因此传输层协议引入了端口的概念,用于区分同一个传输层协议下应该把数据包发送给哪个进程的socket
  • 传输层的协议通过源/目的ip、端口来分别一个连接
    • 不同协议的端口互不影响
    • 同一个端口(要求未被某个进程给bind独占了)可以向不同的ip:port发起connect,只要四元组不冲突即可

TCP 与 UDP 的区别

  1. TCP是否面向连接的,是可靠传输,是有状态的,UDP不是
  2. TCP传输的时候多了连接、确认、重传等机制,效率低
  3. TCP 是面向字节流的(将应用层报文以字节流划分为多个tcp段,每个段都有自己的tcp头部)
  4. UDP 是面向报文的(应用层报文只用一个upd头部修饰,分片交给ip)

TCP

TCP报文

  • 标志位
    • ACK :确认号,表示下一个接受的包的SEQ值
    • SYN :同步序列号,用于建立连接,会携带初始序列号(ISN,一个随机数(基于时钟计时器递增),避免阻塞的旧历史报文被新连接接受)
    • RST :出现异常,要求接收方强制关闭连接
      • RST包被接收的前提是ack的序列号是对方接收的合法序列号
    • FIN :发送方不再有数据需要发送,请求终止连接
三次握手

  1. 流程
    • 一次握手:客户端发送带有 SYN(SEQ=x) 标志的数据包到服务端,然后客户端进入 SYN_SEND 状态,等待服务端的确认
    • 二次握手:服务端发送带有 SYN-ACK(SEQ=y,ACK=x+1) 标志的数据包到客户端,然后服务端进入 SYN_RECV 状态;
      • 服务端内核同时把该连接放入半连接队列
    • 三次握手:客户端发送带有 ACK(ACK=y+1) 标志的数据包(可以携带传输数据)到服务端,然后客户端进入ESTABLISHED 状态,服务端接收后也进入ESTABLISHED 状态; TCP 三次握手完成
      • 服务端内核同时把该连接从半连接队列移动到全连接队列,等待后续socket调用accept函数处理
  2. 三次握手的意义
    1. 确保双方都ack了对方的初始序列号 :1,2步确认服务端可接收客户端消息;2,3步确认客户端可接受服务端消息
    2. 防止网络阻塞时,重传的多个SYN包均被建立连接并初始化了
      • 如果是一次握手后服务端就直接进入ESTABLISHED状态,并被分配资源。但是这个连接实际是历史连接(后续的syn连接才是新连接)
      • 服务端中间态SYN_RECV发送的SYN+ACK包,如果ack的不是当前服务端请求的syn包,则客户端会发送RST包终止这个半连接
  3. SYN Flood攻击
    • 短时间伪造不同 IP 地址的 SYN 报文,占满服务端半连接队列导致正常连接无法建立
    • SYN Cookies策略(无状态SYN)
      • 队列占满时,不再分配资源来保持新连接状态
      • 根据四元组,以及一个只有服务器知道的密钥来计算哈希值作为SYN包的序列号并发送SYN-ACK包
      • 客户端回复ACK时,服务端再次计算出该哈希值,若ack序列号与计算的一致则建立连接
四次挥手

  1. 流程(前2次挥手是说明客户端不会再有消息需要发送,后2次挥手是为了说明服务器不会再有消息发送 ;双方都可以主动断开,但是一般会由服务端结束服务后直接断开,减少监听到断开请求再close的损耗)
    • 第一次挥手:发送方调用close函数,并发送一个 FIN(SEQ=x) 标志的数据包到接收方,表示其不再有数据传输,然后发送方进入 FIN-WAIT-1 状态
    • 第二次挥手:接收方发送一个 ACK (ACK=x+1)标志的数据包到发送方,然后进入 CLOSE-WAIT 状态。发送方收到后进入 FIN-WAIT-2 状态。
      • 不同时发送SEQ,是因为接收方可能还有数据未传输完,所以第三次挥手再确认。
    • 第三次挥手:接收方调用close函数,并发送一个 FIN (SEQ=y)标志的数据包到发送方,然后进入 LAST-ACK 状态
    • 第四次挥手:发送方发送 ACK (ACK=y+1)标志的数据包到接收方,然后进入TIME-WAIT状态。接收方在收到 ACK (ACK=y+1)标志的数据包后进入 CLOSE 状态。
      • 此时如果发送方等待 2MSL(Maximum Segment Lifetime,报文最大生存时间) 后依然没有收到回复,就证明接收方已正常关闭,随后发送方也可以关闭连接了(发送方等待防止server为收到ack持续fin确认)。发送方每次接收到接收方的FIN包,都会重置定时器
      • 2MSL的目的是确保ack发送过去后(最多消耗1msl),如果ack未到达,会接收到重传的fin包(又是最多1msl)
      • 保证了超过2msl时直接关闭连接,网络上该tcp连接的报文都被丢弃了,防止旧报文被后续的新tcp连接接收
  2. 服务端time_wait的情况(主动断开tcp连接)
    • http没有使用长连接,每次http响应结束则主动断开tcp
    • http长连接超时 :如果指定时间内,http长连接不再有请求,则会主动断开
    • http长连接的请求总数量达到上限(每条长连接都是有指定可处理多少次请求)
  3. 服务端出现close_wait的情况(客户端主动断开)
    • 建立连接的tcp,没有成功调用accept函数获取socket(或者获取了但是没有成功注册到epoll),导致客户端超时主动断开,但是服务端因为没注册socket到epoll并未感知到
    • 进入close_wait后服务端未成功调用close函数
TCP 传输可靠性保障(校验和、重传机制、流量控制、拥塞控制)
  1. 流量控制(滑动窗口实现,控制发送方发送速率,保证接收方来得及接收)
    • 滑动窗口还有空间时,发送方无需等待ack,可以继续发送数据
    • 每次以收到的最大ack来移动窗口
    • 当接收窗口为0,又一直未接收到接收窗口的恢复,发送方会定时进行接收窗口大小的探测
    • 发送方每次会根据剩余接收窗口的大小,来制作下一个不大于MSS大小的数据包并发送
  2. 拥塞控制:慢启动(指数递增)、拥塞避免(线性增长),流程如下:
    1. 发送方的窗口大小取决于:接收方允许的窗口和拥塞窗口
    2. 当拥塞窗口(CWND)达到或超过拥塞避免阈值(ssthresh) 时,cwnd改为ssthresh,TCP会从慢启动阶段转入拥塞避免阶段。
    3. 数据包超时说明发生网络拥塞(或者根据计算的RTT往返时延来判断),拥塞避免阈值会重新调整为当前拥塞窗口的一半(即减小),拥塞窗口重新设置为1,进入慢启动
    4. 快速恢复 :发生三次重复 ACK,将 ssthresh和CWND调整为当前 CWND 的一半,进入拥塞避免阶段
  3. 重传机制
    • 超时重传:发送的数据包同时会启动一个定时器,规定时间为收到相应ack,触发重传,每次重传间隔增大(指数退避),多次重传仍未收到ack则关闭连接
    • 快速重传:收到多个重复ack(3个以上),则重传ack所需数据包
      • sack(选择性确认) :如果有缺失数据包,该字段会声明后面接收到的数据包,这样快速重传可以把前面缺少的数据包都重传
TCP的一些可选机制
  • TCP保活机制(keepalive)
    • keepalive机制
      • 如果在tcp_keepalive_time内(默认2h)都没有任何数据交互,会触发keepalive
      • 会进行tcp_keepalive_probes(默认9次)次探测,每次间隔tcp_keepalive_intvl(75s),如果对方一直没有对探测响应,则关闭该连接
      • 当接收到数据时,保活时间都会被重置
    • 默认都会在上层实现keepalive逻辑(并在应用层结束时关闭tcp),而不是直接使用tcp的keepalive

socket套接字

  1. socket是os对网络端到端通信的节点的封装(抽象为一个文件描述符fd,用于网络节点通过socket进行数据交换)
    • 服务端socket
      • 监听socket :将创建的socket bind到指定端口,即可调用listen监听
        • 当客户端连接请求经过传输层协议后,便会被内核记录为io事件并通知监听socket处理
        • 监听socket调用accept处理连接事件,创建新的socket用于与客户端数据通信
      • 数据通信socket :由源/目的端口、ip这个4元组唯一标识。应用层通过该socket来进行数据读写
    • 客户端socket :创建的socket将指定ip:port进行connect,成功后即可通过该socket进行数据读写

应用层

DNS解析(nslookup 命令):域名存在于应用层,属于应用层协议

  • A/AAAA :将域名解析为IPv4/IPv6地址
  • CNAME :将域名作为别名指向另一个域名,与其保持相同解析
  • PTR :DNS反解析,将ip解析为域名

HTTP(无状态协议)

HTTP字段
  1. GET 和 POST 的区别
    1. GET 通常用于获取或查询资源,而 POST 通常用于创建或修改资源。
    2. GET 请求是幂等的,即多次重复执行不会改变资源的状态(该请求可以缓存,无副作用),而 POST 请求是不幂等的,即每次执行可能会产生不同的结果或影响资源的状态。
  2. HTTP 状态码
    • 301,302 :永久/临时重定向,会指明新url,由浏览器自动跳转
    • 304 :Not Modified,请求资源未修改,可直接使用缓存
    • 502 :请求路径上存在服务器无法得到其上游服务器的有效响应,存在网络错误
    • 503 :服务器繁忙,当前无法响应
  3. 常见HTTP请求头/响应头
    • Content-Type :声明请求/响应体的数据类型,如application/json(json),text/plain; charset=UTF-8(utf8编码的纯文本)
    • ETag :用于唯一标识请求资源
    • If-None-Match :用于缓存验证,会把之前请求资源的ETag作为value发送给服务器,如果服务器检测ETag未变,则返回304
HTTP1.1:默认使用长连接(客户端可以建立tcp连接池来利用tcp长连接的特性)
  • 单个TCP连接在同一时刻只能处理一个http请求,任意两个 HTTP 请求从开始到结束的时间在同一个 TCP 连接里不能重叠。
    • 在应用层方面存在队头阻塞问题:同一tcp下的http请求,队头阻塞,后面请求都需要等待
    • 流水线(Pipelining)优化(默认不启用,实现复杂且效果不佳):在同一条长连接上发出连续的请求,无需等待响应。服务器需要按接收顺序依次处理请求,响应也必须按顺序返回。
    • http1.1基于文本分割来解析,在遇到分隔符前不能停止解析,不适合划分成基本单位来实现多路复用
  • 性能优化策略
    • 通常采用连接复用(长连接)和同时建立多个TCP连接的方式提升性能。但浏览器对其访问的host域名可建立的TCP连接数有限制(6~8个)
    • http的长连接长时间空闲一般会断开(长时间空闲浪费服务器资源)
    • 域名分片(加速图片等大资源的获取速度):通过将内容拆分到多个子域来提高网页加载速度的技术,突破浏览器对同一域名host可以使用的tcp限制(从多个子域名获取资源,而这些子域名其实都指向同一台目标服务器,这样能并发使用更多tcp连接)
HTTP2:基于https上实现。基于二进制帧(紧凑的二进制结构)来传输数据,不再是明文传输
  1. 基本概念
    • 帧(frame):最小的数据单位,帧包含数据长度、流标识符、帧类型等信息。http请求报文被拆散成帧
    • 消息(message):即http报文(一个请求/响应),消息由帧组成(由头部帧和数据帧组成)
    • 流(stream):一次完整的请求-响应由对应的流id标识,即流是双向的(含有一对请求消息+响应消息)
      • 流id严格递增,不可复用,具有唯一性,其流id对应了一次请求-响应
      • 一般规定客户端发起的流id奇数,服务端发起的流id偶数,避免冲突
      • 同一个流发送的帧要有顺序,接受端按照收到帧的顺序重新组装成消息(http报文)
  2. 多路复用(Multiplexing,一个TCP段可以存在多条流)
    • 同个域名只需要一个 TCP 连接,使用一个连接并行发送多个请求和响应。
    • 一个tcp段会包含多个stream,一个stream可能会分成非连续多次发送。
    • 不同流的帧之间可以乱序发送,因为会按照各自的流id组装
    • 请求优先级:每个流可以设定优先级
    • 流量控制:设定每个流可以占用一个tcp段多少带宽(接收端通过WINDOW_UPDATE帧控制其对应流的流量窗口大小),基于每一跳进行,而非端到端的控制
    • 服务端推送:服务端响应时也可以使用新流推送(预加载)资源给客户端(可被拒绝),减少请求响应次数
  3. 头部压缩(HPACK)
    • 原因:http1.1只针对body进行压缩,而包含协议内容,cookie等字段的请求头,有很多内容重复却没有压缩
    • HPACK 压缩算法
      • 客户端和服务端共同维护了一份字典表,来跟踪和存储之前发送的键值对
      • 每次发送数据时,相同的数据不再通过每次请求和响应发送;新数据/修改数据要么被追加到当前表的末尾,要么替换表中之前的值
  4. 禁用域名分片
    • 破坏多路复用优势:分片强制使用多个连接,反而增加握手和头部冗余。
    • 加剧头部压缩失效:多个子域名的请求需携带独立头部信息,无法充分复用HPACK字典。
  5. 缺点
    • TCP层面的队头阻塞并没有彻底解决:“超时重传”机制,丢失的包必须重传确认,后面的TCP包必须等待
    • 多路复用没有限制同时请求数,可能导致服务器压力上升
    • 多路复用容易 Timeout: 大批量并行多个流,导致每个流的资源会被稀释,虽然它们开始时间相差更短,但却都可能超时

HTTPS(使用 SSL/TLS 协议用作加密和安全认证)

  1. SSL原理
    • 非对称加密:发送方有公匙用于加密,接收方有私匙用于解密。安全性高,但代价高。用于传输对称加密密匙
    • 对称加密:通信双方共享唯一密钥 k,加解密算法已知,双方利用密钥 k 加密/解密。代价低,但保密性差。用于传输数据
    • CA证书:有CA数字签名的服务器合法,其公匙可信,不会被伪造,CA使用CA私匙对服务器的公匙加密附在证书中,把证书颁发给服务器
  2. https传输原理
    1. 客户端先获取服务器证书,使用CA公匙验签后获取服务器公匙(私匙加密,公匙解密用于验签)
    2. 客户端生成对称加密密匙,用服务器公匙加密后发送给服务器(公匙加密,私匙解密,用于传输密匙)
    3. 服务器用服务器私匙解密,获取对称加密密匙,以后双方使用该密匙通讯

URL(Uniform Resource Locators,即统一资源定位器)

  1. URL的组成结构
    1. 协议:URL的前缀采用了何种应用层协议:HTTP/HTTPS/FTP等
    2. 域名(DNS解析域名和ip地址的映射),端口, 资源路径
    3. 请求参数:采用键值对的形式key=value,每一个键值对使用&隔开
    4. 锚点:显示该网页页面的位置,锚点以#开头,且不会作为请求的一部分发送给服务端
  2. 从输入 URL 到页面展示的过程
    1. 在浏览器中输入指定网页的 URL,浏览器通过 DNS 协议(应用层),获取域名对应的 IP 地址
    2. 浏览器根据 IP 地址和端口号,使用socket向ip:port发起connect
    3. connect时会先进行tcp连接请求,成功后该连接被放入全连接队列并唤醒服务端的监听socket
    4. 服务端的监听socket处理连接事件并调用accept建立一个新socket负责与该客户端的通信
    5. connect成功后,浏览器对http请求报文层层封装,并发送到服务端
    6. 服务端内核解封数据包后通过四元组交由对应的socket处理
    7. 服务端应用层通过socket获取到 HTTP 请求报文后,处理请求,并返回 HTTP 响应报文给浏览器
    8. 浏览器收到 HTTP 响应报文后,解析响应体中的 HTML 代码,渲染网页的结构和样式,同时根据 HTML 中的其他资源的 URL(如图片、CSS、JS 等),再次发起 HTTP 请求,获取这些资源的内容,直到网页完全加载显示
    9. 浏览器在不需要和服务器通信时,可以主动关闭 TCP 连接,或者等待服务器的关闭请求。

参考文章

本文作者:WQhuanm
本文链接:https://wqhuanm.github.io/Issue_Blog/2025/01/26/4_网络分层模型及各层功能/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可