使用WebSocket实现聊天室功能

本文详细介绍了如何在SpringBoot项目中搭建WebSocket框架,包括添加依赖、编写通信握手拦截器、配置WebSocket服务处理类以及自定义服务器端点。此外,还展示了前端HTML页面的实现,用于与后端WebSocket服务器进行交互。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、搭建项目框架,添加依赖

1、添加相关依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>

1)、编写通信握手拦截器

/**
* WebSocket 通信握手拦截器
* 它只拦截握手
*/
@Component
public class SpringBootHandshakeInterceptor implements HandshakeInterceptor{
/**
* 握手之前
* ServerHttpRequest : 请求对象
* ServerHttpResponse : 响应对象
* WebSocketHandler : WebSocket 服务处理类,在这里指的是 SpringBootWebSocketHandler * attributes : WebSocketSession.getAttributes() */
@Override
public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse
serverHttpResponse, WebSocketHandler wsHandler,Map<String, Object> attributes) throws Exception {
ServletServerHttpRequest servletServerHttpRequest = (ServletServerHttpRequest) serverHttpRequest;
//得到 Http 协议的请求对象
HttpServletRequest request = servletServerHttpRequest.getServletRequest();
HttpSession session = request.getSession(true);
System.out.println("握手拦截器^^^^^^^
request.getParameter(\"param\")=>"+request.getParameter("param"));
System.out.println("握手拦截器^^^^^^^^^^^^
request.getParameter(\"token\")=>"+request.getParameter("token"));
System.out.println("握手拦截器^^^^^^^^^^^^ HttpSession.getAttribute(\"user\")=>"+session.getAttribute("user"));
//数据中转 可以把 http 协议的会话对象数据中转到 ws 协议的会话对象中
attributes.put("param", request.getParameter("param"));
//非前后端分离架构:把 HttpSession 中的数据中转到 WebSocketSession 中
if(session.getAttribute("user") != null)attributes.put("user", session.getAttribute("user"));
//如果是前后端分离架构:把 Http 协议中的 token 令牌中转到 ws 协议的 WebSocketSession 中
attributes.put("token", request.getParameter("token"));
return true;
}
/**
* 握手之后
*/
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler
wsHandler,Exception exception) {
System.out.println(“握手之后”);
}
}

2)、编写 WebSocket 服务配置类

/**
* WebSocket 服务配置类
*
*/
@Configuration //单例模式 bean
@EnableWebSocket //启动 WebSocket 服务器
public class SpringBootWebSocketConfigurer implements WebMvcConfigurer , WebSocketConfigurer{
@Autowired
private SpringBootWebSocketHandler handler;
@Autowired
private SpringBootHandshakeInterceptor handshakeInterceptor;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
//-------------------- 允许跨域访问 WebSocket ------------------------

String[] allowsOrigins = {"*"};//允许连接的域,只能以 http 或 https 开头
/**
* http://localhost:8080
* http://localhost:8080/index.html */
//7. 设置 websocket 服务器地址 ws://localhost:8080/SpringBootWebSocket
registry.addHandler(handler, "/SpringBootWebSocket").addInterceptors(handshakeInterceptor).setAllowedOrigins(allowsOrigins);
}
}

3)、自定义实现 WebSocket 服务处理类

