网站首页 > 技术文章 正文
什么是WebSocket?
因为 HTTP 协议有一个缺陷:通信只能由客户端发起,HTTP 协议做不到服务器主动向客户端推送信息。
WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端
快速上手
<!-- websocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
WebSocketConfig启用WebSocket的支持
/**
* 开启WebSocket支持
* @author zhucan
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
WebSocketServer
因为WebSocket是类似客户端服务端的形式(采用ws协议),那么这里的WebSocketServer其实就相当于一个ws协议的Controller
直接@ServerEndpoint("/imserver/{userId}") 、@Component启用即可,然后在里面实现@OnOpen开启连接,@onClose关闭连接,@onMessage接收消息等方法。
新建一个ConcurrentHashMap webSocketMap 用于接收当前userId的WebSocket,方便IM之间对userId进行推送消息。单机版实现到这里就可以。
集群版(多个ws节点)还需要借助mysql或者redis等进行处理,改造对应的sendMessage方法即可。
/**
* @author
*/
@ServerEndpoint("/imserver/{userId}")
@Component
public class WebSocketService {
/**
* concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
*/
private static ConcurrentHashMap<String, WebSocketService> webSocketMap = new ConcurrentHashMap<>();
/**
* 与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
private Session session;
/**
* 接收userId
*/
private String userId = "";
/**
* 连接建立成功调用的方法
* <p>
* 1.用map存 每个客户端对应的MyWebSocket对象
*/
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId) {
this.session = session;
this.userId = userId;
if (webSocketMap.containsKey(userId)) {
webSocketMap.remove(userId);
webSocketMap.put(userId, this);
//加入set中
} else {
webSocketMap.put(userId, this);
//加入set中
}
//服务端向客户端发送信息
sendMessage("用户" + userId + "上线了,当前在线人数为:" + webSocketMap.size() + "。");
}
/**
* 报错
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
error.printStackTrace();
}
/**
* 实现服务器推送到对应的客户端
*/
public void sendMessage(String message) {
try {
this.session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 自定义 指定的userId服务端向客户端发送消息
*/
public static void sendInfo(String message, String userId, String toUserId) {
//log.info("发送消息到:"+userId+",报文:"+message);
if (StringUtils.isNotBlank(toUserId) && webSocketMap.containsKey(toUserId)) {
webSocketMap.get(toUserId).sendMessage(message);
} else {
webSocketMap.get(userId).sendMessage(toUserId + "已经下线。");
}
}
/**
* 自定义关闭
*
* @param userId
*/
public static void close(String userId) {
if (webSocketMap.containsKey(userId)) {
webSocketMap.remove(userId);
}
}
/**
* 获取在线用户信息
*
* @return
*/
public static Map getOnlineUser() {
return webSocketMap;
}
}
controller 对外提供发送 关闭websocket方法
@RestController
public class DemoController {
@PostMapping("/push")
public ResponseEntity<String> pushToWeb(String message, String toUserId,String userId) throws IOException {
WebSocketService.sendInfo(message,userId,toUserId);
return ResponseEntity.ok("MSG SEND SUCCESS");
}
@GetMapping("/close")
public String close(String userId){
WebSocketService.close(userId);
return "ok";
}
@GetMapping("/getOnlineUser")
public Map getOnlineUser(){
return WebSocketService.getOnlineUser();
}
}
html 前端页面
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>websocket通讯</title>
</head>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
var socket;
function openSocket(flag) {
var socketUrl = "http://localhost:8449/imserver/" + $("#userId").val();
socketUrl = socketUrl.replace("https", "ws").replace("http", "ws");
if (socket != null) {
socket.close();
socket = null;
}
socket = new WebSocket(socketUrl);
//打开事件
socket.onopen = function () {
//获得消息事件
socket.onmessage = function (msg) {
$('#contain').html(msg.data);
};
//发生了错误事件
socket.onerror = function () {
alert("websocket发生了错误");
}
$("#userId").attr("disabled", true);
}
}
function sendMessage() {
$.ajax({
url:"http://localhost:8449/push",
type:"POST",
data:{
'message':$("#contentText").val(),
'toUserId':$("#toUserId").val(),
'userId':$("#userId").val()
}
})
}
function closeSocket() {
var userId =$("#userId").val()
$.ajax({
type:"get",
url:"http://localhost:8449/close?userId="+userId,
success:function(res) {
alert('111');
}
});
$("#userId").attr("disabled", false);
$('#contain').html("下线成功!");
}
</script>
<body>
<p>【内容】:<span id="contain"></span></p>
<p>【当前用户】:<div><input id="userId" name="userId" type="text" value="10"></div>
<p>【发送用户】:<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><button onclick="openSocket()">登录socket</button></div>
<p>【操作】:<div><button onclick="closeSocket()">关闭socket</button></div>
<p>【操作】:<div><button onclick="sendMessage()">发送消息</button></div>
</body>
</html>
- 上一篇: 前端面试被问到项目中的难点有哪些?
- 下一篇: 一文解读前端实现电子签名
猜你喜欢
- 2024-12-13 一文让你彻底搞懂 vuex,满满的干货
- 2024-12-13 vue前端通过Base64编码解码
- 2024-12-13 「前端进阶」高性能渲染十万条数据(虚拟列表)
- 2024-12-13 Web前端新人笔记之了解Jquery
- 2024-12-13 typeScript 学习笔记(二)——数据类型大全
- 2024-12-13 对于async和await的使用方式、作用效果不理解 ?没关系,看这篇
- 2024-12-13 一文解读前端实现电子签名
- 2024-12-13 前端面试被问到项目中的难点有哪些?
- 2024-12-13 js的15种循环遍历,你掌握了几种
- 2024-12-13 基于HTML5+tracking.js,刷脸支付的实现
你 发表评论:
欢迎- 500℃几个Oracle空值处理函数 oracle处理null值的函数
- 494℃Oracle分析函数之Lag和Lead()使用
- 493℃Oracle数据库的单、多行函数 oracle执行多个sql语句
- 481℃0497-如何将Kerberos的CDH6.1从Oracle JDK 1.8迁移至OpenJDK 1.8
- 472℃Oracle 12c PDB迁移(一) oracle迁移到oceanbase
- 468℃【数据统计分析】详解Oracle分组函数之CUBE
- 453℃Oracle有哪些常见的函数? oracle中常用的函数
- 448℃最佳实践 | 提效 47 倍,制造业生产 Oracle 迁移替换
- 最近发表
-
- Spring Boot跨域难题终结者:3种方案,从此告别CORS噩梦!
- 京东大佬问我,SpringBoot为什么会出现跨域问题?如何解决?
- 在 Spring Boot3 中轻松解决接口跨域访问问题
- 最常见五种跨域解决方案(常见跨域及其解决方案)
- Java Web开发中优雅应对跨域问题(java跨域问题解决办法)
- Spring Boot解决跨域最全指南:从入门到放弃?不,到根治!
- Spring Boot跨域问题终极解决方案:3种方案彻底告别CORS错误
- Spring Cloud 轻松解决跨域,别再乱用了
- Github 太狠了,居然把 "master" 干掉了
- IntelliJ IDEA 调试 Java 8,实在太香了
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端react (48)
- 前端aes加密 (58)
- 前端脚手架 (56)
- 前端md5加密 (54)
- 前端富文本编辑器 (47)
- 前端路由 (55)
- 前端数组 (65)
- 前端定时器 (47)
- Oracle RAC (73)
- oracle恢复 (76)
- oracle 删除表 (48)
- oracle 用户名 (74)
- oracle 工具 (55)
- oracle 内存 (50)
- oracle 导出表 (57)
- oracle 中文 (51)
- oracle链接 (47)
- oracle的函数 (57)
- 前端调试 (52)
- 前端登录页面 (48)
本文暂时没有评论,来添加一个吧(●'◡'●)