赞
踩
1.加入依赖
2.后端建立socket服务端
3.前端建立客户端
<!-- websocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
// 配置开启WebSocket
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
/** * @author websocket服务 */ @ServerEndpoint(value = "/imserver/{userId}") @Component public class WebSocketServer { private static UserService userService; private static RedisTemplate redisTemplate; public static void setUserService(ApplicationContext context){ userService = context.getBean(UserServiceImpl.class); redisTemplate = (RedisTemplate) context.getBean("redisTemplate"); } private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class); /** * 记录当前在线连接数 */ public static final Map<String, Session> sessionMap = new ConcurrentHashMap<>(); /** * 连接建立成功调用的方法 */ // 当前用户 private UserVo userVo; // 连接上服务端触发的方法 @OnOpen public void onOpen(Session session, @PathParam("userId") String userId) { if (StringUtils.hasText(userId)){ // 加入新用户 if (sessionMap.containsKey(userId)){ sessionMap.remove(userId); } sessionMap.put(userId, session); this.userVo = userService.findById(Long.valueOf(userId)); // 统计所有在线用户 List<UserVo> list = new LinkedList<>(); sessionMap.forEach((userId1,session1) -> { UserVo userVo = userService.findById(Long.valueOf(userId1)); list.add(userVo); }); try { // 发送给所有在线的用户,更新在线人数 sendAllMessage(JSON.toJSONString(list)); } catch (Exception e) { e.printStackTrace(); } log.info("有新用户加入,userId={}, 当前在线人数为:{}", userId, sessionMap.size()); } } /** * 连接关闭调用的方法 */ @OnClose public void onClose(Session session, @PathParam("userId") String userId) { sessionMap.remove(userId); // 统计所有在线用户 List<UserVo> list = new LinkedList<>(); sessionMap.forEach((userId1,session1) -> { UserVo userVo = userService.findById(Long.valueOf(userId1)); list.add(userVo); }); sendAllMessage(JSON.toJSONString(list)); log.info("有一连接关闭,移除userId={}的用户session, 当前在线人数为:{}", userId, sessionMap.size()); } /** * 收到客户端消息后调用的方法 * 后台收到客户端发送过来的消息 * onMessage 是一个消息的中转站 * 接受 浏览器端 socket.send 发送过来的 json数据 * @param message 客户端发送过来的消息 */ @OnMessage public void onMessage(String message, Session session, @PathParam("userId") String userId) { userVo = userService.findById(Long.valueOf(userId)); log.info("服务端收到用户username={},id={}的消息:{}", userVo.getNickname(),userId, message); // 解析消息 JSONObject jsonObject1 = JSON.parseObject(message); String toUserId = jsonObject1.getString("toUserId"); String text = jsonObject1.getString("text"); // 判断是给指定人发,还是群发 if (StringUtils.hasText(toUserId)){ // {"to": "admin", "text": "聊天文本"} Session toSession = sessionMap.get(toUserId); // 根据 to用户名来获取 session,再通过session发送消息文本 if (toSession != null) { // 服务器端 再把消息组装一下,组装后的消息包含发送人和发送的文本内容 // {"from": "zhang", "text": "hello"} JSONObject jsonObject = new JSONObject(); jsonObject.put("fromUser",userVo); jsonObject.put("toUser",userService.findById(Long.valueOf(toUserId))); jsonObject.put("text",text); this.sendMessage(jsonObject.toJSONString(), toSession); log.info("发送给用户userId={},消息:{}", toUserId, jsonObject.toJSONString()); } else { log.info("发送失败,未找到用户username={}的session", toUserId); } }else{ JSONObject jsonObject = new JSONObject(); jsonObject.put("fromUser",userVo); jsonObject.put("text",text); this.sendAllMessage(jsonObject.toJSONString()); // 将消息存入redis redisTemplate.opsForList().rightPush("messageList",jsonObject.toJSONString()); redisTemplate.expire("messageList",60*60, TimeUnit.SECONDS); // 过期时间 log.info("发送给所有用户,消息:{}", toUserId, jsonObject.toJSONString()); } } @OnError public void onError(Session session, Throwable error) { log.error("发生错误"); error.printStackTrace(); } /** * 服务端发送消息给客户端 */ private void sendMessage(String message, Session toSession) { try { log.info("服务端给客户端[{}]发送消息{}", toSession.getId(), message); toSession.getBasicRemote().sendText(message); } catch (Exception e) { log.error("服务端发送消息给客户端失败", e); } } /** * 服务端发送消息给所有客户端 */ private void sendAllMessage(String message) { try { for (Session session : sessionMap.values()) { log.info("服务端给客户端[{}]发送消息{}", session.getId(), message); session.getBasicRemote().sendText(message); } } catch (Exception e) { log.error("服务端发送消息给客户端失败", e); } } } // WebSocket服务类无法进行bean的注入,所以要自己调用ApplicationContext获取bean再注入 @SpringBootApplication public class BlogApplication { public static void main(String[] args) { ConfigurableApplicationContext applicationContext = SpringApplication.run(BlogApplication.class, args); WebSocketServer.setUserService(applicationContext); } }
<template> <div class="socket"> <el-row> <el-col :span="6" class="online"> <h3 style="background:linear-gradient(to left,#cae9ff,#c7d8ff);text-align:center;padding:5px;">在线用户</h3> <el-scrollbar style="height:280px"> <div v-for="user in onlineUser" :key="user.id" style="padding:5px" class="onlineUser"> <el-avatar shape="square" size="medium" :src="user.avatar"></el-avatar> <span style="margin-left:8px">{{user.account}}</span> </div> </el-scrollbar> </el-col> <el-col :span="17" class="container"> <h3 style="background:linear-gradient(to left,#cae9ff,#c7d8ff);text-align:center;padding:5px;">当前用户(<span style="color:#66b1ff">{{user.account}}</span>)</h3> <el-scrollbar style="height:420px" ref="scroll"> <div class="content"> <div class="message" v-for="item,index in messageList" :key="index" style="margin-top:5px;margin-left:5px;position:relative" :style="{justifyContent: item.fromUser.id == user.id ? 'flex-end' : 'flex-start'}"> <el-avatar shape="square" size="medium" :src="item.fromUser.avatar" :style="{order:user.id == item.fromUser.id ? '1':'0'}"></el-avatar> <span style="margin-left:8px;" class="text">{{item.text}}</span> </div> </div> </el-scrollbar> <div style="margin-top: 15px;" class="write"> <el-input placeholder="请输入内容" maxlength="100" clearable v-model="text" class="input-with-select"> <el-button slot="append" type="primary" @click="sendMessage">发送</el-button> </el-input> </div> </el-col> </el-row> </div> </template> <script> import config from '@/utils/config' import {getCacheMessage} from '@/api/socket' import {mapState} from 'vuex' export default { name:'lm', data(){ return { text:'', onlineUser:[], messageList:[], socket:'', baseUrl:config.socketUrl } }, computed:{ ...mapState(['user']) }, methods:{ // 连接socket onOpen(){ if (typeof WebSocket == 'undefined'){ console.log('你的浏览器不支持webSocket') return; } const url = this.baseUrl+this.user.id this.socket = new WebSocket(url); this.socket.onopen = ()=>{ console.log('websocket打开了,'+this.user.id+'连接成功') } this.socket.onmessage = (data)=>{ var message = JSON.parse(data.data) if(message.hasOwnProperty("text")){ this.messageList.push(message) this.text = '' let scroll = this.$refs['scroll'].$refs['wrap'] this.$nextTick(() =>{ scroll.scrollTop = scroll.scrollHeight }) }else{ // 统计在线人数 this.onlineUser = message } } this.socket.onclose = function(){ console.log('断开连接成功') } }, sendMessage(){ const message = { text:this.text, userId:this.user.id } if(this.text == ''||this.text == null){ this.$message.warning('请输入内容') }else{ this.socket.send(JSON.stringify(message)) } }, // 初始化,缓存的消息 getCacheMessage(){ getCacheMessage().then( res => { const data = res.data for(var i in data){ this.messageList.push(JSON.parse(data[i])) } } ) } }, mounted(){ this.getCacheMessage() this.onOpen() }, beforeDestroy(){ this.socket.close() // 关闭socket } } </script> <style scoped> .socket{ width: 1200px; height: 600px; padding:26px; background-color: #fff; border-radius: 5px; } .socket .online{ background-color: #f5f5f5; height: 300px; border-radius: 5px; overflow: hidden; } .socket .online .onlineUser{ display: flex; justify-content: flex-start; align-items: flex-start; } .socket .container{ position: relative; margin-left: 26px; background-color: #f5f5f5; height: 500px; border-radius: 5px; } .socket .el-button--primary{ background-color: #66b1ff; color: #fff; } .socket .container .content{ min-height: 400px; border: 1px solid #d6d6d6; border-radius: 4px; } .socket .container .content .message{ display: flex; align-content: flex-start; } .socket .write{ position:absolute; bottom: 10px; width: 500px; right: 50%; transform: translateX(50%); } .socket .text{ min-height: 24px; line-height: 20px; padding: 8px; font-size: 16px; background-color: #fff; border-radius: 5px; max-width: 280px; overflow-wrap:break-word; word-wrap:break-word; word-break: bread-all; white-space:pre-wrap; overflow: hidden; } </style>
springboot集成websocket实现聊天室的功能。如有不足之处,还望大家斧正。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。