一 基本概念
1 计算机网络体系结构
- 数据在各层之间的传递过程:在向下的过程中,需要添加下层协议所需要的首部或者尾部,而在向上的过程中不断拆开首部和尾部


- 五层协议(讲述原理)
| 名称 | 作用 |
|---|---|
| 应用层 | 为特定进程提供数据传输服务,例如 HTTP、DNS 等协议,数据单位为报文 |
| 传输层 | 为进程提供通用数据传输服务(通用:多种应用可以使用同一个传输层服务),包括两种协议:传输控制协议 TCP,提供面向连接、可靠的数据传输服务,数据单位为 报文段 (主要提供完整性服务);用户数据报协议 UDP,提供无连接、尽最大努力的数据传输服务,数据单位为 用户数据报(主要提供及时性服务 ) |
| 网络层 | 为主机提供数据传输服务,把传输层传递下来的报文段或者用户数据报封装成分组 |
| 数据链路层 | 为同一链路(同一个局域网,不通过路由器转发)的主机提供数据传输服务,把网络层传下来的分组封装成帧 |
| 物理层 | 在传输媒体上传输数据比特流 |
2.OSI七层协议(理论,未采用)
- 应用层、表示层、会话层、传输层、网络层、数据链路层、物理层
- 表示层 :数据压缩、加密以及数据描述,这使得应用程序不必关心在各台主机中数据内部格式不同的问题
- 会话层 :建立及管理会话
- 五层协议没有表示层和会话层,而是将这些功能留给应用程序开发者处理
3.TCP/IP 协议(具体实现)
- 应用层、传输层、网际层、网络接口层
- 相当于五层协议中数据链路层和物理层合并为网络接口层
- 不严格遵循 OSI 分层概念,应用层可能会直接使用 IP 层或者网络接口层
- IP 是最重要的协议:
Everything over IP & IP over everything

2 时延
总时延 = 排队时延 + 处理时延 + 传输时延 + 传播时延
- 排队时延:分组在路由器的输入队列和输出队列中排队等待的时间,取决于网络当前的通信量
- 处理时延:主机或路由器收到分组时进行处理所需要的时间,例如分析首部、从分组中提取数据、进行差错检验或查找适当的路由等
- 传输时延:主机或路由器传输数据帧所需要的时间
- 传播时延:电磁波在信道中传播所需要花费的时间,电磁波传播的速度接近光速
二 应用层:HTTP
- HTTP 本身是无连接的,但它底层的 TCP 是面向连接的,可以保证数据的正确传输
1 请求和响应报文、常见 header
- 客户端发送一个请求报文给服务器,服务器根据请求报文中的信息进行处理,并将处理结果放入响应报文中返回给客户端
- 请求报文
- 请求行:包含请求方法、URL、协议版本
- 请求 Header:多个以冒号分隔的键值对
- 分隔行
- 请求体
GET http://www.example.com/ HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cache-Control: max-age=0
Host: www.example.com
If-Modified-Since: Thu, 17 Oct 2019 07:18:26 GMT
If-None-Match: "3147526947+gzip"
Proxy-Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 xxx
param1=1¶m2=2
- 响应报文
- 响应行:包含协议版本、状态码以及描述
- 响应 Header:多个以冒号分隔的键值对
- 分隔行
- 响应体
HTTP/1.1 200 OK
Age: 529651
Cache-Control: max-age=604800
Connection: keep-alive
Content-Encoding: gzip
Content-Length: 648
Content-Type: text/html; charset=UTF-8
Date: Mon, 02 Nov 2020 17:53:39 GMT
Etag: "3147526947+ident+gzip"
Expires: Mon, 09 Nov 2020 17:53:39 GMT
Keep-Alive: timeout=4
Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
Proxy-Connection: keep-alive
Server: ECS (sjc/16DF)
Vary: Accept-Encoding
X-Cache: HIT
<!doctype html>
<html>
<head>
<title>Example Domain</title>
// 省略...
</body>
</html>
Request header
- 【Accept】 浏览器可接收的数据格式
- 【Accept-Encoding】 浏览器可接收的压缩算法,如 gzip
- 【Accept-Language】 浏览器可接收的语言,如 zh-CN
- 【Connection】 keep-alive 持久性连接,close 非持久性连接
- 【cookie】
- 【Host】 请求的域名
- 【User-Agent】浏览器信息
- 【Content-Type】 发送数据的格式(POST),如 application/json
Response header
- 【Content-Type】 返回数据的格式,如 application/json
- 【Content-length】 返回数据的字节数
- 【Content-Encoding】 返回数据的压缩算法,如 gzip
- 【Set-Cookie】服务器给客户端的 cookie
2 URL & RESTful API
URI/URL
- URI(Uniform Resource Identifier,统一资源标识符)是抽象的标识资源的方式,它的具体实现包括 URL( Uniform Resource Locator,统一资源定位符) 和 URN (Uniform Resource Name,统一资源名称)
- 以上并不代表 URI = URL + URN,而是代表 URL 是 URI,URN 也是 URI
- HTTP 使用 URL 来定位资源
RESTful
- RESTful 架构可以充分的利用 HTTP 协议的各种功能,是 HTTP 协议的最佳实践
- 把 HTTP 方法作为动词,URL 作为名词(视为资源),作为一次请求

