使用WebSocket方式能将群聊信息实时群发给所有在线用户

这篇具有很好参考价值的文章主要介绍了使用WebSocket方式能将群聊信息实时群发给所有在线用户。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、WebSocket概述

1.1 什么是WebSocket

WebSocket是一种在单个TCP连接上进行全双工通信的网络协议。它是为了在Web浏览器和Web服务器之间提供实时、双向的通信而设计的。传统的HTTP协议是一种单向通信协议,客户端发送请求,服务器响应,然后连接就关闭了。而WebSocket允许在客户端和服务器之间建立持久连接,使得双方可以通过该连接随时发送数据。

WebSocket协议通过在HTTP握手阶段使用Upgrade头来升级连接,使其成为全双工通信通道。一旦升级完成,WebSocket连接就保持打开状态,允许双方在任何时候发送数据。

WebSocket协议的特点包括:

  1. 全双工通信: 客户端和服务器之间可以同时发送和接收数据,而不需要等待响应。
  2. 低延迟: 由于连接保持打开状态,可以更快地传输数据,适用于实时性要求较高的应用,如在线游戏、聊天应用等。
  3. 跨域支持: 与AJAX请求不同,WebSocket允许跨域通信,提供更大的灵活性。

WebSocket 端点通常会触发一些生命周期事件,这些事件可以用于处理数据、管理连接的状态等。

1.2 WebSocket的生命周期事件

  1. onOpen 事件: 在端点建立新WebSocket连接时并且在任何其他事件发生之前,将触发onOpen 事件。在这个事件中,可以执行一些初始化操作,例如记录连接信息、添加到连接池等。
  2. onMessage 事件: 当端点接收到客户端发送的消息时,将触发onMessage 事件。在这个事件中,可以处理接收到的消息并根据需要做出相应的反应。
  3. onError 事件: 当在 WebSocket 连接期间发生错误时,将触发onError事件。在这个事件中可以处理错误情况。
  4. onClose 事件: 当连接关闭时,将触发onClose事件。在这个事件中可以执行一些清理工作,例如从连接池中移除连接、记录连接关闭信息等。

二、WebSocket实现群聊功能

2.1 服务端:注解式端点事件处理

在服务端使用@ServerEndpoint注解将 Java 类声明成 WebSocket 服务端端点。

@ServerEndpoint(value = "/chat", configurator= GetHttpSessionConfigurator.class)
@Component // SpringBoot 的组件注解
public class ChatServer

对于注解式服务端端点,WebSocket API中的生命周期事件要求使用以下方法级注解:@OnOpen @OnMessage @OnError @OnClose。

@ServerEndpoint(value = "/chat", configurator= GetHttpSessionConfigurator.class)
@Component
public class ChatServer {
    // WebSocker 生命周期函数: 连接建立时调用
    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        // 从 EndpointConfig 中获取之前从握手时获取的 httpSession
        this.httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
        String userName = (String) this.httpSession.getAttribute("username");
        // 保存已登录用户 session
        clients.put(userName, session);
        log.info(userName + "已与服务器建立连接");
    }

    // WebSocker 生命周期函数: 收到消息时调用
    @OnMessage
    public void onMessage(String message) {
        log.info("服务器收到消息: " + message);
        // 服务器群发消息
        groupSend(message);
    }

    // WebSocker 生命周期函数: 连接断开时调用
    @OnClose
    public void onClose() {
        String userName = (String) this.httpSession.getAttribute("username");
        clients.remove(userName);
        log.info(userName + "已与服务器断开连接");
    }
    
    ......
}

其中,服务端在收到消息时,在OnMessage事件中向客户端群发消息

// WebSocker 生命周期函数: 收到消息时调用
@OnMessage
public void onMessage(String message) {
    log.info("服务器收到消息: " + message);
    // 服务器群发消息
    groupSend(message);
}
private void groupSend(String message) {
    // 遍历所有连接,向客户端群发消息
    message = this.httpSession.getAttribute("username") + ": " + message;
    Set<Map.Entry<String, Session>> entries = clients.entrySet();
    for (Map.Entry<String, Session> client : entries) {
        Session session = client.getValue();
        try {
            session.getBasicRemote().sendText(message); // 发送消息
        } catch (IOException e) {
        }
    }  
}

2.2 客户端:JavaScript中的WebSocket对象

在客户端使用JavaScript的WebSocket对象作为客户端端点

// new WebSocket("ws://url")
ws = new WebSocket("ws://localhost:8848/chat");

对于JavaScript的WebSocket对象,其生命周期事件为onopen,onmessage,onerror和onclose。

ws.onmessage = function (msg) {
    let message = msg.data; // 获取服务端发送的信息
    // 将消息显示到页面中
    let li = document.createElement('li');
    li.textContent = message;
    let messages = document.getElementById('messages');
    messages.appendChild(li);
    window.scrollTo(0, document.body.scrollHeight);
}

