Swoole与Go系列教程之WebSocket服务的应用

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: 在 WebSocket 协议出现之前,Web 应用为了能过获取到实时的数据都是通过不断轮询服务端的接口。轮询的效率、延时很低,并且很耗费资源。

大家好,我是码农先森。

写在前面

在 WebSocket 协议出现之前,Web 应用为了能过获取到实时的数据都是通过不断轮询服务端的接口。轮询的效率、延时很低,并且很耗费资源。在2008年 Google 的一位工程师 Ian Hickson 发明并起草了 Websocket 协议的相关规范,后来经过讨论和改进,由 WHATWG 组织负责制定了 WebSocket 的标准。最终,WebSocket 协议在2011年被正式发布为 RFC 6455 标准,同时得到了广泛应用和支持。WebSocket 协议的发明填补了 Web 应用中实时双向通信的空白,为大家提供了更便捷、高效的方式来开发实时性较强的应用程序,例如:聊天网站、在线游戏等一些需要进行实时数据交互的Web应用。

WebSocket 协议原理

WebSocket是一种在Web浏览器和Web服务器之间进行全双工通信的协议。它提供了一个持久化的连接,允许服务器主动向客户端推送数据,而无需客户端发送请求。相对于传统的HTTP协议,它更加高效、实时,并且减少了通信的延迟。

请在此添加图片描述

  • FIN(Finale)是一个控制帧标志位,用于指示消息是否是一个完整的消息片段或者是最后一个消息片段。

    • 当 FIN 设置为 1 时,表示该消息是一个完整的消息片段或者是最后一个分片。
    • 当 FIN 设置为 0 时,表示该消息是一个消息片段的一部分,还需要后续的分片来组成完整的消息。
  • RSV1、RSV2 和 RSV3 是三个保留位(Reserved Bits)。

    • 这些保留位最初设计用于未来扩展协议的目的。WebSocket 规范规定,在当前版本的协议中,RSV1、RSV2 和 RSV3 的初始值必须为 0。
    • 如果服务器或客户端收到的数据帧中的这些保留位为 1,而且尚未定义对应的扩展协议,那么它们应该关闭连接。这样可以确保当前协议版本的兼容性和互操作性。
  • opcode(操作码)表示数据帧(Frame)的类型或用途。

    • 0x0 (ContinuationFrame):用于传输分片(fragmented)的消息,一个完整的消息可能会被分为多个数据帧。
    • 0x1 (TextFrame):用于传输文本数据,表示消息的内容是文本。
    • 0x2 (BinaryFrame):用于传输二进制数据,表示消息的内容是二进制数据。
    • 0x8 (ConnectionClose):用于关闭连接,表示一个终止连接的请求或响应。
    • 0x9 (Ping):用于心跳检测,由客户端发起,服务器必须回复一个对应的 Pong 数据帧。
    • 0xA (Pong):用于心跳检测,作为对 Ping 数据帧的响应。
  • MASK(掩码)是一个用于对数据帧有效载荷进行加密的机制。它是用于保护数据的完整性和安全性。

    • 如果 MASK 标志位设置为 1,并且存在掩码密钥(Masking Key),则表示有效载荷被掩码加密过。
    • 如果 MASK 标志位设置为 0,则表示有效载荷没有经过掩码处理。
  • Payload Length(有效载荷长度)字段用于指示数据帧的有效载荷(payload)的长度。Payload Length 字段的值可以是 7 位、16 位或 64 位。

    • 如果 Payload Length 字段的值为 0 到 125(含),则表示有效载荷的长度就是该值,直接读取对应的字节数作为有效载荷的长度。
    • 如果 Payload Length 字段的值为 126,则表示有效载荷的长度用后续的 16 位无符号整数表示,需要读取后续的 16 位作为有效载荷长度。
    • 如果 Payload Length 字段的值为 127,则表示有效载荷的长度用后续的 64 位无符号整数表示,需要读取后续的 64 位作为有效载荷长度。
  • Extended Payload Length(扩展有效载荷长度)用于表示有效载荷超过 125 字节时的情况。

    • 当 Payload Length 字段的值为 126 时,表示随后的 16 位无符号整数字段指示了有效载荷的实际长度。这个 16 位字段被称为 Extended Payload Length 字段。
    • Extended Payload Length 的值表示有效载荷的长度。通过读取 Extended Payload Length 字段的 16 位无符号整数,可以确定有效载荷的确切长度(以字节为单位)。
  • Masking Key(掩码密钥)是用于对数据帧有效载荷进行加密的关键,是一个随机生成的 32 位(4 字节)长度的值。

    • 在 Mask 字段设置为 1 的情况下,接收方才需要从 Masking Key 字段中获取掩码密钥,并使用该密钥对有效载荷进行解码操作。
    • 在 Mask 字段设置为 0 的情况下,有效载荷不需要进行解码操作,可以直接使用。
    • 通过对有效载荷数据和掩码密钥进行异或运算,可以实现对数据进行加密和解密的效果。确保数据在传输过程中的安全性和完整性,防止被恶意攻击者窃取或篡改数据。
  • Payload Data(有效载荷数据)是 WebSocket 数据帧中携带的实际数据内容部分。它包含了应用程序要传输的具体信息。

    • 有效载荷数据可以是任意类型的数据,例如文本、二进制数据、JSON、XML 等等,取决于应用程序的需求。