- 客户端请求时,要明确告诉服务器,接受 JSON 格式,请求的 HTTP 头的
ACCEPT属性要设成application/json - 服务端返回的数据应该是一个 JSON 对象,服务器回应的 HTTP 头的
Content-Type属性要设为application/json
3 HTTP 协议通信过程
- HTTP 是应用层协议,它以 TCP(传输层)作为底层协议,默认端口为 80
- 服务器在 80 端口等待客户的请求
- 浏览器发起到服务器的 TCP 连接(创建套接字 Socket)
- 服务器接收来自浏览器的 TCP 连接
- 浏览器(HTTP 客户端)与 Web 服务器(HTTP 服务器)交换 HTTP 消息
- 关闭 TCP 连接
4 HTTP 方法
- 安全的 HTTP 方法不会改变服务器的状态。从这个角度出发,GET、HEAD、OPTIONS 是安全的,而 POST、DELETE‘PUT 是不安全的
- 幂等的 HTTP 方法,同样的请求被执行一次与连续执行多次的效果是一样的(幂等方法不应该具有副作用)。从这个角度出发,GET,HEAD,PUT 和 DELETE 等方法都是幂等的,而 POST 方法不是
- GET
获取资源 - HEAD
获取报文首部,和GET方法类似,但是不返回报文实体主体部分
主要用于确认 URL 的有效性以及资源更新的日期时间等 - POST
传输实体主体 - PUT
上传文件
没有验证机制,存在安全性问题,一般不使用 - PATCH
对资源进行部分修改
PUT也可以用于修改资源,但是只能完全替代原始资源,PATCH允许部分修改 - DELETE
与PUT功能相反,并且同样不带验证机制 - OPTIONS
查询指定的 URL 能够支持的 HTTP 方法 - CONNECT
要求在与代理服务器通信时建立隧道
使用 SSL(Secure Sockets Layer,安全套接层)和 TLS(Transport Layer Security,传输层安全)协议把通信内容加密后经网络隧道传输
- TRACE
追踪路径,服务器会将通信路径返回给客户端
发送请求时,在Max-Forwards首部字段中填入数值,每经过一个服务器就会减 1,当数值为 0 时就停止传输
5 HTTP 状态码
- 服务器返回的 响应报文 中第一行为状态行,包含了状态码以及原因短语,用来告知客户端请求的结果

- 常见状态码
| 状态码 | 含义 |
|---|---|
| 200 OK | 成功 |
| 301 Permanently Moved | 永久重定向(浏览器自动处理,每次直接访问新地址) |
| 302 Found | 临时重定向(浏览器自动处理,每次先访问老地址,再重定位到新地址) |
| 304 Not Modified | 资源未修改,不会返回网页内容 |
| 403 Forbidden | 没有权限 |
| 404 Not Found | 资源未找到 |
| 500 Internal Server Error | 服务器错误 |
| 504 Gateway Timeout | 网关超时 |
6 HTTP 1.0 - 1.1 - 2.0
- HTTP 1.0
- 采用短连接,短连接每进行一次 HTTP 通信就要新建一个 TCP 连接
- 缺点是对于持续的数据传输场景,引发频繁的连接建立和拆除
- HTTP 1.1
- 采用长连接,长连接只需要建立一次 TCP 连接就能进行多次 HTTP 通信
- 缺点是维护过多的长连接给服务器造成压力
- 引入 Cookie 功能
- HTTP 2.0
- 使用了多路复用技术,一个 TCP 连接可以并发处理多个请求
- 对 Header 的数据进行压缩,提升数据传输吞吐量
7 Cookie

