目 录CONTENT

文章目录

websocket 实时双工的通信通道

Administrator
2025-02-05 / 0 评论 / 0 点赞 / 11 阅读 / 0 字

WebSocket 是什么

传统的 HTTP 请求通常是一次请求一次响应的,而 WebSocket 则可以建立一个持久连接,允许服务器即时向客户端推送数据,同时也可以接收客户端发送的数据。WebSocket 相比于传统的轮询或长轮询方式,能够显著减少网络流量和延迟,提高数据传输的效率和速度。它对实时 Web 应用程序和在线游戏的开发非常有用;

websocket 基础原理

WebSocket 是什么

WebSocket 是 HTML5 开始推出的基于 TCP 协议的双向通信协议,其优势在于与 HTTP 协议兼容、开销小、通信高效。WebSocket 让客户端和服务器之间建立连接,并通过这个持久连接实时地进行双向数据传

WebSocket 最主要的特点就是建立了一个可持久化的 TCP 连接,这个连接会一直保留,直到客户端或者服务器发起中断请求为止。WebSocket 通过 HTTP/1.1 协议中的 Upgrade 头信息来告诉服务器,希望协议从 HTTP/1.1 升级到 WebSocket 协议

WebSocket 建立在 HTTP 协议之上,所有的 WebSocket 请求都会通过普通的 HTTP 协议发送出去,然后在服务器端根据 HTTP 协议识别特定的头信息 Upgrade,服务端也会判断请求信息中 Upgrade 是否存在。 这里面 HTTP 是必不可少的,不然 WebSocket 根本无法建立。特别的,WebSocket 在握手时采用了 Sec-WebSocket-Key 加密处理,并采用 SHA-1 签名。

一旦建立了 WebSocket 连接,客户端和服务器端就可以互相发送二进制流或 Unicode 字符串。所有的数据都是经过 mask 处理过的,mask 的值是由服务器端随机生成的。在数据进行发送之前,必须先进行 mask 处理,这样可以有效防止数据被第三方恶意篡改。

WebSocket 与 Socket、TCP、HTTP 的关系及区别

power by openai

WebSocket、Socket、TCP 和 HTTP 都涉及到计算机网络中的数据传输,但它们的协议、用途、工作方式等方面有显著的差异。下面是它们之间的关系和主要区别:

1. WebSocket

  • 定义:WebSocket 是一种基于 TCP 协议的全双工通信协议,它使得客户端和服务器之间可以建立一个持久的、双向的通信通道。WebSocket 允许客户端和服务器在同一个连接上进行实时数据交换,适合用于需要频繁交互的应用(如聊天应用、在线游戏等)

  • 工作方式:WebSocket 在初始连接时通过 HTTP 协议进行握手,但之后会升级为 WebSocket 协议,建立一个持久的 TCP 连接。这意味着一旦连接建立,数据可以在客户端和服务器之间双向流动,而无需每次请求都重新建立连接。

  • 优点

    • 双向通信:客户端和服务器都可以主动发送数据。

    • 低延迟:避免了 HTTP 请求-响应的开销,减少了频繁建立连接的成本。

    • 实时性:适合实时应用,如股票行情、在线游戏、聊天系统等。

  • 使用场景:实时通信、在线游戏、在线协作、即时通知等。

2. Socket

  • 定义Socket 是一种提供数据传输的接口,是程序与网络之间通信的基础。它是操作系统层面的抽象,能够让应用程序进行网络通信。Socket 可以支持多种协议(如 TCP、UDP 等),并提供了一种抽象接口,让开发者无需关注底层的网络协议。

  • 工作方式:Socket 本身并不是一个协议,而是一个通信接口。它允许应用程序通过 IP 地址和端口进行数据发送和接收。Socket 本身可以基于不同的协议实现,如 TCP 或 UDP。

  • 与 WebSocket 的关系:WebSocket 也是通过 Socket 来实现通信的。WebSocket 连接实际上是通过 TCP Socket 建立的,它使用 TCP 作为传输层协议,且通过特定的握手协议与 HTTP 进行兼容。

  • 使用场景:一般的客户端-服务器通信,TCP/UDP 应用开发等。

