WebSocket实现网站点赞通知

WebSocket 详解

什么是 WebSocket?

WebSocket 是一种在单个 TCP 连接 上进行全双工通信 的网络协议,实现了浏览器与服务器之间的实时双向通信

与 HTTP 的根本区别

HTTP(请求-响应模式)

复制代码
客户端: 请求 → 服务器
客户端: ← 响应 服务器
(连接关闭)

WebSocket(全双工模式)

复制代码
客户端: ↔ 服务器
客户端: ↔ 服务器  
客户端: ↔ 服务器
(持续连接,双向实时通信)

WebSocket 协议特点

1. 一次握手,持久连接

javascript 复制代码
// 握手过程
客户端 → 服务器: HTTP Upgrade 请求
客户端 ← 服务器: HTTP 101 Switching Protocols
// 之后就是 WebSocket 协议通信

2. 极小的协议开销

java 复制代码
// 建立连接后,数据帧头很小
// HTTP 每次请求都有完整的头部,WebSocket 只有 2-14 字节的帧头

3. 真正的实时性

  • 服务器可以主动推送 数据给客户端
  • 无需客户端轮询询问
  • 毫秒级延迟

WebSocket 核心技术原理

连接建立过程

java 复制代码
// 1. 客户端发起握手
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

// 2. 服务器响应
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

数据帧格式

复制代码
 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
|N|V|V|V|       |S|             |   (if payload len==126/127)   |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|     Extended payload length continued, if payload len == 127  |
+ - - - - - - - - - - - - - - - +-------------------------------+
|                               |Masking-key, if MASK set to 1  |
+-------------------------------+-------------------------------+
| Masking-key (continued)       |          Payload Data         |
+-------------------------------- - - - - - - - - - - - - - - - +
:                     Payload Data continued ...                :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                     Payload Data continued ...                |
+---------------------------------------------------------------+

Spring Boot 中的 WebSocket 实现

1. 依赖配置

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

2. 配置类

java 复制代码
package com.panda.wiki.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

3. 端点类(注解方式)

java 复制代码
package com.panda.wiki.websocket;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

@Component
@ServerEndpoint("/ws/{token}")
public class WebSocketServer {
    private static final Logger LOG = LoggerFactory.getLogger(WebSocketServer.class);  // 修正 Logger

    /**
     * 每个客户端一个token
     */
    private String token = "";
    
    /**
     * 使用线程安全的 ConcurrentHashMap
     */
    public static ConcurrentHashMap<String, Session> map = new ConcurrentHashMap<>();

    /**
     * 连接成功
     */
    @OnOpen  // 修正注解大小写
    public void onOpen(Session session, @PathParam("token") String token) {  // 修正参数语法
        map.put(token, session);
        this.token = token;
        LOG.info("有新连接:token:{},session id:{},当前连接数:{}", token, session.getId(), map.size());  // 修正日志语法
    }

    /**
     * 连接关闭
     */
    @OnClose  // 修正注解大小写
    public void onClose(Session session) {
        map.remove(this.token);
        LOG.info("连接关闭,token:{},session id:{},当前连接数:{}", this.token, session.getId(), map.size());  // 修正日志语法
    }

    /**
     * 收到消息
     */
    @OnMessage
    public void onMessage(String message, Session session) { 
        LOG.info("收到消息: {},内容: {}", token, message);  // 修正日志语法
    }

    /**
     * 连接错误
     */
    @OnError
    public void onError(Session session, Throwable error) { 
        LOG.error("发生错误", error); 
    }

    /**
     * 群发消息
     */
    public static void sendInfo(String message) {  // 改为静态方法
        for (String token : map.keySet()) {
            Session session = map.get(token);
            try {
                if (session.isOpen()) {  // 检查会话是否仍然打开
                    session.getBasicRemote().sendText(message);
                    LOG.info("推送消息: {}, 内容: {}", token, message);  // 修正日志文字
                } else {
                    // 如果会话已关闭,从map中移除
                    map.remove(token);
                }
            } catch (IOException e) {
                LOG.error("推送消息失败: {}, 内容: {}", token, message, e);  // 修正日志文字
                // 发送失败,从map中移除无效连接
                map.remove(token);
            }
        }
    }

    /**
     * 向指定用户发送消息
     */
    public static void sendToUser(String token, String message) {
        Session session = map.get(token);
        if (session != null && session.isOpen()) {
            try {
                session.getBasicRemote().sendText(message);
                LOG.info("向用户 {} 发送消息: {}", token, message);
            } catch (IOException e) {
                LOG.error("向用户 {} 发送消息失败", token, e);
                map.remove(token);  // 移除无效连接
            }
        } else {
            LOG.warn("用户 {} 不在线或连接已关闭", token);
            if (session != null) {
                map.remove(token);  // 清理无效连接
            }
        }
    }

    /**
     * 获取当前在线连接数
     */
    public static int getOnlineCount() {
        return map.size();
    }

    /**
     * 检查用户是否在线
     */
    public static boolean isOnline(String token) {
        Session session = map.get(token);
        return session != null && session.isOpen();
    }
}
相关推荐
雪兽软件3 小时前
SaaS 安全是什么以及如何管理风险
网络·安全
纸带3 小时前
USB -- SET_ADDRESS or --SET_ADDRESS or --SET_CONFIGURATION or --SET_INTERFACE
网络
white-persist3 小时前
CSRF 漏洞全解析:从原理到实战
网络·python·安全·web安全·网络安全·系统安全·csrf
嫄码4 小时前
TCP/IP 四层模型
网络·网络协议·tcp/ip
游戏开发爱好者84 小时前
FTP 抓包分析实战,命令、被动主动模式要点、FTPS 与 SFTP 区别及真机取证流程
运维·服务器·网络·ios·小程序·uni-app·iphone
liebe1*14 小时前
第三章 常用协议
网络
key064 小时前
大模型在网络安全领域的应用与评测
网络·人工智能·web安全
望获linux5 小时前
【实时Linux实战系列】实时 Linux 的自动化基准测试框架
java·大数据·linux·运维·网络·elasticsearch·搜索引擎
北京耐用通信5 小时前
破解工业通信瓶颈:耐达讯自动化Modbus转Profinet网关连接驱动器的奥秘
人工智能·物联网·网络协议·自动化·信息与通信