- HTTP 是无状态的协议,HTTP/1.1 引入 Cookie 来保存状态信息
- Cookie 由服务器发送到用户浏览器,并存放在客户端,在浏览器之后向同一服务器再次发起请求时被携带上,用于告知服务端两个请求是否来自同一浏览器
- 只能存储 ASCII 字符串
- Cookie 创建过程
# 服务器发送的响应报文包含 Set-Cookie 首部字段
# 客户端得到响应报文后把 Cookie 内容保存到浏览器中
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
[page content]
# 客户端之后对同一个服务器发送请求时会从浏览器中取出 Cookie 信息
# 通过 Cookie 请求首部字段发送给服务器
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
8 Session
- Session 存储在服务器端,对用户透明,相较于 Cookie 更加安全
- 敏感数据存放在 Session 中,因为 Cookie 不安全
- 普通数据存放在 Cookie 中,减少服务器资源占用
- Session 通常配合 Cookie 使用,Session ID 被包装在 Cookie 中
- Session 使用过程
- 用户进行登录时,用户提交包含用户名和密码的表单,放入 HTTP 请求报文中
- 服务器验证该用户名和密码,如果正确则把用户信息存储到 Redis 中,它在 Redis 中的 Key 称为 Session ID
- 服务器返回的响应报文的 Cookie 包含了 Session ID,客户端收到响应报文之后将该 Cookie 值存入浏览器中
- 客户端之后对同一个服务器进行请求时会包含该 Cookie 值,服务器收到之后提取出 Session ID,从 Redis 中取出用户信息,继续之前的业务操作
Cookie 被禁用怎么办?
- 常用方法是利用 URL 重写把 Session ID 直接附加在 URL 路径的后面
三 应用层:HTTPS
- 默认端口号是 443
- HTTP 先和 SSL(Secure Sockets Layer)通信,再由 SSL 和 TCP 通信,也就是说 SSL 作用于 HTTP 和 TCP 之间
- 通过使用 SSL,HTTPS 具有了加密(防窃听)、认证(防伪装)和完整性保护(防篡改)

1 加密方式
对称密钥:加密和解密使用同一密钥

非对称密钥:公开密钥所有人都可以获得,通信发送方获得 接收方的公开密钥 之后,就可以使用公开密钥进行加密,接收方收到通信内容后使用私有密钥解密

HTTPS 采用混合加密机制:
- 使用 非对称密钥 加密方式,传输 对称密钥 加密方式所需要的 Secret Key,从而保证安全性;
- 获取到 Secret Key 后,再使用 对称密钥加密方式进行通信 ,从而保证效率
2 证书认证
根本目的是传递服务器的公钥
- 数字证书认证机构(CA,Certificate Authority)是客户端与服务器双方都可信赖的第三方机构
- CA 知道服务器的公钥,向服务器颁发证书,并附上 CA 私钥对消息摘要的加密签名
- 服务器获得 CA 办法的证书,并将证书传递给客户端
- 客户端获得证书,并且客户端知道 CA 公钥,使用 CA 公钥对证书上的签名解密,同时对消息进行散列处理得到摘要
- 比较摘要,验证证书的真实性。如果客户端验证服务器的证书是真实的,则信任服务器的公钥


四 应用层:其它协议
1 DNS
- DNS 是一个分布式数据库,提供了主机名和 IP 地址之间 相互转换 的服务
- 域名具有层次结构,从上到下依次为:根域名、顶级域名、二级(权威)域名
- 每个 ISP 都有一个本地域名服务器
- 一台主机发送 DNS 查询请求时,请求报文会发送到本地域名服务器
- 本地域名服务器不属于层次结构
- 本地域名服务器无法处理的请求,首先将请求转发到根域名服务器

- DNS 可以使用 UDP 或者 TCP 进行传输,使用的端口号都为 53
- 一般使用 UDP 进行传输,除非返回的响应超过的 512 字节(UDP 最大只支持 512 字节的数据)
- 查询可以通过两种方式:递归查询和迭代查询


