说一下实用springboot搭建一个简单的websocket 的实时推送应用

websocket是什么

WebSocket是一种在单个TCP连接上进行全双工通信的协议

我们以前用的http协议只能单向的浏览器给服务器发请求,然后服务器再去相应返回数据。

websocket呢就是可以服务器主动给浏览器发数据

优点

较少的控制开销

更强的实时性

保持连接状态

更好的二进制的支持

支持扩展

更换的压缩效果

pom文件

springboot项目的话只需要下面这个依赖就可以了

  <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

springboot的项目搭建

这里看我上一篇文章就不在多说了

我们需要去注入ServerEndpointExporter

这是一个检测类型的bean 检测带注释@ServerEndpoint的bean并注册它们

稍后我们可以讲一下@configuration @bean 和@component @autowired 等注解

  package com.gzh;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
* @Configuration
* @bean
* 表示给spring容器注入bean 他们两个一般一起使用
* 他们和@component的区别是他们使用了动态代理cglib 所以每次调用都会返回同一个实例
*/
@Configuration
public class WebSocketConfig {

/**
* 给spring容器注入这个ServerEndpointExporter对象
* 相当于xml:
* <beans>
* <bean id="serverEndpointExporter" class="org.springframework.web.socket.server.standard.ServerEndpointExporter"/>
* </beans>
*
* 检测所有带有@serverEndpoint注解的bean并注册他们。
* @return
*/
@Bean
public ServerEndpointExporter serverEndpointExporter(){
System.out.println("我被注入了");
return new ServerEndpointExporter();
}
}

下面使我们的websocket类

下面一共写了五种方法

建立通信

关闭通信

接收消息

发送消息

异常

  package com.gzh;

import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

/**
* @Component 将类注入到容器
* @ServerEndpoint 前端通过这个url进行访问通信 建立连接
*/
@ServerEndpoint("/websocket")
@Component
public class MyWebSocket {

//存放websocket 的线程安全的无序的集合
private static CopyOnWriteArraySet<MyWebSocket> websocket = new CopyOnWriteArraySet<MyWebSocket>();

private Session session;

public static CopyOnWriteArraySet<MyWebSocket> getWebsocket() {
return websocket;
}

public static void setWebsocket(CopyOnWriteArraySet<MyWebSocket> websocket) {
MyWebSocket.websocket = websocket;
}

public Session getSession() {
return session;
}

public void setSession(Session session) {
this.session = session;
}

/**
* 连接建立成功调用的方法
* */
@OnOpen
public void onOpen(Session session) {
this.session = session;
websocket.add(this); //加入set中
// addOnlineCount(); //在线数加1
System.out.println("进入onOpen方法");
try {
sendMessage("连接已建立成功.");
} catch (Exception e) {
System.out.println("IO异常");
}
}


/**
* 关闭通信连接
* @param session
*/
@OnClose
public void onClose(Session session){
//关闭连接后将此socket删除
websocket.remove(this);
System.out.println("进入onClose方法");
}

/**
* 获取客户端发来的信息
*/
@OnMessage
public void onMessage(String message){
System.out.println("进入onMessage方法; message = " + message);
}

/**
* 给客户端推送信息
*/
public void sendMessage(String message) throws IOException {
System.out.println("进入sendMessage方法");
this.session.getBasicRemote().sendText(message);
}

/**
* 异常方法
*/
@OnError
public void onError(Session session, Throwable error){
System.out.println("进入error方法");
error.printStackTrace();
}

}
 

这里呢我们稍微分析下这个方法里面的东西

  • 注解@ServerEndpoint

  package javax.websocket.server;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.websocket.Decoder;
import javax.websocket.Encoder;
import javax.websocket.server.ServerEndpointConfig.Configurator;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface ServerEndpoint {
//注释类应映射到的URI或URI模板(必写的参数)。
String value(); //子协议
String[] subprotocols() default {};

//解码器 输入websocket消息 输出java对象
Class<? extends Decoder>[] decoders() default {};

//编码器 输入java对象 输出websocket
Class<? extends Encoder>[] encoders() default {};

//配置器
Class<? extends Configurator> configurator() default Configurator.class;
}
  • CopyOnWriteArraySet(看完就知道我们为什么要用它了)

    • java.util.concurrent包下 俗称 JUC

    • 线程安全的无序的集合,可以将它理解成线程安全的HashSet

    • Set:不可重复,检索元素效率低下,删除和插入效率高,

    • List:可重复,和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低

  • @onopen @onclose等注解

    • 和前台websocket.onerror等方法相对应