3. TCP (Transmission Control Protocol)

  • 定义:TCP 是一种面向连接的、可靠的传输协议。它在数据传输之前需要通过三次握手建立连接,并且在传输过程中提供错误检测、流量控制、数据重传等机制,确保数据正确、可靠地到达目标。

  • 工作方式:TCP 通过端口号来识别通信的应用进程,并通过 IP 地址和端口号来建立连接。它确保数据包的顺序和完整性,通过确认应答、重传等机制实现可靠性。

  • 与 WebSocket 的关系:WebSocket 使用 TCP 作为底层传输协议。WebSocket 连接在建立时,实际上就是通过 TCP 来进行数据传输的,因此 WebSocket 能够利用 TCP 提供的可靠性和连接保持特性。

  • 使用场景:需要可靠数据传输的场合,如文件传输、HTTP 请求、数据库通信等。

4. HTTP (HyperText Transfer Protocol)

  • 定义:HTTP 是一种无状态、请求-响应式的应用层协议客户端(如浏览器)向服务器发送请求,服务器响应数据。HTTP 是 Web 应用最常用的协议,用于浏览器和 Web 服务器之间的通信。

  • 工作方式:HTTP 是基于 TCP 协议的,客户端和服务器之间通过 HTTP 请求-响应模式交换数据。每次客户端发送请求时,都会重新建立一个连接(除非使用 HTTP/2 的连接复用功能)。这种请求-响应模式适合静态内容和不频繁交互的场合

  • 与 WebSocket 的关系WebSocket 在初始连接时使用 HTTP 协议进行握手。在握手成功后,HTTP 连接会“升级”到 WebSocket 协议,从而切换到基于 TCP 的全双工通信。HTTP 和 WebSocket 都使用 TCP 作为底层协议,但 WebSocket 在通信过程中不再遵循 HTTP 的请求-响应模型。

  • 使用场景:Web 浏览、API 调用、文件下载、网页加载等。


关系与区别总结

特性

WebSocket

Socket

TCP

HTTP

协议类型

应用层协议(基于 TCP)

操作系统接口(支持多协议)

传输层协议(面向连接的协议)

应用层协议(基于 TCP)

通信方式

双向全双工

基于 TCP/UDP 协议进行数据传输

面向连接的、可靠的传输协议

请求-响应模式(单向)

连接方式

初始通过 HTTP 握手,后续通过 TCP 连接

通过 IP 地址和端口建立连接

通过三次握手建立连接

每次请求建立连接(除非使用持久连接)

实时性

高,适合实时数据交换

依赖底层协议的实现

高,可靠的传输

低,适合非实时通信

适用场景

实时通信(如聊天、在线游戏)

通用网络通信接口

需要可靠数据传输的场合(如文件传输)

Web 浏览、API 调用等

简要总结:

  • WebSocket 适合需要双向实时通信的应用,如在线聊天、实时游戏等。它是建立在 TCP 之上的,初始连接使用 HTTP 协议进行握手。

  • Socket 是用于网络通信的接口,可以基于 TCPUDP 协议实现。

  • TCP 是传输层协议,提供可靠的数据传输,WebSocket 在底层依赖 TCP

  • HTTP 是请求-响应模型的协议,适用于静态页面加载、API 请求等,但不适合实时通信。WebSocket 在初次连接时使用 HTTP 握手。

代码

JavaScript

var ws = new WebSocket("wss://echo.websocket.org");

ws.onopen = function(evt) { 
  console.log("Connection open ..."); 
  ws.send("Hello WebSockets!");
};

ws.onmessage = function(evt) {
  console.log( "Received Message: " + evt.data);
  ws.close();
};

ws.onclose = function(evt) {
  console.log("Connection closed.");
};      

这个代码创建了一个 WebSocket 客户端,连接到 wss://echo.websocket.org。
在连接建立后,它会发送 "Hello WebSockets!" 到服务器。
服务器会返回相同的消息(因为是回声服务器)。
客户端收到消息后,会打印消息并关闭 WebSocket 连接。

