Websocket整合实现聊天操作

在实际开发中,尤其是web开发,我该如何做才可以实现消息或者数据的实时更新呢。

这里我为大家介绍,websocket长连接,它可以简历连接,且创建一个通道,通道中的数据可以实时更新。

废话不多说,接下来我将使用vue+springboot基于websocket来实现一个简单的聊天实现。

vue前端代码,这里主要的功能就是连接后端websocket服务器,然后发送消息。

​
<script setup>
import { reactive, ref, onMounted, onBeforeUnmount } from 'vue';

const messageCe = ref('');
const receivedMessages = ref([]);
const HEART_CHECK_TIME = 30000;
const data = reactive({
    readyLine: 0,
    onLine: 0,
    outLine: 0,
    errLine: 0,
});

const userId = 1;
const wsuri = `ws://localhost:8080/websocket/${userId}`;
let ws = new WebSocket(wsuri);

const heartCheck = createHeartCheck(ws, { userId });

onMounted(() => {
    ws.onopen = () => {
        ws.send(JSON.stringify({ test: "12345连接无误", toUserId: userId }));
    };

    ws.onerror = () => {
        reconnect();
    };

    ws.onmessage = (event) => {
        handleMessage(event.data);
        heartCheck();
    };

    ws.onclose = () => {
        console.log("已经关闭连接");
    };
});

onBeforeUnmount(() => {
    ws.close();
});

const sendMessage = () => {
    if (messageCe.value.trim() === '') return; // 防止发送空消息
    const data_mm = JSON.stringify({ message: messageCe.value }); // 将输入消息序列化
    ws.send(data_mm);
    messageCe.value = ''; // 清空输入框
};

function handleMessage(data) {
    try {
        const obj = JSON.parse(data);
        if (obj.message) {
            // 记录所有接收到的消息
            receivedMessages.value.push(`${obj.username || '匿名'}: ${obj.message}`);
        }
    } catch (e) {
        console.error('JSON字符串格式错误:', e);
    }
}

function reconnect() {
    console.log("尝试重新连接...");
    setTimeout(() => {
        ws = new WebSocket(wsuri);
    }, 3000);
}

function createHeartCheck(ws, { userId }) {
    let timer = null;
    let timeoutTimer = null;

    return () => {
        clearTimeout(timer);
        clearTimeout(timeoutTimer);
        timer = setTimeout(() => {
            const message = JSON.stringify({ checkHeart: 'ping', toUserId: userId });
            ws.send(message);
            console.log(`【发送消息】 ${message}`);
            timeoutTimer = setTimeout(() => {
                ws.close(); // 超时关闭连接
            }, HEART_CHECK_TIME);
        }, HEART_CHECK_TIME);
    };
}
</script>

<template>
    <div class="chat-container">
        <div class="messages" v-for="(msg, index) in receivedMessages" :key="index">
            {{ msg }}
        </div>
        <div class="input-container">
            <input type="text" v-model="messageCe" @keyup.enter="sendMessage" placeholder="输入消息..." />
            <button @click="sendMessage">发送</button>
        </div>
    </div>
</template>

<style scoped>
.chat-container {
    display: flex;
    flex-direction: column;
    height: 400px;
    width: 300px;
    border: 1px solid #ccc;
    border-radius: 8px;
    overflow: hidden;
    background-color: #f9f9f9;
    margin: 20px auto;
}

.messages {
    flex: 1;
    padding: 10px;
    overflow-y: auto; /* 允许垂直滚动 */
    border-bottom: 1px solid #ddd;
}

.input-container {
    display: flex;
    padding: 10px;
    background-color: #fff;
}

input {
    flex: 1;
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 4px;
    margin-right: 10px;
}

button {
    padding: 10px 15px;
    border: none;
    border-radius: 4px;
    background-color: #007bff;
    color: white;
    cursor: pointer;
}

button:hover {
    background-color: #0056b3;
}
</style>

​

spring boot代码

pom.xml依赖

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

