基于SpringBoot的websocket聊天室DEMO

283 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情 >>

前言

websocket是一种网络通信协议。我们更多使用的是HTTP协议,http是一个无状态的、无连接、单向应用层协议。只能客户端向服务端发请求,而当服务器想向客户端发请求时,大多采用轮询的访问方式,轮询耗资源、浪费时间。而websocket是一个双向通讯的协议,服务端可以向客户端发请求,客户端也可以给服务器发请求。

image.png

websocket协议

协议分两部分

  • 握手:基于http协议
  • 数据交互

客户端的实现

1.创建一个websocket对象

var ws = new WebSocket("ws://localhost:8088/chat");
  • 注意:当是https协议时,ws需改为wss

2.websocket事件

  • open:建立连接时触发 ex:
ws.onopen = function () {
    $('#chatMeu').html('<p>用户:' + username + "<span style='float: right;color: greenyellow; height: 20px'>在线</span></p>")
};
- 当连接建立完成后需要在客户端页面上渲染XXX用户上线了
  • message:客户端接受服务端消息的时候触发

ex:

ws.onmessage = function (evt) {
    //获取数据
    var dataStr = evt.data;
    //将dataStr转为json对象
    //jsonData 格式举例(需要判断是否是系统消息):{“systemMsgFlag”: false, "fromName": "YYJ", "message": “你在哪里呀?”}
    var jsonData = JSON.parse(dataStr);
    //判断是否是系统消息
    if (jsonData.systemMsgFlag) {
        //是系统消息,处理
         //1.好友列表处理
        var friendsList = "";
        var systemMsg = "";
        var allNames = jsonData.message;
        for (var name of allNames) {
            if (username !== name) {
                friendsList += "<li><a style='text-decoration: underline' onclick='chatWith(""+ name +"")'>"+name+"</a></li>";
                systemMsg += "<li>"+name+" 上线啦!</li>";
            }
        }
        //渲染页面
        $("#friendsList").html(friendsList);
        $("#systemMsg").html(systemMsg);
    }else {
        //不是系统消息,是发送给指定用户的消息,示例值:{“systemMsgFlag”: false, "fromName": "YYJ", "message": “你在哪里呀?”}
        var data = jsonData.message;
        var cnt = "<div class="atalk"><span id="asay">"+data+"</span></div>"
        if (toName === jsonData.fromName) {
            $("#chatCnt").append(cnt);
        }
        //sessionStorage
        var chatData = sessionStorage.getItem(jsonData.fromName);
        if (chatData != null) {
            cnt = chatData + cnt;
        }
        sessionStorage.setItem(jsonData.fromName ,cnt);


    }
};
- 当收到服务端发来的消息时,需要去渲染到客户端,注意此处需要做不同的用户之间的隔离,否则他人发消息全局都可以看到了。
  • close:关闭连接时触发
//关闭连接触发
ws.onclose = function () {
    $('#chatMeu').html('<p>用户' + username + "<span style='float: right;color: #d50a0a; height: 20px'>离线</span></p>")
};
- 关闭连接后需要将用户下线
  • error:发生错误时触发

3.方法

  • send()方法

ex:

//发送按钮点击
$("#submit").click(function () {
    //获取发送输入框中的内容
    var data = $("#tex_content").val();
    //点击发送后,清空输入内容框架
    $("#tex_content").val("");
    var sendJson = {"toName": toName, "message": data};
    //聊天框显示发送内容
    var cnt = "<div  class="btalk"><span id="bsay">" + data+ "</span></div>";
    $("#chatCnt").append(cnt);
    var chatData = sessionStorage.getItem(toName);
    if (chatData != null) {
        cnt = chatData + cnt;
    }
    sessionStorage.setItem(toName,cnt);

    //发送数据给服务端
    ws.send(JSON.stringify(sendJson));

});

服务端实现

  • tomcat7.05版本就支持了websocket,Java中websocket应用由一系列websocket Endpoint组成。

我们有两种方式定义websocket

  • 编程方式:继承类javax.websocket.Endpoint且实现其方法。
  • 注解方式:定义一个类,添加ServerEndpoint注解

Endpoint在websocket握手时创建,websocket方法(注解方式)

  • @onClose:当会话关闭后调用
  • @onOpen:当开启一个新的会话时调用,该方法就是当客户端与服务端连接成功后的调用方法
  • @onError:当连接过程中遇到异常调用

image.png

代码展示(文章末尾会放上源码):

  • 建立session
    @OnOpen
//    连接建立时被调用
    public void onOpen(Session session, EndpointConfig config) {

//        将局部的session赋值给成员session
        this.session=session;
//          获取httpsession对象
        HttpSession httpsession = (HttpSession)config.getUserProperties().get(HttpSession.class.getName());
        this.httpSession=httpsession;
//        从httpsession对象中获取用户名
        String username = (String) httpSession.getAttribute("username");


//        将当前对象存到这个容器中 所有在线对象
        onlineUsers.put(username,this);
//        将当前在线用户的用户名推送给所有客户端
//        1.获取消息
        String message = MessageUtils.getMessage(true, null, getNames());
//        2.调用方法进行系统消息的推送
        broadcastAllUsers(message);


    }
  • 收发消息:
 @OnMessage
//    接收到客户端发送的数据时
    public void onMessage(String message,Session session){
        ObjectMapper mapper = new ObjectMapper();
        try {
            //        将message转换成message对象

            Message mess = mapper.readValue(message, Message.class);
//              获取要将数据发送给的用户
            String toName = mess.getToName();
//            获取消息数据
            String data = mess.getMessage();
//            获取当前登录的用户
            String userName=(String)httpSession.getAttribute("username");
//            获取推送给指定用户的消息格式数据
            String resultMessage = MessageUtils.getMessage(false, userName, data);
//            发送数据
            onlineUsers.get(toName).session.getBasicRemote().sendText(resultMessage);
        }
        catch (Exception e){
            e.printStackTrace();
        }


    }
  • 关闭连接
    @OnClose
//    连接关闭
    public void onClose(Session session){

        String username = (String) httpSession.getAttribute("username");
//从容器中删除指定的用户
        onlineUsers.remove(username);
        String message = MessageUtils.getMessage(true, null, getNames());
        broadcastAllUsers(message);
    }
  • 以上代码可以得知,用户会话都是存在session中的,通过session识别是哪个用户。

源码:gitee.com/chang-haipe…