一、WebSocket(ws)
1、为什么要有WebSocket?区别http协议
1>http特点:半双工通信,服务器不会主动向客户端发起请求,只会响应。
1》半双工通信:HTTP是一个请求-响应协议,客户端发起请求后,服务器进行响应。在传统HTTP中,服务器不能主动向客户端推送数据,除非客户端先发出请求。
2》无状态:每次请求都是独立的,服务器不会保存任何关于之前请求的信息(除非使用了会话机制如Cookies、Session等)。
3》不适合实时应用:对于需要频繁更新的应用场景(例如在线游戏、实时聊天、股票市场更新),HTTP的长轮询或短轮询会导致不必要的延迟和资源消耗。
2>Websocket特点:全双工通信,允许服务端主动向客户端发送数据。【双向数据交互】
1》全双工通信:一旦建立连接,服务器和客户端都可以随时发送消息给对方,而不需要等待对方的请求或响应。
2》持久连接:连接一旦建立就会保持打开状态,直到有一方明确关闭它。
3》适合实时应用:由于其双向通信能力,WebSocket非常适合用于需要快速、低延迟的数据交换的应用程序,如即时通讯、在线游戏、实时数据分析等。
2、WebSocket是如何建立的?(对应的使用步骤在三、2、)——WebSocket只在最开始建立时借用了HTTP协议来进行握手,但是一旦握手完成,后续的通信就不再依赖HTTP了
以下是WebSocket连接建立的步骤:
1>客户端发起HTTP请求:客户端通过发送一个特殊的HTTP请求到服务器。
这个请求包含了Upgrade: websocket
头部,表示希望将现有的HTTP连接升级为WebSocket连接。此外,还有其他必要的头部信息,如Sec-WebSocket-Key
(用于验证)和Sec-WebSocket-Version
(指定WebSocket版本)。
2>服务器响应
如果服务器支持WebSocket并且同意升级,则返回一个HTTP 101 Switching Protocols状态码,并包含确认握手成功的头部信息,如Upgrade: websocket
和Connection: Upgrade
,以及计算后的Sec-WebSocket-Accept
值。
3>连接升级
当客户端接收到101响应时,意味着HTTP连接已经成功升级为WebSocket连接。从这一刻起,双方可以开始使用WebSocket协议进行通信,而不受HTTP约束。
4>WebSocket通信【即下面3、】
在WebSocket连接上,客户端和服务端可以通过帧(frame)的形式互相发送文本或二进制数据。每个帧都有自己的结构,包括操作码(Opcode)、掩码位(Mask)、负载长度(Payload Length)等字段,这些字段定义了帧的内容类型和格式。
5>连接关闭
当任意一方想要关闭连接时,它可以发送一个关闭控制帧(close control frame),另一方接收到后也会相应地关闭连接。这确保了优雅地终止连接,而不是突然断开。
3、Websocket通信使用“帧”的数据格式
1>opcde:用定义帧的数据类型
0x1
: 文本帧(UTF-8编码的文本消息)0x2
: 二进制帧0x8
: 关闭连接
2>paload长度 (7位 或者更多):负载数据的长度,选用最开始 7位 为做标志位。从最开始的7位 Payload 长度开始,根据它的取值决定还要不要继续使用 [扩展用] 。
- 如果负载长度小于等于125,则直接使用这7位表示;
- 如果负载长度为126,则接下来的两个字节(16位)表示实际负载长度;
- 如果负载长度为127,则接下来的八个字节(64位)表示实际负载长度(2^7=128。最高位为符号位,一般设为0)。
3>payload数据:实际传输的数据内容,可以是文本也可以是二进制数据,取决于Opcode
。
4>举例:解析WebSocket帧
通过ReceiveAsync
方法接收到来自WebSocket的数据【即上述3>】。
并检查result.MessageType
以确定消息类型【即上述1>】。
对于文本消息,我们简单地将字节数组转换成字符串并打印出来;对于二进制消息,我们可以选择性地进行相应的处理;而对于关闭连接的操作码,则执行关闭逻辑。
public async Task HandleWebSocketMessages(WebSocket webSocket)
{
var buffer = new byte[1024 * 4];// 缓冲区大小设置为4KB
// 当WebSocket连接处于打开状态时,持续监听并处理消息
while (webSocket.State == WebSocketState.Open)
{
try
{
// 通过ReceiveAsync方法接收到来自WebSocket的数据
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
// 解析操作码,以确定消息类型
switch (result.MessageType)
{
case WebSocketMessageType.Text:
// 处理文本消息,将字节数组转换成字符串并打印出来
string receivedText = Encoding.UTF8.GetString(buffer, 0, result.Count);
Console.WriteLine($"Received text message: {receivedText}");
break;
case WebSocketMessageType.Binary:
// 处理二进制消息,
Console.WriteLine("Received binary data.");
break;
case WebSocketMessageType.Close:
// 处理关闭连接信号
Console.WriteLine("Received close signal.");
await webSocket.