Karley Blog:Karley的Java个人博客全纪录之SpringBoot整合WebSocket实现实时通讯

本来8,这个模块是想用netty结合websocket,用主从reactor模式搞一个多线程实时通讯,但是一直没搞明白主服务器端口跟netty服务器端口不一样怎么破还有会带来什么影响的问题,所以目前暂时先用websocket搞一个实时通讯,等我搞明白的种种问题就再把netty接进去。

首先pom.xml不能少:

        <!--websocket-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
            <version>2.2.2.RELEASE</version>
        </dependency>

然后搞一个WebSocketServer.java:

package com.lin.karley.Netty.WebSocket;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Component;
import org.thymeleaf.util.StringUtils;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description Description
 * @Author Karley LIn
 * @Date Created in 2020/6/1
 */
@Component
@ServerEndpoint("/chat/{sid}")
@SuppressWarnings("all")
public class WebSocketServer {
    //静态变量,记录当前在线连接数
    private static int onlineCount = 0;
    //concurrent包的线程安全set,用来存放每个客户端对应的mywebsocket对象
    private static ConcurrentHashMap<String,WebSocketServer> webSocketMap =new ConcurrentHashMap<>();
    //与某客户端连接会话,需要通过它来给客户端发送数据
    private Session session;
    //接受sid
    private String sid="";

    /**
     * 链接成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session,@PathParam("sid") String sid){
        this.session=session;
        this.sid=sid;
        if (webSocketMap.containsKey(sid)){
            webSocketMap.remove(sid);
            webSocketMap.put(sid,this);
        }else {
            webSocketMap.put(sid,this);
            addOnlineCount();
        }

        try {
            JSONObject jsonObject = new JSONObject();
//            jsonObject.put("username",sid);
//            jsonObject.put("msg","连接成功");

            Set<Map.Entry<String, WebSocketServer>> entrySet = webSocketMap.entrySet();
            Integer i =0;
            for (Map.Entry<String,WebSocketServer> entry:entrySet){
//                for (int i=0;i<webSocketMap.size();i++){
                jsonObject.put(String.valueOf(i),entry.getKey());
                System.out.println(jsonObject);
                i++;
//                }
//                System.out.println(entry.getKey());
//                webSocketMap.get(entry.getKey()).sendMessage(jsonObject.toJSONString());

            }
            for (Map.Entry<String,WebSocketServer> entry:entrySet){
                webSocketMap.get(entry.getKey()).sendMessage(jsonObject.toJSONString());
            }
        }catch (IOException e){
            System.out.println("WebSocket IO异常");
        }
//        System.out.println(this.sid);
//        System.out.println(this);
        System.out.println("有新链接加入!当前在线人数为"+getOnlineCount());
    }

    @OnClose
    public void onClose(){
        if (webSocketMap.containsKey(sid)){
            webSocketMap.remove(sid);
            subOnlineCount();
        }
        System.out.println("有人离开!当前在线人数为"+getOnlineCount());
    }

    @OnMessage
    public void onMessage(String message,Session session){
        System.out.println("来自客户端的信息"+message);
        try {
            if (!StringUtils.isEmpty(message)) {
                JSONObject jsonObject = JSON.parseObject(message);
                jsonObject.put("fromUserId", this.sid);
                String toId = jsonObject.getString("toId");
//                String msg = jsonObject.getString("msg");
                if (!StringUtils.isEmpty(toId) && webSocketMap.containsKey(toId)) {
                    webSocketMap.get(toId).sendMessage(jsonObject.toJSONString());
                    webSocketMap.get(sid).sendMessage(jsonObject.toJSONString());
                } else {
                    System.out.println("请求的userId" + toId + "不在该服务器上");
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @OnError
    public void onError(Session session,Throwable error){
        System.out.println("发生错误");
        error.printStackTrace();
    }

    /**
     * 实现服务器主动推送
     * @param message
     * @throws IOException
     */
    public void sendMessage(String message) throws IOException{
        this.session.getBasicRemote().sendText(message);
    }

    /**
     * 群发
     */
    private void sendAll(String message){
        for (String key : webSocketMap.keySet()){
            try {
                webSocketMap.get(key).sendMessage("连接成功");
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }


    /**
     * 群发自定义消息
     */
    public static void sendInfo(String message, @PathParam("sid") String sid) throws IOException{
        System.out.println("推送消息到窗口"+sid+",推送内容:"+message);
        if (!sid.equals(null) && webSocketMap.containsKey(sid)){
            webSocketMap.get(sid).sendMessage(message);
        }else{
            System.out.println("用户"+sid+"不在线");
        }
    }

    private static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }

    private static synchronized int getOnlineCount() {
        return onlineCount;
    }

    private static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }
}

