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. vue生命周期调用

    <template> <div> <!-- 用户页的面包屑导航 --> <nav aria-label="breadcrumb"> ...

  2. helm部署redis集群

    Redis 集群部署流程 前提:K8s+helm安装完成 1. 安装 NFS 服务器 1.1 安装 NFS 工具包 在 NFS 服务器上安装 nfs-utils 包: sudo yum install ...

  3. protobuf优缺点及编码原理

    什么是protobuf protobuf(Google Protocol Buffers),官方文档对 protobuf 的定义:protocol buffers 是一种语言无关.平台无关.可扩展的序 ...

  4. [源码系列:手写spring] IOC第七节:加载xml文件中定义的Bean

    目录 主要内容 代码分支 核心代码 BeanDefinitionReader AbstractBeanDefinitionReader XmlBeanDefinitionReader 测试 bean定 ...

  5. 【数据结构与算法】不同路径 III:使用哈密尔顿路径算法实现

    [数据结构与算法]不同路径 III:使用哈密尔顿路径算法实现 Java 不同路径 III https://leetcode-cn.com/problems/unique-paths-iii/ 解题思路 ...

  6. C#连接小智服务器并将音频解码播放过程记录

    前言 最近小智很火,本文记录C#连接小智服务器并将音频解码播放的过程,希望能帮助到对此感兴趣的开发者. 如果没有ESP-32也想体验小智AI,那么这两个项目很适合你. 1.https://github ...

  7. 大模型 Token 究竟是啥:图解大模型Token

    前几天,一个朋友问我:"大模型中的 Token 究竟是什么?" 这确实是一个很有代表性的问题.许多人听说过 Token 这个概念,但未必真正理解它的作用和意义.思考之后,我决定写篇 ...

  8. MySurvey 问卷调查, 一个简单的Biwen.QuickApi示例项目

    MySurvey 项目 很久没更新我的博客了,之前开发的Biwen.QuickApi微框架 一直没有开发一个示例项目,最近有点时间,写了一个示例项目稍微介绍下 项目简介 这是一个基于 Biwen.Qu ...

  9. 基于UPD的快速局域网聊天室

    UPD与TCP对比: UDP是无连接的协议,也不保证可靠交付,只在IP数据报服务之上增加了很少的功能,主要是复用和分用以及差错检测的功能.这适用于要求源主机以恒定速率发送数据,允许网络拥塞时丢失数据, ...

  10. public void add(int index, E element)的方法源码分析

    public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess ...