您的位置:首页 > 科技 > IT业 > Websocket学习笔记

Websocket学习笔记

2024/11/17 2:25:47 来源:https://blog.csdn.net/qq_32261191/article/details/139349034  浏览:    关键词:Websocket学习笔记

WebSocket是什么?

WebSocket是一种在Web应用程序中实现实时双向通信的协议。它允许在客户端和服务器之间建立持久性连接,使得双方可以通过该连接进行长时间的数据传输。

WebSocket协议位于应用层,它提供了一种基于TCP协议的全双工通信机制,WebSocket在建立连接时依赖HTTP/HTTPS协议。

主要用途

WebSocket的主要用途是实现实时的双向通信。它可以用于许多不同类型的应用,包括但不限于:

  1. 在线聊天应用:允许用户实时发送和接收消息,而无需页面刷新或轮询服务器。
  2. 实时协作应用:支持多用户实时编辑文档或共享白板等场景。
  3. 实时游戏:允许多个玩家之间进行实时的游戏交互。
  4. 实时数据展示:用于显示实时数据,如股票市场变化、天气预报更新等。
  5. 实时通知和提醒:用于向用户发送实时的通知消息,如新邮件提醒、社交媒体通知等。
  6. 在线会议和视频通话:支持实时的音视频通信。

通信模式

WebSocket 支持以下几种通信模式:

  1. 一对一通信
    • WebSocket 最常见的使用方式是一对一的通信模式,其中一个客户端与一个 WebSocket 服务器之间建立一条连接,实现双向的实时通信。这种模式适用于聊天应用、实时通知等场景。
  2. 一对多通信
    • WebSocket 也支持多个客户端同时连接到同一个 WebSocket 服务器的通信模式。在这种模式下,服务器可以向所有连接到它的客户端广播消息,或者向特定的客户端发送消息。这种模式适用于群聊、广播通知等场景。
  3. 多对多通信
    • WebSocket 支持多对多的通信模式,其中多个客户端之间建立 WebSocket 连接,并且能够相互之间进行通信。在这种模式下,每个客户端都可以与其他客户端进行直接的双向通信,而服务器则充当中间人进行消息的转发和管理。这种模式适用于实时协作、实时游戏等应用场景。

优缺点

优点缺点
实现了实时双向通信不支持跨域通信
具有较的网络开销和较实时部分浏览器和网络设备可能不支持 WebSocket
简单易用,易于集成到现有的 Web 应用中需要额外的服务器资源来维护长连接
支持服务器主动向客户端推送数据可能会增加服务器端的复杂性
可以减少 HTTP 请求头和响应头的大小,降低网络延迟流量需要保证 WebSocket 连接的稳定性可靠性

报文格式

WebSocket报文格式相对简单,由Frame)组成。

基本的WebSocket帧结构包括:FINRSVOpcodeMaskPayload LengthMasking KeyPayload Data等字段。

  1. FIN (1 bit):表示该帧是否是消息的最后一帧。如果该位被设置为1,表示这是消息的最后一帧;如果为0,表示后续还有帧组成同一个消息。
  2. RSV1, RSV2, RSV3 (1 bit each):保留位,用于扩展,目前应该始终为0。
  3. Opcode (4 bits):指示帧的类型,有以下几种可能的取值:
    • 0x0:表示一个连续的数据帧。
    • 0x1:表示一个文本帧。
    • 0x2:表示一个二进制帧。
    • 0x8:表示一个连接关闭帧。
    • 0x9:表示一个Ping帧。
    • 0xA:表示一个Pong帧。
    • 其他值为保留值,不常用。
  4. Mask (1 bit):标识是否对Payload Data进行掩码处理。如果为1,表示数据被掩码处理;如果为0,表示数据没有被掩码处理。
  5. Payload Length (7 bits):指示Payload Data的长度。如果Payload Length的值在0~125之间,则表示Payload Data的实际长度。如果值为126,则后续2个字节将被用来表示Payload Data的扩展长度。如果值为127,则后续8个字节将被用来表示Payload Data的扩展长度。
  6. Extended Payload Length:当Payload Length的值为126或127时,用于表示Payload Data的扩展长度。长度为2个字节或8个字节,取决于Payload Length字段的值。
  7. Masking-key (4 bytes):如果Mask标志位为1,则存在4字节的掩码密钥,用于对Payload Data进行解码。如果Mask标志位为0,则不存在该字段。
  8. Payload Data:WebSocket 帧中携带的实际数据,它是根据 Payload Length 字段指示的长度而确定的。在 WebSocket 协议中,Payload Data 可以是文本数据、二进制数据或其他任何形式的数据,具体取决于发送方和接收方之间的协商。

握手过程

