SpringBoot 整合WebSocket 实现简单聊天室

项目结构:
SpringBoot 整合WebSocket 实现简单聊天室_第1张图片

效果展示:

SpringBoot 整合WebSocket 实现简单聊天室_第2张图片

实现步骤

步骤一:添加依赖

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-thymeleafartifactId>
        dependency>

        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>1.2.54version>
        dependency>

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-devtoolsartifactId>
            <optional>trueoptional>
        dependency>

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-websocketartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>

步骤二:Java 代码
1,配置类:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExportelr() {
        return new ServerEndpointExporter();
    }
}

2,控制层


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
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.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author ylwang
 * @version 1.0
 * @date 2021/8/15 0015 15:23
 */
@ServerEndpoint("/websocket/{username}")
@Controller
public class WebsocketController {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    //在线人数
    private static int onlineNumber = 0;

    private static Map<String, WebsocketController> clients =
            new ConcurrentHashMap<>();
    //会话
    private Session session;

    //用户名称
    private String username;

    /**
     * 进入聊天室 --> 项目中读取用户信息获取用户名
     */
    @RequestMapping("/websocket")
    public String webSocket(Model model) {
        //定义随机时间戳名称
        String name = "游客:";
        String dataName = new SimpleDateFormat("yyyyMMddHHmmsss").format(new Date());
        name = name + dataName;

        //websocket链接地址 + 游客名   ----> 项目种请定义在配置文件 ---> 或直接读取服务器,ip 端口
        String path = "ws://127.0.0.1:8080/websocket/";
        //讲用户、地址信息放在域中
        model.addAttribute("path", path);
        model.addAttribute("username", name);
        //返回到html页面
        return "socket";
    }

    /**
     * 监听连接(有用户连接,立马到来执行这个方法)
     * session 发生变化
     *
     * @param username
     * @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> userLists = clients.keySet();

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

            //给自己发一条消息,告诉自己现在都有谁在线
            HashMap<String, Object> map2 = new HashMap<>();
            //messageType 1代表上线 2代表下线 3代表在线名单 4代表普通消息
            map2.put("messageType", 3);
            //把所有用户放入map2
            map2.put("onlineUsers", userLists);
            //返回在线人数
            map2.put("number", onlineNumber);
            //消息发送指定人(所用的在线用户信息)
            sendMessageAll(JSON.toJSONString(map2), username);
        } catch (Exception 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 (Exception e){
            logger.info(username + "下线的时候通知所有人发生了错误");
        }
        logger.info("有连接关闭! 当前在线人数" + onlineNumber);
    }

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

    /**
     * 监听消息(收到客户端的消息立即执行)
     *
     * @param message 消息
     * @param session 会话
     */
    @OnMessage
    public void OnMessage(String message,Session session){
        try {
            logger.info("来自客户端消息:" + message +"客户端的id是:"+ session.getId());
            //用户发送的信息
            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代表消息
            HashMap<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("发生了错误了");
        }
    }

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


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

    //在线人数
    public static synchronized int getOnlineCount(){
        return onlineNumber;
    }
}

如果在配置类中写path:
SpringBoot 整合WebSocket 实现简单聊天室_第3张图片

	url:
	  path: ws://127.0.0.1:8080/websocket/

控制层:

	@Value("${url.path}")
    private String path;

方便实现的话,可以直接写在控制层,不过建议写在配置文件中

步骤三:socket页面

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>websockettitle>
    <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>
<body>



<input type="hidden" th:value="${path}" id="path" style="display: none"/>

<input type="hidden" th:value="${username}" id="username" style="display: none"/>
<br>


<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: #24eb04;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: #bbebc7;float:left;overflow-y :auto;overflow :auto;">
    <li style="text-align: center">私聊信息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() +
                    " 
  • 系统消息:[登陆成功]
  • "
    ) }; //接收后台服务端的消息 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() + "
  • 系统消息:[" + onlineName + "]上线了" + "
  • "
    ); onLineUser.html(onLineUser.html() + " + onlineName + ""); } var onlineName = obj.onlineUsers; //所有在线用户 miax.html("
  • 在线用户--[" + onlineName.length + "]
  • "
    ); for (var i = 0; i < onlineName.length; i++) { if ((onlineName[i] != $("#username").val())) { //展示除不等于自己的所有用户 miax.html(miax.html() + "
  • ---" + onlineName[i] + "
  • "
    ); } } //miax.html(miax.html()+"
  • "+ onlineName +"
  • ");
    } //下线通知+在线列表刷新 else if (obj.messageType == 2) { if ((onlineName != $("#username").val())) { //展示除不等于自己的所有用户 miqx.html(miqx.html() + "
  • 系统消息:[" + onlineName + "]下线了" + "
  • "
    ); } var onlineName = obj.onlineUsers; //剩余所有在线用户 miax.html("
  • 在线用户--[" + onlineName.length + "]
  • "
    ); onLineUser.html(""); for (var i = 0; i < onlineName.length; i++) { if ((onlineName[i] != $("#username").val())) { //展示除不等于自己的所有用户 miax.html(miax.html() + "
  • ---" + onlineName[i] + "
  • "
    ); onLineUser.html(onLineUser.html() + " + onlineName[i] + ""); } } } //在线列表 else if (obj.messageType == 3) { var onlineName = obj.onlineUsers; //所有在线用户 miax.html("
  • 在线用户--[" + onlineName.length + "]
  • "
    ); onLineUser.html(""); for (var i = 0; i < onlineName.length; i++) { if (onlineName[i] != $("#username").val()) { //展示除不等于自己的所有用户 miax.html(miax.html() + "
  • ---" + onlineName[i] + "
  • "
    ); onLineUser.html(onLineUser.html() + " + onlineName[i] + ""); } } } //信息接收 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() + "
  • [" + obj.fromusername + "]说:-" + obj.textMessage + "
  • "
    ); } else { //发给指定人 mizx.html(mizx.html() + "
  • [" + obj.fromusername + "]说:-" + obj.textMessage + "
  • "
    ); } } //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 + '
    '
    ; } 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"; /*
  • 群聊信息
  • 靠右
  • 靠左
  • */
    miqx.html(miqx.html() + "
  • " + message + " -- [" + usernameX + "]
  • "
    ); } else { mizx.html(mizx.html() + "
  • " + "你对-[" + usernameY + "]说:-" + message + "
  • "
    ); } 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>

    整天感觉还是挺简单的,不要只看不做,最好的学习方式就是自己实现一下,动起手来吧!!!

    你可能感兴趣的:(项目需求,websocket,spring,boot)