SSM+WEBSOCKET 实现单一聊天和群组聊天以及广播(附源代码)

SSM+WEBSOCKET

SSM+WEBSOCKET 实现单一聊天和群组聊天以及广播

一、什么是WEBSOCKET?
简单来说WEBSOCKET就是一个通道,基于TCP,也基于HTTP(HTTP基于TCP),这样说可能还是不太清楚。更简单来说,就是如果服务器有信息,则主动发送给前端,客户端。和传统的方案AJAX轮询不一样的就在于主动和被动。
比较AJAX轮询和WEBSOCKET:

AJAX轮询

	 A:你有我的消息吗                           14:00:00
	 服务器:没有                                     14:00:00
	 A:你有我的消息吗						 14:00:01
	 服务器:没有									 14:00:01
	 A:你有我的消息吗                         14:00:02
	 服务器:有了,消息为HELLO           14:00:02
	 C:你有我的消息吗		                     14:00:03
	 服务器:.....我炸 ERROR					 14:00:03

WEBSOCKET

   	A:我们握手吧                                    14:00:00
   	服务器:好的,建立连接成功,有消息我会通知你的         14:00:01
   	服务器:B给你发消息了,消息为
   				"HELLO!"                             15:00:00
	 
从以上两个例子可以看出,WEBSOCKET在执行效率,已经空间利用率等方面都完爆AJAX轮询,当然曾经还有用长轮询等来实现即时通讯,当然这里我就不详细说明了。从以上WEBSOCKET的例子想必你也大概了解了WEBSOCKET的运行模式,当然这并不全面,大家可以通过源码进行底层分析。

···
二、SSM实现WEBSOCKET
有人就要问我了,你说了这么多有什么用?我要的是源码!是源码!
行吧,源码来了。
PS:这里我们是通过注解的方式来实现WEBSOCKET搭建的
WEBSOCKET 我们首先需要核心配置类
1.WebSocketConfig.class

public class WebSocketConfig implements ServerApplicationConfig{


    /**
     * 注解方式
     * 扫描src下所有类@ServerEndPoint注解的类。类似于
     */
    public Set<ServerEndpointConfig> getEndpointConfigs(
            Set<Class<? extends Endpoint>> scanned) {
        return null;
    }

    //注解的方式
    public Set<Class<?>>getAnnotatedEndpointClasses(Set<Class<?>> scanned) {
        System.err.println("START CONFIG:" + scanned.size());
        return scanned;
    }
}

2.WebSocketServer

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/*
*这里的cuttingMergeId可以理解为房间Id,
*/
@ServerEndpoint("/webSocket/chat/{cuttingMergeId}/{permId}")
public class WebSocketServer {
    // 使用map来收集session,key为cuttingMergeId,value为同一个房间的用户集合
    // concurrentMap的key不存在时报错,不是返回null
    private static final Map<String, Set<Session>> rooms = new ConcurrentHashMap();
    private static final Map<String, String> permIdList = new ConcurrentHashMap();
    @OnOpen
    public void connect(@PathParam("cuttingMergeId") String cuttingMergeId,
                        @PathParam("permId") String permId, Session session) throws Exception {
        // 将session按照房间名来存储,将各个房间的用户隔离
        if (!rooms.containsKey(cuttingMergeId)) {
            System.out.println("xxxxx");
            // 创建房间不存在时,创建房间
            Set<Session> room = new HashSet<Session>();
            // 添加用户
            room.add(session);

            rooms.put(cuttingMergeId, room);
        } else {
            // 房间已存在,直接添加用户到相应的房间
            rooms.get(cuttingMergeId).add(session);
        }
        System.err.println("permId"+permId);
        System.out.println("a client has connected!");
    }

    @OnClose
    public void disConnect(@PathParam("cuttingMergeId") String cuttingMergeId,@PathParam("permId") String permId, Session session) {
        rooms.get(cuttingMergeId).remove(session);
        System.out.println("a client has disconnected"+permId);
    }

    @OnMessage
    public void receiveMsg(@PathParam("cuttingMergeId") String cuttingMergeId,@PathParam("permId") String permId,
                           String msg, Session session) throws Exception {
        // 此处应该有html过滤
        msg = permId + ":" + msg;
        System.out.println(msg);
        // 接收到信息后进行广播
        broadcast(cuttingMergeId, msg);
    }

