备忘
前端(Vue):
<template>
<div></div>
</template>
<script>
export default {
name: "chatRoomSurvey",
data() {
return {
chatRoomWebsocket: null
}
},
computed: {
// ...
},
created() {
// 初始化 websocket
this.initWebSocket()
},
methods: {
// 初始化 websocket 连接
initWebSocket() {
if (typeof WebSocket != 'undefined') {
this.supported = "支持 websocket"
} else {
this.supported = "不支持 websocket"
}
//ws地址
const wsuri = "ws://" + this.currentUrlPath + "/websocket/" + this.$route.params.chatRoomId + "/" + this.$store.state.user.token + "/" + this.chatRoomData.hostUser;
this.chatRoomWebsocket = new WebSocket(wsuri);
this.chatRoomWebsocket.onerror = this.websocketOnError
this.chatRoomWebsocket.onmessage = this.websocketOnMessage
this.chatRoomWebsocket.onclose = this.websocketOnClose
},
//连接发生错误的回调方法
websocketOnError() {
console.log("WebSocket 连接发生错误")
},
//接收到消息的回调方法
websocketOnMessage(event) {
// 接收消息
let data = JSON.parse(event.data)
// ...
},
//连接关闭的回调方法
websocketOnClose(e) {
console.log("WebSocket 连接关闭", e)
console.log(this.chatRoomWebsocket)
},
//关闭 WebSocket 连接
websocketClose() {
this.chatRoomWebsocket.close()
},
// 判断是否是 url
isURL(str_url) {
var strRegex = /^((https|http|ftp|rtsp|mms)?:\/\/)?(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?((\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5]$)|([0-9a-z_!~*'()-]+\.)*([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\.[a-z]{2,6})(:[0-9]{1,4})?((\/?)|(\/[0-9a-zA-Z_!~*'().;?:@&=+$,%#-]+)+\/?)$/;
var re = new RegExp(strRegex);
return re.test(str_url)
},
// 发送消息
sendContent() {
let messageData = {
chatRoomId: this.$route.params.chatRoomId,
chatUser: this.$store.state.user.token,
chatAvatar: this.userInfo.userAvatar,
chatNickName: this.userInfo.userNickNameTem + this.backgroundInfo,
chatContent: this.text,
chatType: "1"
}
if (this.chatRoomWebsocket.readyState != '1') {
// 如果按下按钮时socket 不是连接状态,重连并等到连接成功再发送消息
this.initWebSocket()
this.chatRoomWebsocket.onopen = () => {
this.chatRoomWebsocket.send(JSON.stringify(messageData));
this.content.push({
askImg: this.userInfo.userAvatar,
askContent: this.text,
askNickName: this.userInfo.userNickNameTem + this.backgroundInfo,
userId: this.chatRoomData.hostUser
})
this.$refs.sTest.value = '' // 清空输入框的内容
// viewbox 移动到最底部
this.viewboxScrollToBottom()
}
} else {
this.chatRoomWebsocket.send(JSON.stringify(messageData));
this.content.push({
askImg: this.userInfo.userAvatar,
askContent: this.text,
askNickName: this.userInfo.userNickNameTem + this.backgroundInfo,
userId: this.chatRoomData.hostUser
})
this.$refs.sTest.value = '' // 清空输入框的内容
// viewbox 移动到最底部
this.viewboxScrollToBottom()
}
},
// viewbox 移动到最底部
viewboxScrollToBottom() {
if (this.$refs.viewBox) {
this.$nextTick(() => {
// DOM 更新后再执行
this.$refs.viewBox.scrollTo(this.$refs.viewBox.getScrollBody().scrollHeight + "55")
})
}
},
},
components: {}
}
</script>
<style scoped>
</style>
后端(Java):
// ...
import net.sf.json.JSONObject;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.server.standard.SpringConfigurator;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
@ServerEndpoint(value = "/websocket/{chatRoomId}/{userId}/{hostUserId}")
@Component
public class MyWebSocket {
//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount = 0;
//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
private static ConcurrentHashMap<String, MyWebSocket> webSocketSet = new ConcurrentHashMap<String, MyWebSocket>();
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
//当前发消息的聊天室编号
private String chatRoomId = "";
//当前发消息的用户编号
private String userId = "";
//该次连接所属的聊天室的主持人 id
private String hostUserId = "";
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(@PathParam(value = "chatRoomId") String param1, @PathParam(value = "userId") String param2, @PathParam(value = "hostUserId") String param3, Session session) throws Exception {
chatRoomId = param1;
userId = param2;
hostUserId = param3;
this.session = session;
webSocketSet.put("room" + param1 + "-" + "user" + param2, this); //加入 map 中
addOnlineCount(); //在线数加1
System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
// 若该用户不是主持人
if (!userId.equals(hostUserId)) {
// 普通用户上线时给主持人发送当前房间在线情况
System.out.println("普通用户上线时给主持人发送当前房间在线情况");
sendOnlinePersonnel(chatRoomId, hostUserId);
// 遍历 hashmap 查询主持人是否上线
for (String key : webSocketSet.keySet()) {
if (key.equals("room" + param1 + "-" + "user" + hostUserId)) {
// 给自己发主持人已上线
JSONObject jso = new JSONObject();
jso.put("messageType", "2");
jso.put("data", true);
System.out.println("自己不是主持人,主持人已上线");
sendMessage(jso.toString());
return;
}
}
// 给自己发主持人未上线
JSONObject jso = new JSONObject();
jso.put("messageType", "2");
jso.put("data", false);
System.out.println("自己不是主持人,主持人未上线");
sendMessage(jso.toString());
} else {
// 群发主持人已上线
JSONObject jso = new JSONObject();
jso.put("messageType", "2");
jso.put("data", true);
System.out.println("自己是主持人,主持人已上线");
sendInfoForAll(chatRoomId, userId, jso.toString());
}
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() throws Exception {
System.out.println("当前想要删除的webSocketSet:" + "room" + chatRoomId + "-" + "user" + userId);
if (!userId.equals("")) {
webSocketSet.remove("room" + chatRoomId + "-" + "user" + userId); //从set中删除
subOnlineCount(); //在线数减1
System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
// 若该用户是主持人
if (userId.equals(hostUserId)) {
// 给所有人群发发主持人已下线
JSONObject jso = new JSONObject();
jso.put("messageType", "2");
jso.put("data", false);
sendInfoForAll(chatRoomId, userId, jso.toString());
} else {
// 普通用户离线时给主持人发送当前房间在线情况
System.out.println("普通用户下线时给主持人发送当前房间在线情况");
sendOnlinePersonnel(chatRoomId, hostUserId);
}
}
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, Session session) throws Exception {
JSONObject jsStr = JSONObject.fromObject(message);
System.out.println("-------------------------------------------");
// 发来的不是查询在线情况的,也不是公告相关
if (!jsStr.get("chatType").toString().equals("3") && !jsStr.get("chatType").toString().equals("2")) {
// 普通文字消息需要保存到数据库中,视频或者图片另有别的后台 api 保存
if (jsStr.get("chatType").toString().equals("1")) {
System.out.println("来自客户端的消息:" + jsStr.get("chatContent").toString());
// 发来的是聊天消息
ChatRoomRecode chatRoomRecode = new ChatRoomRecode();
chatRoomRecode.setChatContent(jsStr.get("chatContent").toString());
chatRoomRecode.setChatRoomId(Long.valueOf(jsStr.get("chatRoomId").toString()));
chatRoomRecode.setChatUser(Long.valueOf(jsStr.get("chatUser").toString()));
chatRoomRecode.setChatTime(new java.sql.Date(new Date().getTime()));
chatRoomRecode.setChatType(jsStr.get("chatType").toString());
chatRoomRecode.setChatNickName(jsStr.get("chatNickName").toString());
chatRoomRecode.setChatAvatar(jsStr.get("chatAvatar").toString());
// 往数据库中存入聊天的信息
IChatRoomRecodeService chatRoomRecodeService = SpringCtx.getBeanByClass(IChatRoomRecodeService.class);
Boolean isOK = chatRoomRecodeService.insert(chatRoomRecode);
}
// 发送消息
JSONObject jso = new JSONObject();
jso.put("chatContent", jsStr.get("chatContent").toString());
jso.put("chatAvatar", jsStr.get("chatAvatar").toString());
jso.put("chatNickName", jsStr.get("chatNickName").toString());
jso.put("chatUser", jsStr.get("chatUser").toString());
// 普通的消息是 1,主持人上下线的消息是 2
jso.put("messageType", "1");
sendInfoForAll(chatRoomId, userId, jso.toString());
} else if (jsStr.get("chatType").toString().equals("3")) {
// 发来的是需要查询当前聊天室人员在线情况的消息
sendOnlinePersonnel(chatRoomId, hostUserId);
}
}
/**
* 发生错误时调用
*
* @OnError
*/
public void onError(Session session, Throwable error) {
System.out.println("发生错误");
error.printStackTrace();
}
/**
* 发送消息
*
* @OnError
*/
public void sendMessage(String message) throws IOException {
// this.session.getBasicRemote().sendText(message);
this.session.getAsyncRemote().sendText(message);
}
/**
* 给该聊天室的主持人发送当前聊天室内的人员在线情况
*
* @OnError
*/
public static void sendOnlinePersonnel(String chatRoomId, String hostUserId) throws IOException {
JSONObject jsonObject = new JSONObject();
jsonObject.put("messageType", "3");
JSONObject jsonObjectPersonnel = new JSONObject();
// 遍历 hashmap
for (String key : webSocketSet.keySet()) {
String[] keyArr = key.split("-");
// 房间号相同
if (keyArr[0].equals("room" + chatRoomId)) {
jsonObjectPersonnel.put(keyArr[1].substring(4), true);
}
}
jsonObject.put("onlinePersonnel", jsonObjectPersonnel);
if (!(webSocketSet.get("room" + chatRoomId + "-" + "user" + hostUserId) + "").equals("null")) {
webSocketSet.get("room" + chatRoomId + "-" + "user" + hostUserId).sendMessage(jsonObject.toString());
}
}
/**
* 群发给该聊天室不是自己的所有人
*
* @OnError
*/
public static void sendInfoForAll(String chatRoomId, String userId, String info) throws IOException {
// 群发消息
// 遍历 hashmap
for (String key : webSocketSet.keySet()) {
try {
String[] keyArr = key.split("-");
// System.out.println("-------------------------------------------");
// System.out.println("keyArr[0].equals(\"room\" + chatRoomId)");
// System.out.println(keyArr[0].equals("room" + chatRoomId));
// System.out.println("!keyArr[1].equals(\"user\" + userId)");
// System.out.println(!keyArr[1].equals("user" + userId)); && !keyArr[1].equals("user" + userId)
if (keyArr[0].equals("room" + chatRoomId) && !keyArr[1].equals("user" + userId)) {
webSocketSet.get(key).sendMessage(info);
}
} catch (IOException e) {
System.out.println("e:" + e);
e.printStackTrace();
}
}
}
/**
* 群发自定义消息
*/
// public static void sendInfo(String message) throws IOException {
// for (MyWebSocket item : webSocketSet) {
// try {
// item.sendMessage(message);
// } catch (IOException e) {
// continue;
// }
// }
// }
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
MyWebSocket.onlineCount++;
}
public static synchronized void subOnlineCount() {
MyWebSocket.onlineCount--;
}
}