WebSocket 握手过程是客户端和服务器之间建立 WebSocket 连接的过程,它遵循 HTTP 协议的规范。

下面是 WebSocket 握手的基本步骤:

  1. 客户端发送握手请求
    • 客户端向服务器发送一个 HTTP 请求,请求的路径是 WebSocket 的目标地址(URL)。
    • 请求头中包含了一些 WebSocket 相关的头信息,如 Upgrade: websocketConnection: UpgradeSec-WebSocket-Key 等。其中,Sec-WebSocket-Key 是一个随机的 Base64 编码的字符串,用于确保服务器能够识别客户端的 WebSocket 请求。
  2. 服务器响应握手请求
    • 服务器收到客户端的握手请求后,返回一个 HTTP 响应,状态码为 101 Switching Protocols,表示协议切换。
    • 响应头中包含了一些必要的信息,如 Upgrade: websocketConnection: UpgradeSec-WebSocket-Accept 等。其中,Sec-WebSocket-Accept 是通过将客户端请求头中的 Sec-WebSocket-Key 加上一个特定的 GUID(全局唯一标识符),然后计算 SHA-1 摘要后进行 Base64 编码得到的。
  3. 建立连接
    • 客户端收到服务器的响应后,表示 WebSocket 连接建立成功,此时客户端和服务器之间的通信将转换为 WebSocket 协议。
    • 之后的通信将通过 WebSocket 协议进行,不再受限于传统的 HTTP 请求-响应模式。

如何使用?

绝大多数主流的编程语言都有支持 WebSocket 的库或框架,使得开发者可以方便地在其应用程序中实现 WebSocket 功能。以下是一些常见编程语言及其对应的 WebSocket 库:

  1. JavaScript/Node.jswssocket.io
  2. Pythonwebsocketssocket.io-client
  3. Javajavax.websocketJava-WebSocket
  4. C#WebSocketSharpSignalR
  5. Gogorilla/websocketnhooyr/websocket
  6. Rubywebsocket-rubyfaye-websocket
  7. PHPratchet/pawlcboden/Ratchet

websocket如何保证通信的安全性

WebSocket 本身并没有提供通信的安全性,但可以通过其他机制来增强通信的安全性,例如使用 TLS/SSL 加密连接。

以下是保证 WebSocket 通信安全性的一些方法:

  1. 使用 TLS/SSL 加密连接:通过在 WebSocket 连接上使用 TLS/SSL 加密,可以确保通信数据在传输过程中是加密的,从而防止数据被窃取或篡改。您可以在 WebSocket URL 中使用 wss:// 协议来指定安全的 WebSocket 连接。
  2. 身份验证:您可以在 WebSocket 连接建立时进行客户端和服务器之间的身份验证,以确保只有授权用户才能建立连接并进行通信。可以使用令牌、证书或其他身份验证机制来实现身份验证。
  3. 消息签名和验证:在发送和接收消息时,可以使用数字签名来确保消息的完整性和真实性。发送方使用私钥对消息进行签名,接收方使用公钥验证签名,以确保消息没有被篡改。
  4. 防止跨站请求伪造(CSRF)攻击:在使用 WebSocket 时,确保采取措施来防止 CSRF 攻击。可以通过实现基于令牌的身份验证和验证来源来防止 CSRF 攻击。
  5. 限制连接:可以限制允许连接到 WebSocket 服务器的客户端数量,并实施连接速率限制,以防止恶意行为和拒绝服务攻击。
  6. 定期审查和更新安全策略:定期审查和更新安全策略,包括 TLS/SSL 配置、身份验证机制和访问控制策略,以确保与最新的安全标准和最佳实践保持一致。

Python使用WebSocket

以下是一些常用的Python WebSocket库:

