效果图先看一下页面效果,有点简单粗暴!哈哈哈哈哈,别介意.
本文参考:SpringBoot2.0集成WebSocket,实现后台向前端推送信息
新建一个springboot项目
项目结构引入相关依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.0</version>
</dependency>
<!--com.alibaba/fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
<!--websocket依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
Java代码
WebSocketConfig
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
PageController
@RestController
public class PageController {
@GetMapping("chat")
public ModelAndView toChat(){
return new ModelAndView("/chat");
}
}
WebSocketServer
@ServerEndpoint("/chat/{userName}")
@Component
@Slf4j
public class WebSocketServer {
/**静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。*/
private static int onlineCount = 0;
/**concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。*/
private static ConcurrentHashMap<String,WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
/**与某个客户端的连接会话,需要通过它来给客户端发送数据*/
private Session session;
/**接收userId*/
private String userName="";
/**
* @Description: 连接建立成功调用的方法
* @params: [session, userId]
* @return: void
* @Author: wangxianlin
* @Date: 2020/5/9 9:13 PM
*/
@OnOpen
public void onOpen(Session session,@PathParam("userName") String userName) {
this.session = session;
this.userName=userName;
if(webSocketMap.containsKey(userName)){
webSocketMap.remove(userName);
webSocketMap.put(userName,this);
//加入set中
}else{
webSocketMap.put(userName,this);
//加入set中
addOnlineCount();
//在线数加1
}
log.info("用户连接:"+userName+",当前在线人数为:" + getOnlineCount());
try {
sendMessage("{\"msg\":\"连接成功\"}");
} catch (IOException e) {
log.error("用户:"+userName+",网络异常!!!!!!");
}
}
/**
* @Description: 连接关闭调用的方法
* @params: []
* @return: void
* @Author: wangxianlin
* @Date: 2020/5/9 9:13 PM
*/
@OnClose
public void onClose() {
if(webSocketMap.containsKey(userName)){
webSocketMap.remove(userName);
//从set中删除
subOnlineCount();
}
log.info("用户退出:"+userName+",当前在线人数为:" + getOnlineCount());
}
/**
* @Description: 收到客户端消息后调用的方法
* @params: [message, session]
* @return: void
* @Author: wangxianlin
* @Date: 2020/5/9 9:13 PM
*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("用户消息:"+userName+",报文:"+message);
//可以群发消息
//消息保存到数据库、redis
if(StringUtils.isNotBlank(message)){
try {
//解析发送的报文
JSONObject jsonObject = JSON.parseObject(message);
//追加发送人(防止串改)
jsonObject.put("sender",this.userName);
String receiver=jsonObject.getString("receiver");
//传送给对应toUserId用户的websocket
if(StringUtils.isNotBlank(receiver)&&webSocketMap.containsKey(receiver)){
webSocketMap.get(receiver).sendMessage(jsonObject.toJSONString());
}else{
log.error("请求的receiver:"+receiver+"不在该服务器上");
//否则不在这个服务器上,发送到mysql或者redis
}
}catch (Exception e){
e.printStackTrace();
}
}
}
/**
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
log.error("用户错误:"+this.userName+",原因:"+error.getMessage());
error.printStackTrace();
}
/**
* 实现服务器主动推送
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
/**
* @Description: 发送消息
* @params: [message, userId]
* @return: void
* @Author: wangxianlin
* @Date: 2020/5/9 9:09 PM
*/
public static void sendInfo(String message,@PathParam("userId") String userId) throws IOException {
log.info("发送消息到:"+userId+",报文:"+message);
if(StringUtils.isNotBlank(userId)&&webSocketMap.containsKey(userId)){
webSocketMap.get(userId).sendMessage(message);
}else{
log.error("用户"+userId+",不在线!");
}
}
/**
* @Description: 获取在线人数
* @params: []
* @return: int
* @Author: wangxianlin
* @Date: 2020/5/9 9:09 PM
*/
public static synchronized int getOnlineCount() {
return onlineCount;
}
/**
* @Description: 在线人数+1
* @params: []
* @return: void
* @Author: wangxianlin
* @Date: 2020/5/9 9:09 PM
*/
public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}
/**
* @Description: 在线人数-1
* @params: []
* @return: void
* @Author: wangxianlin
* @Date: 2020/5/9 9:09 PM
*/
public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
}
前端HTMl
<div class="layui-container">
<div class="layui-row">
<div class="layui-col-md12">
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 25px;">
<legend style="margin-left: 40%;">简单粗暴的聊天界面</legend>
</fieldset>
<div class="grid-demo grid-demo-bg2" style="text-align: center">
<form class="layui-form">
<div class="layui-form-item">
<label class="layui-form-label">接收者</label>
<div class="layui-input-block">
<input type="text" id="receiver" lay-verify="title" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">发送者</label>
<div class="layui-input-block">
<input type="text" id="sender" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">消息</label>
<div class="layui-input-block">
<textarea placeholder="请输入内容" class="layui-textarea" id="msg"></textarea>
</div>
</div>
<div class="layui-col-md12" style="margin-left: 4%">
<div class="layui-tab layui-tab-card" style="height: 200px;overflow: auto">
<fieldset class="layui-elem-field layui-field-title">
<legend>消息记录</legend>
</fieldset>
<div id="msgDiv">
</div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button type="button" class="layui-btn layui-btn-primary" onclick="establishConnection()">
建立连接
</button>
<button type="button" class="layui-btn layui-btn-primary" onclick="sendMessage()">发送消息
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<script type="text/javascript" th:src="@{/javascript/jquery-2.1.4.js}"></script>
<!--layui-->
<script type="text/javascript" th:src="@{/plugins/layui/layui.js}"></script>
JavaScript代码
var socket;
if (typeof(WebSocket) == "undefined") {
console.log("您的浏览器不支持WebSocket");
} else {
/***建立连接*/
function establishConnection() {
var userName = $("#sender").val();
if (userName == '' || userName == null) {
alert("请输入发送者");
return;
}
//实现化WebSocket对象,指定要连接的服务器地址与端口 建立连接
var socketUrl = "" + window.location.protocol + "//" + window.location.host + "/chat/" + userName;
socketUrl = socketUrl.replace("https", "ws").replace("http", "ws");
if (socket != null) {
socket.close();
socket = null;
}
socket = new WebSocket(socketUrl);
//打开事件
socket.onopen = function () {
console.log("开始建立链接....")
};
//关闭事件
socket.onclose = function () {
console.log("websocket已关闭");
};
//发生了错误事件
socket.onerror = function () {
console.log("websocket发生了错误");
};
/**
* 接收消息
* @param msg
*/
socket.onmessage = function (msg) {
msg = JSON.parse(msg.data);
if (msg.msg != '连接成功') {
$("#msgDiv").append('<p class="user">用户名:' + msg.sender + '</p><p class="chat">' + msg.msg + '</p>');
}
};
}
/**
* 发送消息
*/
function sendMessage() {
var msg = $("#msg").val();
if (msg == '' || msg == null) {
alert("消息内容不能为空");
return;
}
var receiver = $("#receiver").val();
if (receiver == '' || receiver == null) {
alert("接收人不能为空");
return;
}
var msgObj = {
receiver: receiver,
msg: msg
};
socket.send(JSON.stringify(msgObj));
}
}
网友评论