SpringBoot和Vue2集成WebSocket,实现聊天室功能
1.加入依赖
2.后端建立socket服务端
3.前端建立客户端
后端
// 配置开启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
/**
* 连接建立成功调用的方法
*/
// 当前用户
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
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
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);
}
}
前端
在线用户
{{user.account}}
当前用户({{user.account}})
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
}
}
.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;
}
springboot集成websocket实现聊天室的功能。如有不足之处,还望大家斧正。
参考链接
发表评论