在客户端使用WebSocket对象的send(message)方法向服务端发送消息

三、Session、Cookie实现24小时内自动识别用户

在服务端登录验证的handler中,创建携带验证信息的Cookie(这里图方便直接携带了用户名,实际应该使用根据用户UID加密过的token)并返回给客户端浏览器,设定有效期为24h。

同时,在会话域Session 中保存用户名以便 WebSocket 获取(Servlet在新建Session时也会返回给客户端带有JSESSIONID的Cookie):

@RestController
public class UserController {
    @PostMapping("/login")
    public String login(@RequestBody UserEntity user, HttpServletRequest request, HttpServletResponse response) {
        if(loginCheck(user)) {
            Cookie cookie = new Cookie("username", user.getUserName());
            cookie.setMaxAge(24 * 60 * 60); // 设置 Cookie 的有效时间为 24h
            response.addCookie(cookie);
            // 在 session 中设置 userName 以便 WebSocket 获取
            request.getSession().setAttribute("username", user.getUserName());
            return "success";
        } else  {
            return "failed";
        }
    }
    ......
}

此后24h内,客户端浏览器访问服务端时会携带以上Cookie,即使会话连接断开,服务端也可以根据该Cookie直接验证用户信息并重新在Session中保存,实现24h内用户免登录。若会话未断开,直接从Session中即可获取用户信息。

// 拦截器:登录校验, 不通过则跳转到登录界面
@Component
public class LoginProtectInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        // 先使用 session 进行登录校验
        String username = (String) request.getSession().getAttribute("username");
        if(username != null){
            return true; // 放行
        }
        // session 校验失败则用 cookie校验
        Cookie[] cookies = request.getCookies();
        for(Cookie cookie : cookies){
            if("username".equals(cookie.getName())){
                // 根据 cookie获取 userName
                String userName = cookie.getValue();
                // 在 session 中设置 userName
                request.getSession().setAttribute("username", userName);
                return true; // 放行
            }
        }
        // 校验失败 跳转到登录界面
        response.sendRedirect("/login.html");
        return false;
    }
}

上面是在拦截器中进行登陆验证,需要对拦截器进行配置。

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Autowired
    private LoginProtectInterceptor loginProtectInterceptor;
    // 登录验证拦截器配置
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 访问聊天室前需要登录验证
        registry.addInterceptor(loginProtectInterceptor).addPathPatterns("/chat.html");
    }
}

四、实验中遇到的一些问题及其解决

4.1 WebSocket获取httpSession的方法

实验中需获取Session中的用户信息,由于WebSocket与Http协议的不同,故需要在WebSocket中故在获取HttpSession,这里参考了以下链接中的方法。

https://blog.csdn.net/Zany540817349/article/details/90210075

在@ServerEndpoint注解的源代码中,可以看到要求一个ServerEndpointConfig接口下的Configurator子类,该类中有个modifyHandshake方法,这个方法可以修改在握手时的操作,将httpSession加进webSocket的配置中。

因此继承这个ServerEndpointConfig.Configurator子类,重写其modifyHandshake方法:

/** 继承 ServerEndpointConfig.Configurator 类
 *  重写其中的 modifyHandshake 方法
 *  在建立连接时将当前会话的 httpSession 加入到 webSocket 的 Server端的配置中
 */
@Configuration
public class GetHttpSessionConfigurator extends ServerEndpointConfig.Configurator {
    @Override
    public void modifyHandshake(ServerEndpointConfig sec,
                                HandshakeRequest request, HandshakeResponse response) {
        HttpSession httpSession=(HttpSession) request.getHttpSession();
        sec.getUserProperties().put(HttpSession.class.getName(),httpSession);
    }
}

将继承类加入到@ServerEndpoint注解的configurator属性中,这样即可在EndpointConfig中获取HttpSession:

@Component
@ServerEndpoint(value = "/chat", configurator= GetHttpSessionConfigurator.class)
public class ChatServer {
    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        // 从 EndpointConfig 中获取之前从握手时获取的 httpSession
        this.httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
        String userName = (String) this.httpSession.getAttribute("username");
        ......

4.2 WebSocket获取httpSession为空(Session不一致)的问题

4.1中的获取httpSession的方法是正确的,但是在modifyHandshake中getHttpSession()时会报空指针异常,这里参考了以下链接,发现是服务端url写错。

https://blog.csdn.net/csu_passer/article/details/78536060

在前端连接WebSocket的时候,我的代码是这样的:

new WebSocket("ws://127.0.0.1:8848/chat");

但是浏览器的地址栏是

http://localhost:8848/chat.html

链接中解释说如果不使用同一个host,则会创建不同的连接请求,将WebSocket中服务端地址修改为与浏览器地址栏一致,则可以正确获取到httpSession。

new WebSocket("ws://localhost:8848/chat");

实验源代码

https://gitee.com/amadeuswyk/ustc-courses-net-web-socket/tree/master/

参考资料

https://blog.csdn.net/Zany540817349/article/details/90210075
https://blog.csdn.net/csu_passer/article/details/78536060文章来源地址https://www.toymoban.com/news/detail-774095.html

到了这里,关于使用WebSocket方式能将群聊信息实时群发给所有在线用户的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • TCP案例-实时群聊