2 FTP
- 使用两个 TCP 连接进行文件传输,分别是数据连接和控制连接
- 数据连接:用来传送文件数据
- 控制连接:服务器打开端口号 21 等待客户端的连接,客户端主动建立连接后,使用这个连接将客户端的命令传送给服务器,并传回服务器的应答
- 根据 数据连接 (而非控制连接,因为控制连接一定是客户端主动建立的)是否是服务器端主动建立,FTP 有主动和被动两种模式
- 主动连接:服务器端主动建立数据连接,服务器端口号为20,客户端端口号随机
- 被动模式:客户端主动建立数据连接,客户端端口号由客户端自己指定,服务器端口号随机
3 SMTP、POP3、IMAP

- SMTP 是发送协议,POP3 和 IMAP 是读取协议
- 因为需要保证邮件传递可靠,所以都基于 TCP 连接
如何判断邮箱是否存在?利用 SMTP 协议
- 查找邮箱域名对应的 SMTP 服务器地址
- 尝试与服务器建立连接
- 连接成功后尝试向需要验证的邮箱发送邮件
- 根据返回结果判定邮箱地址的真实性
4 DHCP
- 动态主机配置协议提供了即插即用的连网方式:新加入网络的主机,不再需要手动配置 IP 地址等信息
- 包含了 RARP 协议,实现 MAC 地址到 IP 地址的映射
- 通常通过 DHCP 中继代理(路由器)和位于其它网络的 DHCP 服务器通信,而不是在每个局域网配置 DHCP 服务器
- 客户端发送 Discover 报文,该报文的目的地址为 255.255.255.255:67,源地址为 0.0.0.0:68,被放入 UDP 中,该报文被广播到同一个子网的所有主机上。如果客户端和 DHCP 服务器不在同一个子网,就需要使用中继代理
- DHCP 服务器收到 Discover 报文之后,发送 Offer 报文给客户端,该报文包含了客户端所需要的信息。因为客户端可能收到多个 DHCP 服务器提供的信息,因此客户端需要进行选择
- 如果客户端选择了某个 DHCP 服务器提供的信息,那么就发送 Request 报文给该 DHCP 服务器
- DHCP 服务器发送 Ack 报文,表示客户端此时可以使用提供给它的信息

五 传输层:TCP、UDP
- 传输层的主要功能
- 复用:发送方不同的进程可以使用同一个传输层协议传送数据
- 分用:接收方的传输层在去掉首部后,可以把数据正确地交付给目的进程
- 端口是应用层和传输层交互的地点,端口号仅具有本地意义(不同的主机,相同的端口不存在关联)
1 两种协议的区别
| UDP - User Datagram Protocol | TCP - Transmission Control Protocol |
|---|---|
| 无连接 | 面向连接 |
| 尽最大可能交付 | 提供可靠交付 |
| 面向报文(对于应用程序传下来的报文不合并也不拆分,只是添加 UDP 首部) | 面向字节流(把应用层传下来的报文看成字节流,把字节流组织成大小不等的数据块) |
| 没有拥塞控制、流量控制 | 有流量控制,拥塞控制 |
| 支持一对一、一对多、多对一和多对多的交互通信 | 每一条 TCP 连接只能是点对点的(一对一) |
- TCP 是面向字节流的,每一个字节都按顺序编号
- 发送方 TCP 将应用层交付的数据视为无结构的字节流
- 接收方 TCP 将字节流交付给应用层,所以应用层必须有能力识别收到的字节流,将其还原成有意义的应用层数据
- 显然 TCP 的连接是逻辑连接,因为网络层的 IP 是无连接的,所以 TCP 不可能是物理连接
- TCP 的连接指的是发送方和接收方同时维护的逻辑上认为是连接的状态
2 TCP
2.1 报文段首部格式

| 字段 | 含义 |
|---|---|
| 序号 | 当前报文段发送的数据,第一个字节的序号 |
| 确认号 | 由接收方向发送方传递,期望收到对方下一个报文段的序号(若确认号为N,则序号N-1及之前的所有数据都正确收到) |
| 数据偏移 | 指出当前报文段的数据起始处(即报文段的首部长度) |
| 标志位 URG | 设置为1时,表示当前报文段含有紧急数据,应当尽快传送;配合紧急指针使用 |
| 标志位 ACK | 仅当设置为1时,确认号才生效 |
| 标志位 SYN | 设置为1时,表示当前报文是连接请求或连接接受报文 |
| (接收)窗口 | 由接收方向发送方传递,允许发送方发送的字节数 |
| 检验和 | 校验报文段头部和数据的正确性;类似 UDP,计算校验和同样需要伪首部 |
| 紧急指针 | 指出紧急数据的字节数(紧急数据在报文段数据部分的最前面),紧急数据处理完时,TCP 告知应用程序恢复正常操作 |
2.2 三次握手

