最近,因为工作的需求需要实现一个小型的客服聊天系统,在网上搜一一圈,发现搜到的都是websocket,所以自己和根据大家的帖子写了一个看看。那就开始吧!
1.导入依赖
<!--websocket依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2.编写配置类
package com.kuaidian.edu.school.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Description;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
import javax.websocket.server.ServerEndpoint;
@Description("编写一个websocketconfig配置类,注入对象serverEndpointExporter," +
"这个bean会自动注册使用@serverEndpoint注解声明的Websocket endpoint")
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
3.接收消息类
package com.kuaidian.edu.school.model;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Set;
@Data
@ApiModel(description = "websocket消息内容")
public class MsgVo {
@ApiModelProperty(value = "用户id")
private String nickName;
@ApiModelProperty(value = "消息")
private String msg;
@ApiModelProperty(value = "在线人数")
private int count;
@ApiModelProperty(value = "在线用户")
private Set<String> onlineList;
@ApiModelProperty(value = "发送者")
private String fromUser;
@ApiModelProperty(value = "发送者房间号")
private String fromSid;
@ApiModelProperty(value = "接受者")
private String toUser;
@ApiModelProperty(value = "发送者房间号")
private String ToSid;
@ApiModelProperty(value = "消息类型")
private Integer messageType;
}
4.编写controller类
package com.kuaidian.edu.school.controller;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.kuaidian.edu.school.model.MsgVo;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
@Slf4j
@Component
@ServerEndpoint("/groupChat/{sid}/{nickName}")
public class WebSocketServerController {
public static Map<String, Map<String, Session>> groupMemberInfoMap = new HashMap<String, Map<String, Session>>();
//房间号---》组成员信息
// private static ConcurrentHashMap<String,Session> groupMemberInfoMap = new ConcurrentHashMap<>();
//房间号---》在线人数
private static ConcurrentHashMap<String, Set<String>> onlineUserMap = new ConcurrentHashMap<>();
/**
* 收到消息调用的方法,群成员发送消息
* @param sid
* @param nickName
* @param message
*/
@OnMessage
public void onMessage(Session session,@PathParam("sid") String sid, @PathParam("nickName") String nickName, String message){
System.out.println("来自客户端消息:"+nickName+message+"客户端的ID是"+session.getId());
System.out.println("---------------"+message);
Set<String> onlineUserList = onlineUserMap.computeIfAbsent(sid, k -> new HashSet<>());
MsgVo msgVo = JSONObject.parseObject(message, MsgVo.class);
String textMessage=msgVo.getMsg();
Integer messageType=msgVo.getMessageType();
String toUser= msgVo.getToUser();
msgVo.setOnlineList(onlineUserList);
msgVo.setFromSid(sid);
msgVo.setFromUser(nickName);
msgVo.setCount(onlineUserList.size());
if(messageType == 4){
sendMessageTo(JSONObject.toJSONString(msgVo),toUser,sid);
}else{
sendMessageAll(JSONObject.toJSONString(msgVo),sid);
}
}
/**
* 建立连接调用的方法,群成员加入
* @param session
* @param sid
* @param nickName
*/
@OnOpen
public void onOpen(Session session, @PathParam("sid") String sid, @PathParam("nickName") String nickName){
//如果map中存在指定的key,那么就返回这个key对应的value值;
//如果指定的key不存在,那么就把key和newvalue存下map,并返回null;null指指定的那个key之前是没有对应值得
Map<String, Session> SessionMember = groupMemberInfoMap.computeIfAbsent(sid, k -> new HashMap<String, Session>());
Set<String> onlineUserList = onlineUserMap.computeIfAbsent(sid, k -> new HashSet<>());
onlineUserList.add(nickName);
SessionMember.put(nickName,session);
System.out.println("来自客户端消息:"+nickName+"客户端的ID是"+session.getId());
MsgVo msgVo = new MsgVo();
//messageType 1 代表上线 2 代表下线 3代表群发 4代表私发消息
//先给所有人发送通知,说我上线了
msgVo.setOnlineList(onlineUserList);
msgVo.setCount(onlineUserList.size());
msgVo.setFromSid(sid);
msgVo.setFromUser(nickName);
msgVo.setMessageType(1);
msgVo.setMsg("频道号:"+sid+"用户:"+nickName+"上线了!");
sendMessageAll(JSONObject.toJSONString(msgVo),sid);
System.out.println("有连接!当前在线人数"+onlineUserList.size());
}
public void sendMessageTo(String message,String Touser,String sid){
Map<String, Session> SessionMap = groupMemberInfoMap.get(sid);
//Set<String> onlineUserList = onlineUserMap.get(sid);
for (String key : SessionMap.keySet()){
if (key.equals(Touser)) {
try {
SessionMap.get(key).getBasicRemote().sendText(message);
break;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void sendMessageAll(String message,String sid){
Map<String, Session> SessionMap = groupMemberInfoMap.get(sid);
// Set<String> onlineUserList = onlineUserMap.get(sid);
for (String key : SessionMap.keySet()){
try {
SessionMap.get(key).getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 关闭连接调用的方法,群成员退出
* @param session
* @param sid
* @param nickName
*/
@OnClose
public void onClose(Session session,@PathParam("sid") String sid, @PathParam("nickName") String nickName){
Map<String, Session> SessionMap = groupMemberInfoMap.get(sid);
SessionMap.remove(nickName);
Set<String> onlineUserList = onlineUserMap.get(sid);
onlineUserList.remove(nickName);
//messageType 1 代表上线 2 代表下线 3代表群发 4代表私发消息
//先给所有人发送通知,说我上线了
MsgVo msgVo = new MsgVo();
msgVo.setCount(onlineUserList.size());
msgVo.setOnlineList(onlineUserList);
msgVo.setMessageType(2);
msgVo.setNickName(nickName);
msgVo.setMsg("频道号:"+sid+"用户:"+nickName+"下线了!");
sendMessageAll(JSONObject.toJSONString(msgVo),sid);
System.out.println("有连接关闭!当前在线人数"+onlineUserList.size());
// 发送离线通知
}
/**
* 传输消息错误调用的方法
*
* @param error
*/
@OnError
public void OnError(Throwable error) {
log.info("Connection error");
}
}
4.vue前端页面编写
<template>
<section >
<div style="display: flex; flex-direction: column">
<span >用户:{{nickName}}</span>
<span>在线人数:{{resData.count}}</span>
<span>登录用户:</span>
<span v-for="(item, index) in onlineUsers" v-bind:key="index" @click="PrivateSendMessage(item)" style="margin-left: 40px">{{item}}</span>
</div>
<div>
<!-- <el-input v-model="nickName" placeholder="请输入用户名" ></el-input>-->
<!-- <el-button @click="initWebSocket">连接</el-button>-->
<el-input v-model="contentText" placeholder="发送的消息" ></el-input>
<el-button @click="sendText">发送</el-button>
<div v-for="(item, index) in allList" v-bind:key="index">
<div v-if="item.messageType === 4">
{{item.person}}私聊:{{item.toPerson}}
<span id="messageTo" >
<br/> {{item.message}}</span>
</div>
<span id="message" v-else>
{{item.person}}群聊:<br/> {{item.message}}
</span>
</div>
</div>
</section>
</template>
<script>
export default {
name: 'websocketServer',
data () {
return {
ws: null,
count: 0,
nickName: '', // 当前用户昵称
toUser: '',
onlineUsers: [],
list: [], // 聊天记录的数组
contentText: '', // input输入的值
resData: [], // 服务器返回结果
allList: [] // 消息数组
}
},
mounted () {
this.nickName = this.$route.query.nickName
this.initWebSocket()
},
destroyed () {
// 离开页面时关闭websocket连接
this.ws.onclose(undefined)
},
methods: {
PrivateSendMessage: function (item) {
var _this = this
_this.toUser = item
console.log('对' + item + '发起私聊')
},
// 发送聊天信息
sendText () {
let _this = this
if (!_this.contentText) {
return
}
let params = {
msg: _this.contentText
}
console.log('频道号' + _this.toUser)
console.log('频道号' + _this.toUse === null)
if (_this.toUser === '') {
params.messageType = 3
} else {
params.messageType = 4
params.toUser = _this.toUser
}
_this.ws.send(JSON.stringify(params)) // 调用WebSocket send()发送信息的方法
_this.contentText = ''
},
// 进入页面创建websocket连接
initWebSocket () {
let _this = this
// 模拟用户的号码npm
// 判断页面有没有存在websocket连接
if (window.WebSocket) {
let serverHot = window.location.hostname
let sip = '1010'
console.log('can')
if (_this.nickName === '' || _this.nickName === undefined) {
console.log('_this.nickName爲空')
return
}
// 填写本地IP地址 此处的 :9101端口号 要与后端配置的一致!
let url = 'ws://' + serverHot + ':8093' + '/groupChat/' + sip + '/' + this.nickName // `ws://127.0.0.1/9101/groupChat/10086/聊天室`
let ws = new WebSocket(url)
_this.ws = ws
ws.onopen = function (e) {
console.log('服务器连接成功: ' + url)
}
ws.onclose = function (e) {
console.log('服务器连接关闭: ' + url)
}
ws.onerror = function () {
console.log('服务器连接出错: ' + url)
}
ws.onmessage = function (e) {
// 接收服务器返回的数据
let resData = JSON.parse(e.data)
_this.resData = resData
_this.count = resData.count
_this.onlineUsers = resData.onlineList
_this.list = [
_this.list,
{ nickName: resData.nickName, content: resData.msg, onlineUsers: resData.onlineList }
]
// 1 代表上线 2 代表下线 3代表群发 4代表私发消息
if (resData.messageType === 2) {
// document.getElementById('message').innerHTML += resData.nickName + ':' + '<br/>' + resData.msg + '<br/>'
let item = {
person: resData.nickName,
message: resData.msg
}
_this.allList.push(item)
}
if (resData.messageType === 4) {
// document.getElementById('messageTo').innerHTML += resData.fromUser + ':' + '(私聊)' + resData.toUser + '<br/>' + resData.msg + '<br/>'
let item = {
person: resData.fromUser,
message: resData.msg,
toPerson: resData.toUser,
messageType: resData.messageType
}
_this.allList.push(item)
console.log('添加', _this.allList)
} else {
let item = {
person: resData.fromUser,
message: resData.msg,
messageType: resData.messageType
}
_this.allList.push(item)
console.log('添加', _this.allList)
}
/* if (resData.messageType === 1 || resData.messageType === 3) {
document.getElementById('message').innerHTML += resData.fromUser + ':' + '(群发)' + '<br/>' + resData.msg + '<br/>'
let item ={
person: resData.nickName,
message: resData.msg
}
allList.push(item)
} */
console.log('服务器返回的数据', resData)
}
}
}
}
}
</script>
<style scoped>
</style>
到此结束!
|