    服务器端循环获取所有的客户端socket,放到一个 socket的list 中,等到需要通信的时候,调用相对应的管道就可以了 客户端和服务器端一样,纪要发送消息,也要接受消息 读取线程是子线程 只能实现群发,无法端到端 也会发给自己

    2024年04月28日
    浏览(30)
  • 问题:Feem无法发送信息OR无法连接(手机端无法发给电脑端)

    目录 前言 问题分析 资源、链接 其他问题 前言 需要在小米手机、华为平板、Dell电脑之间传输文件,试过安装破解的华为电脑管家、小米的MIUI文件传输等,均无果。(小米“远程管理”ftp传输倒是可以,但速度太慢了,且平板打不开) 后来在B站评论中发现feem,可以很方便

    2024年01月20日
    浏览(42)
  • SpringBoot整合 websocket 实现群聊,点对点聊天

    1.websocket websocket最伟大之处在于服务器和客户端可以在给定的时间范围内的任意时刻,相互推送信息。浏览器和服务器只需要要做一个握手的动作,在建立连接之后,服务器可以主动传送数据给客户端,客户端也可以随时向服务器发送数据。 第一、WebSocket是HTML5中的协议,支

    2024年02月08日
    浏览(36)
  • python的WebSocket编程详解,案例群聊系统实现

    1.1为什么要用websocket 如果有需求要实现服务端向客户端主动推送消息时(比如聊天室,群聊室)有哪几种方案 轮训:让浏览器每隔两秒发送一次请求,缺点:有延时,请求太多网站压力大; 长轮训:客户端向服务端发送请求,服务端最多夯20秒,一旦有新的数据就立即返回

    2024年02月02日
    浏览(34)
  • vue+websokect实现实时聊天,可单聊、可群聊(一)

    效果网站链接,安全性不符合实际上线使用,仅供学习交流 https://livequeen.top  效果小程序二维码 (需现在web端获取账号)             一个实时聊天功能 第一,肯定要能够实现 单聊或者群聊 ,所以不能无差别广播,要实现向指定用户发送广播。 第二, 区分在线和离线

    2023年04月16日
    浏览(36)
  • 基于Springboot+WebSocket+Netty实现在线聊天、群聊系统

    此文主要实现在好友添加、建群、聊天对话、群聊功能,使用Java作为后端语言进行支持,界面友好,开发简单。 2.1、下载安装IntelliJ IDEA(后端语言开发工具),Mysql数据库,微信Web开发者工具。 1.创建maven project 先创建一个名为SpringBootDemo的项目,选择【New Project】 然后在弹出

    2024年02月14日
    浏览(41)
  • 了解JS三种实时通信方式——Eventsource、websocket与socket.io之间的差异和优缺点

    EventSource EventSource 是一种轻量级的 API,用于获取来自服务器的实时事件。它是 WebSockets 的替代方案,因为它比 WebSockets 更简单,更适合处理服务器向客户端发送数据的情况。使用 EventSource ,只有服务器能够发送消息,所以它更安全。但是,它不支持双向通信或客户端发送消

    2024年02月08日
    浏览(48)
  • 推荐一个.Net Core开发的Websocket群聊、私聊的开源项目

    今天给大家推荐一个使用Websocket协议实现的、高性能即时聊天组件,可用于群聊、好友聊天、游戏直播等场景。 这是一个基于.Net Core开发的、简单、高性能的通讯组件,支持点对点发送、群聊、在线状态的订阅。 该项目还包含群聊例子,可以用于学习。 1、跨平台:基于.N

    2024年02月03日
    浏览(55)
  • 【WebSocket】前端使用WebSocket实时通信

    最近写项目,需要实现消息通知和实时聊天的功能,就去了解了一些关于websocket的知识,总结如下。 WebSocket 是一种在 Web 应用中实现实时通信的协议。与传统的 HTTP 请求不同,WebSocket 连接在客户端和服务器之间建立一个 持久性 的 双向通信管道 ,使得数据可以在连接打开后

    2024年02月11日
    浏览(32)
  • SpringBoot与webSocket实现在线聊天室——实现私聊+群聊+聊天记录保存

    引用参考:原文章地址:https://blog.csdn.net/qq_41463655/article/details/92410518 在此基础上实现对聊天记录的保存。 代码地址:链接:https://pan.baidu.com/s/1IJFZDa4S_DF08773sKJWeA 提取码:jkui 思路:新建一个实体类用于保存聊天记录,在消息发送时,设置对象的各个值然后保存到数据库中。

    2024年02月02日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包