- 三次握手的目的是建立可靠的通信信道,让双方确认自己与对方的发送与接收是正常的
- 第一次握手(SYN):Client 什么都不能确认;Server 确认了对方发送正常,自己接收正常
- 第二次握手(SYN/ACK):Client 确认自己发送、接收正常,对方发送、接收正常;Server 确认对方发送正常,自己接收正常
- 第三次握手(ACK):Client 确认自己发送、接收正常,对方发送、接收正常;Server 确认自己发送、接收正常,对方发送、接收正常
- 为什么需要第三次握手
- A 第一次发出连接请求,因为网络阻塞的原因没有送达 B
- A 第二次发出连接请求,连接建立成功,传输数据后连接成功关闭
- 假设此时 A 第一次发出的请求到达 B,如果没有第三次握手,A 和 B 将会再次建立连接,但没有数据传输,造成资源浪费
2.3 四次挥手

TCP 是全双工的,前两次挥手拆除客户端->服务器的连接(客户端无需再向服务器发送数据),后两次挥手拆除服务器->客户端的连接(服务器无需再向客户端发送数据)
四次挥手的过程
- 第一次挥手(FIN):客户端-发送一个 FIN,用来关闭客户端到服务器的数据传送
- 第二次挥手(ACK):服务器-收到这个 FIN,它发回一 个 ACK,确认序号为收到的序号加 1 。和 SYN 一样,一个 FIN 将占用一个序号
- 第三次挥手(FIN/ACK):服务器-关闭与客户端的连接,发送一个 FIN 给客户端
- 第四次挥手(ACK):客户端-发回 ACK 报文确认,并将确认序号设置为收到序号加 1
为什么客户端接收到服务器端的 FIN 报文后,进入 TIME_WAIT 状态而不是 CLOSED 状态
- 确保 A 发送的最后一个确认报文能够到达,如果 B 没收到 A 发送来的确认报文,那么就会重新发送连接释放请求报文(超时重传)
- TIME_WAIT 状态持续 2MSL(如果报文段在网络中的活动时间超过 MSL 未被接收,那么就会被丢弃),等待比较长的时间,确保本次连接传输的所有报文段都被接收或丢弃
2.4 TCP 如何实现可靠传输
- TCP 给发送的每一个包进行编号,接收方对数据包进行排序,把有序数据传送给应用层
- TCP 报文段校验和:端到端的检验和,如果收到段的校验和有差错,TCP 将丢弃这个报文段
- TCP 的接收端会丢弃重复的数据
- 流量控制:当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失(接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率)
- 拥塞控制:当网络拥塞时,减少数据的发送(发送方的 发送窗口大小,即已发送未确认的大小 为 拥塞窗口 和接收方的 接收窗口 中较小的一个:
LastByteSent - LastByteAcked = min{receive_window, crowd_window}) - 超时重传: 当 TCP 发出一个段后,启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到确认,将重发报文段
2.5 滑动窗口协议
GBN 回退N步协议
- 限制发送方 已发送但未确认的分组数 最大为N
- 采用 累计确认 的机制
- 优点是接收方不需要缓存乱序分组,只需关注下一个分组的序号
- 缺点是丢弃乱序到达的分组,发生重传的分组数增加
SR 选择重传协议
- 发送方只重传真正丢失的分组
- 发送方只确认已经到达的分组的序号,而不采用累计确认
- 接收方缓存失序到达的分组,同时接收方永远不会把分组失序地交给应用层,而是先要等待那些更早发送的分组到达
2.6 流量控制
- 流量控制是速度匹配服务,即发送方的发送速率和接收方的接收速率相匹配,发送方的发送速度不能超过接收方的处理速度
- 流量控制是端到端的问题
- 当接收方的接收窗口为0,即缓存满的时候,发送方定时发送只有一个字节数据的报文段(零窗口探测报文段),保证了后续的传输能正常进行
2.7 拥塞控制
- 出现拥塞的原因是数据输入的速度大于网络节点的处理速度(网络瓶颈)
- 拥塞控制是网络全局的问题
- 如果简单地将网络的某个部分的处理速率提高,可能会缓解拥塞情况,但往往将瓶颈转移到其它地方。因为问题的本质是整个系统的各个部分不匹配,只有所有部分都平衡了,拥塞问题才能解决
- TCP 采用的是端到端的拥塞控制解决方法,限制发送放的发送速率
慢启动
- 慢启动指,TCP 发送速率起始慢,但以指数增长
- 初始发送时,cwnd 通常设置为一个较小值 MSS,发送报文段后等待确认,如果收到确认则 cwnd 翻倍
- 到达慢启动阈值(默认
ssthresh = 16,丢包时设置慢启动阈值ssthresh = cwnd / 2)时,停止慢启动,进入拥塞避免
拥塞避免
- 当 cwnd 到达慢启动阈值时,接近拥塞状态,此时 cwnd 线性增长
- 如果遇到拥塞,
ssthresh = cwnd / 2,根据产生拥塞的原因采取不同的策略- 如果收到3个重复 ACK,说明网络还能够传输一些报文段,进入 3.快速重传、快速恢复
- 如果发生超时,说明发送严重拥塞,设置
cwnd = 1,进入 1.慢启动

