demo篇---同一个用户开启多个webSocket连接

目的:

一个用户下建立多个连接,例如:一个用户开启多个网页,每个网页都建立一个socket连接。

最终表现:

用户的多个页面,能收到统一的消息:

具体使用方式:

项目地址:
https://gitee.com/brinjaul/fjpdemo.git

前端

客户端页面使用 http://www.websocket-test.com/ 与项目建立连接,充当web客户端

注意下图的红框中是我们服务段地址,其中 /websocket/fjp 是我们服务端注解里的
在这里插入图片描述

@ServerEndpoint("/websocket/{userId}")

{userId} 类似于SpringMVC中的 @PathVariable(“param”) 的使用方式,即,restful风格形式的动态传传参

或者使用项目里配置的,WebSocketTest.html,与服务端页面交互。

后端:

代码:

package com.fjp.websocket;

import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

/**
 * @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
 * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
 */
@ServerEndpoint("/websocket/{userId}")
public class WebSocketTest {

    private static ConcurrentHashMap<String, CopyOnWriteArraySet<WebSocketTest>> userwebSocketMap = new ConcurrentHashMap<String, CopyOnWriteArraySet<WebSocketTest>>();

    private static ConcurrentHashMap<String, Integer> count = new ConcurrentHashMap<String, Integer>();

    private String userId;