    // 按照房间名进行广播
    public static void broadcast(String cuttingMergeId, String msg) throws Exception {
        for (Session session : rooms.get(cuttingMergeId)) {
            session.getBasicRemote().sendText(msg);
        }
    }

这里可以理解为 WEBSOCKET的生命周期,我们通过ws的url去获取到房间Id和userId以及Message,而此时session用于存储交互数据
3.websocket.jsp(这里只是一个简单的demo,界面略显简陋)

<%--
  Created by IntelliJ IDEA.
  User: BAIMAO
  Date: 2019-04-24
  Time: 0:19
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<style type="text/css">
    .msg_board {
        width: 322px;
        height: 100px;
        border: solid 1px darkcyan;
        padding: 5px;
        overflow-y: scroll;
    // 文字长度大于div宽度时换行显示
    word-break: break-all;
    }
    /*set srcoll start*/
    ::-webkit-scrollbar
    {
        width: 10px;
        height: 10px;
        background-color: #D6F2FD;
    }
    ::-webkit-scrollbar-track
    {
        -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
        /*border-radius: 5px;*/
        background-color: #D6F2FD;
    }
    ::-webkit-scrollbar-thumb
    {
        height: 20px;
        /*border-radius: 10px;*/
        -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);
        background-color: #89D7F7;
    }
    /*set srcoll end*/
</style>
<body>
<label>房间名</label>
<input id="input_roomName" size="10" maxlength="10">
<label>用户名</label>
<input id="username" size="10" maxlength="10">
<input type="button"  value="进入聊天室" onclick="initWebSocket()" />
<input type="button" value="退出聊天室" onclick="closeWs()" /><br>
<div class="msg_board"></div>
<input id="input_msg" size="43" maxlength="40">
<input type="button" value="发送" onclick="send_msg()" />
</body>
<script type="text/javascript">
    var webSocket;

    function send_msg() {
        if (webSocket != null) {
            var input_msg = document.getElementById("input_msg").value.trim();
            if (input_msg == "") {
                return;
            }
            webSocket.send(input_msg);
            // 清除input框里的信息
            document.getElementById("input_msg").value = "";
        } else {
            alert("您已掉线,请重新进入聊天室...");
        }
    };

    function closeWs() {
        webSocket.close();
    };

    function initWebSocket() {
        var roomName = document.getElementById("input_roomName").value;
        // 房间名不能为空
        if (roomName == null || roomName == "") {
            alert("请输入房间名");
            return;
        }
        var username = document.getElementById("username").value.trim();
        if (username == "" || username==null) {
            alert("用户名不能为空")
            return;
        }
        if ("WebSocket" in window) {
//            alert("您的浏览器支持 WebSocket!");
            if (webSocket == null) {
                var url = "ws://localhost:8088/webSocket/chat/" + roomName+"/"+username;
                // 打开一个 web socket
                webSocket = new WebSocket(url);
            } else {
                alert("您已进入聊天室...");
            }

            webSocket.onopen = function () {
                alert("已进入聊天室,畅聊吧...");
            };

            webSocket.onmessage = function (evt) {
                alert(evt.data)
                var msg_board = document.getElementsByClassName("msg_board")[0];
                var received_msg = evt.data;
                var old_msg = msg_board.innerHTML;
                msg_board.innerHTML = old_msg + received_msg + "<br>";
                // 让滚动块往下移动
                msg_board.scrollTop = msg_board.scrollTop + 40;
            };

            webSocket.onclose = function () {
                // 关闭 websocket,清空信息板
                alert("连接已关闭...");
                webSocket = null;
                document.getElementsByClassName("msg_board")[0].innerHTML = "";
            };
        }
        else {
            // 浏览器不支持 WebSocket
            alert("您的浏览器不支持 WebSocket!");
        }
    }
</script>
</html>

实现效果展示
在这里插入图片描述
有问题请留言区评论进行讨论哦

发布了4 篇原创文章 · 获赞 2 · 访问量 3463

猜你喜欢

转载自blog.csdn.net/qq_40479674/article/details/90138350