- 快速重传、快速恢复
- 快速重传:当收到3个重复ACK时,立刻重传丢失的报文段
- 快速恢复:当收到3个重复ACK时,
cwnd = cwnd / 2 - 快速恢复的改进:当收到3个重复ACK时,
cwnd = ssthresh + 3- 加3是因为收到3个重复的ACK,表明有3个旧数据包离开了网络
- 再收到重复的ACK时,
cwnd++ - 收到新的数据包的ACK时,
cwnd = ssthresh,原因是确认了新的数据,说明重复ACK的数据都已收到,可以回到恢复之前的状态(拥塞避免状态)
- 结合流量控制、拥塞控制得出结论:发送窗口上限 = MIN(接收窗口, 拥塞窗口)
3 UDP
- UDP 在 IP 的基础上增加了很少的功能:复用和分用、简单的差错检测
- UDP 校验和同时检验 UDP 用户数据报的首部和数据部分,检测错误的能力不强,但是计算效率高
- 伪首部仅用于计算 UDP 校验和,不向上层提交,也不向下层传递

- UDP 是面向报文的,对应用层交付的报文不做合并和拆分,添加首部后直接交付给网络层
- 如果报文过长,IP 协议需要将其拆分到多个 IP 数据报,导致效率降低
- 如果报文过短,IP 数据报的首部相对长度太大,同样会降低效率
- 如果接收方 UDP 发现报文中的目的端口号不正确,则丢弃报文,并通过 IP 数据报携带 ICMP 报文,发送端口不可达的差错报文给发送方
4 TCP 引发的应用层粘包、半包问题
TCP 是面向字节流的,本身不存在“包”的概念,粘包、半包都是应用层面临的问题
例子:A用桶往水管里倒水,水进入水管后就没有了属于哪一桶的概念UDP是没有半包、粘包的问题,因为 UPD 是面向用户数据报的,可以根据消息的格式区分消息的开始和结尾,UDP 和 TCP 两个发送消息就好像一个用桶运水,一个用水管运水,用水管运水的你是没办法区分那部分的水是属于哪一桶的
例子:A用桶向目的地运水,水属于哪个桶是可以划分的发生半包的原因:TCP 报文段大小有限制,应用发出的消息包过大,会执行拆分
发生粘包的原因:当应用发送数据包太小,TCP为了减少网络请求次数的开销,会等待多个消息,打成一个 TCP 数据包一次发送出去
要解决粘包、半包问题,本质上是给 TCP 传输的数据流定义消息边界
长度边界
- 应用层在发送消息的时候指定每个消息的固定长度,当接收方读取消息的时候,每次也截取固定长度的流作为一个消息解析
- 这种方式的问题在于应用层不能发送超过固定大小的数据,所以使用这种方式的前提知道了消息大小会在哪个范围之内,如果不能确定消息的大小范围不太适合用这种方式,这样会导致大的消息发出去会有问题,小的消息又需要大量的数据填充
符号边界
- 应用层在发送消息前和发送消息后标记一个特殊的标记符,接收方根据标记符确定消息的边界
- 选取不会在消息中出现的符号,避免混淆
组合边界
- 定义一个 Header+Body 格式,Header 消息头里面定义了开始标记+内容的长度,这个内容长度就是 Body 的实际长度,Body 里面是消息内容
- 当接收方接收到数据流时,先根据消息头里的特殊标记来区分消息的开始,获取到消息头里面的内容长度描述时,再根据内容长度描述来截取 Body 部分
5 使用 UDP 时如何使应用更可靠
- 类比 TCP 实现可靠传输,将这些功能在应用层实现
- 例如给用户数据报指定序号、确认号,实现按序交付,丢弃重复数据报
- 设计超时重传机制
六 网络层
1 网络层基本概念
- 路由器的功能
- 路由选择(控制层):维护路由表
- 分组转发(数据层):将 IP 数据报转发到下一跳的目标
- 路由器是划分网络的边界
- 两台主机通信无需路由器转发,则位于同一个局域网内,分组转发可以直接交付;否则属于不同的网络,分组转发需要间接交付(先交付给某个路由器)
- 在 CIDR 的基础上,网络前缀不同,网络就不同
- 无分类编址 CIDR
- IP 地址的格式为
{网络前缀+主机号, 子网掩码},子网掩码用于标志网络前缀的位数 - 路由器一般具有多个 IP 地址(同时属于多个局域网),且不同的地址网络前缀不同
- 如果在某个局域网内,网络前缀为31位,即局域网最多只有两个 IP 地址,那么这个地址块专门给点对点的连接使用,这种网络称为匿名网络
- CIDR 的优点
- 替换了之前的分类编址方案,使 IP 地址分配更方便
- 路由器根据目的主机的网络前缀(而非具体的 IP 地址)来转发分组,大大减少了转发表的条目
- IP 地址的格式为
2 基础协议:IP
- IP 协议提供不可靠的、无连接的、无序的、尽力交付的 IP 数据报传输服务
- IPv4 数据报的格式

