你知道在springboot中如何使用WebSocket吗
一、背景
我们都知道 http 协议只能浏览器单方面向服务器发起请求获得响应,服务器不能主动向浏览器推送消息。想要实现浏览器的主动推送有两种主流实现方式:
- 轮询:缺点很多,但是实现简单
- websocket:在浏览器和服务器之间建立 tcp 连接,实现全双工通信
springboot 使用 websocket 有两种方式,一种是实现简单的 websocket,另外一种是实现STOMP协议。这一篇实现简单的 websocket,STOMP 下一篇在讲。
注意:如下都是针对使用 springboot 内置容器
二、实现
1、依赖引入
要使用 websocket 关键是@ServerEndpoint这个注解,该注解是 javaee 标准中的注解,tomcat7 及以上已经实现了,如果使用传统方法将 war 包部署到 tomcat 中,只需要引入如下 javaee 标准依赖即可:
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
如使用 springboot 内置容器,无需引入,springboot 已经做了包含。我们只需引入如下依赖即可:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>1.5.3.RELEASE</version>
<type>pom</type>
</dependency>
2、注入 Bean
首先注入一个ServerEndpointExporterBean,该 Bean 会自动注册使用@ServerEndpoint 注解申明的 websocket endpoint。代码如下:
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
3、申明 endpoint
建立MyWebSocket.java类,在该类中处理 websocket 逻辑
@ServerEndpoint(value = "/websocket") //接受websocket请求路径
@Component //注册到spring容器中
public class MyWebSocket {
//保存所有在线socket连接
private static Map<String,MyWebSocket> webSocketMap = new LinkedHashMap<>();
//记录当前在线数目
private static int count=0;
//当前连接(每个websocket连入都会创建一个MyWebSocket实例
private Session session;
private Logger log = LoggerFactory.getLogger(this.getClass());
//处理连接建立
@OnOpen
public void onOpen(Session session){
this.session=session;
webSocketMap.put(session.getId(),this);
addCount();
log.info("新的连接加入:{}",session.getId());
}
//接受消息
@OnMessage
public void onMessage(String message,Session session){
log.info("收到客户端{}消息:{}",session.getId(),message);
try{
this.sendMessage("收到消息:"+message);
}catch (Exception e){
e.printStackTrace();
}
}
//处理错误
@OnError
public void onError(Throwable error,Session session){
log.info("发生错误{},{}",session.getId(),error.getMessage());
}
//处理连接关闭
@OnClose
public void onClose(){
webSocketMap.remove(this.session.getId());
reduceCount();
log.info("连接关闭:{}",this.session.getId());
}
//群发消息
//发送消息
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
//广播消息
public static void broadcast(){
MyWebSocket.webSocketMap.forEach((k,v)->{
try{
v.sendMessage("这是一条测试广播");
}catch (Exception e){
}
});
}
//获取在线连接数目
public static int getCount(){
return count;
}
//操作count,使用synchronized确保线程安全
public static synchronized void addCount(){
MyWebSocket.count++;
}
public static synchronized void reduceCount(){
MyWebSocket.count--;
}
}
4、客户的实现
客户端使用 h5 原生 websocket,部分浏览器可能不支持。代码如下:
<html>
<head>
<title>websocket测试</title>
<meta charset="utf-8" />
</head>
<body>
<button onclick="sendMessage()">测试</button>
<script>
let socket = new WebSocket("ws://localhost:8080/websocket");
socket.onerror = err => {
console.log(err);
};
socket.onopen = event => {
console.log(event);
};
socket.onmessage = mess => {
console.log(mess);
};
socket.onclose = () => {
console.log("连接关闭");
};
function sendMessage() {
if (socket.readyState === 1) socket.send("这是一个测试数据");
else alert("尚未建立websocket连接");
}
</script>
</body>
</html>
三、测试
建立一个 controller 测试群发,代码如下:
@RestController
public class HomeController {
@GetMapping("/broadcast")
public void broadcast(){
MyWebSocket.broadcast();
}
}
然后打开上面的 html,可以看到浏览器和服务器都输出连接成功的信息:
浏览器:
Event {isTrusted: true, type: "open", target: WebSocket, currentTarget: WebSocket, eventPhase: 2, …}
服务端:
2018-08-01 14:05:34.727 INFO 12708 --- [nio-8080-exec-1] com.fxb.h5websocket.MyWebSocket : 新的连接加入:0
点击测试按钮,可在服务端看到如下输出:
2018-08-01 15:00:34.644 INFO 12708 --- [nio-8080-exec-6] com.fxb.h5websocket.MyWebSocket : 收到客户端2消息:这是一个测试数据
再次打开 html 页面,这样就有两个 websocket 客户端,然后在浏览器访问localhost:8080/broadcast测试群发功能,每个客户端都会输出如下信息:
MessageEvent {isTrusted: true, data: "这是一条测试广播", origin: "ws://localhost:8080", lastEventId: "", source: null, …}
源码可在 github 下载 上下载,记得点赞,star 哦
本文原创发布于:https://www.tapme.top/blog/detail/2018-08-25-10-38
你知道在springboot中如何使用WebSocket吗的更多相关文章
- Springboot中的事件Event
事件Event作为一种常用的线程通讯工具,在Springboot中可以方便地提供开发者进行线程交互. 1.事件定义 1 import org.springframework.context.Appli ...
- springBoot于tomcat7搭建websocket服务
最近在项目中需要服务端进行推送服务,于是决定使用webSocket进行双向通讯.刚开始觉得应该没问题,参考网上的博客进行开发配置.我们的开发环境使用的是springBoot.tomcat7.servl ...
- springboot中redis取缓存类型转换异常
异常如下: [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested ...
- SpringBoot中yaml配置对象
转载请在页首注明作者与出处 一:前言 YAML可以代替传统的xx.properties文件,但是它支持声明map,数组,list,字符串,boolean值,数值,NULL,日期,基本满足开发过程中的所 ...
- 如何在SpringBoot中使用JSP ?但强烈不推荐,果断改Themeleaf吧
做WEB项目,一定都用过JSP这个大牌.Spring MVC里面也可以很方便的将JSP与一个View关联起来,使用还是非常方便的.当你从一个传统的Spring MVC项目转入一个Spring Boot ...
- springboot中swaggerUI的使用
demo地址:demo-swagger-springboot springboot中swaggerUI的使用 1.pom文件中添加swagger依赖 2.从github项目中下载swaggerUI 然 ...
- spring-boot+mybatis开发实战:如何在spring-boot中使用myabtis持久层框架
前言: 本项目基于maven构建,使用mybatis-spring-boot作为spring-boot项目的持久层框架 spring-boot中使用mybatis持久层框架与原spring项目使用方式 ...
- 由浅入深学习springboot中使用redis
很多时候,我们会在springboot中配置redis,但是就那么几个配置就配好了,没办法知道为什么,这里就详细的讲解一下 这里假设已经成功创建了一个springboot项目. redis连接工厂类 ...
- Springboot中使用AOP统一处理Web请求日志
title: Springboot中使用AOP统一处理Web请求日志 date: 2017-04-26 16:30:48 tags: ['Spring Boot','AOP'] categories: ...
随机推荐
- javaEE(11)_事务处理
一.事务的概念 •事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功. •例如:A——B转帐,对应于如下两条sql语句 update from account set mon ...
- java在线聊天项目1.1版 ——开启多个客户端,分别实现注册和登录功能,使用客户端与服务端信息request机制,重构线程,将单独的登录和注册线程合并
实现效果图: eclipse项目中初步整合之前的各个客户端和服务端的窗口与工具类,效果如下图: 已将注册服务器线程RegServer功能放到LoginServer中,使用客户端与服务端的request ...
- ios调试技巧
一.概述1.掌握调试技巧,调试技术最基本,最重要的调试手段包括:单步跟踪,断点,变量观察等.单步跟踪(Step)所谓单步跟踪是指一行一行地执行程序,每执行一行语句后就停下来等待指示,这样你就能够仔细了 ...
- web资源持续更新----20150213
响应式设计创意收集网站:http://mediaqueri.es css禅意花园 http://www.csszengarden.com/
- css兼容处理-hack
浏览器兼容之旅的第二站:各浏览器的Hack写法 Browser CSS Hacks Moving IE specific CSS into @media blocks Detecting browse ...
- laravel中的scope作用域
laravel中在模板中处理(属于不属于)的数据(增删改查),引入了scope来处理 也就是在模板定义方法中,加上前缀scope laravel中要求在定义的方法scope后面跟的字母要大写 后面那我 ...
- int main(int argc,char *argv[])的具体含义
int main(int argc,char * argv[]) argv为指针的指针 argc为整数 char **argv or: char *argv[] or: char argv[][] m ...
- JavaScript正则表达式-字符
普通字符 大小写字母.数字.其他任何符号. 转义字符 转义字符 含义 \f 换页符 \n 换行符 \r 回车符 \t 制表符 \b 退格符 \o 空字符 \xnn 由十六进制数nn指定的ASCII码对 ...
- oracle11gR2下scott用户以及表的建立
目录 oracle11gR2下scott用户以及表的建立 找到系统带的sql文件(utlsample.sql) 根据SQL的内容操作 新建用户并授权 scott登录 表操作 查询表(使用pl/sql) ...
- MySQL 待解决死锁
官方文档:https://dev.mysql.com/doc/refman/5.6/en/innodb-locks-set.html 线上出现一个死锁现象,信息显示的是两条对同一个表的不同记录的upd ...