赞
踩
随着实时通信需求的增加,开发人员寻求有效的解决方案来实现即时通讯功能。在这篇博客中,我们将介绍如何在Spring Boot和Vue.js应用程序中使用Socket.IO库实现私聊和群聊功能。Socket.IO是一个基于Node.js的实时应用程序框架,它为客户端和服务器之间提供了双向的实时通信。
Socket.IO是一个基于Node.js的实时应用程序框架,旨在实现实时、双向的通信。它通过使用WebSocket协议提供了一种简单而强大的方式,使服务器和客户端能够进行实时通信。
Socket.IO是一个开源的JavaScript库,可在客户端和服务器之间建立持久的、双向的通信通道。它允许实时数据传输,不需要客户端发起请求来获取更新的数据。Socket.IO支持多种传输协议,包括WebSocket、轮询和长轮询等,以确保在各种环境下都能实现实时通信。
实时性:Socket.IO基于WebSocket协议,通过建立持久的双向连接,能够实现实时的双向通信,使得服务器能够主动推送数据给客户端。
跨平台和跨浏览器:Socket.IO可用于Web、移动设备和桌面应用程序,支持多种平台和浏览器,具有广泛的兼容性。
自适应传输:Socket.IO可以根据环境自动选择最佳的传输协议,包括WebSocket、轮询和长轮询等,以确保在各种网络条件下都能进行实时通信。
容错性和可靠性:Socket.IO具备自动重新连接、心跳检测和故障恢复等机制,能够处理网络中断或其他故障情况,并保持通信的可靠性。
简单易用:Socket.IO提供了简单而灵活的API,使得开发人员能够轻松地构建实时应用程序,并处理客户端和服务器之间的实时数据传输。
引入依赖
<dependency>
<groupId>com.corundumstudio.socketio</groupId>
<artifactId>netty-socketio</artifactId>
<version>1.7.7</version>
</dependency>
在yml中配置属性
socketio: host: localhost port: 3000 # 设置最大每帧处理数据的长度,防止他人利用大数据来攻击服务器 maxFramePayloadLength: 1048576 # 设置http交互最大内容长度 maxHttpContentLength: 1048576 # socket连接数大小(如只监听一个端口boss线程组为1即可) bossCount: 1 workCount: 100 allowCustomRequests: true # 协议升级超时时间(毫秒),默认10秒。HTTP握手升级为ws协议超时时间 upgradeTimeout: 1000000 # Ping消息超时时间(毫秒),默认60秒,这个时间间隔内没有接收到心跳消息就会发送超时事件 pingTimeout: 6000000 # Ping消息间隔(毫秒),默认25秒。客户端向服务器发送一条心跳消息间隔 pingInterval: 25000
创建Socket.IO配置类
@Configuration public class SocketIOConfig implements InitializingBean { @Resource private SocketIOHandler socketIOHandler; @Value("${socketio.host}") private String host; @Value("${socketio.port}") private Integer port; @Value("${socketio.bossCount}") private int bossCount; @Value("${socketio.workCount}") private int workCount; @Value("${socketio.allowCustomRequests}") private boolean allowCustomRequests; @Value("${socketio.upgradeTimeout}") private int upgradeTimeout; @Value("${socketio.pingTimeout}") private int pingTimeout; @Value("${socketio.pingInterval}") private int pingInterval; @Override public void afterPropertiesSet() throws Exception { SocketConfig socketConfig = new SocketConfig(); socketConfig.setReuseAddress(true); socketConfig.setTcpNoDelay(true); socketConfig.setSoLinger(0); com.corundumstudio.socketio.Configuration configuration = new com.corundumstudio.socketio.Configuration(); configuration.setSocketConfig(socketConfig); // host在本地测试可以设置为localhost或者本机IP,在Linux服务器跑可换成服务器IP configuration.setHostname(host); configuration.setPort(port); // socket连接数大小(如只监听一个端口boss线程组为1即可) configuration.setBossThreads(bossCount); configuration.setWorkerThreads(workCount); configuration.setAllowCustomRequests(allowCustomRequests); // 协议升级超时时间(毫秒),默认10秒。HTTP握手升级为ws协议超时时间 configuration.setUpgradeTimeout(upgradeTimeout); // Ping消息超时时间(毫秒),默认60秒,这个时间间隔内没有接收到心跳消息就会发送超时事件 configuration.setPingTimeout(pingTimeout); // Ping消息间隔(毫秒),默认25秒。客户端向服务器发送一条心跳消息间隔 configuration.setPingInterval(pingInterval); SocketIOServer socketIOServer = new SocketIOServer(configuration); //添加事件监听器 socketIOServer.addListeners(socketIOHandler); //启动SocketIOServer socketIOServer.start(); System.out.println("SocketIO启动完毕"); } }
创建用户缓存
@Component public class ClientCache { private static Map<String, Map<UUID, SocketIOClient>> userClient = new ConcurrentHashMap<>(); public boolean saveClient(String username, SocketIOClient client, UUID sessionId){ Map<UUID, SocketIOClient> hashMap = userClient.get(username); if(hashMap == null){ hashMap = new HashMap<>(); hashMap.put(sessionId,client); userClient.put(username,hashMap); return true; }else{ return false; } } public void deleteClient(UUID sessionId){ for (Map.Entry<String, Map<UUID, SocketIOClient>> mapEntry : userClient.entrySet()) { if(mapEntry.getValue().get(sessionId) != null){ userClient.remove(mapEntry.getKey()); } } } public SocketIOClient getClient(String username){ Map<UUID, SocketIOClient> map = userClient.get(username); if (map != null) { for (Map.Entry<UUID, SocketIOClient> entry : map.entrySet()) { return entry.getValue(); } } return null; } public Map<UUID,SocketIOClient> getUser(String username){ return userClient.get(username); } public Map<String, Map<UUID, SocketIOClient>> getUsers(){ return userClient; } public Map<String,Map<UUID,SocketIOClient>> getUsers(List<String> usersname){ Map<String,Map<UUID,SocketIOClient>> map = new ConcurrentHashMap<>(); for (String username : usersname) { if(userClient.containsKey(username)){ map.put(username,userClient.get(username)); } } return map; } }
创建消息类
@Getter
@Setter
@Component
@NoArgsConstructor
@AllArgsConstructor
public class MessageInfo {
private String sentUsername;
private String message;
private String receiveUsername;
}
创建服务端事务处理
private ClientCache clientCache; public SocketIOHandler(ClientCache clientCache) { this.clientCache = clientCache; } @OnConnect public void connect(SocketIOClient client){ System.out.println("连接"); client.getHandshakeData().getHeaders(); } @OnDisconnect public void disconnect(SocketIOClient client){ UUID sessionId = client.getSessionId(); clientCache.deleteClient(sessionId); System.out.println("已断开连接"); } //监听客户端传递的用户信息 @OnEvent("userInfo") public void userInfo(SocketIOClient client, AckRequest ackRequest, String username){ UUID sessionId = client.getSessionId(); if(!clientCache.saveClient(username,client,sessionId)){ client.sendEvent("userInfo", "用户已存在)); return; } client.sendEvent("userInfo","连接成功"); } //监听私聊消息,@OnEvent的变量是与客户端发送的变量要相同 @OnEvent("privateMessage") public void privateUser(SocketIOClient client, AckRequest ackRequest, MessageInfo info){ SocketIOClient ioClient = clientCache.getClient(info.getReceiveUsername()); client.sendEvent("privateSent","发送成功"); ioClient.sendEvent("privateReceive",info.getMessage()); } //监听群聊消息 @OnEvent("groupMessage") public void groupUser(SocketIOClient client, AckRequest ackRequest, MessageInfo info){ Chat chat = chatService.getById(info.getChatId()); List<String> usersname = info.getUsersname(); //获取群内所有在线用户 Map<UUID, SocketIOClient> clientMap = clientCache.getUsers(usersname).values().stream() .flatMap(map -> map.entrySet().stream()) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); //给群组发消息 for (Map.Entry<UUID, SocketIOClient> entry : clientMap.entrySet()) { entry.getValue().sendEvent("groupReceive",ApiResult.success(info)); } client.sendEvent("groupReceive",info); }
安装依赖(socket.io-client似乎没有用到,但还是安装了好一点)
npm i vue-socket-io
npm i socket.io-client
main.js中配置连接服务端端口
import Vue from 'vue'
import VueSocketIO from 'vue-socket.io'
Vue.use(new VueSocketIO({
//是否打印日志
debug:false,
//服务端端口地址
connection: 'http://localhost:3000',
options:{
//是否自动连接
autoConnect:false
}
}))
import App from './App.vue'
import router from './router'
import store from './store'
注!!!VueSocketIO最好写在导入了vue之后就导入,不然容易有bug(至少我是这样)
这里我就不写视图层的代码了
export default { data(){ return{ message:"", username:"", chatMessages:{ //可以在这里创建接收私聊与群聊消息的数组 privateMessage:[], groupMessage:[], } } }, methods:{ openSocket(){ //打开socket连接 //在测试时将username后的数字更改 this.username = '用户123'; this.$socket.open(); }, sendPrivateMessage(){ //发送私聊消息 //第一个参数是与服务端约定好的监听变量名,第二个是要传输的数据,可以是一个对象 this.$socket.emit("privateMessage","这是一条私聊消息"); }, sendGroupMessage(){ this.$socket.emit("groupMessage","这是一条群聊消息"); } }, sockets:{ connect(){ //向客户端发送用户信息 this.$socket.emit("userInfo",{username:this.username}); console.log("连接成功"); }, connecting(){ console.log("正在连接"); }, disconnect(){ console.log("断开连接"); }, connect_error() { console.log("Socket 连接失败"); }, //监听用户信息的返回 userInfo(data){ console.log(data); }, //监听私聊消息 //这种方法无法使用this.*** //发送消息后自己的监听 privateSent(data){ console.log(data); }, //对方接收的消息 privateReceive(data){ console.log(data); }, //监听群聊消息 groupMessage:function (data){ console.log(data); } } }
这样基本就能够实现一个使用springboot+vue+socket.io进行的实时通信了,如果不行的话可能是我哪里写错或者泄露了,可以联系我帮你找bug。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。