WebSocket 即时消息推送系统

1. 项目概述

1.1 项目背景

在现代 Web 应用中,实时通信功能越来越重要,例如在线聊天、实时通知、股票行情更新等。本项目基于 WebSocket 技术,构建一个高效、稳定的即时消息推送系统

1.2 技术选型

  • 后端:Go(使用 gorilla/websocket 实现 WebSocket 服务器)

  • 前端:Python(websockets 库作为客户端示例)

  • 通信协议:WebSocket

  • 并发管理sync.Map 维护在线客户端,支持高并发

  • 异常处理:超时检测、心跳检测、自动断线处理


2. 代码实现

2.1 WebSocket 服务器(Go 语言)

服务器监听 :8808/chat 端口,支持多客户端连接、消息广播和心跳检测。

package main

import (
"context"
"github.com/gorilla/websocket"
"log"
"net/http"
"sync"
"time"
) // Client 结构体封装 WebSocket 连接,保证写操作的线程安全
type Client struct {
Conn *websocket.Conn
mu sync.Mutex
} var (
clients sync.Map // 存储所有在线的客户端
broadcast = make(chan []byte) // 消息广播通道
) // WebSocket 升级器
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool { return true },
} func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel() http.HandleFunc("/chat", handleChat)
go handleMessages(ctx) log.Println("Server started on localhost:8808")
if err := http.ListenAndServe(":8808", nil); err != nil {
log.Fatal("ListenAndServe: ", err)
}
} // 处理 WebSocket 连接
func handleChat(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println("Upgrade error:", err)
return
}
client := &Client{Conn: conn}
clientID := conn.RemoteAddr().String()
clients.Store(clientID, client)
log.Printf("New connection: %s", clientID) defer func() {
clients.Delete(clientID)
conn.Close()
log.Printf("Connection closed: %s", clientID)
}() // 设置 Pong 处理,避免超时断连
conn.SetReadDeadline(time.Now().Add(60 * time.Second))
conn.SetPongHandler(func(string) error {
conn.SetReadDeadline(time.Now().Add(60 * time.Second))
return nil
}) // 发送心跳 Ping
go func() {
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
client.mu.Lock()
err := client.Conn.WriteMessage(websocket.PingMessage, nil)
client.mu.Unlock()
if err != nil {
log.Println("Ping error:", err)
conn.Close()
clients.Delete(clientID)
return
}
}
}
}() for {
_, message, err := conn.ReadMessage()
if err != nil {
break
}
log.Printf("Received message from %s: %s", clientID, string(message))
broadcast <- message
}
} // 处理消息广播
func handleMessages(ctx context.Context) {
for {
select {
case message := <-broadcast:
distributeMessage(message)
case <-ctx.Done():
return
}
}
} // 分发消息给所有在线客户端
func distributeMessage(message []byte) {
clients.Range(func(key, value interface{}) bool {
client := value.(*Client)
client.mu.Lock()
defer client.mu.Unlock() if err := client.Conn.WriteMessage(websocket.TextMessage, message); err != nil {
log.Println("WriteMessage error:", err)
client.Conn.Close()
clients.Delete(key)
}
return true
})
}

2.2 WebSocket 客户端 A(Python)

客户端 A 负责发送消息,并接收 WebSocket 服务器的响应。

import json
import time
import datetime
import logging
import asyncio
import websockets _logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
async def send_message(uri: str, message: dict, timeout: int = 10) -> dict:
"""
发送 WebSocket 消息,并在 `timeout` 秒内等待响应
"""
try:
async with websockets.connect(uri) as websocket:
await asyncio.wait_for(websocket.send(json.dumps(message)), timeout)
response = await asyncio.wait_for(websocket.recv(), timeout)
return json.loads(response) except asyncio.TimeoutError:
return {"error": "Connection timed out"}
except websockets.exceptions.ConnectionClosedError as e:
return {"error": f"Connection closed: {e}"}
except json.JSONDecodeError as e:
return {"error": f"JSON decode error: {e}"}
except Exception as e:
return {"error": f"Unexpected error: {e}"} def send_message_sync(message: dict) -> dict:
"""同步调用 WebSocket 消息发送"""
return asyncio.run(send_message("ws://127.0.0.1:8808/chat", message)) if __name__ == "__main__":
"""
注意这里的:版本:websockets==15.0
""" for i in range(1000):
test_message = {"message": "Hello WebSocket", "time": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
response = send_message_sync(test_message)
print(f"Received response: {response}")
time.sleep(1)

2.3 WebSocket 客户端 B(Go 语言)

客户端 B 只接收服务器的消息。

package main

import (
"fmt"
"log"
"os"
"os/signal"
"syscall"
"time" "github.com/gorilla/websocket"
) func main() {
// 创建WebSocket连接
u := "ws://127.0.0.1:8808/chat"
log.Printf("连接到 %s", u)
c, _, err := websocket.DefaultDialer.Dial(u, nil)
if err != nil {
log.Fatal("连接错误:", err)
}
defer c.Close() // 处理中断信号
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) // 启动协程接收消息
go func() {
for {
_, message, err := c.ReadMessage()
if err != nil {
log.Println("读取消息错误:", err)
break
}
fmt.Println("WebSocket服务 Get message:", string(message))
}
}() // 等待中断信号
<-interrupt
log.Println("接收到中断信号,关闭连接")
err = c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
log.Println("关闭连接错误:", err)
}
time.Sleep(time.Second)
}

3. 测试数据

3.1、客户端A:产生数据

3.2、服务端:接收数据

3.2、客户端B:消费数据

3. 总结

