SpringBoot2+WebSocket之聊天应⽤实战(优化版本)
背景
之前再中已经进⾏过⼀次demo,⽽这次的demo更加明确,优化了相关代码,为IM⽽⽣
前提
前提当然是导⼊相关的包,以及配置WebSocketConfig.java,请⽤上篇⽂章的内容即可。这⾥只做优化。
实战
例如从CopyOnWriteArraySet改为ConcurrentHashMap,保证多线程安全同时⽅便利⽤(userId)进⾏推送到指定端⼝。
相⽐之前的Set,Set遍历是费事且⿇烦的事情,⽽Map的get是简单便捷的,当WebSocket数量⼤的时候,这个⼩⼩的消耗就会聚少成多,影响体验,所以需要优化。
import java.io.IOException;
import urrent.ConcurrentHashMap;
import urrent.CopyOnWriteArraySet;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.softdev.system.likeu.util.ApiReturnUtil;
import org.apachemons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
slf4j.Slf4j;
@ServerEndpoint("/im/{userId}")
@Component
public class ImController {
static Log (ImController.class);
//静态变量,⽤来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount = 0;
//旧:concurrent包的线程安全Set,⽤来存放每个客户端对应的MyWebSocket对象。
//private static CopyOnWriteArraySet<ImController> webSocketSet = new CopyOnWriteArraySet<ImController>();
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
//新:使⽤map对象,便于根据userId来获取对应的WebSocket
private static ConcurrentHashMap<String,ImController> websocketList = new ConcurrentHashMap<>();
//接收sid
private String userId="";
/**
* 连接建⽴成功调⽤的⽅法*/
@OnOpen
public void onOpen(Session session,@PathParam("userId") String userId) {
this.session = session;
websocketList.put(userId,this);
log.info("websocketList->"+JSONString(websocketList));
//webSocketSet.add(this); //加⼊set中
addOnlineCount(); //在线数加1
log.info("有新窗⼝开始监听:"+userId+",当前在线⼈数为" + getOnlineCount());
this.userId=userId;
try {
JSONString(ApiReturnUtil.success("连接成功")));
} catch (IOException e) {
<("websocket IO异常");
}
}
/**
* 连接关闭调⽤的⽅法
*/
@OnClose
public void onClose() {
(this.userId)!=null){
//ve(this); //从set中删除
subOnlineCount(); //在线数减1
log.info("有⼀连接关闭!当前在线⼈数为" + getOnlineCount());
}
}
/**
* 收到客户端消息后调⽤的⽅法
*
* @param message 客户端发送过来的消息*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("收到来⾃窗⼝"+userId+"的信息:"+message);
if(StringUtils.isNotBlank(message)){
JSONArray list=JSONArray.parseArray(message);
for (int i = 0; i < list.size(); i++) {
try {
//解析发送的报⽂
JSONObject object = JSONObject(i);
String String("toUserId");
String String("contentText");
object.put("fromUserId",this.userId);
/
/传送给对应⽤户的websocket
if(StringUtils.isNotBlank(toUserId)&&StringUtils.isNotBlank(contentText)){
ImController (toUserId);
//需要进⾏转换,userId
if(socketx!=null){
socketx.JSONString(ApiReturnUtil.success(object))); //此处可以放置相关业务代码,例如存储到数据库
}
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
/**
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
<("发⽣错误");
error.printStackTrace();
}
/**
* 实现服务器主动推送
*/
*/
public void sendMessage(String message) throws IOException {
BasicRemote().sendText(message);
}
/**
* 发⾃定义消息
* */
/*public static void sendInfo(String message,@PathParam("userId") String userId) throws IOException {
log.info("推送消息到窗⼝"+userId+",推送内容:"+message);
前端websocket怎么用for (ImController item : webSocketSet) {
try {
//这⾥可以设定只推送给这个sid的,为null则全部推送
if(userId==null) {
item.sendMessage(message);
}else if(item.userId.equals(userId)){
item.sendMessage(message);
}
} catch (IOException e) {
continue;
}
}
}*/
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
}
public static synchronized void subOnlineCount() {
}
}
⽹页
这⾥的路径是写死的,反正你如果有freemarker最好是根据${tPath}这种动态变量来获取。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>websocket通讯</title>
</head>
<script src="cdn.bootcss/jquery/3.3.1/jquery.js"></script>
<script>
var socket;
function openSocket() {
if(typeof(WebSocket) == "undefined") {
console.log("您的浏览器不⽀持WebSocket");
}else{
console.log("您的浏览器⽀持WebSocket");
//实现化WebSocket对象,指定要连接的服务器地址与端⼝建⽴连接
//等同于socket = new WebSocket("ws://localhost:8888/xxxx/im/25");
/
/var socketUrl="${tPath}/im/"+$("#userId").val();
var socketUrl="localhost:8888/xxxx/im/"+$("#userId").val();
place("https","ws").replace("http","ws");
console.log(socketUrl)
socket = new WebSocket(socketUrl);
//打开事件
console.log("websocket已打开");
//socket.send("这是来⾃客户端的消息" + location.href + new Date());
};
//获得消息事件
console.log(msg.data);
//发现消息进⼊开始处理前端触发逻辑
};
//关闭事件
console.log("websocket已关闭");
};
//发⽣了错误事件
console.log("websocket发⽣了错误");
}
}
}
function sendMessage() {
if(typeof(WebSocket) == "undefined") {
console.log("您的浏览器不⽀持WebSocket");
}else {
console.log("您的浏览器⽀持WebSocket");
console.log('[{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+$("#contentText").val()+'"}]'); socket.send('[{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+$("#contentText").val()+'"}]'); }
}
</script>
<body>
<p>【userId】:<div><input id="userId" name="userId" type="text" value="25"></div>
<p>【toUserId】:<div><input id="toUserId" name="toUserId" type="text" value="26"></div>
<p>【toUserId】:<div><input id="contentText" name="contentText" type="text" value="嗷嗷嗷"></div> <p>【操作】:<div><a onclick="openSocket()">开启socket</a></div>
<p>【操作】:<div><a onclick="sendMessage()">发送消息</a></div>
</body>
</html>
效果
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论