一、前言
HTML
HTML是创建和构造网页的标准标记语言。它使用一组标记标签描述网页上的内容结构。HTML文档由HTML元素的嵌套结构组成,每个元素由尖括号(<>
)括起的标签表示。这些元素定义了网页的各个部分,如标题、段落、图像、链接、表单等。
JavaScript
JavaScript是一种高级、解释性的编程语言,允许您向网页添加交互性和动态行为。它主要用作Web浏览器中的客户端脚本语言,使开发人员能够操作文档对象模型(DOM)、处理事件并与服务器通信。
SpringBoot
Spring Boot 是一个简化了配置的 Spring 框架版本,可以快速构建基于 Java 的后端应用。它提供了诸多功能,包括 RESTful API 开发、数据库集成、安全性等。
WebSocket
WebSocket 是一种在客户端和服务器之间建立持久性连接的协议,允许实时双向通信。它可以用于实现聊天应用、实时数据展示、在线游戏等场景。
本文使用Vue和SpringBoot分别构建在线群聊系统的前端和后端程序,通过websocket完成用户登录和聊天信息的管理,实现简单的聊天室。
二、项目展示
项目链接
三、具体实现
1.前端
1) 登录界面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
</head>
<body>
<div style="margin: 200px auto 0 auto;height: 60px;width: 200px;">
<div>>> 给自己取个名字吧:</div>
<div>
<input id="username" name="username" type="text">
</div>
<div style="margin-top: 10px;"><button id="submit">开始聊天!</button></div>
</div>
2) 运行界面
下面代码创建了一个包含聊天室界面的页面,包括聊天内容显示区域、消息输入框、发送按钮等元素。初始化了一个 WebSocket 对象,尝试连接到服务器的 WebSocket 地址(需要根据实际情况更改地址)。在连接成功、失败或关闭时,通过回调函数给用户显示相应的系统消息。提供了一个函数用于在页面上显示新消息,通过将新消息附加到聊天内容的文本区域。监听了发送按钮的点击事件,将用户输入的消息发送到服务器。监听了键盘的 Enter 键按下事件,实现了在消息输入框中按 Enter 键即可发送消息的功能。在窗口关闭前,通过监听窗口关闭事件,关闭了 WebSocket 连接,以防止在窗口关闭时仍有未断开的连接。并引入了 jQuery 库,简化了对 DOM 元素的操作和事件处理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>聊天室</title>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
</head>
<body>
<div style="width: 1000px;padding:40px;margin: 100px auto 100px auto;border: 1px solid #C9C9C9;">
<div style="margin-bottom: 10px;">聊天室</div>
<textarea id="content" style="width: 100%;height: 300px;resize:none;"></textarea>
<textarea id="message" style="width: 100%;height: 100px;resize:none;margin-top:-6px;border-top: none;"></textarea>
<div style="text-align: right;">
<button id="send" style="margin-top: 10px;">发送/[Enter]</button>
</div>
</div>
<script type="text/javascript">
var websocket = null;
function getQueryVariable(variable)
{
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] === variable){return pair[1];}
}
return null;
}
//将消息显示在网页上
function setMessage(newMessage) {
var old = $('#content').val();
$('#content').val(old + "\n" + newMessage);
}
function initWebSocket() {
//判断当前浏览器是否支持WebSocket, 主要此处要更换为自己的地址
if ('WebSocket' in window) {
websocket = new WebSocket("ws://" + window.location.host + "/websocket/chat");
} else {
alert('Not support websocket')
}
//连接发生错误的回调方法
websocket.onerror = function () {
setMessage("系统消息: 网络异常!");
};
//连接成功建立的回调方法
websocket.onopen = function (event) {
}
//接收到消息的回调方法
websocket.onmessage = function (event) {
setMessage(event.data);
}
//连接关闭的回调方法
websocket.onclose = function () {
setMessage("系统消息: 连接已断开!");
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
websocket.close();
}
}
$(function () {
initWebSocket();
$("#send").click(function () {
let message = $("#message").val();
websocket.send(message);
$("#message").val('');
});
$(document).keyup(function (event) {
if (event.keyCode === 13) {
$("#send").trigger("click");
}
});
});
</script>
</body>
</html>
3) WebSocket初始化
初始化 WebSocket 连接,并定义了连接状态变化、消息接收等事件的处理函数
function initWebSocket() {
//判断当前浏览器是否支持WebSocket, 主要此处要更换为自己的地址
if ('WebSocket' in window) {
websocket = new WebSocket("ws://" + window.location.host + "/websocket/chat");
} else {
alert('Not support websocket')
}
//连接发生错误的回调方法
websocket.onerror = function () {
setMessage("系统消息: 网络异常!");
};
//连接成功建立的回调方法
websocket.onopen = function (event) {
}
//接收到消息的回调方法
websocket.onmessage = function (event) {
setMessage(event.data);
}
//连接关闭的回调方法
websocket.onclose = function () {
setMessage("系统消息: 连接已断开!");
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
websocket.close();
}
}
2.后端
1) 消息封装
@Data
public class Result<T> {
private int code;
private T data;
private String message;
public static <R> Result<R> ok(R data) {
Result<R> ret = new Result<>();
ret.setCode(0);
ret.setData(data);
ret.setMessage("ok");
return ret;
}
public static <R> Result<R> error(String message) {
Result<R> ret = new Result<>();
ret.setCode(-1);
ret.setMessage(message);
return ret;
}
}
2)WebSocket服务配置
使用sessionMap存储每个用户对象,管理 WebSocket 用户连接,记录在线用户数量、检查用户名是否存在以及注册新的用户连接。
public class WebSocketSession {
private static Map<String, Session> sessionMap = new ConcurrentHashMap<>();
private static AtomicInteger onlineNum = new AtomicInteger();
public static int getOnlineUserNum() {
return onlineNum.get();
}
public static boolean isUsernameExist(String username) {
return sessionMap.containsKey(username);
}
public static boolean register(String username, Session session) {
if (!sessionMap.containsKey(username)) {
sessionMap.put(username, session);
onlineNum.incrementAndGet();
return true;
}
return false;
}
public static void remove(String username) {
sessionMap.remove(username);
onlineNum.decrementAndGet();
}
public static Session getSession(String username) {
return sessionMap.get(username);
}
public static List<Session> getSessionList() {
return new ArrayList<>(sessionMap.values());
}
}
设置登录拦截以及session拦截,对未登录的用户拒绝访问,跳转到登录界面,并且识别已登录用户24小时内自动访问
@Component
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthInterceptor())
.addPathPatterns("/**").excludePathPatterns("/login");
}
@Bean
public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> containerCustomizer() {
return container -> {
container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND,
"/"));
};
}
@Override
public void addCorsMappings(CorsRegistry registry) {
// 设置允许跨域的路由
registry.addMapping("/**")
// 设置允许跨域请求的域名
.allowedOriginPatterns("*")
// 是否允许证书(cookies)
.allowCredentials(true)
// 设置允许的方法
.allowedMethods("*")
// 跨域允许时间
.maxAge(24*3600);
}
3)WebSocket应用
根据特定的会话或用户名来发送 WebSocket 消息,允许向特定的用户或会话发送信息。
public static void send(String username, String message) throws IOException {
Session session = WebSocketSession.getSession(username);
if(null != session) {
session.getBasicRemote().sendText(message);
}
}
public static void send(Session session, String message) throws IOException {
if(null != session) {
session.getBasicRemote().sendText(message);
}
}
/**
* 群发
*/
public static synchronized void groupSend(HttpSession httpSession, String message) throws IOException {
List<Session> sessionList = WebSocketSession.getSessionList();
for (Session session : sessionList) {
session.getBasicRemote().sendText(message);
}
}
结合 WebSocket 协议和 HTTP 会话,对发送的消息进行接收,以及对于登录和离开和错误消息的处理
-
@OnOpen
: 当客户端建立 WebSocket 连接时触发该方法。在这里,通过EndpointConfig
获取到HttpSession
,并根据HttpSession
中的用户信息注册用户到 WebSocket 会话中,然后发送系统消息欢迎用户加入聊天室。 -
@OnClose
: 当连接关闭时触发该方法。它从HttpSession
中获取用户名,然后将用户从 WebSocket 会话中移除,并发送离开聊天室的系统消息。 -
@OnMessage
: 当接收到客户端发送的消息时触发该方法。它从HttpSession
获取用户真实姓名,获取当前时间并格式化,然后将消息发送给聊天室中的所有用户,显示发送者的姓名、时间和消息内容。文章来源:https://www.toymoban.com/news/detail-810564.html -
@OnError
: 当出现错误时触发该方法。在这里,它会打印出错信息,用于调试和异常处理。文章来源地址https://www.toymoban.com/news/detail-810564.html -
@OnOpen public void onOpen(Session session, EndpointConfig config) { httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName()); String username = UserHolder.getUsername(httpSession); WebSocketSession.register(username, session); try { String realName = UserHolder.getUsername(httpSession); String message = "系统消息: 欢迎" + realName + "加入聊天室!"; Message.groupSend(httpSession, message); } catch (IOException e) { e.printStackTrace(); } } //关闭连接时调用 @OnClose public void onClose() throws IOException { String username = UserHolder.getUsername(httpSession); WebSocketSession.remove(username); Message.groupSend(httpSession, "系统消息: " + username + "离开了聊天室!"); } //收到客户端信息 @OnMessage public void onMessage(Session session, String message) throws IOException { String realName = UserHolder.getUsername(httpSession); Date date = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss"); String time = dateFormat.format(date); Message.groupSend(httpSession, "[" + time + "] " + realName + ": " + message); } //错误时调用 @OnError public void onError(Session session, Throwable throwable) throws IOException { throwable.printStackTrace(); }
-
四、具体总结
- 整个项目的完成过程中,我体会到了前后端协作的必要性和重要性。前端与后端的无缝连接让整个应用得以顺利运行,而 WebSocket 技术的运用则让用户能够获得实时性强的体验。通过这个项目,我对实时通信和全栈开发有了更深入的了解,并对 JavaScript、Spring Boot 和 WebSocket 这些技术有了更为深刻的体验和理解。在此期间也非常感谢孟宁老师的教导,孟老师独特的教学方式,让我对网络程序设计产生了浓烈的兴趣,学习到了许多东西。
到了这里,关于springboot+websocket实现简单的聊天室的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!