计算机网络
OSI七层模型
OSI 七层模型把网络通信分为七个层次,每一层都有特定的功能,层与层之间通过接口交互。模型的目的是便于不同系统之间实现标准化互联。
物理层
- 作用:负责比特流的传输(0 和 1),定义机械、电气、功能和过程特性
- 关键点:传输介质、接口、电压、电缆标准。
- 设备:网线、光纤、集线器、中继器。
- 协议/标准:RJ45、IEEE 802.3(以太网)、RS232。
数据链路层
- 作用:将比特流组合成帧,保证点对点传输的可靠性,进行差错检测与纠正
- 关键点:MAC 地址、流量控制、差错检测(CRC)。
- 设备:交换机、网卡。
- 协议:以太网、PPP、HDLC、帧中继。
网络层
- 作用:负责选择路径(路由)、实现源到目的主机的逻辑寻址。
- 关键点:IP 地址、路由选择。
- 设备:路由器。
- 协议:IP、[[因特网控制报文协议ICMP|ICMP]]、IGMP、OSPF、BGP。
传输层
- 作用:提供端到端的可靠传输,进行分段、流量控制、错误恢复。
- 关键点:端口号,面向连接 / 无连接。
- 设备:[[防火墙]](部分工作在此层)。
- 协议:[[TCP]](可靠,面向连接)、[[UDP]](无连接,效率高)。
会话层
- 作用:建立、管理和终止会话,负责对话的同步。
- 关键点:控制应用之间的对话。
- 例子:RPC、SQL 会话、NetBIOS。
表示层
- 作用:数据的表示、转换和加密解密。
- 关键点:数据压缩、加密、编码格式转换。
- 例子:JPEG、MP3、TLS、SSL、加密协议。
应用层
- 作用:为用户提供各种应用服务,是用户与网络交互的窗口。
- 关键点:直接面向用户的应用。
- 协议:HTTP、FTP、SMTP、DNS、Telnet。
TCP/IP网络模型
应用层:最上层的,直接能接触到的,应用软件APP就是在应用层实现的。比如HTTP、DNS
传输层:是为应用层的应用传输之间提供网络支持,也就是通信。TCP
传输层是对网络层功能的补充和增强,弥合网络层提供的原始、不稳定的服务与应用层对高质量通信的需求之间的差距。
网络层:专注于数据包在整个互联网中的流动。它关心的是数据包“从哪到哪”(哪个主机),以及“走哪条路(路由)。IP
网络接口层:最底层,直接与物理网络硬件(如网卡、网线、光纤等)打交道。主要职责是提供将数据包传输到物理链路上
网络接口层的传输单位是帧(frame),IP 层的传输单位是包(packet),TCP 层的传输单位是段(segment),HTTP 的传输单位则是消息或报文(message)。但这些名词并没有什么本质的区分,可以统称为数据包。
HTTP(S)
超文本传输协议
超文本:文字、图片、视频等的混合体,有超链接,就像HTML。
传输:双向的
协议:一种行为约定和规范
常见的状态码
HTTP的主要字段
Header
通用头:适用于请求和响应,不关乎数据本身
Connection:一般设置成Keep-Alive,也就是HTTP长连接。
实体头:描述消息体内容的元数据,关乎数据本身
Content-Length:表明消息体的字节长度,解决粘包问题的关键。
Content-Encoding:消息体的压缩格式。
Content-Type:消息体的格式类型。
请求头:仅用于客户端发出的请求
Host:就是客户端发送请求时候要访问的服务器的地址,或者是IP+端口或者是域名,都可能成为Host。
Cookie:客户端向服务器发送先前存储的 Cookie 数据。
响应头:仅用于服务器发出的响应
Set-Cookie:服务器用来向客户端设置 Cookie。
Body
格式及对应Content-Type类型
JSON:application/json
HTML:text/html
HTTP的版本
| 特性 | HTTP/1.0 | HTTP/1.1 | HTTP/2.0 | HTTP/3.0 |
|---|---|---|---|---|
| 底层协议 | TCP | TCP | TCP | QUIC (基于 UDP) |
| 连接管理 | 非持续连接 (每次请求需建立/关闭) | 持续连接 (默认) | 持续连接 | 持续连接 |
| 请求方式 | 串行 | 串行 (非流水线) 或 乱序 (流水线) | 多路复用 (乱序) | 多路复用 (乱序) |
| 头部传输 | 文本格式 (冗余) | 文本格式 (冗余) | 头部压缩 (HPACK) | 头部压缩 (QPACK) |
| 队头阻塞 | 严重 (连接级别) | 严重 (应用层/流水线级别) | 解决 (应用层) | 彻底解决 (传输层) |
| 域名分片 | 无 | 需使用 6-8 个 TCP 连接分片 | 不需要 (单连接多流) | 不需要 (单连接多流) |
HTTPS
HTTPS的改进
HTTPS 是基于 HTTP 的,也是用 TCP 作为底层协议,并额外使用 SSL/TLS 协议用作加密和安全认证。默认端口号是 443.
SSL/TLS
先用 慢但安全 的 非对称加密 和 数字签名 (握手),解决 身份认证 和 密钥交换 的问题;再用 快但高效 的 对称加密 (传输),解决 大数据量加密 的性能问题。
作用:结合TCP协议对通信数据进行加密,同时通过数字证书保障身份认证
非对称加密:一个公钥,一个私钥,可以实现 “公钥加密,私钥解密” 来安全地传递秘密,或实现 “私钥签名,公钥验证” 来进行身份认证。
用处:刚链接的时候
对称加密:一个秘钥,加密解密都是一个
用处:一个会话密钥。用于 TLS 数据传输阶段
Get和Post的区别
语义(主要区别):GET 通常用于获取或查询资源,而 POST 通常用于创建或修改资源。
- 幂等:GET 请求是幂等的,即多次重复执行不会改变资源的状态,而 POST 请求是不幂等的,即每次执行可能会产生不同的结果或影响资源的状态。
- 格式:GET 请求的参数通常放在 URL 中,形成查询字符串(querystring),而 POST 请求的参数通常放在请求体(body)中,可以有多种编码格式,如 application/x-www-form-urlencoded、multipart/form-data、application/json 等。GET 请求的 URL 长度受到浏览器和服务器的限制,而 POST 请求的 body 大小则没有明确的限制。不过,实际上 GET 请求也可以用 body 传输数据,只是并不推荐这样做,因为这样可能会导致一些兼容性或者语义上的问题。
- 缓存:由于 GET 请求是幂等的,它可以被浏览器或其他中间节点(如代理、网关)缓存起来,以提高性能和效率。而 POST 请求则不适合被缓存,因为它可能有副作用,每次执行可能需要实时的响应。
- 安全性:GET 请求和 POST 请求如果使用 HTTP 协议的话,那都不安全,因为 HTTP 协议本身是明文传输的,必须使用 HTTPS 协议来加密传输数据。
TCP
TCP和UDP区别
| 区别 | TCP | UDP |
|---|---|---|
| 连接方式 | 面向连接的,必须建立连接 | 无连接的,直接把数据包扔过去 |
| 是否可靠 | 可靠,见保证可靠性部分 | 不可靠,尽最大努力交付,不一定送到,也无所谓顺序 |
| 是否有状态 | 有状态,见保证可靠性部分 | 无状态,不维护状态,同不可靠的特点 |
| 传输效率 | 低 | 高 |
| 传输形式 | 字节流 | 报文,给多大块发多大块 |
| 对应关系 | 点对点一对一 | 一对一、一对多 |
TCP:面向连接的、可靠的、基于字节流的数据传输服务。
UDP: 提供无连接 的,尽最大努力 的数据传输服务(不保证数据传输的可靠性),简单高效。
三次握手
- 第一次握手 (SYN): 客户端向服务端发送一个 SYN报文段,其中包含一个由客户端随机生成的初始序列号,例如 seq=x。发送后,客户端进入 SYN_SEND 状态,等待服务端的确认。
第二次握手 (SYN+ACK)
: 服务端收到 SYN 报文段后,如果同意建立连接,会向客户端回复一个确认报文段。该报文段包含两个关键信息:
- SYN:服务端也需要同步自己的初始序列号,因此报文段中也包含一个由服务端随机生成的初始序列号,例如 seq=y。
- ACK (Acknowledgement):用于确认收到了客户端的请求。其确认号被设置为客户端初始序列号加一,即 ack=x+1。
- 发送该报文段后,服务端进入 SYN_RECV 状态。
- 第三次握手 (ACK): 客户端收到服务端的 SYN+ACK 报文段后,会向服务端发送一个最终的确认报文段。该报文段包含确认号 ack=y+1。发送后,客户端进入 ESTABLISHED 状态。服务端收到这个 ACK 报文段后,也进入 ESTABLISHED 状态。
| 参数 | 对应生活场景 | 核心意义和作用 |
|---|---|---|
| SYN (同步序号) | “喂,我想跟你通话/同步一下我们的起点!” | 设立初始序列号 (ISN)。它标志着我要从哪个数字(字节序号)开始发送我的数据。它是建立连接的信号。 |
| ACK (确认) | “好的,我收到了你的请求/信息!” | 确认收到了对方发送的报文。它是确保可靠性的关键,防止丢包。 |
seq=x (序列号) | “这是我 (客户端) 给你发的 (第) 100 号信件。” (这里的 x 是一个随机数) | 代表本次报文段携带的数据的第一个字节的序号。它用于保证数据有序,防止乱序。 |
ack=x+1 (确认号) | “我期待收到你的下一封 (第) 101 号信件。” | 代表期望收到对方下一个报文段的起始序号。它确认了 x 之前(包括 x)的数据都已收到。 |
携带数据问题:
这里面**第三次握手**是**可以携带数据**的。
如果第三次握手的 ACK 确认包丢失,但是客户端已经开始发送携带数据的包,那么服务端在收到这个携带数据的包时,如果该包中包含了 ACK 标记,服务端会将其视为有效的第三次握手确认。这样,连接就被认为是建立的,服务端会处理该数据包,并继续正常的数据传输流程。
精简化过程:
- 客户端→服务端:发送SYN,包含seq=x ,客户端进入 SYN_SEND 状态。
- 服务端→客户端:收到SYN,发送ACK、SYN,包含seq=y、ack=x+1,服务端进入 SYN_RECV 状态。
- 客户端→服务端:收到ACK、SYN,发送最终的ACK,确认号为ack=y+1,发送后,客户端进入 ESTABLISHED 状态。服务端收到这个 ACK 报文段后,也进入 ESTABLISHED 状态。
如果是两次握手:服务端收到这个失效的 SYN1 后,会误认为是一个新的连接请求,并立即分配资源、建立连接。但这将导致服务端单方面维持一个无效连接,白白浪费系统资源,因为客户端并不会有任何响应。
有了第三次握手:服务端收到失效的 SYN1 并回复 SYN+ACK 后,会等待客户端的最终确认(ACK)。由于客户端当前并没有发起连接的意图,它会忽略这个 SYN+ACK 或者发送一个 RST (Reset) 报文。这样,服务端就无法收到第三次握手的 ACK,最终会超时关闭这个错误的连接,从而避免了资源浪费。
为什么要三次握手
确认双方都能进行接收和发送
协商并确定连接的初始序列号,这是后续数据包去重和排序的基础。
四次挥手
下面的客户端也可以是服务端开始第一次挥手
- 第一次挥手 (FIN):当客户端决定关闭连接时,它会向服务端发送一个 FIN(Finish)标志的报文段,表示自己已经没有数据要发送了。该报文段包含一个序列号 seq=u。发送后,客户端进入 FIN-WAIT-1 状态。
- 第二次挥手 (ACK):服务端收到 FIN 报文段后,会立即回复一个 ACK 确认报文段。其确认号为 ack=u+1。发送后,服务端进入 CLOSE-WAIT 状态。客户端收到这个 ACK 后,进入 FIN-WAIT-2 状态。此时,TCP 连接处于半关闭(Half-Close)状态:客户端到服务端的发送通道已关闭,但服务端到客户端的发送通道仍然可以传输数据。
- 第三次挥手 (FIN):当服务端确认所有待发送的数据都已发送完毕后,它也会向客户端发送一个 FIN 报文段,表示自己也准备关闭连接。该报文段同样包含一个序列号 seq=y。发送后,服务端进入 LAST-ACK 状态,等待客户端的最终确认。
- 第四次挥手:客户端收到服务端的 FIN 报文段后,会回复一个最终的 ACK 确认报文段,确认号为 ack=y+1。发送后,客户端进入 TIME-WAIT 状态。服务端在收到这个 ACK 后,立即进入 CLOSED 状态,完成连接关闭。客户端则会在 TIME-WAIT 状态下等待 2MSL后,才最终进入 CLOSED 状态。
精简化过程:
- 客户端→服务端:发送FIN,包含seq=u ,客户端进入 FIN-WAIT-1 状态。
- 服务端→客户端:收到FIN,发送ACK,确认号为ack=u+1,服务端进入 CLOSE-WAIT 状态,客户端收到这个 ACK 后,进入 FIN-WAIT-2 状态。
- 服务端→客户端:发送FIN,包含squ=y,服务端进入 LAST-ACK 状态。
- 客户端→服务端:收到FIN,发送最终的ACK,确认号为ack=y+1,客户端进入 TIME-WAIT 状态,。服务端在收到这个 ACK 后,立即进入 CLOSED 状态。客户端则会在 TIME-WAIT 状态下等待 2MSL后,才最终进入 CLOSED 状态。
1、客户端决定关闭了
2、服务端收到关闭信息,但是可能自己还有要发的
3、服务端发完自己要发的,决定关闭了
4、客户端确定关闭(一会之后),服务端关闭
为什么要四次挥手
TCP 是全双工通信,可以双向传输数据。一方发出释放连接的信号,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,发出连接释放通知,对方确认后就完全关闭了 TCP 连接。
为什么不能把服务器发送的 ACK 和 FIN 合并起来,变成三次挥手?
ACK(确认收到)和 FIN(请求关闭发送)的时机是不确定的:ACK 几乎是立即回复的,FIN 是在 Server 上层应用决定关闭连接并发送完所有数据后才发送的。
客户端在发送 FIN 后会启动一个重传计时器。如果在计时器超时之前没有收到服务端的 ACK,客户端会认为 FIN 报文丢失,并重新发送 FIN 报文
第二次挥手和第三次挥手的目的不同,第二次的目的是回应客户端的关闭请求的,第三次的目的是通知客户端所有数据已经发送完毕。
为什么第四次挥手需要等待 2*MSL时间后才进入 CLOSED 状态
第四次挥手时,客户端发送给服务端的 ACK 有可能丢失,如果服务端因为某些原因而没有收到 ACK 的话,服务端就会重发 FIN。
如果客户端在 2*MSL 的时间内收到了 FIN,就会重新发送 ACK 并再次等待 2MSL,防止 Server 没有收到 ACK 而不断重发 FIN。
长连接短连接
长连接:也叫持久连接,在TCP层握手成功后,不立即断开连接,并在此连接的基础上进行多次消息(包括心跳)交互,直至连接的任意一方(客户端OR服务端)主动断开连接,此过程称为一次完整的长连接。HTTP 1.1相对于1.0最重要的新特性就是引入了长连接。
短连接:顾名思义,与长连接的区别就是,客户端收到服务端的响应后,立刻发送FIN消息,主动释放连接。也有服务端主动断连的情况,凡是在一次消息交互(发请求-收响应)之后立刻断开连接的情况都称为短连接。
注:短连接是建立在TCP协议上的,有完整的握手挥手流程,区别于UDP协议。
TCP是怎么保证可靠性的?
1.数据本身
校验、排序、不重复
1.1基于数据块传输:(分块传输)确保了数据的完整性。TCP会将应用数据分割成最适合传输的报文段,并为每个段计算校验和。接收端会验证这个校验和,如果数据在传输中出错,则会直接丢弃该报文段,并不发送确认,从而触发发送方的重传。
1.2序列号机制:TCP传输的每个包一个序列号,有了序列号-->解决数据乱序和重复问题。
2.传输过程
不丢包、不压垮、不堵路
2.1重传机制:当数据包丢失或确认超时,TCP负责重传。这包括了基于计时器的超时重传,以及更高效的快速重传(当连续收到三个重复ACK时,即使没超时也会立即重传)。
2.2流量控制:滑动窗口接收方会告知发送方自己剩余的缓冲区大小,发送方根据这个窗口大小来调整发送速率,从而防止接收方因处理不过来而导致数据丢失。
2.3拥塞控制:网络拥堵的时候,减少数据的发送。
TCP 在发送数据的时候,需要考虑两个因素:一是接收方的接收能力,二是网络的拥塞程度。
接收方的接收能力由滑动窗口表示,表示接收方还有多少缓冲区可以用来接收数据。网络的拥塞程度由拥塞窗口表示,它是发送方根据网络状况自己维护的一个值,表示发送方认为可以在网络中传输的数据量。发送方发送数据的大小是滑动窗口和拥塞窗口的最小值,这样可以保证发送方既不会超过接收方的接收能力,也不会造成网络的过度拥塞。
滑动窗口细聊
总体:TCP 采用的是滑动窗口机制,这个窗口是动态变化的,并且是以字节为单位的。
发送窗口:
- 内部滑动:当发送方收到对“已发送未确认”数据的 ACK 时,发送窗口就会向右滑动。窗口左沿的移动距离,就是确认的字节数,如图
![]()
- 实际外部增大:rwnd也就是接收窗口和cwnd拥塞窗口的最小值决定的
- 结果:控制发送速率和保障数据可靠性
接收窗口:
- 内部滑动:数据按序确认: TCP 确认号(ACK Number)表明了接收方期望收到的下一个字节序号。当接收方收到按序到达的数据时,接收窗口的左沿也随之移动。数据从“已接收未确认”变成 “已接收已确认”。
![]()
- 实际外部增大:上层应用层读取数据: 当上层应用从 TCP 接收缓冲区中读取数据后,这部分空间就被释放了,接收窗口 就会增大。
- 结果:保证了流量控制和处理乱序。
拥塞窗口cwnd:拥塞窗口是发送方的一个变量,由发送方根据网络拥塞情况动态调整,比如到底是快速重传还是慢慢的还是什么
作用: cwnd(如慢启动、拥塞避免、快重传/快恢复)决定了发送方对网络链路拥塞的保守程度。
TCP粘包问题
问题:
粘包现象:
- 发送端粘包: 应用程序发送的几块数据(例如发送了两次 Write 操作),TCP 协议层可能为了提高效率,将这几块数据合并成一个更大的 TCP 段发送出去。
- 接收端粘包: 接收方的 TCP 协议栈可能会将收到的多个 TCP 段数据合并,一次性交付给应用程序
解决:
这个问题不在传输层解决,在应用层解决→因为职责划分,传输层只负责传输,边界的事情交给应用层。
方法1:定长消息,规定每一个消息的长度都是固定的 L字节。
对数据格式非常严格的应用
方法2:特殊分隔符,在每条消息的末尾加上一个固定的、在消息内容中不会出现的特殊字符或字符序列作为分隔符(例如 \n)。
传输纯文本的时候
方法3:消息头 + 消息体
HTTP解决粘包问题的方案
IP
IPv4地址
IPv4是以32位二进制整数表示的,每八位一组,每组用.隔开
ABC类
这类IP主要分为两部分,网络号和主机号
主机号全0指某个特定网络
主机号全1指这个网络下所有主机,也就是广播
广播地址用于同一个链路中相互连接的主机之间发送数据包
本地广播:在本网络之间的广播,比如192.168.0.0/24的情况下,广播地址就是192.168.0.255。
直接广播:在不同网络之间的广播例如网络地址为 192.168.0.0/24 的主机向 192.168.1.255/24 的目标地址发送 IP 包。
DE类
没有主机号,只有网络号,D类用于多播:将包发送给特定组内的所有主机。E类是暂留的分类,没用过。
无分类地址CIDR
32位的IP地址被完全分成两部分:网络号和主机号,不再有分类的编号
表示形式就是上面ABC类的a.b.c.d/x,其中 /x 表示前 x 位属于网络号, x 的范围是0 ~ 32
子网掩码
这是的X就是子网掩码,比如X=24,那就是子网掩码的前24位都是1,也就是255.255.255.0
作用:
1、划分主机号和网络号
2、确定子网范围,同1一样,AND之后就得到了网络地址
3、本地远程判断,计算一次本地网络地址,计算一次目标网络地址,相等就是在一个子网下,不相等就不在,让路由器转发。
原理:和IP进行按位与运算(AND),01和1与都会不变,01和0与都会变成0,这样主机号部分清零
IPv6地址
IPv6是以128位二进制整数表示的,每16位一组,每组用:隔开
访问页面全过程
在浏览器输入网页的URL
浏览器通过DNS协议,获取域名对应的IP
浏览器根据IP和端口号,先目标服务器发起一个TCP连接请求
浏览器在TCP连接上,向服务器发送一个HTTP请求报文,请求获取网页内容
服务器收到HTTP请求报文后,处理请求,返回HTTP响应报文给浏览器
浏览器收到HTTP响应报文后,解析响应体中的 HTML 代码,渲染网页的结构和样式。
URL
统一的资源定位器,每一个文件就对应一个URL。
成分:
- 协议:应用层协议HTTP或HTTPS
- 域名
- 端口
- 资源路径:/什么什么
- 参数:如?id=1
- 锚点:也就是一个页面不同位置的书签
DNS
解决的是域名和IP地址的映射问题
工作原理和过程
这个过程中分为两种查询:递归和迭代
①⑧是递归
②③④⑤⑥⑦是迭代
| 维度 | 递归查询 (Recursive) | 迭代查询 (Iterative) |
|---|---|---|
| 关系本质 | 服务代理 (Proxy Service) | 协作指引 (Cooperative Routing) |
| 消息类型 | 单一(LDNS 必须处理,直到成功或失败) | 多次(LDNS 需要根据回复,发起多次新的查询请求) |
| 系统压力 | 将全球查询压力集中到 LDNS(LDNS 负责消耗资源) | 将查询压力分散到全球数百个根、TLD、权威服务器上 |
| 扩展性 | 易于为客户端增加功能(如缓存),但 LDNS 易成为瓶颈。 | 全球 DNS 系统易于新增域和服务器,极具扩展性。 |
| 视角 | 递归查询 (Recursive) | 迭代查询 (Iterative) |
|---|---|---|
| 对发起者的承诺 | 我承诺给你一个最终的 IP 地址。 | 我承诺给你一个下一步的指引(方向或最终答案)。 |
| 对接收者的要求 | 接收者(LDNS)必须承担所有后续工作,直到拿到 IP。 | 接收者(根/TLD)只需指路,无需代劳后续的查询。 |
迭代查询:“只给出它知道的下一级服务器的地址”,是为了强调它不承担替查询者继续追问的责任,而不是说它不需要返回任何信息。它返回的信息是 LDNS 完成解析链条所必需的“下一步线索”。
思考:
问:但是实际上ldns是分发给了下面的dns系统中,也就是说ldns自己也不一定能拿到ip,那迭代和递归的区别只有职责上的区别?没有什么别的
答:您说得非常对:LDNS 自己也不一定能拿到 IP,它需要分发给下面的 DNS 系统(即根域、TLD、权威 DNS)。
常见的dns记录类型
| 记录类型 | 完整名称 | 作用/底层机制 | 示例应用场景 |
|---|---|---|---|
| A | Address Record | 将域名映射到 IPv4 地址。这是最基础的记录,实现用户访问网站。 | 将 www.example.com 指向 192.0.2.1 |
| AAAA | IPv6 Address Record | 将域名映射到 IPv6 地址。与 A 记录功能相同,只是地址格式不同。 | 将 www.example.com 指向一个 IPv6 地址 |
| CNAME | Canonical Name Record | 别名记录。将一个域名(别名)指向另一个域名(规范名)。CNAME 记录不能指向 IP 地址。 | 将 blog.example.com 指向 example.github.io,便于统一管理。 |
Socket
是应用层和传输层中间的一个API:套接字,Socket 是通道/接口,用于数据传输
Socket 是一个五元组的抽象表示:{协议, 本地IP地址, 本地端口号, 远程IP地址, 远程端口号}。它将网络通信抽象为文件操作,应用层程序可以像读写文件一样读写网络数据。
WebSocket
以前用的
网页的前端代码里不断定时发 HTTP 请求到服务器,服务器收到请求后给客户端响应消息。
也就是轮询,最优的方法是长轮询
如果我们的 HTTP 请求将超时设置的很大,比如 30 秒,在这 30 秒内只要服务器收到了扫码请求,就立马返回给客户端网页。如果超时,那就立马发起下一次请求。
为什么现在出现WebSocket
我们知道 TCP 连接的两端,同一时间里,双方都可以主动向对方发送数据。这就是所谓的全双工。
而现在使用最广泛的HTTP/1.1,也是基于TCP协议的,同一时间里,客户端和服务器只能有一方主动发数据,这就是所谓的半双工。
也就是说,好好的全双工 TCP,被 HTTP/1.1 用成了半双工。
为什么?
这是由于 HTTP 协议设计之初,考虑的是看看网页文本的场景,能做到客户端发起请求再由服务器响应,就够了,根本就没考虑网页游戏这种,客户端和服务器之间都要互相主动发大量数据的场景。
所以,为了更好的支持这样的场景,我们需要另外一个基于TCP的新协议。
于是新的应用层协议WebSocket就被设计出来了。
怎么建立WebSocket
WebSocket 连接通过 HTTP 协议升级完成建立。
- 客户端请求:在 TCP 连接建立后,客户端发送包含
Connection: Upgrade和Upgrade: WebSocket的 HTTP 请求,并附带一个随机密钥 (Sec-WebSocket-Key)。 - 服务器响应:服务器同意升级,返回 HTTP 状态码
101 Switching Protocols,并计算校验值 (Sec-WebSocket-Accept) 返回。 - 协议切换:客户端验证校验值无误后,双方即完成握手。
数据格式
WebSocket的数据格式也是数据头(内含payload长度) +消息体 payload data 的形式。














