文章目录
- [1. 消息推送常见方式](#1. 消息推送常见方式)
-
- [1.1 轮询 VS 长轮询](#1.1 轮询 VS 长轮询)
- [1.2 SSE(server-sent event)服务器发送事件](#1.2 SSE(server-sent event)服务器发送事件)
- [2. websocket介绍](#2. websocket介绍)
-
- [2.1 介绍](#2.1 介绍)
- [2.2 原理](#2.2 原理)
- [2.3 websoket API](#2.3 websoket API)
-
- [2.3.1 客户端【浏览器】API](#2.3.1 客户端【浏览器】API)
- [2.3.2 服务端API](#2.3.2 服务端API)
- [3. 代码实现](#3. 代码实现)
-
- [3.1 流程分析](#3.1 流程分析)
- [3.2 pom依赖](#3.2 pom依赖)
- [3.3 配置类](#3.3 配置类)
- [3.4 消息格式](#3.4 消息格式)
- [3.5 消息类](#3.5 消息类)
- [4. 前端代码(非视频源码)](#4. 前端代码(非视频源码))
参考视频
【后端&网络&大数据&数据库目录贴】
1. 消息推送常见方式
- 轮询
- 长轮询
- websocket
- SSE
1.1 轮询 VS 长轮询
1.2 SSE(server-sent event)服务器发送事件
- SSE在服务器和客户端之间打开一个单向通道
- 服务端响应的不再是一次性的数据包,而是text/event-stream类型的数据流信息
- 服务器有数据变更时将数据流式传输到客户端
2. websocket介绍
2.1 介绍
websocket是一种基于TCP链接上进行
全双工
通信的协议
- 全双工:允许数据在两个方向上
同时
传输。(TCP
是全双工,HTTP
是基于TCP的)- 半双工:允许数据在两个方向上传输,但是
同一个时间段
内只允许一个方向上传输。
2.2 原理
ws://localhost?chat
依然是http协议Connection:Upgrade
和Upgrade: websocket
表明连接升级响应码 101
说明已经切换成功
2.3 websoket API
2.3.1 客户端【浏览器】API
- websocket对象创建
js
let ws=new WebSocket(URL);
- websocket对象相关事件
- websocket对象提供的方法
2.3.2 服务端API
- 服务端如何接收客户端发送的数据呢?
- 服务端如何推送数据给客户端呢?
3. 代码实现
3.1 流程分析
3.2 pom依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
3.3 配置类
3.4 消息格式
3.5 消息类
java
@ServerEndpoint(value = "/chat", configurator = GetHttpSessionConfigurator.class)
@Component
public class ChatEndpoint {
public static final Map<String, Session> sessionsMap = new ConcurrentHashMap<>();
private HttpSession httpSession;
/**
* 建立websocket连接后,被调用
*
* @param session
*/
@OnOpen
public void onOpen(Session session, EndpointConfig config) {
//1.将session进行保存
this.httpSession = (HttpSession) config.getUserProperties().get("httpSession");
String userName = (String) httpSession.getAttribute("userName");
ChatEndpoint.sessionsMap.put(userName, session);
//2. 广播消息,需要将登录的所有的用户推送给所有用户
String sendMessage = MessageUtil.sendMessage(true, null, userName + "上线");
boardcast(sendMessage);
}
/**
* 广播消息
*/
private void boardcast(String message) {
// 遍历 map 集合
Set<Map.Entry<String, Session>> entries = ChatEndpoint.sessionsMap.entrySet();
for (Map.Entry<String, Session> entry : entries) {
Session session = entry.getValue();
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
// 记录日志
}
}
}
@OnMessage
public void onMessage(String message) {
try {
String fromName = (String) this.httpSession.getAttribute("userName");
// 将消息推送给指定的用户 message : {"toName":"张三","message":"你好"}
ClientMessage clientMessage = JSON.parseObject(message, ClientMessage.class);
Session session = ChatEndpoint.sessionsMap.get(clientMessage.getToName());
String sendMessage = MessageUtil.sendMessage(false, fromName, clientMessage.getMessage());
session.getBasicRemote().sendText(sendMessage);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 当websocket连接断开时,此方法会被处罚
*/
@OnClose
public void onClose(Session session) {
// 从在线用户集合中剔除断开连接的用户
String userName = (String) this.httpSession.getAttribute("userName");
ChatEndpoint.sessionsMap.remove(userName);
// 通知其他用户当前用户下线
String sendMessage = MessageUtil.sendMessage(true, null, userName + "上线");
boardcast(sendMessage);
}
}
4. 前端代码(非视频源码)
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/jquery.min.js"></script>
</head>
<body>
<p>【fromUserId】:<div><input id="userId" name="userId" type="text" value="10"></div>
<p>【toUserId】:<div><input id="toUserId" name="toUserId" type="text" value="20"></div>
<p>【发送的消息】:<div><input id="contentText" name="contentText" type="text" value="hello websocket"></div>
<p>【收到的内容】:<div><textarea id="receText"></textarea></div>
<button id="openSocket">开启socket</button>
<button id="sendMessage">发送消息</button>
<button id="closeWs">关闭连接</button>
<script>
$(function (){
console.log('加兹阿勒')
const urlParams = new URLSearchParams(window.location.search);
console.log(urlParams)
var fromUserId = urlParams.get('fromUserId');
$('#userId').val(fromUserId)
})
var ws ;
function openSocket() {
if(typeof(WebSocket) == "undefined") {
console.log("您的浏览器不支持WebSocket");
}else{
console.log("您的浏览器支持WebSocket");
//实现化WebSocket对象,指定要连接的服务器地址与端口 建立连接
//等同于socket = new WebSocket("ws://localhost:8888/xxxx/im/25");
//var socketUrl="${request.contextPath}/im/"+$("#userId").val();
var socketUrl="http://localhost:8081/chat";
socketUrl=socketUrl.replace("https","wss").replace("http","ws");
console.log(socketUrl);
if(ws !=null){
ws .close();
ws =null;
}
ws = new WebSocket(socketUrl);
// 当 WebSocket 成功建立连接时触发
ws.onopen = function(event) {
console.log("WebSocket is connected now.");
// 在这里你可以发送一些初始消息到服务器
// ws.send("Hello, Server!");
};
// 当接收到来自服务器的消息时触发
ws.onmessage = function(event) {
console.log("Received data from server: " + event.data);
// 处理从服务器接收到的数据
var parse = JSON.parse(event.data);
$('#receText').val(parse.message);
};
// 当 WebSocket 连接关闭时触发
ws.onclose = function(event) {
if (event.wasClean) {
console.log("WebSocket connection closed cleanly, code=" + event.code + " reason=" + event.reason);
} else {
// 例如,服务器进程被终止而关闭
console.log("WebSocket connection died");
}
};
// 当 WebSocket 连接出现错误时触发
ws.onerror = function(error) {
console.error("WebSocket Error: " + error);
};
}
}
function sendMessage() {
if(typeof(WebSocket) == "undefined") {
console.log("您的浏览器不支持WebSocket");
}else {
console.log("您的浏览器支持WebSocket");
console.log('{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+$("#contentText").val()+'"}');
ws.send('{"toName":"'+$("#toUserId").val()+'","message":"'+$("#contentText").val()+'"}');
}
}
function closeWs(){
ws.close();
}
$("#openSocket").on('click', openSocket);
$("#sendMessage").on('click',sendMessage);
$("#closeWs").on('click', closeWs);
</script>
</body>
</html>