目录
编写WebSocketServer及WebSocketUtil工具类
WebSocket概述
Websocket是一种在单个TCP连接上进行全双工通信的协议,允许客户端和服务器之间进行实时数据传输。与传统的HTTP请求-响应模型不同,WebSocket建立了一种持久连接,可以在客户端和服务器之间实时地推送数据,非常适合需要实时更新的应用场景。Websockt特点如下:
- 建立在 TCP 协议之上,服务器端的实现比较容易。
- 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
- 数据格式比较轻量,性能开销小,通信高效。
- 可以发送文本,也可以发送二进制数据。
- 没有同源限制,客户端可以与任意服务器通信。
- 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
SpringBoot整合WebSocket步骤
添加websocket依赖
XML
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
启用WebSocket的支持
java
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Component
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
编写WebSocketServer及WebSocketUtil工具类
核心:@ServerEndpoint("/webSocket/{userId}") ,里面实现@OnOpen开启连接,@onClose关闭连接,@onMessage接收消息等方法。
java
import com.haokai.taxielder.utils.WebSocketUtil;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* websocket接口处理类
*/
@Component
@ServerEndpoint(value = "/webSocket/{userId}")
@Slf4j
public class WebSocket {
/**
* 连接事件,加入注解
* @param userId
* @param session
*/
@OnOpen
public void onOpen(@PathParam(value = "userId") String userId, Session session) {
log.info("用户[{}]加入websocket连接!", userId);
// 添加到session的映射关系中
WebSocketUtil.addSession(userId, session);
}
/**
* 连接事件,加入注解
* 用户断开链接
*
* @param userId
* @param session
*/
@OnClose
public void onClose(@PathParam(value = "userId") String userId, Session session) {
log.info("用户[{}]退出了websocket连接...", userId);
// 删除映射关系
WebSocketUtil.removeSession(userId);
}
/**
* 当接收到用户上传的消息
*
* @param userId
* @param session
*/
@OnMessage
public void onMessage(@PathParam(value = "userId") String userId, Session session, String message) {
log.info("接收到用户[{}]的消息:{}", userId, message);
}
/**
* 处理用户活连接异常
*
* @param session
* @param throwable
*/
@OnError
public void onError(Session session, Throwable throwable) {
try {
session.close();
} catch (IOException e) {
e.printStackTrace();
}
throwable.printStackTrace();
}
}
java
import jakarta.websocket.RemoteEndpoint;
import jakarta.websocket.Session;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class WebSocketUtil {
/**
* 记录当前在线的Session
*/
private static final Map<String, Session> ONLINE_SESSION = new ConcurrentHashMap<>();
/**
* 添加session
* @param userId
* @param session
*/
public static void addSession(String userId, Session session){
// 此处只允许一个用户的session链接。一个用户的多个连接,我们视为无效。
ONLINE_SESSION.putIfAbsent ( userId, session );
}
/**
* 关闭session
* @param userId
*/
public static void removeSession(String userId){
ONLINE_SESSION.remove ( userId );
}
/**
* 给单个用户推送消息
* @param session
* @param message
*/
public static void sendMessage(Session session, String message){
if(session == null){
return;
}
// 异步发送
RemoteEndpoint.Async async = session.getAsyncRemote ();
async.sendText ( message );
}
/**
* 向所有在线人发送消息
* @param message
*/
public static void sendMessageForAll(String message) {
//jdk8 新方法
ONLINE_SESSION.forEach((sessionId, session) -> sendMessage(session, message));
}
}
前端Demo界面编写
html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>websocket client</title>
<script type="text/javascript">
var ws = null;
function WebSocketTest()
{
if ("WebSocket" in window)
{
alert("浏览器支持 WebSocket!");
// 打开一个 web socket
var name = document.getElementById("name").value;
ws = new WebSocket("ws://localhost:8081/elder-api/webSocket/" + name);
//构件一个发送消息框
var context = '<div class="sendMsg">\n' +
' 输入发送内容:<input id="sendWebMsg" />' +
' <button onclick="javascript:sendMsgToWS()">发送</button>' +
' </div>';
document.getElementById("sendDiv").innerHTML = context;
ws.onmessage = function (evt)
{
var received_msg = evt.data;
alert("接收到数据:" + received_msg);
};
ws.onclose = function()
{
// 关闭 websocket
alert("连接已关闭...");
};
}
else
{
// 浏览器不支持 WebSocket
alert("浏览器不支持 WebSocket!");
}
}
function sendMsgToWS(){
if(ws != null){
var msg = document.getElementById("sendWebMsg").value;
ws.send(msg);
}
}
</script>
</head>
<body>
<div id="sse">
名称:<input id="name" />
<button onclick="javascript:WebSocketTest()">连接 WebSocket</button>
</div>
<div id="sendDiv">
</div>
</body>
</html>
测试
当后台实时发生了变动,产生了消息,消息广播到前台所有连接用户
java
private void broadcastMessage(ScheduleOrderMsg scheduleOrderMsg) {
ScheduleOrderMsgDto scheduleOrderMsgDto = new ScheduleOrderMsgDto();
BeanUtil.copyProperties(scheduleOrderMsg, scheduleOrderMsgDto);
//设置ID
scheduleOrderMsgDto.setMsgId(scheduleOrderMsg.getMsgId() + "");
// 获取订单信息
Order scheduleOrder = orderMapper.getByOrderId(scheduleOrderMsg.getScheduleOrderId());
if(scheduleOrder != null) {
BeanUtil.copyProperties(scheduleOrder, scheduleOrderMsgDto);
}
//序列化
JSONConfig jsonConfig = JSONConfig.create();
jsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
String scheduleOrderMsgJson = JSONUtil.toJsonStr(scheduleOrderMsgDto,jsonConfig);
WebSocketUtil.sendMessageForAll(scheduleOrderMsgJson);
}