项目结构

config配置代码

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

​

 testController代码

@RestController
@RequestMapping("/test")
@Api(tags = { "测试" })
public class TestController {
 
    //推送数据接口
    @PostMapping("/socket/push/{sid}")
    public Map pushToWeb(@PathVariable String sid, String message) {
        Map<String,Object> result = new HashMap<>();
        try {
            WebSocketServer.sendInfo(message, sid);
            result.put("code", sid);
            result.put("msg", message);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }
    
}

server也就是websocket的代码,这里是整个项目的核心,这里有相关路径,而且提供每个生命节点的相关解决方法。

package my.websocket.service;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.ArrayList;

import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import lombok.extern.slf4j.Slf4j;

@Component
@Slf4j
@Service
@ServerEndpoint("/websocket/{sid}")
public class WebSocketServer {
    private static int onlineCount = 0;
    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();

    // 用于存储历史消息
    private static List<String> historyMessages = new ArrayList<>();

    private Session session;
    private String sid = "";

    @OnOpen
    public void onOpen(Session session, @PathParam("sid") String sid) {
        this.session = session;
        webSocketSet.add(this);
        this.sid = sid;
        addOnlineCount();

        try {
            // 发送连接成功消息
            sendMessage("conn_success");

            // 发送历史消息
            for (String message : historyMessages) {
                sendMessage(message);
            }

            log.info("有新窗口开始监听:" + sid + ",当前在线人数为:" + getOnlineCount());
        } catch (IOException e) {
            log.error("websocket IO Exception");
        }
    }

    @OnClose
    public void onClose() {
        webSocketSet.remove(this);
        subOnlineCount();
        log.info("释放的sid为:" + sid);
        log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("收到来自窗口" + sid + "的信息:" + message);

        // 存储历史消息
        historyMessages.add(message);

        // 群发消息
        for (WebSocketServer item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

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

    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    public static void sendInfo(String message, @PathParam("sid") String sid) throws IOException {
        log.info("推送消息到窗口" + sid + ",推送内容:" + message);
        for (WebSocketServer item : webSocketSet) {
            try {
                if (sid == null || item.sid.equals(sid)) {
                    item.sendMessage(message);
                }
            } catch (IOException e) {
                continue;
            }
        }
    }

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

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

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

    public static CopyOnWriteArraySet<WebSocketServer> getWebSocketSet() {
        return webSocketSet;
    }
}

接下来就是启动前后端的项目了。

然后前端界面长这样

这里可以看到我们连接到了后台 

接下来我们就聊聊这里我们做了啥,我们现在是实现前端界面发送消息之后,其他界面可以接收到,并且在我们发送消息后,再打开一个新页面,它可以同步历史消息

 

 

这里可以发现,俩个连接都可以实现互相发送消息,且历史数据也会被读出,

并且后台也会接收到消息 

这里都是窗口1发的消息的原因是,他们连接的都是同一个窗口,因为我这里只是初步演示,所以在前端就没有多做处理,实际开发中都是以为这个为基本进行开发的,而且细心的人应该也能发现,我在vue代码中绑定了一个死数据id,所以会全是一个窗口。

接下来我们再模拟一下通过后台发送消息,这里我们使用apifox这个软件来实现发送消息。

这里我之前前端代码没有实现,所以我修改了一个函数,代码如下

​
function handleMessage(data) {
    try {
        console.log(data);
        const obj = JSON.parse(data);
        if (obj.message) {
            // 记录所有接收到的消息
            receivedMessages.value.push(`${obj.username || '匿名'}: ${obj.message}`);
        }
    } catch (e) {
        console.error('JSON字符串格式错误:', e);
        receivedMessages.value.push(`系统消息: ${data}`);
    }
}

​

 先编写后台api对应的参数

然后发送数据,这里可以看到我们前端接收到了后台发送过来的数据,支持,spring boot整合websocket结束

 

如有相关疑问,欢迎加入q裙463727795一起探讨编程问题!!! 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值