    /*
     * 与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    private Session session;

    /**
     * 连接建立成功调用的方法
     *
     * @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") final String userId) {
        this.session = session;
        this.userId = userId;
        if (!exitUser(userId)) {
            initUserInfo(userId);
        } else {
            CopyOnWriteArraySet<WebSocketTest> webSocketTestSet = getUserSocketSet(userId);
            webSocketTestSet.add(this);
            userCountIncrease(userId);
        }
        System.out.println("有" + userId + "新连接加入!当前在线人数为" + getCurrUserCount(userId));
    }


    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        CopyOnWriteArraySet<WebSocketTest> webSocketTestSet = userwebSocketMap.get(userId);
        //从set中删除
        webSocketTestSet.remove(this);
        //在线数减1
        userCountDecrement(userId);
        System.out.println("有一连接关闭!当前在线人数为" + getCurrUserCount(userId));
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     * @param session 可选的参数
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        CopyOnWriteArraySet<WebSocketTest> webSocketSet = userwebSocketMap.get(userId);
        System.out.println("来自客户端" + userId + "的消息:" + message);
        //群发消息
        for (WebSocketTest item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
                continue;
            }
        }
    }

    /**
     * 发生错误时调用
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }

    /**
     * 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
     *
     * @param message
     * @throws IOException
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
        //this.session.getAsyncRemote().sendText(message);
    }


    public boolean exitUser(String userId) {
        return userwebSocketMap.containsKey(userId);
    }

    public CopyOnWriteArraySet<WebSocketTest> getUserSocketSet(String userId) {
        return userwebSocketMap.get(userId);
    }

    public void userCountIncrease(String userId) {
        if (count.containsKey(userId)) {
            count.put(userId, count.get(userId) + 1);
        }
    }


    public void userCountDecrement(String userId) {
        if (count.containsKey(userId)) {
            count.put(userId, count.get(userId) - 1);
        }
    }

    public void removeUserConunt(String userId) {
        count.remove(userId);
    }

    public Integer getCurrUserCount(String userId) {
        return count.get(userId);
    }

    private void initUserInfo(String userId) {
        CopyOnWriteArraySet<WebSocketTest> webSocketTestSet = new CopyOnWriteArraySet<WebSocketTest>();
        webSocketTestSet.add(this);
        userwebSocketMap.put(userId, webSocketTestSet);
        count.put(userId, 1);
    }

}


WebSocketTest 类相当于Controller,@ServerEndpoint(“/websocket/{userId}”) 访问的

核心思想,使用rest 风格动态变化用户id,每个用户建立一次连接的session要保存到容器中,保证每个用户享有自己的session记录表。

后端 推送给前端的数据 ,我们用一个页面来实现,WebSocketTest.html ,即服务端要发送的数据通过这个页面输入,然后 端收到后 发给 客户端 http://www.websocket-test.com/ 页面

动态效果图: 请点击

https://gitee.com/brinjaul/fjpdemo/blob/master/JavaWebSocket/doc/%E6%A8%A1%E7%B3%8A%E7%9A%84%E5%8A%9F%E8%83%BD%E6%BC%94%E7%A4%BA%E5%8A%A8%E5%9B%BE.gif
上传不了那么大的图片,就保留在码云了

项目地址:
https://gitee.com/brinjaul/fjpdemo.git

  • 3
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
1. 添加依赖 首先,需要在 pom.xml 文件中添加 spring-boot-starter-websocket 依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` 2. 创建 WebSocket 配置类 创建一个 WebSocketConfig 类,用于配置 WebSocket 相关的组件: ``` @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new SocketHandler(), "/socket").setAllowedOrigins("*"); } } ``` 其中,SocketHandler 是自定义的一个 WebSocket 处理器类。 3. 创建 WebSocket 处理器 创建一个 SocketHandler 类,用于处理 WebSocket连接、断开、消息发送等操作: ``` @Component public class SocketHandler extends TextWebSocketHandler { // 存储所有在线的 WebSocket 连接 private static final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>(); @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { sessions.put(session.getId(), session); } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { sessions.remove(session.getId()); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { // 处理消息 } } ``` 在 afterConnectionEstablished 方法中,将新建立的 WebSocket 连接添加到 sessions 中;在 afterConnectionClosed 方法中,将关闭的 WebSocket 连接从 sessions 中移除;在 handleTextMessage 方法中,处理收到的文本消息。 4. 实现多个房间群聊 为了实现多个房间的群聊,可以在 SocketHandler 中添加一个 Map,用于存储每个房间中的在线用户: ``` @Component public class SocketHandler extends TextWebSocketHandler { // 存储所有在线的 WebSocket 连接 private static final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>(); // 存储每个房间中的在线用户 private static final Map<String, Set<String>> rooms = new ConcurrentHashMap<>(); @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { sessions.put(session.getId(), session); } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { sessions.remove(session.getId()); for (Map.Entry<String, Set<String>> entry : rooms.entrySet()) { String room = entry.getKey(); Set<String> users = entry.getValue(); users.remove(session.getId()); sendMessage(room, users.size() + " 人在线"); } } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { String payload = message.getPayload(); String[] parts = payload.split(":"); String room = parts[0]; String content = parts[1]; if (!rooms.containsKey(room)) { rooms.put(room, new HashSet<>()); } Set<String> users = rooms.get(room); users.add(session.getId()); for (WebSocketSession s : sessions.values()) { if (users.contains(s.getId())) { s.sendMessage(new TextMessage(content)); } } } private void sendMessage(String room, String message) throws IOException { Set<String> users = rooms.get(room); if (users != null) { for (WebSocketSession s : sessions.values()) { if (users.contains(s.getId())) { s.sendMessage(new TextMessage(message)); } } } } } ``` 在 handleTextMessage 方法中,根据收到的消息中的房间名,将当前连接加入到对应的房间中,并向该房间中的所有在线用户发送消息。 在 afterConnectionClosed 方法中,将当前连接从所有房间中移除,并向这些房间中的在线用户发送消息。 5. 测试 启动应用程序后,打开两个浏览器窗口,分别连接http://localhost:8080/index.html。 在第一个窗口中,输入房间名和用户名,点击“加入”按钮,进入该房间。在第二个窗口中,输入相同的房间名和不同的用户名,点击“加入”按钮,也进入该房间。 在任意一个窗口中,输入消息并按回车键,即可将消息发送到该房间中的所有在线用户。在另一个窗口中,也可以看到这些消息。同时,窗口中会显示当前房间中的在线用户数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值