springboot -- 整合websocket 实现在线聊天 - Go语言中文社区

springboot -- 整合websocket 实现在线聊天


项目demo:
链接:https://pan.baidu.com/s/1xZtU-Rqc58m0-v397OW3hQ
提取码:01pt
复制这段内容后打开百度网盘手机App,操作更方便哦

websocket 实现在线聊天效果

用户1 发送消息
在这里插入图片描述
用户4 接收消息
在这里插入图片描述
关闭 2 3 4 页面,用户下线通知
在这里插入图片描述

pom.xml

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>

添加 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 {

    /**
     * 服务器节点
     *
     * 如果使用独立的servlet容器,而不是直接使用springboot的内置容器,就不要注入ServerEndpointExporter,因为它将由容器自己提供和管理
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}

后端接收处理消息


package com.example.springboot_websocket.controller;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

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

/**
 * websocket类
 * @ServerEndpoint: socket链接地址
 */
@ServerEndpoint("/websocket/{username}")
@Controller
public class WebsocketController {
 
    private Logger logger = LoggerFactory.getLogger(this.getClass());




    
    /**
     * 在线人数
     */
    public static int onlineNumber = 0;
    /**
     * 以用户的姓名为key,WebSocket为对象保存起来
     */
    private static Map<String, WebsocketController> clients = new ConcurrentHashMap<String, WebsocketController>();
    /**
     * 会话
     */
    private Session session;
    /**
     * 用户名称
     */
    private String username;
    
    
    /**
     *   进入聊天室 --> 项目中读取用户信息获取用户名 
     */
    @RequestMapping("/websocket")
    public String webSocket(Model model) {
        
        //定义随机时间戳名称
        String name = "游客:";
        //String  datename = new SimpleDateFormat("yyyyMMddHHmmsss").format(new Date());
        String  datename = new SimpleDateFormat("msss").format(new Date());
        name = name + datename;
        
        //websock链接地址+游客名-->  项目中请定义在配置文件 -->或直接读取服务器,ip 端口
        // 读取服务器,ip 端口可看:https://blog.csdn.net/qq_41463655/article/details/92002474
        String path="ws://192.168.100.7:8080/websocket/";
        model.addAttribute("path",path);
        model.addAttribute("username",name);
        return "socket";
    }

    
    /**
     * 监听连接(有用户连接,立马到来执行这个方法)
     * session 发生变化
     *
     * @param session
     */
    @OnOpen
    public void onOpen(@PathParam("username") String username, Session session) {
        onlineNumber++;
        //把新用户名赋给变量
        this.username = username;
        //把新用户的 session 信息赋给变量
        this.session = session;
        //输出 websocket 信息
        logger.info("现在来连接的客户id:" + session.getId() + "用户名:" + username);
        logger.info("有新连接加入! 当前在线人数" + onlineNumber);
        try {
            //把自己的信息加入到map当中去,this=当前类(把当前类作为对象保存起来)
            clients.put(username, this);
            //获得所有的用户
            Set<String> lists = clients.keySet();

            // 先给所有人发送通知,说我上线了
            //messageType 1代表上线 2代表下线 3代表在线名单 4代表普通消息
            Map<String, Object> map1 = new HashMap();
            //  把所有用户列表
            map1.put("onlineUsers", lists);
            //  返回上线状态
            map1.put("messageType", 1);
            //  返回用户名
            map1.put("username", username);
            //  返回在线人数
            map1.put("number", onlineNumber);
            //  发送全体信息(用户上线信息)
            sendMessageAll(JSON.toJSONString(map1), username);


            // 给自己发一条消息:告诉自己现在都有谁在线
            Map<String, Object> map2 = new HashMap();
            //messageType 1代表上线 2代表下线 3代表在线名单 4代表普通消息
            map2.put("messageType", 3);
            //把所有用户放入map2
            map2.put("onlineUsers", lists);
            //返回在线人数
            map2.put("number", onlineNumber);
            // 消息发送指定人(所有的在线用户信息)
            sendMessageTo(JSON.toJSONString(map2), username);
        } catch (IOException e) {
            logger.info(username + "上线的时候通知所有人发生了错误");
        }
    }


    /**
     * 监听连接断开(有用户退出,会立马到来执行这个方法)
     */
    @OnClose
    public void onClose() {
        onlineNumber--;        
        //所有在线用户中去除下线用户
        clients.remove(username); 
        try {
            //messageType 1代表上线 2代表下线 3代表在线名单  4代表普通消息
            Map<String, Object> map1 = new HashMap();
            map1.put("messageType", 2);
            //所有在线用户
            map1.put("onlineUsers", clients.keySet());
            //下线用户的用户名
            map1.put("username", username);
            //返回在线人数
            map1.put("number", onlineNumber);
            //发送信息,所有人,通知谁下线了
            sendMessageAll(JSON.toJSONString(map1), username);
        } catch (IOException e) {
            logger.info(username + "下线的时候通知所有人发生了错误");
        }
        logger.info("有连接关闭! 当前在线人数" + onlineNumber);
    }

    @OnError
    public void onError(Session session, Throwable error) {
        logger.info("服务端发生了错误" + error.getMessage());
        //error.printStackTrace();
    }

    /**
     * 监听消息(收到客户端的消息立即执行)
     *
     * @param message 消息
     * @param session 会话
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        try {
            logger.info("来自客户端消息:" + message + "客户端的id是:" + session.getId());
            //用户发送的信息
            com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(message);
            //发送的内容
            String textMessage = jsonObject.getString("message");
            //发送人
            String fromusername = jsonObject.getString("username");
            //接收人  to=all 发送消息给所有人 || to= !all   to == 用户名
            String tousername = jsonObject.getString("to");

            //发送消息  -- messageType 1代表上线 2代表下线 3代表在线名单  4代表消息
            Map<String, Object> map1 = new HashMap();
            map1.put("messageType", 4);
            map1.put("textMessage", textMessage);
            map1.put("fromusername", fromusername);
            if (tousername.equals("All")) {
                //消息发送所有人(同步)
                map1.put("tousername", "所有人");
                sendMessageAll(JSON.toJSONString(map1), fromusername);
            } else {
                //消息发送指定人(同步)
                map1.put("tousername", tousername);
                sendMessageTo(JSON.toJSONString(map1), tousername);
            }
        } catch (Exception e) {
            logger.info("发生了错误了");
        }
    }

    /**
     *  消息发送指定人
     */
    public void sendMessageTo(String message, String ToUserName) throws IOException {
        //遍历所有用户
        for (WebsocketController item : clients.values()) {
            if (item.username.equals(ToUserName)) {
                //消息发送指定人(同步)
                item.session.getBasicRemote().sendText(message);
                break;
            }
        }
    }

    /**
     *  消息发送所有人
     */
    public void sendMessageAll(String message, String FromUserName) throws IOException {
        for (WebsocketController item : clients.values()) {
            //消息发送所有人(同步)getAsyncRemote
            item.session.getBasicRemote().sendText(message); 
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineNumber;
    }
}





/*
 *  注解说明
 *  @MessageMapping(value = "/chat")   // 匹配客户端 send 消息时的URL
 *  @SendTo("/topic/getResponse")      //用于给客户端订阅广播消息
 *  @SendToUser(value = "/personal")   //用于给客户端订阅点对点消息;
 *  @Payload:使用客户端 STOMP 帧的 body 赋值
 *  @Header(“xxx”):使用客户端 STOMP 帧的 headers 中的 xxx 赋值
 *
 **/

/**
 * 广播推送
 **/
//    @MessageMapping(value = "/chat") // 匹配客户端 send 消息时的URL
//    @SendTo("/topic/getResponse")   //分别用于给客户端订阅广播消息
//    public String talk(@Payload String text, @Header("simpSessionId") String sessionId) throws Exception {
//        return "【" + sessionId + "】说:【" + text + "】";
//    }
  
/**
 * 点对点推送
 */
/*
    @MessageMapping(value = "/speak")  // 匹配客户端 send 消息时的URL
    @SendToUser(value = "/personal")   //分别用于给客户端订阅点对点消息;
    public String speak(@Payload String text, @Header("simpSessionId") String sessionId) throws Exception {
        return text;
    }
    */

/**
 * 异常信息推送
 */
/*
    @MessageExceptionHandler
    @SendToUser(value = "/errors")
    public String handleException(Throwable exception) {
        return exception.getMessage();
    }*/


Html 页面发送接收消息

<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <link rel="stylesheet" href="../frame/layui/css/layui.css">
    <link rel="stylesheet" href="../frame/static/css/style.css">
    <link rel="icon" href="../frame/static/image/code.png">
    <title>websocket</title>
    <script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js"></script>
    <script src="http://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script>
    <script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script>
</head>
<css>

</css>
<body>


<!--   socket url -->
<input  type="hidden" th:value="${path}" id="path"  style="display: none" />
<!--  用户名 -->
<input  type="hidden" th:value="${username}" id="username"  style="display: none" />


<!-- ===============================================================================================================  -->
<br>

<!--<input  type="hidden" value="所有人" id="onLineUser" text="所有人" style="display: none" />-->


<!-- ===============================================================================================================  -->
<!--    overflow-y :auto;overflow :auto;  宽高自适应滚动条-->
<span id = "miqx" style="width:80%;height:300px; background-color: papayawhip;float:left;overflow-y :auto;overflow :auto;">
    <li style="text-align: center">群聊信息</li>
</span>
<span id = "miax" style="width:20%;background-color: #F2F2F2;float:left;overflow-y :auto;overflow :auto;">
    <li style="text-align: center">在线列表</li>
</span>

<textarea id="text" placeholder="请输入内容-发送消息[Ctrl+回车键]" rows="3%" cols="60%"></textarea>


<input onclick="send()" type="button" value="发送">
<td>消息发送至:</td>
<select id="onLineUser" size="1" style="width: 10%;height:30px">
    <option  value="所有人">所有人</option>
</select>


<div  id = "mizx"  style="width:80%;height:300px;background-color: #FFEEE8;float:left;overflow-y :auto;overflow :auto;">
    <li style="text-align: center">私聊信息</li>
    <!--    <li style="text-align: right">靠右</li>
        <li style="text-align: left" >靠左</li>-->
</div>
<br>
<br>

<!-- ===============================================================================  -->

</body>
<script type="text/javascript">
    function uaername(name){
        alert(name)
    }
    var miqx = $("#miqx");  //群聊
    var miax = $("#miax");   //在线列表
    var mizx = $("#mizx");  //私聊
    var onLineUser = $("#onLineUser");    //发送人select选择框

    var webSocket;
    var commWebSocket;http:
        if ("WebSocket" in window){
            //192.168.100.7:8080/
            webSocket = new WebSocket(document.getElementById('path').value +document.getElementById('username').value);
            //连通之后的回调事件
            webSocket.onopen = function()
            {
                miqx.html(miqx.html()+" <li style='text-align: center'>系统消息:[登陆成功]</li>")
            };

            //接收后台服务端的消息
            webSocket.onmessage = function (evt)
            {
                var received_msg = evt.data;           //接收到的数据
                var obj = JSON.parse(received_msg);    //json数据
                var messageType = obj.messageType;      //数据类型(1上线/2下线/3在线名单/4发信息)
                var onlineName = obj.username;         //用户名
                var number = obj.number;               //在线人数

                //上线通知+在线列表刷新
                if(obj.messageType==1){
                    if((onlineName != $("#username").val())){ //展示除不等于自己的所有用户
                        miqx.html(miqx.html()+" <li style='text-align: center'>系统消息:["+ onlineName+"]上线了"+"</li>");
                        onLineUser.html(onLineUser.html()+"<option  value='"+ onlineName +"'>"+ onlineName +"</option>");
                    }
                    var onlineName = obj.onlineUsers;  //所有在线用户
                    miax.html("<li style='text-align: center'>在线用户--["+ onlineName.length  +"]</li>");
                    for(var i=0;i<onlineName.length;i++){
                        if((onlineName[i] != $("#username").val())){ //展示除不等于自己的所有用户
                            miax.html(miax.html()+"<li style='text-align: left'>---"+ onlineName[i] +"</li>" );
                        }
                    }
                    //miax.html(miax.html()+" <li style='text-align: center'>"+ onlineName +"</li>");
                }
                //下线通知+在线列表刷新
                else if(obj.messageType==2){
                    if((onlineName != $("#username").val())){ //展示除不等于自己的所有用户
                        miqx.html(miqx.html()+" <li style='text-align: center'>系统消息:["+ onlineName+"]下线了"+"</li>");
                    }
                    var onlineName = obj.onlineUsers;  //剩余所有在线用户
                    miax.html("<li style='text-align: center'>在线用户--["+ onlineName.length  +"]</li>");
                    onLineUser.html("<option  value='所有人'>所有人</option>");
                    for(var i=0;i<onlineName.length;i++){
                        if((onlineName[i] != $("#username").val())){ //展示除不等于自己的所有用户
                            miax.html(miax.html()+"<li style='text-align: left'>---"+ onlineName[i] +"</li>" );
                            onLineUser.html(onLineUser.html()+"<option  value='"+ onlineName[i] +"'>"+ onlineName[i] +"</option>");
                        }
                    }
                }
                //在线列表
                else if(obj.messageType==3){
                    var onlineName = obj.onlineUsers; //所有在线用户
                    miax.html("<li style='text-align: center'>在线用户--["+ onlineName.length +"]</li>");
                    onLineUser.html("<option  value='所有人'>所有人</option>");
                    for(var i=0;i<onlineName.length;i++){
                        if(onlineName[i] != $("#username").val()){ //展示除不等于自己的所有用户
                            miax.html(miax.html()+ " <li style='text-align: left'>---"+ onlineName[i] +"</li>" );
                            onLineUser.html(onLineUser.html()+"<option  value='"+ onlineName[i] +"'>"+ onlineName[i] +"</option>");
                        }
                    }
                }
                //信息接收
                else{
                    var time2 = new Date();
                    var date = time2.getHours()+":"+time2.getMinutes()+":"+ time2.getSeconds();  //时间
                    if(obj.fromusername != $("#username").val() ){    //自己不接自己的消息
                        if(obj.tousername=="所有人"){
                            //发给所有人
                            miqx.html(miqx.html()+" <li style='text-align: left'>["+ obj.fromusername+"]说:-"+obj.textMessage +"</li>");
                        }else {
                            //发给指定人
                            mizx.html(mizx.html()+" <li style='text-align: left'>["+ obj.fromusername+"]说:-"+obj.textMessage+"</li>");
                        }
                    }
                    //setMessageInnerHTML(obj.fromusername+"对"+obj.tousername+"说:"+obj.textMessage);
                }
            };

            //连接关闭的回调事件
            webSocket.onclose = function()
            {
                console.log("连接已关闭...");
                setMessageInnerHTML("连接已经关闭....");
            };
        }
        else{
            // 浏览器不支持 WebSocket
            alert("您的浏览器不支持 WebSocket!");
        }
    //将消息显示在网页上
    function setMessageInnerHTML(innerHTML) {
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }

    function closeWebSocket() {
        //直接关闭websocket的连接
        webSocket.close();
    }

    //信息发送+ 页面显示发送信息
    $(document).keyup(function(event){
        //浏览器适应
        if(event.ctrlKey && event.which == 13 || event.which == 10) {
            send();
        } else if (event.shiftKey && event.which==13 || event.which == 10) {
            send();
        }
    });

    //信息发送+ 页面显示发送信息
    function send() {
        var usernameX = $("#username").val()         //发送数据人
        var usernameY = $("#onLineUser").val();      //接收数据人
        var message = $("#text").val();               //发送的数据
        if(usernameY=="所有人"){
            usernameY = "All";
            /*  <li style="text-align: center">群聊信息</li>
             <li style="text-align: right">靠右</li>
             <li style="text-align: left" >靠左</li>*/
            miqx.html(miqx.html()+" <li style='text-align: right'>"+ message+" -- ["+usernameX +"]</li>");
        }
        else{
            mizx.html(mizx.html()+" <li style='text-align: right'>"+ "你对-["+usernameY+"]说:-"+message+"</li>");
        }
        var message = {
            "message":message,
            "username":usernameX,
            "to":usernameY
        };
        //发送数据
        webSocket.send(JSON.stringify(message));
        $("#text").val("");
    }

    layui.use(['form', 'layedit', 'laydate'], function () {
        var form = layui.form
            , layer = layui.layer
            , layedit = layui.layedit
            , laydate = layui.laydate;

        //监听指定开关
        form.on('switch(switchTest)', function (data) {
            layer.msg('你以' + (this.checked ? '上线' : '下线'), {
                offset: '6px'
            });
            //layer.tips('温馨提示:请注意开关状态的文字可以随意定义,而不仅仅是ON|OFF', data.othis)
        });
    });
</script>
</html>

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_41463655/article/details/92410518
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