SpringBoot+WebScoket在线聊天室
提示:本次作者使用的环境是Windows10+jdk1.8+maven3.6.1 + idea2020.03
注:本版本只是一个简易版本的聊天室,更多功能可以自己自己去扩充,新手上路,老鸟勿喷,欢迎所有热爱java的朋友加作者wx
文章目录
第一步:创建SpringBoot项目导入maven依赖
代码如下(示例):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- SpringBoot框架websocket起步依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- SpringBoot整合thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- lombok依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
第二步:配置文件
代码如下(示例):
server:
port: 8989
spring:
thymeleaf:
mode: HTML
encoding: UTF-8
prefix: classpath:/templates/ # 静态页面所在的路径,一般在resources文件加下创建
suffix: .html
cache: false #关闭thymeleaf缓存
第二步:创建index.html页面(templates下)
代码如下(示例):
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<title>SpringBoot + WebSocket + html</title>
<link rel="stylesheet" href="/css/bootstrap.min.css">
<script src="/js/bootstrap.min.js"></script>
<script src="/js/Jquery.js"></script>
</head>
<body style="margin: 45px">
<h4>小王在线聊天室</h4>
<div class="form-group">
<label for="content"></label>
<textarea id="content" readonly="readonly" cols="80" rows="15"></textarea>
</div>
<div class="form-group" style="margin-top: 8px">
<textarea id="message" cols="80" rows="5" placeholder="请输入消息"></textarea>
<div style="margin-top: 10px">
<button id="toSend" class="btn-info">发送</button>
<button id="toExit" class="btn-danger">离线</button>
<input id="username" th:value="${username}" style="display: none">
</div>
</div>
<script type="text/javascript">
$(function () {
var ws;
//如果浏览器支持WebSocket
if ("WebSocket" in window) {
var baseUrl = 'ws://localhost:8989//websocket/';
var username = $('#username').val();
ws = new WebSocket(baseUrl + username);
debugger;
//建立连接之后,触发事件
ws.onopen = function () {
console.log("建立 websocket 连接......");
};
//接收后台服务端的消息,触发事件
ws.onmessage = function (event) {
$('#content').append(event.data + '\n\n');
console.log("接收到服务端发送的消息......" + event.data + '\n');
};
//连接关闭时,触发事件
ws.onclose = function () {
$('#content').append('[' + username + ']已离线');
console.log("关闭 websocket 连接......");
};
//发生错误时,触发事件
ws.onerror = function (event) {
console.log("websocket发生错误......" + event + '\n');
};
} else { //如果浏览器不支持WebSocket
alert("很抱歉,您的浏览器不支持WebSocket!!!");
}
//发送按钮触发的行为,客户端发送消息到服务器
$('#toSend').click(function () {
sendMsg();
});
//支持回车键发送消息
$(document).keyup(function (event) {
if (event.keyCode == 13) {
sendMsg();
}
});
//发送消息的函数
function sendMsg() {
ws.send($('#message').val());
$('#message').val("");
}
//离线按钮触发的行为
$('#toExit').click(function () {
if (ws) {
ws.close();
}
})
})
</script>
</body>
</html>
第三步:创建Conteoller页面跳转
代码如下(示例):
/**
* ClassName ChatController
* Description
* @Author: xiaowang
* @Date 2021/6/22 10:45
*/
@Controller
public class ChatController {
//声明原子变量类,确保服务端和客户端之间操作的原子性和可见性
private AtomicInteger atomicInteger = new AtomicInteger();
@RequestMapping("/chat")
public String toChat(Model model) {
model.addAttribute("username", "user:" + atomicInteger.getAndIncrement());
return "index";
}
第四步:创建消息发送工具类
/**
* ClassName ChatUtils
* Description
* @Author: xiaowang
* @Date 2021/6/22 11:44
*/
@Slf4j
public class ChatUtils {
//定义map集合,确保数据共享和安全,这里使用ConcurrentHashMap
//用户名为key,session信息为value
public static final Map<String, Session> CLIENTS = new ConcurrentHashMap<>();
/**
* 使用连接发送消息
* @param session 用户的session
* @param message 发送的消息内容
*/
public static void sendMessage(Session session, String message) {
if (session == null) {
return;
}
final RemoteEndpoint.Basic basic = session.getBasicRemote();
if (basic == null) {
return;
}
try {
basic.sendText(message);
} catch (IOException e) {
e.printStackTrace();
log.error("sendMessage IOException", e);
}
}
/**
* 发送消息给所有人
* @param message
*/
public static void sendMessageAll(String message) {
CLIENTS.forEach((sessionId, session) -> sendMessage(session, message));
}
/**
* 获取所有的在线用户
*/
public static String getOnlineInfo() {
Set<String> userNames = CLIENTS.keySet();
if (userNames.size() == 0) {
return "当前无人在线......";
}
return userNames.toString() + "在线";
}
}
第五步:创建WebScoket相关事件监听
/**
* ClassName ChatServerEndpoint
* Description
* @Author: xiaowang
* @Date 2021/6/22 11:46
*/
@Slf4j
@Component
@ServerEndpoint("/websocket/{username}")
@SuppressWarnings("all")
public class ChatServerEndpoint {
/**
* 连接建立时触发
*/
@OnOpen
public void onOpen(@PathParam("username") String username, Session session) {
log.info("用户{}登录", username);
String message = "用户[" + username + "]已进入聊天室!";
//将该用户登录的消息发送给其他人
ChatUtils.sendMessageAll(message);
//将自己的信息添加到map集合中
ChatUtils.CLIENTS.put(username, session);
//获取当前的在线人数,发给自己查看
String onlineInfo = ChatUtils.getOnlineInfo();
ChatUtils.sendMessage(session, onlineInfo);
}
/**
* 客户端接收服务端发来的数据时触发
*/
@OnMessage
public void onMessage(@PathParam("username") String username, String message) {
log.info("发送消息:{}, {}", username, message);
//广播,把消息同步给其他客户端
ChatUtils.sendMessageAll("[" + username + "]: " + message);
}
/**
* 连接关闭时触发
*/
@OnClose
public void onClose(@PathParam("username") String username, Session session) {
//从当前的map集合中移除该用户
ChatUtils.CLIENTS.remove(username);
//将该用户离线的消息通知给其他人
ChatUtils.sendMessageAll("[" + username + "]已离线!");
try {
//关闭WebSocket下的该Seesion会话
session.close();
log.info("{} 已离线......", username);
} catch (IOException e) {
e.printStackTrace();
log.error("onClose error", e);
}
}
/**
* 聊天通信发生错误时触发
*/
@OnError
public void onError(Session session, Throwable throwable) {
try {
//关闭WebSocket下的该Seesion会话
session.close();
} catch (IOException e) {
e.printStackTrace();
log.error("onError Exception", e);
}
log.info("Throwable msg " + throwable.getMessage());
}
}
第六步:创建配置类
/**
* ClassName ScoketConfig
* Description
* @Author: xiaowang
* @Date 2021/6/22 12:26
*/
@Configuration
public class WebScoketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
第七步:启动测试
以上用多个浏览器打测试就能建立通讯了