springboot结合websocket做在线聊天系统

1 篇文章 0 订阅
1 篇文章 0 订阅

首先服务端的集成,引入依赖包

	<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <netty-socketio.version>1.7.16</netty-socketio.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.corundumstudio.socketio</groupId>
            <artifactId>netty-socketio</artifactId>
            <version>${netty-socketio.version}</version>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

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

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <finalName>spring-boot-demo-websocket-socketio</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

服务端中websocket主要代码

@OnConnect
    public void onConnect(SocketIOClient client) {
        if (client != null) {
            String token = client.getHandshakeData().getSingleUrlParam("token");
            // 模拟用户id 和token一致
            String userId = client.getHandshakeData().getSingleUrlParam("token");
            UUID sessionId = client.getSessionId();

            dbTemplate.save(userId, sessionId);
            log.info("连接成功,【token】= {},【sessionId】= {}", token, sessionId);
        } else {
            log.error("客户端为空");
        }
    }

    /**
     * 添加disconnect事件,客户端断开连接时调用,刷新客户端信息
     *
     * @param client 客户端对象
     */
    @OnDisconnect
    public void onDisconnect(SocketIOClient client) {
        if (client != null) {
            String token = client.getHandshakeData().getSingleUrlParam("token");
            // 模拟用户id 和token一致
            String userId = client.getHandshakeData().getSingleUrlParam("token");
            UUID sessionId = client.getSessionId();

            dbTemplate.deleteByUserId(userId);
            log.info("客户端断开连接,【token】= {},【sessionId】= {}", token, sessionId);
            client.disconnect();
        } else {
            log.error("客户端为空");
        }
    }

    /**
     * 加入群聊
     *
     * @param client  客户端
     * @param request 请求
     * @param data    群聊
     */
    @OnEvent(value = Event.JOIN)
    public void onJoinEvent(SocketIOClient client, AckRequest request, JoinRequest data) {
        log.info("用户:{} 已加入群聊:{}", data.getUserId(), data.getGroupId());
        client.joinRoom(data.getGroupId());

        server.getRoomOperations(data.getGroupId()).sendEvent(Event.JOIN, data);//触发 socket.on()函数 8081端口
    }


    @OnEvent(value = Event.CHAT)
    public void onChatEvent(SocketIOClient client, AckRequest request, SingleMessageRequest data) {
        Optional<UUID> toUser = dbTemplate.findByUserId(data.getToUid());
        System.out.println(toUser);
        if (toUser.isPresent()) {
            log.info("用户 {} 刚刚私信了用户 {}:{}", data.getFromUid(), data.getToUid(), data.getMessage());
            sendToSingle(toUser.get(), data);
            request.sendAckData(Dict.create().set("flag", true).set("message", "发送成功")); //返回客户端,发起请求的request-session 8080端口
        } else {
            request.sendAckData(Dict.create()
                    .set("flag", false)
                    .set("message", "发送失败,对方不想理你(" + data.getToUid() + "不在线)"));
        }
    }

    @OnEvent(value = Event.GROUP)
    public void onGroupEvent(SocketIOClient client, AckRequest request, GroupMessageRequest data) {
        Collection<SocketIOClient> clients = server.getRoomOperations(data.getGroupId()).getClients();

        boolean inGroup = false;
        for (SocketIOClient socketIOClient : clients) {
            if (ObjectUtil.equal(socketIOClient.getSessionId(), client.getSessionId())) {
                inGroup = true;
                break;
            }
        }
        if (inGroup) {
            log.info("群号 {} 收到来自 {} 的群聊消息:{}", data.getGroupId(), data.getFromUid(), data.getMessage());
            sendToGroup(data);
        } else {
            request.sendAckData("请先加群!");
        }
    }

    /**
     * 单聊
     */
    public void sendToSingle(UUID sessionId, SingleMessageRequest message) {
        server.getClient(sessionId).sendEvent(Event.CHAT, message);
    }

    /**
     * 广播
     */
    public void sendToBroadcast(BroadcastMessageRequest message) {
        log.info("系统紧急广播一条通知:{}", message.getMessage());
        for (UUID clientId : dbTemplate.findAll()) {
            if (server.getClient(clientId) == null) {
                continue;
            }
            server.getClient(clientId).sendEvent(Event.BROADCAST, message);
        }
    }

    /**
     * 群聊
     */
    public void sendToGroup(GroupMessageRequest message) {
        server.getRoomOperations(message.getGroupId()).sendEvent(Event.GROUP, message);
    }

