WebSocket 事件
Websocket 使用 ws 或 wss 的统⼀资源标志符,类似于 HTTPS,其中 wss 表示在 TLS 之上的 Websocket。
ws://example.com/wsapi
wss://secure.example.com/
Websocket 使用和 HTTP 相同的 TCP 端⼝口,可以绕过大多数防⽕墙的限制。默认情况下,Websocket 协议使用80 端口;运行在 TLS 之上时,默认使用 443 端口。、
事件 | 事件处理理程序 | 描述 |
---|---|---|
open | Sokcket onopen | 连接建⽴立时触发 |
message | Sokcket onopen | 客户端接收服务端数据时触发 |
error | Sokcket onerror | 通讯发⽣生错误时触发 |
close | Sokcket onclose | 链接关闭时触发 |
Spring Boot 提供了 Websocket
组件 spring-boot-starter-websocket
,⽤来支持在 Spring Boot 环境下对Websocket
的使⽤。
下面是⼀个页面使用 Websocket 的示例:
var ws = new WebSocket("ws://localhost:8080");
ws.onopen = function(evt) {
console.log("Connection open ...");
ws.send("Hello WebSockets!");
};
ws.onmessage = function(evt) {
console.log( "Received Message: " + evt.data);
ws.close();
};
ws.onclose = function(evt) {
console.log("Connection closed.");
};
Websocket 聊天室
- ⽀持用户加入聊天室,对应到 Websocket 技术就是建⽴立连接
onopen
- ⽀持用户退出聊天室,对应到 Websocket 技术就是关闭连接
onclose
- ⽀持用户在聊天室发送消息,对应到 Websocket 技术就是调用
onmessage
发送消息 - 支持异常时提示,对应到 Websocket 技术
onerror
⻚面开发
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>chat room websocket</title>
<link rel="stylesheet" href="bootstrap.min.css">
<script src="jquery-3.2.1.min.js" ></script>
</head>
<body class="container" style="width: 60%">
<div class="form-group" ></br>
<h5>聊天室</h5>
<textarea id="message_content" class="form-control" readonly="readonly" cols="50"
rows="10"></textarea>
</div>
<div class="form-group" >
<label for="in_user_name">⽤用户姓名 </label>
<input id="in_user_name" value="" class="form-control" /></br>
<button id="user_join" class="btn btn-success" >加⼊入聊天室</button>
<button id="user_exit" class="btn btn-warning" >离开聊天室</button>
</div>
<div class="form-group" >
<label for="in_room_msg" >群发消息 </label>
<input id="in_room_msg" value="" class="form-control" /></br>
<button id="user_send_all" class="btn btn-info" >发送消息</button>
</div>
</body>
</html>
接下来在页面添加 WebSocket 通讯代码:
<script type="text/javascript">
$(document).ready(function(){
var urlPrefix ='ws://localhost:8080/chat-room/';
var ws = null;
$('#user_join').click(function(){
var username = $('#in_user_name').val();
var url = urlPrefix + username;
ws = new WebSocket(url);
ws.onopen = function () {
console.log("建⽴立 websocket 连接...");
};
ws.onmessage = function(event){
//服务端发送的消息
$('#message_content').append(event.data+'\n');
};
ws.onclose = function(){
$('#message_content').append('⽤用户['+username+'] 已经离开聊天室!');
console.log("关闭 websocket 连接...");
}
});
//客户端发送消息到服务器器
$('#user_send_all').click(function(){
var msg = $('#in_room_msg').val();
if(ws){
ws.send(msg);
}
});
// 退出聊天室
$('#user_exit').click(function(){
if(ws){
ws.close();
}
});
})
</script>
服务端开发
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
启动类
启动类需要添加 @EnableWebSocket
开启 WebSocket 功能。
@EnableWebSocket
@SpringBootApplication
public class WebSocketApplication {
public static void main(String[] args) {
SpringApplication.run(WebSocketApplication.class, args);
}
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
请求接收
在创建服务端消息接收功能之前,我们先创建一个 WebSocketUtils
⼯具类,⽤来存储聊天室在线的用户信息,以及发送消息的功能。首先定义一个全局变量ONLINE_USER_SESSIONS
用来存储在线⽤户,使⽤
ConcurrentHashMap
提升高并发时效率。
public static final Map<String, Session> ONLINE_USER_SESSIONS = new ConcurrentHashMap<>
();
封装消息发送⽅法,在发送之前首先判单⽤户是否存在再进行发送:
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) {
logger.error("sendMessage IOException ",e);
}
}
聊天室的消息是所有在线用户可见,因此每次消息的触发实际上是遍历所有在线用户,给每个在线⽤户发送消息。
public static void sendMessageAll(String message) {
ONLINE_USER_SESSIONS.forEach((sessionId, session) -> sendMessage(session, message))
;
}
这样我们在创建 ChatRoomServerEndpoint
类的时候就可以直接将⼯具类的方法和全局变量量导入:
import static com.neo.utils.WebSocketUtils.ONLINE_USER_SESSIONS;
import static com.neo.utils.WebSocketUtils.sendMessageAll;
接收类上需要添加 @ServerEndpoint("url")
代表监听此地址的 WebSocket 信息
@RestController
@ServerEndpoint("/chat-room/{username}")
public class ChatRoomServerEndpoint {
}
用户登录聊天室时,将⽤户信息添加到 ONLINE_USER_SESSIONS
中,同时通知聊天室中的人。
@OnOpen
public void openSession(@PathParam("username") String username, Session session) {
ONLINE_USER_SESSIONS.put(username, session);
String message = "欢迎用户[" + username + "] 来到聊天室!";
logger.info("用户登录:"+message);
sendMessageAll(message);
}
当聊天室某个用户发送消息时,将此消息同步给聊天室所有人。
@OnMessage
public void onMessage(@PathParam("username") String username, String message) {
logger.info("发送消息:"+message);
sendMessageAll("⽤户[" + username + "] : " + message);
}
当⽤户离开聊天室后,需要将⽤户信息从 ONLINE_USER_SESSIONS
移除,并且通知到在线的其他⽤户
@OnClose
public void onClose(@PathParam("username") String username, Session session) {
//当前的Session 移除
ONLINE_USER_SESSIONS.remove(username);
//并且通知其他⼈人当前用户已经离开聊天室了了
sendMessageAll("⽤户[" + username + "] 已经离开聊天室了了!");
try {
session.close();
} catch (IOException e) {
logger.error("onClose error",e);
}
}
其中,@OnClose
监听用户断开连接事件。
OnError
public void onError(Session session, Throwable throwable) {
try {
session.close();
} catch (IOException e) {
logger.error("onError excepiton",e);
}
logger.info("Throwable msg "+throwable.getMessage());
}