| 字段 | 含义 |
|---|---|
| 版本 | IPv4/IPv6 |
| 首部长度 | 固定部分的长度是20字节;首部长度必须是4字节的整数倍(如果不是则填充) |
| 总长度 | 首部+数据的总字节数 |
| 标识 | IP数据报超过了数据链路层的 MTU(最大传送单元),需要分片时,标识相同的分片属于同一数据报 |
| 标志 | 低两位有用:MF(后面还有分片),DF(不能分片) |
| 片偏移 | 偏移单位为8字节 |
| 生存时间(跳数限制) | IP 数据报还能经历的跳数,如果初始值设置为1,则只能在本局域网传送 |
| 协议 | 指出数据部分的协议,例如 TCP/UDP/ICMP/IP 等 |
| 首部校验和 | 仅校验 IP 数据报首部是否出现错误,不对数据部分负责 |
(加粗字段和数据链路层分片相关)
3 IP->MAC 地址映射:ARP
介于网络层和数据链路层之间,无需纠结具体属于哪层
作用是在 同一个局域网内(不经过路由器转发) 将 IP 地址映射为 MAC 地址(对于不同局域网的主机,MAC 地址也无意义)
仅用于 IPv4
执行流程

- HostA 首先查看自己的 ARP 表项,确定其中是否包含 HostB 的 IP 地址对应ARP表项
- 如果找到了对应的表项(缓存命中),则 HostA 直接理由 ARP 表项中的 MAC 地址对 IP 数据包封装成帧,并将帧发送给 HostB
- 如果找不到对应的表项(缓存不命中),则暂时缓存该数据包,然后以广播方式发送一个 ARP 请求报文
- ARP 请求报文中的发送端 IP 地址和发送端 MAC 地址为 HostA 的 IP 地址和 MAC 地址;目标IP地址 HostB 的 IP 地址,目标 MAC 地址为全 0
- 由于 ARP 请求报文以广播方式发送,该网段上的所有主机都可以接收到该请求
- HostB 比较自己的 IP 地址和 ARP 请求报文中的目标IP地址,发现两者相同,HostB 将 ARP 请求报文中的发送端(HostA)IP 地址和 MAC 地址存入自己的 ARP 表中,并以单播方式 HostA 发送 ARP 响应,其中包含了自己的 MAC 地址;其他主机不做应答
- HostA 收到 ARP 响应报文后,将 HostB 的 MAC 地址加入到 ARP表中,同时将 IP 数据包用此 MAC 地址为目的地址封装成帧并发送给 HostB
- HostA 首先查看自己的 ARP 表项,确定其中是否包含 HostB 的 IP 地址对应ARP表项
RARP
- 和 ARP 相反,将 MAC 地址映射为 IP 地址
- 目前包含在 DHCP 协议中
- 将打印机服务器等小型嵌入式设备接入网络时会使用到
4 传递网络层控制信息:ICMP