因为我这个聊天的模式比较特殊,是我作为管理员能有多个聊天选项,而普通用户只能有一个就是我的一个聊天选项,所以我的controller分了两步走

ModuleController.java:

    @GetMapping("contactMe")
    public String contactMe(Model model){
        Subject subject = SecurityUtils.getSubject();
        User currentUser = (User) subject.getPrincipal();
        String username = currentUser.getUsername();
        List<User> users;
        if (username.equals("karley")){
            users = userService.selectAll();
        }else{
            users = Collections.singletonList(userService.selectByUsername("karley"));
        }
//        System.out.println(users);
        model.addAttribute("users",users);
        return "module/contactMe";
    }

最后就是聊天页面了,css就省略了,contactMe.html:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
    <meta charset="UTF-8">
    <title>联系我 -Karley's Blog</title>
    <link rel="stylesheet" href="/css/contactMe.css">
</head>
<body>
<div th:replace="~{common/bar::bar(active='contact')}"></div>
<div class="center-nav">
    <!--<div class="layui-container">-->
        <div class="left-nav layui-col-md3 layui-col-xs3">
            <div id="username">
                <span shiro:principal property="username"></span>
                <div class="liner">
                    <p>------------联系人------------</p>
                </div>
            </div>
            <div class="customers">
                <div th:each="user:${users}" class="customer">
                    <p th:text="${user.username}"></p>
                    <p class="onlineStatus" th:id="${user.username}">已离线</p>
                </div>
            </div>
        </div>

        <div class="right-nav layui-col-md9 layui-col-xs9">
            <div class="chatBox" id="chatBox">
                <!--<div class="leftChat" id="sender"></div>-->
                <!--<div class="rightChat" id="receiver"></div>-->
            </div>
            <div class="msgBox">
                <form action="" onsubmit="return false">
                    <textarea name="message" id="message" required lay-verify="required" placeholder="请输入" class="layui-textarea"></textarea>
                    <div class="buttonGroup">
                        <button type="button" class="layui-btn" id="send">发送信息</button>
                        <button type="reset"class="layui-btn layui-btn-warm">清空</button>
                    </div>
                </form>
            </div>
        </div>
    <!--</div>-->
</div>
</body>
<script src="/js/jquery/jquery-3.4.1.min.js"></script>
<script>
    jQuery(document).ready(function ($) {
        var socket;
        if (window.WebSocket) {
            var username=$("#username span").text();
            socket = new WebSocket("ws://localhost:8080/chat/"+username);
            //相当于channelReado,event收到服务器端回送的消息
            socket.onmessage=function(ev){
                console.log(ev.data);
                var Json=JSON.parse(ev.data);
                var fromUserId = Json["fromUserId"];
                if (fromUserId !=null){
                    if (fromUserId==username) {
                        $("#chatBox").append("<div class='rightChat'>"+Json["msg"]+"</div><br><br><br>");
                    }else{
                        $("#chatBox").append("<div class='leftChat'>"+Json["msg"]+"</div><br>");
                    }
                }
                for (var i in Json){
                    console.log(i);
                    console.log(Json[i]);
                    $("#"+Json[i]).text("在线");
                }
            };

            //相当于链接开启
            socket.onopen = function (ev) {
                console.log(ev.data);
                console.log("当前在线")
            };

            //链接关闭
            socket.onclose = function (ev) {
                $(".onlineStatus").text('已离线');
                console.log("socket已关闭");
            };
            $("#send").click(function () {
                send();
            });

            var toUser;
            // function getUser(){
            //     toUser=$(this).childNodes.item(1).attr("id");
            //     console.log(toUser);
            // }
            if (username == "karley"){
                $(".customer").click(function () {
                    toUser=$(this).children("p").last().attr("id");
                    console.log(toUser);
                });
            }else{
                toUser="karley";
            }

            //发送消息到服务器
            function send() {
                // if (!window.socket){    //先判断socket是否创建好
                //     alert("websocket未创建好");
                //     return;
                // }
                if (socket.readyState == WebSocket.OPEN) {
                    //通过socket发送消息
                    var message = document.getElementById("message").value;
                    socket.send(JSON.stringify({toId:toUser,msg:message}));
                    console.log(JSON.stringify({toId:toUser,msg:message}));
                    $("#message").val("");
                }else{
                    alert("发送失败");
                }
            }
        }else{
            alert("你的浏览器不支持websocket");
        }

        // $("#chatBox").children("div:last-child")[0].scrollIntoView();
    });

</script>
</html>

唉我每次都想拿出代码片段分析,又每次不知从何说起,可能这就是理科生的悲哀8,话都不会说了,就只能全部贴上来了。

这是最后的效果啦:

Springboot+Websocket打造实时通讯聊天窗口演示

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ajax小王子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值