前端主要代码

<script>
        const token = 'user' + Math.floor((Math.random() * 1000) + 1);
        const url = `http://127.0.0.1:8088?token=${token}`;
        const socket = io.connect(url);
        socket.on('connect', function () {
            output(`<span class="connect-msg">系统通知: ${token}成功连接至websocket服务器</span>`);
        });

        socket.on('join', function (data) {
            output(`<span class="sys-msg">${data.groupId} 群通知: 新人 ${data.userId} 请爆照</span>`);
        });

        socket.on('chat', function (data) {
            output(`<span class="username-msg">系统通知: 收到来自 ${data.fromUid} 的悄悄话: ${data.message}</span>`);
        });

        socket.on('group', function (data) {
            output(`<span class="username-msg">${data.groupId} 群消息: ${data.fromUid} 说: ${data.message}</span>`);
        });

        socket.on('disconnect', function () {
            output(`<span class="disconnect-msg">系统通知: ${token}已从websocket服务器断开连接</span>`);
        });

        socket.on('broadcast', function (data) {
            output(`<span class="broadcast">${data.message}</span>`);
        });

        function sendConnect() {
            socket.connect();
        }

        function sendDisconnect() {
            socket.disconnect();
        }

        function sendBroadcast() {
            axios.post('/demo/send/broadcast', {
                message: '系统广播通知: 当前时间 ' + moment().format('YYYY-MM-dd HH:mm:ss.SSS')
            }).then((response) => {
                const {flag, message} = response.data;
                if (flag) {
                    layer.msg(message, {icon: 6});
                } else {
                    layer.msg(message, {icon: 5});
                }
            });
        }

        function sendJoin() {
            let joinRequest = {
                userId: token,
                groupId: "666"

            };
            socket.emit('join', joinRequest);
        }

        function sendGroup() {
            let message = $('#msg').val();

            if (message === '') {
                layer.msg('你不说点什么嘛?', {icon: 5});
                return;
            }

            $('#msg').val('');
            let groupRequest = {
                fromUid: token,
                groupId: "666",
                message: message
            };
            socket.emit('group', groupRequest, function (data) {
                if (data) {
                    layer.msg(data, {icon: 5});
                }
            });
        }

        function sendChat() {
            let toUserId = $('#to').val();
            let message = $('#msg').val();

            if (toUserId === '') {
                layer.msg('请输入对方昵称', {icon: 5});
                return;
            }
            if (message === '') {
                layer.msg('你不说点什么嘛?', {icon: 5});
                return;
            }
            $('#to').val('');
            $('#msg').val('');

            let singleRequest = {
                fromUid: token,
                toUid: toUserId,
                message: message
            };
            socket.emit('chat', singleRequest, function (data) {
                output(`<span class="username-msg">系统通知: 你刚刚和 ${singleRequest.toUid} 说了句悄悄话</span>`);
                if (data && data.flag) {
                    output(`<span class="username-msg">系统通知: 悄悄话, ${data.message}</span>`);
                } else {
                    output(`<span class="disconnect-msg">系统通知: 悄悄话, ${data.message}</span>`);
                }
            });
        }

        function output(message) {
            let currentTime = "<span class='time'>" + moment().format('YYYY-MM-dd HH:mm:ss.SSS') + "</span>";
            let element = $("<div>" + currentTime + " " + message + "</div>");
            $('#console').prepend(element);
        }

    </script>

以上代码难点分析
服务端中,websocket一共分为两个端口去,服务端程序本身的端口是8080,一个是8081端口 用于websocket时间监听端口,例如发送消息事件,加入群聊事件,消息事件的触发也就是8081端口,而8080,即服务端端口主要负责传递数据,核心代码如下

request.sendAckData(Dict.create().set("flag", true).set("message", "发送成功")); //返回客户端,发起请求的request-session 8080端口
server.getRoomOperations(data.getGroupId()).sendEvent(Event.JOIN, data);//触发 socket.on()函数 8081端口

前端代码中和后端一样,只用到一个端口8081,负责监听服务端的事件,核心结构为

在这里插入图片描述
在这里插入图片描述

下面附上源码
源码连接 https://github.com/wangjielalala/spring-boot-demo/tree/master/spring-boot-demo-websocket-socketio

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值