- ICMP 报文封装在 IP 数据报的数据部分
- ICMP 报文分为两种类型
- 差错报告报文:终点不可达、超时、参数问题、重定向
- 询问报文:
Echo、Timestamp
- ICMP 的一个重要应用是 PING,使用了
Echo的请求与应答报文,用来测试主机之间的连通性
5 内部网关协议:RIP & OSPF
内部网关协议是 AS(自治系统)内的路由选择协议

RIP
- 不能在两个网络之间同时使用多条路由,只能选择跳数最少的路径
- 仅和 AS 内相邻的路由器交换信息(路由表),即:到 AS 内所有网络的最短距离,以及下一跳的目标路由器
- 跳数限制是15,超过15认为不可达
- 缺点是网络故障时需要较长的时间才能将故障信息传递给所有路由器(“坏消息传播得慢”)
OSPF
- 使用洪泛法向 AS 内所有的路由器发送消息
- 最终所有的路由器都能获得全网的拓扑结构(有多少路由器、哪些路由器相连、代价…);相比之下 RIP 没有全网拓扑结构,只有到了下一跳路由器才知道再下一跳怎么走
6 外部网关协议:BGP
- 外部网关协议是 AS 间的路由选择协议,基于 iBGP 和 eBGP 连接传输 BGP 报文
- 不同 AS 的边界路由器使用 eBGP 连接
- 同一 AS 的所有路由器,即使没有物理连接,也要存在 iBGP 连接
- BGP 路由寻找的是 可以达到目标网络前缀,且较好(而非最佳)的路径
七 常见问题
1 从输入 URL 地址到显示主页的过程
- 用户输入 URL,通过 UDP 连接向 DNS 服务器请求获取域名对应的 IP 地址
- 用户获得 IP 地址,与服务器建立 HTTP 连接(HTTP 基于 TCP 连接,三次握手)
- 用户向服务器发送 HTTP Request
- 服务器处理请求(这里又可以展开到 Spring MVC 的执行流程),返回 HTTP Response
- 浏览器渲染页面
2 HTTP:GET 和 POST 的区别
- GET 一般用于请求,POST 一般用于表单提交
- GET 方法是不安全的,因为会把请求参数拼接到 URL 的后面;POST 可以利用请求体传递参数,用户不可见
- 两种方法都是不安全的,因为 HTTP 本身是一个明文协议,每个 HTTP 请求和响应报文在网络上都是明文传播,想要安全应该使用 HTTPS
- POST 方法不是幂等的,因为幂等性属于语义范畴,HTTP 无法通过语法手段实现
- 就像编译器只能检查语法错误,无法检测语义(逻辑)的问题
| GET | POST | |
|---|---|---|
| 作用 | 请求服务器返回资源 | 表单提交,将信息提交给服务器 |
| 请求参数 | 请求参数会拼接到 URL 后面 | 请求参数在请求体中,用户不可见 |
| 请求参数长度限制 | 参数拼接到 URL 中,长度有限 | 参数在请求体中,长度无限制 |
| 后退或刷新 | 无害 | 数据被重新提交 |
3 应用进程跨网络的通信与操作系统的联系


- 从五层模型来看,应用层处于用户态,传输层及之下处于内核态
- 通过系统调用,从用户态转为内核态,将控制权从用户传递给操作系统
- 套接字是进程为了获得网络通信服务,而与操作系统进行交互的机制
- 只要应用程序使用 TCP/IP 协议通信,就必须通过套接字和操作系统交互,向其请求服务
- 创建套接字时,操作系统给进程分配了网络通信需要的系统资源(内存、CPU时间片、网络带宽等),这些资源由套接字描述符(小的整数)表示