1、socket连接特点
(1) 面向连接的可靠传输
TCP 协议通过以下机制保证可靠性:
- 三次握手建立连接:确保双方通信链路正常,避免 “假连接”。
- 确认与重传机制:发送方会为每个数据包设置超时计时器,若未收到接收方的 ACK 确认,会重新发送数据包。
- 流量控制与拥塞控制:通过滑动窗口机制避免接收方因处理能力不足导致数据丢失。
(2)字节流传输保证顺序性
Redis 主从复制需要严格保证命令执行顺序(如先执行 SET a 1
,再执行 INCR a
,结果必须是 2)。TCP 协议的 “字节流” 特性可以保证:
- 发送方按顺序写入 Socket 的数据,接收方会按相同顺序读取。
- 数据无边界(无消息头),但通过序列号(Sequence Number)和确认号(ACK Number)确保接收方正确拼接字节流。
相比之下,UDP(用户数据报协议)是无连接、不可靠、面向数据报的协议,无法保证顺序性和完整性,因此不适合主从复制场景。
(3)长连接支持持续命令同步
主从复制不仅需要初始的数据同步(全量或增量),还需要长期保持连接以同步后续的写命令(如主节点接收的 SET
、HSET
等操作)。TCP Socket 连接可以保持长时间的通信(除非主动断开或网络故障),而 UDP 每次通信都需要重新寻址,无法高效支持这种 “持续推送” 场景。
2、对其他通信协议的优势
可能有人会问:“为什么不用 HTTP、gRPC 等更上层的协议?” 原因在于:
- HTTP 是短连接 + 请求响应模式:每次通信需要重新建立连接(HTTP/1.1 虽支持长连接,但默认是短连接),无法高效支持主节点主动向从节点 “推送” 命令的场景(主节点需要实时通知从节点新命令)。
- gRPC 等 RPC 框架:虽然支持长连接和流式传输,但会引入额外的序列化 / 反序列化开销(如 Protobuf),而 Redis 的协议(RESP)本身是轻量的文本协议,直接通过 Socket 传输更高效。
3、websocket的原理
好的,面试官。我们来聊聊WebSocket的原理。
WebSocket是什么?
WebSocket是一种在单个TCP连接上进行全双工、双向通信的协议。它旨在解决HTTP协议在实现实时、双向通信方面的不足。
全双工 (Full-Duplex):客户端和服务器可以同时独立地发送和接收数据,不需要像HTTP那样一来一回的请求-响应模式。
双向通信 (Bi-directional):数据可以在客户端到服务器和服务器到客户端两个方向上自由流动。
单个TCP连接:一旦WebSocket连接建立,所有的后续通信都在这一个TCP连接上进行,避免了HTTP协议中频繁建立和关闭TCP连接的开销。
为什么需要WebSocket?HTTP的局限性:
传统的HTTP协议是无状态的、单向的(客户端发起请求,服务器响应)。为了在Web上实现类似即时通讯、实时数据更新等功能,开发者们曾尝试过一些变通方法,但都有其局限性:
轮询 (Polling):客户端定期向服务器发送HTTP请求,询问是否有新数据。
缺点:实时性差(取决于轮询间隔),大量无效请求浪费带宽和服务器资源。
长轮询 (Long Polling):客户端发送一个HTTP请求,服务器保持连接打开,直到有新数据才响应。响应后,客户端立即再次发起新的长轮询请求。
缺点:仍然有连接建立和关闭的开销,服务器需要管理大量挂起的连接,对服务器资源消耗较大。
SSE (Server-Sent Events):允许服务器单向地向客户端推送数据流。
缺点:只能服务器向客户端单向推送,客户端无法通过同一连接向服务器发送数据(需要另外的HTTP请求)。
WebSocket的出现,就是为了在Web上提供一种更高效、更原生的双向实时通信解决方案。
WebSocket的工作原理(核心流程):
握手阶段 (Handshake) - 基于HTTP:
初始连接:WebSocket的连接过程始于一个标准的HTTP请求,但这个请求有一些特殊的头部信息,表明客户端希望将连接从HTTP升级(Upgrade)到WebSocket协议。
客户端请求:
请求方法通常是 GET。
Upgrade: websocket 头部:表明希望升级到WebSocket协议。
Connection: Upgrade 头部:表明连接类型需要升级。
Sec-WebSocket-Key 头部:客户端生成一个随机的Base64编码的字符串,用于后续的安全校验。
Sec-WebSocket-Version 头部:指定了客户端期望使用的WebSocket协议版本(通常是13)。
(可选)Sec-WebSocket-Protocol:客户端可以声明它支持的子协议。
(可选)Origin:表明请求的来源域,用于服务器进行跨域安全检查。
服务器响应:
如果服务器支持WebSocket并且同意升级,它会返回一个特殊的HTTP响应,状态码为 101 Switching Protocols。
Upgrade: websocket 头部:确认升级到WebSocket协议。
Connection: Upgrade 头部:确认连接类型升级。
Sec-WebSocket-Accept 头部:服务器将客户端发送的Sec-WebSocket-Key与一个固定的"魔法字符串"(258EAFA5-E914-47DA-95CA-C5AB0DC85B11)拼接后,计算SHA-1哈希,然后进行Base64编码,得到的值作为这个头部的内容返回。客户端会验证这个值,以确认服务器确实理解WebSocket协议。
(可选)Sec-WebSocket-Protocol:如果服务器从客户端声明的子协议中选择了一个,会通过这个头部返回。
数据传输阶段 (Data Transfer) - 基于WebSocket帧:
连接升级成功:一旦握手成功,底层的TCP连接就不再用于HTTP通信了,而是转为WebSocket协议进行数据传输。
数据帧 (Frames):WebSocket的数据传输是以“帧”(Frame)为单位的。每一条消息(无论是文本还是二进制)都会被分割成一个或多个帧进行传输。
帧的结构:每个WebSocket帧都有一个标准的结构,包含:
FIN位 (Final Fragment bit):标记这是否是消息的最后一个分片。
RSV1, RSV2, RSV3位 (Reserved bits):保留位,用于未来的扩展,通常为0。
Opcode (操作码):指示帧的类型,例如:
0x0:Continuation Frame (连续帧,表示这是一个消息的后续分片)
0x1:Text Frame (文本帧)
0x2:Binary Frame (二进制帧)
0x8:Connection Close Frame (关闭连接帧)
0x9:Ping Frame (Ping帧,用于心跳)
0xA:Pong Frame (Pong帧,Ping的响应)
Mask位 (Masking bit):如果为1,表示Payload Data是经过掩码处理的。从客户端发送到服务器的所有帧,其Mask位必须为1,并且Payload Data必须使用一个32位的掩码密钥进行异或(XOR)处理。 这是为了防止代理服务器缓存攻击。服务器发送到客户端的帧,Mask位必须为0,且Payload Data不进行掩码。
Payload length (载荷长度):指示Payload Data的长度。
Masking-key (掩码密钥):如果Mask位为1,则这里包含32位的掩码密钥。
Payload Data (载荷数据):实际传输的数据(文本或二进制),可能经过掩码处理。
消息的重组:如果一条消息被分割成多个帧,接收方需要根据FIN位和Opcode来将这些帧重新组装成完整的消息。
连接关闭阶段 (Closing Handshake):
WebSocket连接的关闭也有一个定义的握手过程。
任何一方(客户端或服务器)都可以发起关闭请求,通过发送一个Opcode为0x8的Close帧。
收到Close帧的一方,通常会回复一个Close帧作为确认,然后双方关闭TCP连接。
Close帧中可以包含一个状态码和可选的关闭原因。
WebSocket的优势:
真正的双向通信:客户端和服务器可以同时发送数据。
低延迟:一旦连接建立,后续数据传输不需要每次都进行HTTP握手,减少了开销。
减少头部开销:WebSocket帧的头部相比HTTP请求/响应头部小得多。
更好的带宽利用率:避免了轮询等方式产生的无效请求。
事件驱动:非常适合构建事件驱动的实时应用。
安全性考虑:
WSS (WebSocket Secure):WebSocket可以使用TLS/SSL进行加密传输(通过wss:// URL),与HTTPS类似,保证了数据的机密性和完整性。
Origin校验:服务器可以检查握手请求中的Origin头部,以防止跨站WebSocket劫持。
客户端掩码:如前所述,客户端发送的帧需要进行掩码,以防止代理缓存攻击。
总结来说,WebSocket通过一次HTTP握手将连接升级,然后在该TCP连接上使用自定义的帧协议进行全双工、低延迟的数据传输,非常适合需要实时、双向通信的Web应用场景。教程中用它来实现协同编辑的实时操作同步,正是利用了其这些核心优势。
4、websocket如何保持长连接?
好的,面试官。WebSocket能够保持长连接,主要是基于以下几个层面的机制和特性,这与传统的短连接HTTP协议有显著不同:
1. 底层TCP连接的持久性:
握手后的TCP复用:WebSocket的初始连接是通过HTTP协议完成的“升级”握手。一旦这个握手成功(服务器返回101 Switching Protocols),底层的TCP连接就被保留下来并被WebSocket协议接管。
不再是请求-响应后关闭:与HTTP/1.0或HTTP/1.1(非Keep-Alive或Keep-Alive超时)不同,这个TCP连接在WebSocket握手成功后,不会在一次消息交换后就关闭。它会一直保持打开状态,直到客户端或服务器明确发起关闭握手,或者网络发生中断。
2. WebSocket协议自身的设计:
面向连接的协议:WebSocket协议本身就是设计为一种有状态的、面向连接的协议。它的整个生命周期都依赖于这个持久的TCP连接。
持续的数据帧交换:在连接保持期间,客户端和服务器可以随时通过这个连接互相发送WebSocket数据帧,而不需要为每一条消息重新建立连接。
3. 心跳机制 (Heartbeat Mechanism) - 应用层面或协议层面:
虽然TCP连接本身可以保持打开,但长时间没有数据传输的TCP连接可能会被网络中的某些中间设备(如NAT网关、防火墙、负载均衡器)视为空闲连接而被超时关闭。为了防止这种情况,并确保双方都能感知到连接的活性,通常会采用心跳机制:
应用层心跳:
这是最常见的方式。客户端和服务器约定好,定期(例如每隔几十秒)由一方(通常是客户端)发送一个特殊的心跳消息(例如一个空的文本帧,或者一个特定内容的JSON消息,或者一个WebSocket的Ping帧)。
另一方收到心跳消息后,回复一个确认消息(例如一个Pong帧或特定的响应消息)。
作用:
保持连接活跃:通过定期的数据传输,告诉中间网络设备这个连接不是空闲的,不要关闭它。
检测连接断开:如果一方在预设的时间内没有收到对方的心跳或心跳响应,就可以判断连接可能已经断开,从而进行相应的清理或重连操作。
WebSocket协议层面的Ping/Pong帧:
WebSocket协议本身定义了Ping帧 (Opcode 0x9) 和 Pong帧 (Opcode 0xA)。
一方可以发送Ping帧,另一方收到后应尽可能快地回复一个Pong帧。Pong帧的载荷数据通常与它响应的Ping帧的载荷数据相同。
这些帧是WebSocket协议规范的一部分,可以直接用于实现心跳。
许多WebSocket库和服务器都内置了对Ping/Pong帧的支持。
教程中在介绍Disruptor优化时,提到了“Spring WebSocket默认是同步处理,如果某个消息处理耗时比较长,那么后面的消息(比如心跳检测)可能被阻塞”,这间接说明了心跳机制的存在或必要性。
4. TCP Keepalive (TCP层面的机制,可选但有帮助):
TCP协议自身也有一个Keepalive机制。当TCP连接长时间空闲时,操作系统内核可以配置为定期发送TCP Keepalive探测包到对端。
如果对端没有响应(例如,因为网络中断或对端主机崩溃),内核会认为连接已死,并通知上层应用。
与WebSocket心跳的区别:
TCP Keepalive是操作系统内核层面的,对应用透明。其探测间隔通常较长(例如默认2小时),且配置起来不如应用层心跳灵活。
WebSocket应用层心跳或Ping/Pong帧则由应用控制,间隔可以更短,能更快地感知到连接问题,并且可以携带应用层面的信息。
TCP Keepalive可以作为WebSocket心跳的一个补充,但不能完全替代应用层心跳,因为应用层心跳更能反映应用逻辑的连通性。
总结WebSocket如何保持长连接:
HTTP握手升级:将一个HTTP连接转换为持久的WebSocket连接,底层的TCP连接被复用且不主动关闭。
协议设计:WebSocket协议本身就是面向连接的,支持在已建立的连接上持续双向传输数据帧。
心跳机制(关键):通过客户端和服务器之间定期发送和响应心跳消息(可以是应用自定义的,也可以是协议层面的Ping/Pong帧),来:
防止网络中间设备因超时而关闭空闲连接。
及时检测到连接的非正常断开。
(可选辅助) TCP Keepalive:操作系统层面的TCP连接保活机制。
通过这些机制的协同工作,WebSocket连接能够有效地保持长时间的开放状态,从而实现高效的实时双向通信。在教程的协同编辑场景中,这个长连接是用户操作能够被实时广播给其他协作者的基础。