python

  1. The default implementation builds upon asyncio, Python’s built-in asynchronous I/O library. It provides an elegant coroutine-based API. It’s ideal for servers that handle many client connections.

  2. The threading implementation is a good alternative for clients, especially if you aren’t familiar with asyncio. It may also be used for servers that handle few client connections.

  3. The Sans-I/O implementation is designed for integrating in third-party libraries, typically application servers, in addition being used internally by websockets.

asyncio

#!/usr/bin/env python

"""Echo server using the asyncio API."""

import asyncio
import datetime
from websockets.asyncio.server import serve


async def echo(websocket):
    """
    async for message in websocket:异步接收 WebSocket 客户端发送的每一条消息。WebSocket 连接是双向的,这里我们接收客户端发来的消息,并通过 message 变量处理。
    await websocket.send(message):接收到消息后,立即将该消息发送回客户端。这意味着服务器是一个“回显”服务器,客户端发送什么消息,服务器就回复相同的消息。
    """
    async for message in websocket:
        # 获取当前时间
        current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        # 创建带有时间的消息
        message_with_time = f"{current_time} - {message}"
        await websocket.send(message_with_time)


async def main():
    async with serve(echo, "localhost", 8765) as server:
        print("Serving on", server.sockets[0].getsockname())
        await server.serve_forever()


if __name__ == "__main__":
    print("Serving on port 8765")
    asyncio.run(main())


#!/usr/bin/env python

"""Client using the asyncio API."""

import asyncio
from websockets.asyncio.client import connect


async def hello():
    async with connect("ws://localhost:8765") as websocket:
        await websocket.send("Hello world!")
        message = await websocket.recv()
        print(message)


if __name__ == "__main__":
    asyncio.run(hello())

threading

#!/usr/bin/env python

"""Echo server using the threading API."""

import datetime
import time
from websockets.sync.server import serve


def echo(websocket):
    for message in websocket:
        time.sleep(10)
        # 获取当前时间
        current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        # 创建带有时间的消息
        message_with_time = f"{current_time} - {message}"
        websocket.send(message_with_time)


def main():
    with serve(echo, "localhost", 8765) as server:
        server.serve_forever()


if __name__ == "__main__":
    main()

# client.py
from websockets.sync.client import connect
import threading
import time

def hello(client_id):
    print(f"Client {client_id} starting at {time.strftime('%H:%M:%S')}")
    with connect("ws://localhost:8765") as websocket:
        websocket.send(f"Hello from client {client_id}!")
        message = websocket.recv()
        print(f"Client {client_id} received at {time.strftime('%H:%M:%S')}: {message}")

def run_clients():
    threads = []
    for i in range(3):  # Testing with 3 clients
        thread = threading.Thread(target=hello, args=(i,))
        threads.append(thread)
        thread.start()
        time.sleep(1)  # Start clients with slight delay
    
    for thread in threads:
        thread.join()

if __name__ == "__main__":
    run_clients()

# client_no_threading.py
from websockets.sync.client import connect
import time

def hello(client_id):
    print(f"Client {client_id} starting at {time.strftime('%H:%M:%S')}")
    with connect("ws://localhost:8765") as websocket:
        websocket.send(f"Hello from client {client_id}!")
        message = websocket.recv()
        print(f"Client {client_id} received at {time.strftime('%H:%M:%S')}: {message}")

def run_clients():
    # Sequential execution without threading
    for i in range(3):
        hello(i)

if __name__ == "__main__":
    run_clients()

独立连接:当两个客户端几乎同时发起连接请求时,服务器会为每个客户端创建一个新的WebSocket连接。这意味着每个客户端都有自己的通信通道,服务器可以独立地与每个客户端进行交互。

同步处理 vs 并发连接:尽管服务器对每个连接的消息处理是同步且顺序进行的(即在处理完当前连接的消息之前不会开始处理下一个连接的消息),但这并不妨碍它接受新的连接。因此,如果两个客户端几乎同时发送消息,服务器实际上是在两个不同的连接上工作,而不是试图在一个连接上同时处理两条消息

应用:fastapi

应用:minio

参考

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区