上面主要是介绍协议传输的标准,接下来我们看看协议的工作流程。
请在此添加图片描述
客户端通过发送一个 HTTP 请求到服务器,服务器接收到该请求后,根据请求头的相关字段进行验证和处理,并返回一个 HTTP 响应。客户端接收到服务器的响应后,如果响应状态码为 101 Switching Protocols,表示协议切换成功。此时就可以使用 WebSocket 协议进行双向通信。

在 Swoole 中的应用

  1. 使用 Swoole\WebSocket\Server 创建一个 WebSocket 服务。
  2. 使用 $server->on('open', function(...){...} 监听 open 连接打开事件。
  3. 使用 $server->on('message', function(...){...} 监听消息接收事件。
  4. 使用 $server->push(...) 方法推送消息给客户端。
  5. 当客户端关闭连接时,使用 $server->on('close', function(...){...} 监听关闭的连接、
  6. 使用 $server->start() 来正式启动 WebSocket 服务。
<?php

// 创建 WebSocket 服务器对象,监听 0.0.0.0:8080 端口
$server = new Swoole\WebSocket\Server('0.0.0.0', 8080);

// 监听 WebSocket 连接打开事件
$server->on('open', function (Swoole\WebSocket\Server $server, $request) {
   
   
    echo "New WebSocket connection opened: {$request->fd}\n";
});

// 监听 WebSocket 消息事件
$server->on('message', function (Swoole\WebSocket\Server $server, $frame) {
   
   
    echo "Received message from {$frame->fd}: {$frame->data}\n";

    // 向客户端发送回复消息
    $server->push($frame->fd, 'Hello from Swoole server!');
});

// 监听 WebSocket 连接关闭事件
$server->on('close', function (Swoole\WebSocket\Server $server, $fd) {
   
   
    echo "WebSocket connection closed: {$fd}\n";
});

// 启动 WebSocket 服务器
$server->start();

使用 wscat 工具进行连接测试。

MacBook-Pro:demo$ wscat -c 127.0.0.1:8080
Connected (press CTRL+C to quit)
> Hello
< Hello from Swoole server!

在 Go 语言中的应用

  1. 使用 http.ListenAndServe 监听 8080 端口。
  2. 定义路由 http.HandleFunc("/websocket", handleWebSocket) 及处理函数。
  3. 升级 HTTP 连接为 WebSocket 连接。
  4. 使用 conn.ReadMessage() 读取连接的客户端数据。
  5. 使用 conn.WriteMessage 回复客户端消息。
  6. 持续监听客户端的 WebSocket 连接。
  7. 这里的 handleWebSocket(...) 每次都是在一个协程当中执行维护的。
package main

import (
    "log"
    "net/http"

    "github.com/gorilla/websocket"
)

// 定义 WebSocket 升级器
var upgrader = websocket.Upgrader{
   
   }

// 处理 WebSocket 连接的请求
func handleWebSocket(w http.ResponseWriter, r *http.Request) {
   
   
    // 升级 HTTP 连接为 WebSocket 连接
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
   
   
        log.Println("Failed to upgrade to WebSocket:", err)
        return
    }
    defer conn.Close()

    // 在这里处理 WebSocket 连接
    for {
   
   
        // 读取来自客户端的消息
        _, msg, err := conn.ReadMessage()
        if err != nil {
   
   
            log.Println("Failed to read message from WebSocket:", err)
            break
        }

        // 处理接收到的消息
        log.Println("Received message:", string(msg))

        // 回复消息给客户端
        err = conn.WriteMessage(websocket.TextMessage, []byte("Hello from Go server!"))
        if err != nil {
   
   
            log.Println("Failed to send message to WebSocket:", err)
            break
        }
    }
}

func main() {
   
   
    // 设置路由和处理函数
    http.HandleFunc("/websocket", handleWebSocket)

    // 启动 HTTP 服务器
    log.Println("Server started on localhost:8080")
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
   
   
        log.Fatal("Failed to start server:", err)
    }
}

使用 wscat 工具进行连接测试。

MacBook-Pro:demo$ wscat -c 127.0.0.1:8080
Connected (press CTRL+C to quit)
> Hello
< Hello from Go server!

总结

  1. 介绍了 WebSocket 协议数据包的格式,及协议的工作流程。
  2. WebSocket 协议的出现解决了一些需要实时数据交互的业务场景,例如:聊天网站、在线游戏等。
  3. 在 Swoole 中要注意使用协程客户端,避免阻塞主进程的执行。
  4. 在 Go 中每一个 Http Handle 就会新建一个协程来处理新的连接请求。

欢迎关注、分享、点赞、收藏、在看,我是微信公众号「码农先森」作者。

相关文章
|
6天前
|
监控 Linux PHP
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
54 20
|
4月前
|
前端开发 JavaScript UED
探索Python Django中的WebSocket集成:为前后端分离应用添加实时通信功能
通过在Django项目中集成Channels和WebSocket,我们能够为前后端分离的应用添加实时通信功能,实现诸如在线聊天、实时数据更新等交互式场景。这不仅增强了应用的功能性,也提升了用户体验。随着实时Web应用的日益普及,掌握Django Channels和WebSocket的集成将为开发者开启新的可能性,推动Web应用的发展迈向更高层次的实时性和交互性。
128 1
|
1月前
|
数据挖掘 UED
WebSocket在实时体育比分网站中的应用
WebSocket 在实时体育比分网站中用于实时比分更新、动态赛事信息推送、交互式功能(如即时聊天和投票)、赛程提醒与推送通知、比分预测与数据分析,以及多平台支持。通过持久连接,服务器可即时推送比分变化、球员动态、比赛状态等信息,减少延迟并提升用户体验。同时,WebSocket 支持双向通信,使用户能实时互动,确保跨平台的实时数据同步。
|
3月前
|
缓存 监控 前端开发
Go 语言中如何集成 WebSocket 与 Socket.IO,实现高效、灵活的实时通信
本文探讨了在 Go 语言中如何集成 WebSocket 与 Socket.IO,实现高效、灵活的实时通信。首先介绍了 WebSocket 和 Socket.IO 的基本概念及其优势,接着详细讲解了 Go 语言中 WebSocket 的实现方法,以及二者集成的重要意义和具体步骤。文章还讨论了集成过程中需要注意的问题,如协议兼容性、消息格式、并发处理等,并提供了实时聊天、数据监控和在线协作工具等应用案例,最后提出了性能优化策略,包括数据压缩、缓存策略和连接管理优化。旨在帮助开发者更好地理解并应用这些技术。
160 3
|
3月前
|
缓存 监控 前端开发
在 Go 语言中实现 WebSocket 实时通信的应用,包括 WebSocket 的简介、Go 语言的优势、基本实现步骤、应用案例、注意事项及性能优化策略,旨在帮助开发者构建高效稳定的实时通信系统
本文深入探讨了在 Go 语言中实现 WebSocket 实时通信的应用,包括 WebSocket 的简介、Go 语言的优势、基本实现步骤、应用案例、注意事项及性能优化策略,旨在帮助开发者构建高效稳定的实时通信系统。
200 1
|
3月前
|
Go UED
Go Web服务中如何优雅平滑重启?
在生产环境中,服务升级时如何确保不中断当前请求并应用新代码是一个挑战。本文介绍了如何使用 Go 语言的 `endless` 包实现服务的优雅重启,确保在不停止服务的情况下完成无缝升级。通过示例代码和测试步骤,详细展示了 `endless` 包的工作原理和实际应用。
79 3
|
3月前
|
JSON Go UED
Go Web服务中如何优雅关机?
在构建 Web 服务时,优雅关机是一个关键的技术点,它确保服务关闭时所有正在处理的请求都能顺利完成。本文通过一个简单的 Go 语言示例,展示了如何使用 Gin 框架实现优雅关机。通过捕获系统信号和使用 `http.Server` 的 `Shutdown` 方法,我们可以在服务关闭前等待所有请求处理完毕,从而提升用户体验,避免数据丢失或不一致。
47 1
|
4月前
|
前端开发 网络协议
netty整合websocket(完美教程)
本文是一篇完整的Netty整合WebSocket的教程,介绍了WebSocket的基本概念、使用Netty构建WebSocket服务器的步骤和代码示例,以及如何创建前端WebSocket客户端进行通信的示例。
464 2
netty整合websocket(完美教程)
|
4月前
|
JavaScript 前端开发 测试技术
前端全栈之路Deno篇(五):如何快速创建 WebSocket 服务端应用 + 客户端应用 - 可能是2025最佳的Websocket全栈实时应用框架
本文介绍了如何使用Deno 2.0快速构建WebSocket全栈应用,包括服务端和客户端的创建。通过一个简单的代码示例,展示了Deno在WebSocket实现中的便捷与强大,无需额外依赖,即可轻松搭建具备基本功能的WebSocket应用。Deno 2.0被认为是最佳的WebSocket全栈应用JS运行时,适合全栈开发者学习和使用。
213 7
|
3月前
|
Kubernetes Cloud Native JavaScript
为使用WebSocket构建的双向通信应用带来基于服务网格的全链路灰度
介绍如何使用为基于WebSocket的云原生应用构建全链路灰度方案。

热门文章

最新文章