我们的主方法执行服务

这里我们每三秒启动个线程

package com.gzh;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* @SpringBootApplication 标注一个主程序,说明这是一个springboot应用
*/
@SpringBootApplication
public class SpringBootMainApplication {

//编写主程序方法
public static void main(String[] args) {
SpringApplication.run(SpringBootMainApplication.class);

for (int i = 0; i <= 10 ; i++){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
}

}
}

线程类

在线程的run方法里让他去发送消息

  package com.gzh;

import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

public class MyThread implements Runnable{

@Override
public void run() {

CopyOnWriteArraySet<MyWebSocket> websocket = MyWebSocket.getWebsocket();
for (MyWebSocket myWebSocket : websocket) {
try {
myWebSocket.sendMessage("我要发消息了"+ Math.random());
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

html前端

这里我们只需要更改我们的地址路径就即可

<!DOCTYPE HTML>
<html>
<head>
<title>My WebSocket</title>
</head>

<body>
Welcome<br/>
<input id="text" type="text" /><button onclick="send()">Send</button> <button onclick="closeWebSocket()">Close</button>
<div id="message">
</div>
</body>

<script type="text/javascript">
var websocket = null;

//判断当前浏览器是否支持WebSocket ,主要此处要更换为自己的地址
if('WebSocket' in window){
websocket = new WebSocket("ws://localhost:8080/websocket");
}
else{
alert('Not support websocket')
}

//连接发生错误的回调方法
websocket.onerror = function(){
setMessageInnerHTML("error");
};

//连接成功建立的回调方法
websocket.onopen = function(event){
setMessageInnerHTML("open");
}

//接收到消息的回调方法
websocket.onmessage = function(event){
setMessageInnerHTML(event.data);
}

//连接关闭的回调方法
websocket.onclose = function(){
setMessageInnerHTML("close");
}

//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function(){
websocket.close();
}

//将消息显示在网页上
function setMessageInnerHTML(innerHTML){
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}

//关闭连接
function closeWebSocket(){
websocket.close();
}

//发送消息
function send(){
var message = document.getElementById('text').value;
websocket.send(message);
}
</script>
</html>

这里说一下我们的路径 ws 我们一般会看到wss 什么时候用哪个呢?

1.Firefox环境下https不能使用ws连接

2.chrome内核版本号低于50的浏览器是不允许https下使用ws链接

3.Firefox环境下https下使用wss链接需要安装证书

所以我们可以对这部分浏览器做个处理

 let protocol = location.protocol === 'https'
? 'wss://localhost:8888'
: 'ws://localhost:8889';
new WebSocket(protocol);
以上就是springboot下实现简单的websocket的应用

springboot搭建一个简单的websocket的实时推送应用的更多相关文章

  1. 【SpingBoot】 测试如何使用SpringBoot搭建一个简单后台1

    很久没写博客了,最近接到一个组内的测试开发任务是做一个使用SpringBoot 开发一个后台程序(还未完成),特写感想记录一下 1. 为什么选择SpringBoot ? 首先是目前很多公司的后台还是J ...

  2. 使用SpringBoot搭建一个简单的web工程

    最近在学习SpringBoot,想写在博客园上记录一下,如有错误之处还望指出. 首先创建一个maven工程,不用勾选骨架. 在pom.xml文件中添加如下内容,使工程变成Springboot应用. & ...

  3. 超详细,新手都能看懂 !使用SpringBoot+Dubbo 搭建一个简单的分布式服务

    来自:JavaGuide Github 地址:https://github.com/Snailclimb/springboot-integration-examples 目录: 使用 SpringBo ...

  4. 使用 SpringBoot+Dubbo 搭建一个简单分布式服务

    实战之前,先来看几个重要的概念 开始实战之前,我们先来简单的了解一下这样几个概念:Dubbo.RPC.分布式.由于本文的目的是带大家使用SpringBoot+Dubbo 搭建一个简单的分布式服务,所以 ...

  5. 用 Go 编写一个简单的 WebSocket 推送服务

    用 Go 编写一个简单的 WebSocket 推送服务 本文中代码可以在 github.com/alfred-zhong/wserver 获取. 背景 最近拿到需求要在网页上展示报警信息.以往报警信息 ...

  6. 使用SignalR ASP.NET Core来简单实现一个后台实时推送数据给Echarts展示图表的功能

    什么是 SignalR ASP.NET Core ASP.NET Core SignalR 是一种开放源代码库,可简化将实时 web 功能添加到应用程序的功能. 实时 web 功能使服务器端代码可以立 ...

  7. 【原创】node+express+socket搭建一个实时推送应用

    技术背景 Web领域的实时推送技术,也被称作Realtime技术.这种技术要达到的目的是让用户不需要刷新浏览器就可以获得实时更新. 应用场景: 监控系统:后台硬件热插拔.LED.温度.电压发生变化 即 ...

  8. 用nodejs搭建一个简单的服务器

    使用nodejs搭建一个简单的服务器 nodejs优点:性能高(读写文件) 数据操作能力强 官网:www.nodejs.org 验证是否安装成功:cmd命令行中输入node -v 如果显示版本号表示安 ...

  9. 初学Node(六)搭建一个简单的服务器

    搭建一个简单的服务器 通过下面的代码可以搭建一个简单的服务器: var http = require("http"); http.createServer(function(req ...

随机推荐

  1. 语义分割学习之SegNet的C++编译

    Abstract 安装好Segnet并使用Python进行训练和测试之后,考虑项目的应用,需要在C++的工程环境下进行继续开发,所以这里的主要内容是用C++建立工程,使用相应的数据集和权重参数文件进行 ...

  2. [LC]88题 Merge Sorted Array (合并两个有序数组 )

    ①英文题目 Given two sorted integer arrays nums1 and nums2, merge nums2 into nums1 as one sorted array. N ...

  3. SVN积极拒绝解决办法

    出现以上情况多数为Linux里面的svn自启动没有设置好,一般是自启文件被废弃了,就算在里面添加自启代码也无效,想要兼容旧版本使用这个文件,只需在root管理员模式下输入代码chmod +x /etc ...

  4. php相关知识(一)

    php是服务器端脚本语言.可以生成动态页面内容,可以对数据库中的数据库进行编辑. php变量以$符号开始,后面是变量名,变量名以字母或下划线开始,变量名不能包含空格,变量名区分大小写. php的数据类 ...

  5. python高阶函数的使用

    目录 python高阶函数的使用 1.map 2.reduce 3.filter 4.sorted 5.小结 python高阶函数的使用 1.map Python内建了map()函数,map()函数接 ...

  6. gdb(ddd,kdevelop等)调试ZeroIce开发的应用程序,中断信号引起的问题

    不作文,只记要点. 1.Ice::Application的程序框架默认对SIGHUP, SIGINT, SIGTERM进行处理.目的就是捕捉Ctrl+C发出信号有序地结束程序.这个功能扰乱了我们使用g ...

  7. 父组件向子组件传值时,值已经传过来却没有触发子组件的watch监听,解决~

    需求: 父组件像封装的子组件传值  (父组件属性传值,子组件props接受)   子组件接受后经过处理回显页面; 预想:子组件接受值 , 经过watch监听,在监听中处理数据,回显数据; 问题:子组件 ...

  8. java中的基本数据类型转换

    Java 中的 8 种基本数据类型,以及它们的占内存的容量大小和表示的范围,如下图所示: 重新温故了下原始数据类型,现在来解释下它们之间的转换关系. 自动类型转换 自动类型转换是指:数字表示范围小的数 ...

  9. Chapter 05—Advanced data management(Part 2)

    二. 控制流 statement:一个单独的R语句或者是一个复合的R语句: cond:条件表达式,为TRUE或FALSE: expr:数字或字符表达式: seq:数字或字符串的顺序. 1.循环语句:f ...

  10. (一)sync分析之为啥el-dialog中的visible需要使用.sync

    首先,笔者在使用element-ui 中的dialog组件时,发现visible属性在使用时需要添加.sync才生效,心中好奇,所以研究一下原理 我们先自己创建一个dialog组件,如下 当我们点击关 ...