SpringBoot + Websocket + Vue 构建聊天室

前言

  • 关于 WebSocket:
    1、WebSocket 是 html5 提供的通讯协议
    2、特点是建立在单个 tcp 连接上的全双工协议
    3、浏览器向服务器发起 WebSocket 连接请求,当成功获取到连接后,就可以实现浏览器和服务器之间的数据传输
  • 版本
    后端: springboot 2.0
    前端: vue
    测试: Chrome 8:81.0
  • 源码
    后端: 后端源码 github
    前端: 前端源码 github
  • 在线体验地址 (组件管理/聊天室 test 123456)

正文

  • 客户端
    首先,既然要在前端发起一个 websocket 连接请求,那么肯定得有一个实际存在的地址,也就是我们在服务器端配置的链接地址。然后我们通过 var ws = new WebSocket(url) 来获取 websocket 客户端对象,成功获取到连接之后,我们就可以获取 websocket 回调函数,然后进行数据传输。

    • 回调函数
    事件描述
    ws.onopen连接成功之后调用
    ws.onmessage消息发送之后调用
    ws.close连接关闭之后调用
    ws.error发生异常时调用
    • 代码
//初始化 websocket
initWebSocket(id) {
    this.id = id
    var _this = this
    if (window.WebSocket) {   //判断当前浏览器是否支持 websocket
        var serverHot = window.location.hostname
        //携带参数  id   userId  userName
        var url = 'ws://' + serverHot + ':8082' + '/api/chat/private/' + id + '/' + this.userId + '/' + this.userName
        var ws = new WebSocket(url)
        _this.ws = ws
        ws.onopen = function(e) {   //回调函数
        	console.log('连接成功' + url)
        }
        ws.onmessage = function(e) {
            _this.msgList.push(JSON.parse(e.data))   //msgList 为消息列表
            _this.onlineNum = JSON.parse(e.data).onlineNum   //当前在线人数
            console.log('发送成功' + url)
        }
        ws.onclose = function(e) {
        	console.log('关闭连接' + url)
        }
    }
},
//发送消息
sendMsg() {
     var _this = this
     var params = {
         content: _this.content
     }
     _this.ws.send(JSON.stringify(params))
     this.content = ''
 },
  • 服务端
  • 服务端自动注册
    首先早 pom 文件中引入依赖 spring-boot-starter-websocket 。
    ServerEndpointExporter 是由Spring官方提供的标准实现,用于扫描ServerEndpointConfig配置类和@ServerEndpoint注解实例。使用规则也很简单:1.如果使用默认的嵌入式容器 比如Tomcat 则必须手工在上下文提供ServerEndpointExporter。2. 如果使用外部容器部署war包,则不要提供提供ServerEndpointExporter,因为此时SpringBoot默认将扫描服务端的行为交给外部容器处理。
//开启 websocket 服务端自动注册
@Configuration
public class WebSocketConfig {

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

}
  • 创建 websocket 服务端
    创建 WebSocketController
    1、通过注解 @ServerEndpoint 来声明实例化 websocket ,也就是前端 websocket 对象连接时的地址
    2、通过注解 @OnOpen、@OnMessage、@OnClose、@OnError 来声明 websocket 回调函数
  • 代码
@Component
@ServerEndpoint("/chat/private/{id}/{userId}/{userName}")
public class WebSocketController {

    //群组中的每一个成员的 websocket 连接
    private static ConcurrentHashMap<String, List<Session>> groupMap = new ConcurrentHashMap<>();

    //群组中的消息
    private static ConcurrentHashMap<String, List<MessageDto>> groupMsgMap = new ConcurrentHashMap<>();

    @OnOpen
    public void onOpen(Session session, @PathParam("id") String id, @PathParam("userId") String userId,
                       @PathParam("userName") String userName) {
        List<Session> list = groupMap.get(id);
        if (list != null && list.size() > 0 && list.get(0).getPathParameters().get("userId").equals(userId)) {
            System.out.println("用户 " + userName + " 再次进入聊天室 " + id);
        } else {
            list = groupMap.computeIfAbsent(id, k -> new ArrayList<>());
            list.add(session);
            onMessage(id, userId, userName, "{'content':'用户 " + userName + " 上线了';" + "'onlineNum':" + list.size() + "}");
            System.out.println("用户 " + userName + " 进入聊天室 " + id);
        }
    }

    @OnMessage
    public void onMessage(@PathParam("id") String id, @PathParam("userId") String userId,
                          @PathParam("userName") String userName, String content) {
        List<Session> list = groupMap.get(id);
        list.forEach(item -> {
            MessageDto messageDto = JSON.parseObject(content, MessageDto.class);
            messageDto.setFromUser(userName);
            messageDto.setOnlineNum(list.size());
            String json = JSON.toJSONString(messageDto);
            try {
                item.getBasicRemote().sendText(json);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        System.out.println("用户 " + userName + " 在聊天室 " + id + " 中发送了消息: " + content);
    }

    @OnClose
    public void onClose(Session session, @PathParam("id") String id, @PathParam("userId") String userId,
                        @PathParam("userName") String userName) {
        List<Session> list = groupMap.get(id);
        list.remove(session);
        onMessage(id, userId, userName, "{'content':'用户 " + userName + " 下线了'}");
        System.out.println("用户 " + userName + " 退出聊天室 " + id);
    }

    @OnError
    public void onError(Throwable throwable) {
        System.out.println("出错: " + throwable.getMessage());
    }

}
  • 测试
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    • 服务器控制台
      在这里插入图片描述
      后浪 加油!
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值