本项目实现了一个 WebSocket 即时消息推送系统,支持: 多客户端连接
消息广播
心跳检测
异常处理

此 WebSocket 方案可用于 在线聊天、实时推送、协同编辑等场景

 

WebSocket 实时通信(二)的更多相关文章

  1. Python基于websocket实时通信的实现—GoEasy

    Python websocket实时消息推送 在这里我记录一下之前如何实现服务器端与客户端实时通信: 实现步骤如下: 1.        获取GoEasy appkey. 在goeasy官网上注册一个 ...

  2. PHP基于websocket实时通信的实现—GoEasy

    PHP websocket实时消息推送 在这里我记录一下之前如何实现服务器端与客户端实时通信: 实现步骤如下: 1.        获取GoEasy appkey. 在goeasy官网上注册一个账号, ...

  3. C(++)基于websocket实时通信的实现—GoEasy

    c(++) websocket实时消息推送 在这里我记录一下之前如何实现服务器端与客户端实时通信: 实现步骤如下: 1.        获取GoEasy appkey. 在goeasy官网上注册一个账 ...

  4. WebSocket(二)-WebSocket、Socket、TCP、HTTP区别

    原文地址:Socket 与 WebSocket 1. 概述 WebSocket 是为了满足基于 Web 的日益增长的实时通信需求而产生的.在传统的 Web 中,要实现实时通信,通用的方式是采用 HTT ...

  5. websocket之二:WebSocket编程入门

    一.WebSocket客户端 websocket允许通过JavaScript建立与远程服务器的连接,从而实现客户端与服务器间双向的通信.在websocket中有两个方法: 1.send() 向远程服务 ...

  6. WebSocket 介绍(二)-WebSocket API

    这一章介绍如何用WebSocket API来控制协议和创建应用,运用http://websocket.org 提供的现有WebSocket服务器,我们可以收发消息.创建一些简单的WebSocket应用 ...

  7. websocket(二) websocket的简单实现,识别用户属性的群聊

    没什么好说的,websocket实现非常简单,我们直接看代码. 运行环境:jdk8 tomcat8 无须其他jar包. 具体环境支持自己百度 package com.reach.socketContr ...

  8. WebSocket刨根问底(二)

    上篇文章[WebSocket刨根问底(一)]中我们对WebSocket的一些基本理论进行了介绍,但是并没有过多的涉及到一些实战的内容,今天我希望能够用几个简单的案例来向小伙伴们展示下WebSocket ...

  9. Sword websocket分析二

    //websocket发送数据 int send(uint8_t* message, uint64_t message_size) { //掩码 ] = { 0x12, 0x34, 0x56, 0x7 ...

  10. WebSocket教程(二)

    运行环境:jdk8 tomcat8 无须其他jar包. package com.reach.socketController; import java.io.IOException; import j ...

随机推荐

  1. Echarts与Vue3中获取DOM节点可能出现的异常错误

    useTemplateRef 的简单介绍 官方:返回一个浅层 ref,其值将与模板中的具有匹配 ref attribute 的元素或组件同步. 参数匹配机制‌:useTemplateRe的参数需与模板 ...

  2. Linux系列:如何用 C#调用 C方法造成内存泄露

    一:背景 1. 讲故事 好久没写文章了,还是来写一点吧,今年准备多写一点 Linux平台上的东西,这篇从 C# 调用 C 这个例子开始.在 windows 平台上,我们常常在 C++ 代码中用 ext ...

  3. Golang 入门 : 整型

    整型介绍 Go语言同时提供了有符号和无符号类型的整数运算.这里有 int8.int16.int32 和 int64 四种截然不同大小的有符号整形数类型,分别对应 8.16.32.64 bit大小的有符 ...

  4. JavaScript 图片弹窗

    html <!-- 触发弹窗 - 图片改为你的图片地址 --> <img loading="lazy" id="myImg" src=&quo ...

  5. 关于普通程序员该如何参与AI学习的三个建议以及自己的实践

    大部分程序员在学习大语言模型的时候都比较痛苦,感觉AI是如此之近又如此之远,仿佛能搞明白一点,又好像什么也没明白.就像我们在很远的地方看珠穆拉玛峰,感觉它就像一个不大的山包,感觉只要自己做足准备咬咬牙 ...

  6. oracle审计详解

    Oracle使用大量不同的审计方法来监控使用何种权限,以及访问哪些对象.审计不会防止使用这些权限,但可以提供有用的信息,用于揭示权限的滥用和误用. 下表中总结了Oracle数据库中不同类型的审计. 审 ...

  7. Unsloth更快训练大模型并导出GGUF - Windows

    环境搭建 系统环境 需要Nvidia显卡,至少8G显存,且专用显存与共享显存之和大于20G 建议将非安装版的环境文件都放到非系统盘,方便重装或移植 以Windows11为例,非安装环境文件都放在 E ...

  8. Towards Accurate Alignment in Real-time 3D Hand-Mesh Reconstruction论文解读

    Towards Accurate Alignment in Real-time 3D Hand-Mesh Reconstruction论文解读 这是发表在ICCV2021的一篇文章,主要的工作内容是R ...

  9. [源码系列:手写spring] IOC第十四节:容器事件和事件监听器

    代码分支 https://github.com/yihuiaa/little-spring/tree/event-and-event-listenerhttps://github.com/yihuia ...

  10. 继承中构造方法访问特点--java 进阶day01

    1.子类不可以继承父类的构造方法 构造方法的名称必须与类名一致,上图中类名是Zi,而构造方法名是Fu,肯定不行 2.子类在初始化之前,需要对父类初始化 子类在初始化的过程中,很有可能会调用到父类的数据 ...