/**
    * WebSocket 服务处理类
    */
    @Component
    public class SpringBootWebSocketHandler implements WebSocketHandler{
    //存储所有客户端的会话 WebSocketSession,key 使用客户端的唯一标识方便存取
    private static Map<String, WebSocketSession> allWebSocketSession = new HashMap<String, WebSocketSession>();
    /**
    * 1. 客户端成功建立连接时触发
    */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
    System.out.println("客户端成功建立连接=>"+session.getId());
    //在 ws 服务类中任何位置都可以使用以下取值
    System.out.println("WebSocketSession.getAttributes().get(\"param\")"+session.getAttributes().get("param"));
    System.out.println("WebSocketSession.getAttributes().get(\"user\")"+session.getAttributes().get("user"));
    System.out.println("WebSocketSession.getAttributes().get(\"token\")"+session.getAttributes().get("token"));
    //存储所有客户端的 WebSocketSession
    allWebSocketSession.put((String)session.getAttributes().get("param"), session);
    }
    /**
    * 2. 接收客户端数据时触发
    */
    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
    System.out.println("接收客户端数据=>"+message.getPayload().toString());
    //给客户端回一句话意思意思,随便给客户端发数据
    this.send(session, "&&&&&&服务器回复的&&&&&&"+message.getPayload().toString());
    }
    /**
    * 3. 通信异常时触发
    */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
    System.out.println("通信异常=>"+session.getId());
    }
    /**
    * 4.客户端关闭连接时触发
    */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
    System.out.println("客户端关闭连接=>"+session.getId());
    //移除通行关闭的客户端
    allWebSocketSession.remove((String)session.getAttributes().get("param"));
    }
    /**
    * 是否支持分段传输
    */
    @Override
    public boolean supportsPartialMessages() {
    return false; //一次输出完毕
    }
    /**
    * 5.服务器主动发送数据
    * @param webSocketSession
    * @param msg
    */
    public void send(WebSocketSession webSocketSession,String msg) {
    try {
    webSocketSession.sendMessage(new TextMessage(msg));
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    /**
    * 6. 服务器主动关闭通信
    * @param webSocketSession
    * @param msg
    */
    public void close(WebSocketSession webSocketSession,String msg) {
    try {
    webSocketSession.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }

2)、自定义服务器端点,创建 ChatRoomServerEndpoint 类。代码如下:

/**
* 聊天室服务端
* 标注为端点:@ServerEndpoint,其中"/chat-room/{username}"为访问路径
*/
@ServerEndpoint("/chat-room/{username}")
public class ChatRoomServerEndpoint {
/**
* 存储所有存活的用户
* *我们需要周期性的去检查用户是否还处于活跃状态,不活跃的,移除该用户的 session
*/
private static Map<String , Session> livingSessions = new ConcurrentHashMap<>();
/**
* 前端一旦启用 WebSocket,就会调用@OnOpen 注解标注的方法
* @param username 路径参数 *
* @param session 会话,每个访问对象都会有一个单独的会话
*/
@OnOpen
public void openSession(@PathParam("username") String username, Session session){
livingSessions.put(session.getId(), session);
sendTextAll("欢迎用户【" + username +"】来到聊天室!");
}
/**
* 服务端发送消息给前端时调用 *
* @param username 路径参数 *
* @param session 会话,每个访问对象都会有一个单独的会话
* @param message 待发送的消息
*/
@OnMessage
public void onMessage(@PathParam("username") String username, Session session, String message){
sendTextAll("用户【" + username + "】:" + message);
}
/** *
* 客户端关闭 WebSocket 连接时,调用标注@OnClose 的方法 *
* @param username 路径参数
* @param session 会话,每个访问对象都会有一个单独的会话
* */
@OnClose
public void onClose(@PathParam("username") String username, Session session){
//将当前用户移除
livingSessions.remove(session.getId());
//给所有存活的用户发送消息
sendTextAll("用户【" + username +"】离开聊天室!");
}
/** *
* 向指定 Session(用户)发送 message * */
private void sendText(Session session, String message){
//发送消息对象
RemoteEndpoint.Basic basicRemote = session.getBasicRemote();
try {
//发送消息
basicRemote.sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
/** *
* 遍历所有存活的用户,并发送消息(PS:就是广播消息)
* */
private void sendTextAll(String message){
Iterator<String> sids=livingSessions.keySet().iterator();
while(sids.hasNext()) {
String sid=sids.next();
Session session=livingSessions.get(sid);
this.sendText(session, message);
}
}
}

3)、改造启动接口,激活 WebSocket,并注册WebSocket 相关类和端点,参考代码如下:

/**
* * 激活 WebSocket:@EnableWebSocket */
@SpringBootApplication
@EnableWebSocket
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
/**
* * 注册 ServerEndpointExporter Bean 对象(因为 Springboot 没有自动注册,所以得手动注册)
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
/**
* * 注册 端点对象
*/
@Bean
public ChatRoomServerEndpoint chatRoomServerEndpoint() {
return new ChatRoomServerEndpoint();
}
}

4)、前端 chat.html 页面,参考代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>聊天室</title>
<script src="jquery.min.js"></script>
<script src="sockjs.min.js"></script>
<script>
$(document).ready(function () {
var urlPrefix = "ws://localhost:8080/chat-room/";
var ws = null;
//加入聊天室
$('#bt_join').click(function () {
var username = $('#username').val();
//这一步,会调用服务端的@OnOpen 注解标注的方法
ws = new WebSocket(urlPrefix + username);
ws.onmessage = function (event) {
//接收服务端返回给前端的消息
$('#text_chat_content').append(event.data + "\n");
        };
ws.onclose = function () {
$('#text_chat_content').append("用户【" + username +"】离开聊天室!" + "\n");
        };
});
//发送消息
$('#bt_send').click(function () {
if (ws){
ws.send($('#in_msg').val());
}
});
//离开聊天室
$('#bt_left').click(function () {
if (ws){
ws.close();
        }
 });
})
</script>
</head>
<body>
聊天消息内容:
<br />
<textarea id="text_chat_content" readonly="readonly" cols="60"
rows="20"></textarea>
<br /> 用户:
<input id="username" type="text" />
<button id="bt_join">加入聊天室</button>
<button id="bt_left">离开聊天室</button>
<br /> 输入框:
<input id="in_msg" type="text" />
<button id="bt_send">发送消息</button>
</body>
</html>

需要在引入两个js文件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WMJ_wmj75

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值