库名称说明
websockets1. 提供了简单而强大的API,易于使用
2. 支持异步操作,适用于高性能的WebSocket应用程序。
3. 可以同时作为客户端服务器端使用。
4. 具有良好的文档和活跃的社区支持。
autobahn-python1. 功能丰富,提供了完整的WebSocket协议实现。
2. 支持高级特性,如消息压缩、WebSocket扩展等。
3. 提供了WebSocket客户端服务器端的实现。
4. 与Twisted框架紧密集成,支持异步操作。
5. 适用于复杂的WebSocket应用场景,如实时游戏、聊天应用等。
socket.io-client-python1. 是Python版本的Socket.IO客户端库,兼容JavaScript版Socket.IO
2. 提供了与JavaScript版Socket.IO类似的API,易于使用
3. 支持WebSocket传输和轮询传输两种方式。
4. 适用于与使用Socket.IO实现的WebSocket服务器进行通信。
Tornado1. 是一个强大的异步Web框架,支持WebSocket通信。
2. 提供了WebSocket客户端服务器端的实现。
3. 集成了WebSocket路由和处理器,方便WebSocket应用程序的开发。
4. 适用于需要高性能异步通信的应用场景,如实时数据传输、即时通讯等。
django-websocket-redis1. 基于Django框架,使用Redis作为消息队列,用于实现WebSocket通信。
2. 可以轻松地集成到Django应用程序中。
3. 提供了简单而有效的API来处理WebSocket连接。
4. 适用于需要在Django应用程序中实现WebSocket通信的场景。
aiowebsocket1. 是一个基于异步IO的WebSocket库,专门用于Python的asyncio框架。
2. 提供了异步的WebSocket客户端和服务器端实现。
3. 适用于需要在asyncio框架下进行高性能异步通信的应用场景。
Flask-SocketIO1. 支持房间管理功能,可以将客户端分组到不同的房间中,并在房间内进行广播或单播消息。
2. 具有跨浏览器兼容性,能够在各种主流浏览器中稳定运行。
3. 集成简单,易于安装和使用,且有良好的文档和社区支持。

websockets模块使用

详细用法请查阅:

Python3 学习笔记 - websockets模块

使用websockets模块实现一个简单的聊天室案例:用户通过浏览器进入聊天室,多个用户可实时聊天。前端采用Javascript+html。前端代码如下:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>WebSocket 聊天室</title><style>body {margin: 0;padding: 0;font-family: Arial, sans-serif;}.container {width: 80%;margin: 0 auto;text-align: center;padding-top: 20px;}.chat-container {display: flex;flex-direction: column;border: 1px solid #ccc;border-radius: 5px;overflow: hidden;margin-top: 20px;height: 400px; /* 设置容器的固定高度 */}.message-container {flex-grow: 1;overflow-y: auto;padding: 10px;}.message {margin-bottom: 10px;max-width: 100%; /* 让消息div宽度占满容器 */}.message.received {text-align: left;background-color: #f0f0f0;border-radius: 5px;padding: 5px 10px;}.message.sent {text-align: right;background-color: #e2f3f5;border-radius: 5px;padding: 5px 10px;}#message {width: 100%;height: 40px;margin-top: 20px;border: 1px solid #ccc;border-radius: 5px;padding: 0 10px;box-sizing: border-box;}</style>
</head>
<body><div class="container"><h1>WebSocket 聊天室</h1><div class="chat-container"><div class="message-container" id="messages"></div><input type="text" id="message" placeholder="输入消息并按 Enter 发送"></div></div><script>var socket = new WebSocket("ws://localhost:8765");socket.onopen = function(event) {console.log("WebSocket 连接已建立");};socket.onmessage = function(event) {var messageDiv = document.createElement("div");messageDiv.textContent = event.data;messageDiv.className = "message received";document.getElementById("messages").appendChild(messageDiv);scrollToBottom();};document.getElementById("message").addEventListener("keypress", function(event) {if (event.key === "Enter") {var message = this.value;var messageDiv = document.createElement("div");messageDiv.textContent = message;messageDiv.className = "message sent";document.getElementById("messages").appendChild(messageDiv);scrollToBottom();socket.send(message);this.value = "";}});function scrollToBottom() {var messageContainer = document.getElementById("messages");messageContainer.scrollTop = messageContainer.scrollHeight;}</script>
</body>
</html>

后端代码如下:

import asyncio
import websockets
from datetime import datetime# 客户端列表,用于存储所有连接到服务器的客户端
clients = set()# 处理新连接的函数
async def handle_client(websocket, path):# 添加新连接的客户端到客户端列表clients.add(websocket)try:# 获取当前时间,格式化时分秒now = datetime.now()time_format = now.strftime("%H:%M:%S")# 发送欢迎消息给新连接的客户端client_addr = websocket.remote_addressprint(f"{time_format} Client {client_addr} connected!")welcome_message = "已连接服务器"await websocket.send(welcome_message)# 循环处理客户端发送的消息async for message in websocket:# 将收到的消息发送给所有连接到服务器的客户端for client in clients:# 获取当前时间,格式化时分秒now = datetime.now()time_format = now.strftime("%H:%M:%S")print(f"{time_format} Received Client {client.remote_address} message: {message}")  # 打印收到的消息await client.send(message)finally:# 当连接关闭时,从客户端列表中移除对应的客户端client_addr = websocket.remote_addressprint(f"Client {client_addr} disconnected!")clients.remove(websocket)# 启动 WebSocket 服务器
start_server = websockets.serve(handle_client, "localhost", 8765)# 启动事件循环,等待连接
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

启动WebSocket服务后,启用http服务,然后浏览器即可连接websocket进入聊天室进行群聊:


Hack All Sec 的博客

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com