转载自:https://www.cnblogs.com/GoodHelper/p/7078381.html

一.WebSocket简单介绍

  随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了。近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通信,扩展了浏览器与服务端的通信功能,使服务端也能主动向客户端发送数据。

  我们知道,传统的HTTP协议是无状态的,每次请求(request)都要由客户端(如 浏览器)主动发起,服务端进行处理后返回response结果,而服务端很难主动向客户端发送数据;这种客户端是主动方,服务端是被动方的传统Web模式 对于信息变化不频繁的Web应用来说造成的麻烦较小,而对于涉及实时信息的Web应用却带来了很大的不便,如带有即时通信、实时数据、订阅推送等功能的应 用。在WebSocket规范提出之前,开发人员若要实现这些实时性较强的功能,经常会使用折衷的解决方法:轮询(polling)Comet技术。其实后者本质上也是一种轮询,只不过有所改进。

  轮询是最原始的实现实时Web应用的解决方案。轮询技术要求客户端以设定的时间间隔周期性地向服务端发送请求,频繁地查询是否有新的数据改动。明显地,这种方法会导致过多不必要的请求,浪费流量和服务器资源。

  Comet技术又可以分为长轮询流技术长轮询改进了上述的轮询技术,减小了无用的请求。它会为某些数据设定过期时间,当数据过期后才会向服务端发送请求;这种机制适合数据的改动不是特别频繁的情况。流技术通常是指客户端使用一个隐藏的窗口与服务端建立一个HTTP长连接,服务端会不断更新连接状态以保持HTTP长连接存活;这样的话,服务端就可以通过这条长连接主动将数据发送给客户端;流技术在大并发环境下,可能会考验到服务端的性能。

  这两种技术都是基于请求-应答模式,都不算是真正意义上的实时技术;它们的每一次请求、应答,都浪费了一定流量在相同的头部信息上,并且开发复杂度也较大。

  伴随着HTML5推出的WebSocket,真正实现了Web的实时通信,使B/S模式具备了C/S模式的实时通信能力。WebSocket的工作流程是这 样的:浏览器通过JavaScript向服务端发出建立WebSocket连接的请求,在WebSocket连接建立成功后,客户端和服务端就可以通过 TCP连接传输数据。因为WebSocket连接本质上是TCP连接,不需要每次传输都带上重复的头部数据,所以它的数据传输量比轮询和Comet技术小 了很多。本文不详细地介绍WebSocket规范,主要介绍下WebSocket在Java Web中的实现。

  JavaEE 7中出了JSR-356:Java API for WebSocket规范。不少Web容器,如Tomcat,Nginx,Jetty等都支持WebSocket。Tomcat从7.0.27开始支持 WebSocket,从7.0.47开始支持JSR-356,下面的Demo代码也是需要部署在Tomcat7.0.47以上的版本才能运行。

二.WebSocket优点

以前我们实现推送技术,用的都是轮询,在特点的时间间隔有浏览器自动发出请求,将服务器的消息主动的拉回来,在这种情况下,我们需要不断的向服务器发送请求,然而HTTP request 的header是非常长的,里面包含的数据可能只是一个很小的值,这样会占用很多的带宽和服务器资源。会占用大量的带宽和服务器资源。

WebSocket API最伟大之处在于服务器和客户端可以在给定的时间范围内的任意时刻,相互推送信息。在建立连接之后,服务器可以主动传送数据给客户端。

此外,服务器与客户端之间交换的标头信息很小。

WebSocket并不限于以Ajax(或XHR)方式通信,因为Ajax技术需要客户端发起请求,而WebSocket服务器和客户端可以彼此相互推送信息;

三.springBoot整合webSocket用例

1.引入websocket依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-websocket</artifactId>
  4. </dependency>

完整的pom.xml文件代码如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5.  
  6. <groupId>com.example</groupId>
  7. <artifactId>spring-boot-16</artifactId>
  8. <version>0.0.1-SNAPSHOT</version>
  9. <packaging>jar</packaging>
  10.  
  11. <name>spring-boot-16</name>
  12. <description>Demo project for Spring Boot</description>
  13.  
  14. <parent>
  15. <groupId>org.springframework.boot</groupId>
  16. <artifactId>spring-boot-starter-parent</artifactId>
  17. <version>1.5.3.RELEASE</version>
  18. <relativePath /> <!-- lookup parent from repository -->
  19. </parent>
  20.  
  21. <properties>
  22. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  23. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  24. <java.version>1.8</java.version>
  25. </properties>
  26.  
  27. <dependencies>
  28. <dependency>
  29. <groupId>org.springframework.boot</groupId>
  30. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  31. </dependency>
  32. <dependency>
  33. <groupId>org.springframework.boot</groupId>
  34. <artifactId>spring-boot-starter-web</artifactId>
  35. </dependency>
  36. <dependency>
  37. <groupId>org.springframework.boot</groupId>
  38. <artifactId>spring-boot-starter-websocket</artifactId>
  39. </dependency>
  40.  
  41. <dependency>
  42. <groupId>org.springframework.boot</groupId>
  43. <artifactId>spring-boot-devtools</artifactId>
  44. <scope>runtime</scope>
  45. </dependency>
  46. <dependency>
  47. <groupId>org.springframework.boot</groupId>
  48. <artifactId>spring-boot-starter-test</artifactId>
  49. <scope>test</scope>
  50. </dependency>
  51.  
  52. </dependencies>
  53.  
  54. <build>
  55. <plugins>
  56. <plugin>
  57. <groupId>org.springframework.boot</groupId>
  58. <artifactId>spring-boot-maven-plugin</artifactId>
  59. </plugin>
  60. </plugins>
  61. </build>
  62. </project>

2.1 @EnableWebSocketMessageBroker注解表示开启使用STOMP协议来传输基于代理的消息,Broker就是代理的意思。
2.2 registerStompEndpoints方法表示注册STOMP协议的节点,并指定映射的URL。
2.3 stompEndpointRegistry.addEndpoint("/endpointSang").withSockJS();这一行代码用来注册STOMP协议节点,同时指定使用SockJS协议。
2.4 configureMessageBroker方法用来配置消息代理,由于我们是实现推送功能,这里的消息代理是/topic

总之,首先注册了一个名为/my-websocket的端点,也就是STOMP客户端连接的地址。

此外,定义了服务端处理WebSocket消息的前缀是/app,这个地址用于客户端向服务端发送消息(比如客户端向/app/send这个地址发送消息,那么服务端通过@MessageMapping(“/send”)这个注解来接收并处理消息)

最后,定义了一个简单消息代理,也就是服务端广播消息的路径前缀(比如客户端监听/topic/send这个地址,那么服务端就可以通过@SendTo(“/topic/send”)这个注解向客户端发送STOMP消息)。

  1. package com.example;
  2.  
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.messaging.simp.config.MessageBrokerRegistry;
  5. import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
  6. import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
  7. import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
  8.  
  9. @Configuration
  10. @EnableWebSocketMessageBroker
  11. public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
  12.  
  13. @Override
  14. public void configureMessageBroker(MessageBrokerRegistry config) {
  15. config.enableSimpleBroker("/topic");
  16. config.setApplicationDestinationPrefixes("/app");
  17. }
  18.  
  19. @Override
  20. public void registerStompEndpoints(StompEndpointRegistry registry) {
  21. registry.addEndpoint("/my-websocket").withSockJS();
  22. }
  23. }

3.编写一个DTO类来承载消息:

  1. package com.example;
  2.  
  3. public class SocketMessage {
  4.  
  5. public String message;
  6.  
  7. public String date;
  8.  
  9. }

4.创建App.java类,用于启用spring boot和用于接收、发送消息的控制器。

“send”方法用于接收客户端发送过来的websocket请求。

@EnableScheduling注解为:启用spring boot的定时任务,这与“callback”方法相呼应,用于每隔1秒推送服务器端的时间。

@MessageMapping注解和我们之前使用的@RequestMapping类似。@SendTo注解表示当服务器有消息需要推送的时候,会对订阅了@SendTo中路径的浏览器发送消息。

convertAndSend方法是向前端发送一条消息,第一个参数是浏览器中订阅消息的地址,第二个参数是消息本身。

  1. package com.example;
  2.  
  3. import java.text.DateFormat;
  4. import java.text.SimpleDateFormat;
  5. import java.util.Date;
  6.  
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.boot.SpringApplication;
  9. import org.springframework.boot.autoconfigure.SpringBootApplication;
  10. import org.springframework.messaging.handler.annotation.MessageMapping;
  11. import org.springframework.messaging.handler.annotation.SendTo;
  12. import org.springframework.messaging.simp.SimpMessagingTemplate;
  13. import org.springframework.scheduling.annotation.EnableScheduling;
  14. import org.springframework.scheduling.annotation.Scheduled;
  15. import org.springframework.stereotype.Controller;
  16. import org.springframework.web.bind.annotation.GetMapping;
  17.  
  18. @Controller
  19. @EnableScheduling
  20. @SpringBootApplication
  21. public class App {
  22.  
  23. public static void main(String[] args) {
  24. SpringApplication.run(App.class, args);
  25. }
  26.  
  27. @Autowired
  28. private SimpMessagingTemplate messagingTemplate;
  29.  
  30. @GetMapping("/")
  31. public String index() {
  32. return "index";
  33. }
  34.  
  35. @MessageMapping("/send")
  36. @SendTo("/topic/send")
  37. public SocketMessage send(SocketMessage message) throws Exception {
  38. DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  39. message.date = df.format(new Date());
  40. return message;
  41. }
  42.  
  43. @Scheduled(fixedRate = 1000)
  44. @SendTo("/topic/callback")
  45. public Object callback() throws Exception {
  46. // 发现消息
  47. DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  48. messagingTemplate.convertAndSend("/topic/callback", df.format(new Date()));
  49. return "callback";
  50. }
  51. }

5.在“resources/templates”目录下创建index.html文件,引用angular.js的CDN文件外,还需要引用sockjs和stomp

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>websocket</title>
  5. <script src="//cdn.bootcss.com/angular.js/1.5.6/angular.min.js"></script>
  6. <script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script>
  7. <script src="https://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script>
  8. <script type="text/javascript">
  9. /*<![CDATA[*/
  10.  
  11. var stompClient = null;
  12.  
  13. var app = angular.module('app', []);
  14. app.controller('MainController', function($rootScope, $scope, $http) {
  15.  
  16. $scope.data = {
  17. //连接状态
  18. connected : false,
  19. //消息
  20. message : '',
  21. rows : []
  22. };
  23.  
  24. //连接
  25. $scope.connect = function() {
  26. var socket = new SockJS('/my-websocket');
  27. stompClient = Stomp.over(socket);
  28. stompClient.connect({}, function(frame) {
  29. // 注册发送消息
  30. stompClient.subscribe('/topic/send', function(msg) {
  31. $scope.data.rows.push(JSON.parse(msg.body));
  32. $scope.data.connected = true;
  33. $scope.$apply();
  34. });
  35. // 注册推送时间回调
  36. stompClient.subscribe('/topic/callback', function(r) {
  37. $scope.data.time = '当前服务器时间:' + r.body;
  38. $scope.data.connected = true;
  39. $scope.$apply();
  40. });
  41.  
  42. $scope.data.connected = true;
  43. $scope.$apply();
  44. });
  45. };
  46.  
  47. $scope.disconnect = function() {
  48. if (stompClient != null) {
  49. stompClient.disconnect();
  50. }
  51. $scope.data.connected = false;
  52. }
  53.  
  54. $scope.send = function() {
  55. stompClient.send("/app/send", {}, JSON.stringify({
  56. 'message' : $scope.data.message
  57. }));
  58. }
  59. });
  60. /*]]>*/
  61. </script>
  62. </head>
  63. <body ng-app="app" ng-controller="MainController">
  64. <label>WebSocket连接状态:</label>
  65. <button type="button" ng-disabled="data.connected" ng-click="connect()">连接</button>
  66. <button type="button" ng-click="disconnect()"
  67. ng-disabled="!data.connected">断开</button>
  68. <br />
  69. <br />
  70. <div ng-show="data.connected">
  71. <label>{{data.time}}</label> <br /> <br /> <input type="text"
  72. ng-model="data.message" placeholder="请输入内容..." />
  73. <button ng-click="send()" type="button">发送</button>
  74. <br /> <br /> 消息列表: <br />
  75. <table>
  76. <thead>
  77. <tr>
  78. <th>内容</th>
  79. <th>时间</th>
  80. </tr>
  81. </thead>
  82. <tbody>
  83. <tr ng-repeat="row in data.rows">
  84. <td>{{row.message}}</td>
  85. <td>{{row.date}}</td>
  86. </tr>
  87. </tbody>
  88. </table>
  89. </div>
  90. </body>
  91. </html>

总结

在开发一个基于web的即时通讯应用的过程中,我们还需考虑session的机制。

还需要一个集合来承载当前的在线用户,并做一个定时任务,其目的是用轮询的方式定时处理在线用户的状态,有哪些用户在线,又有哪些用户离线。

spring boot整合websocket的更多相关文章

  1. spring boot整合websocket之使用自带tomcat启动项目报错记录

    项目中用到websocket,就将原来写好的websocket工具类直接拿来使用,发现前端建立连接的时候报404,经查找发现是因为原来用的是配置的外部tomcat启动,这次是spring boot自带 ...

  2. spring boot整合WebSocket示例

    1.运行环境 开发工具:intellij idea JDK版本:1.8 项目管理工具:Maven 4.0.0 2.GITHUB地址 https://github.com/nbfujx/springBo ...

  3. 【Java Web开发学习】Spring MVC整合WebSocket通信

    Spring MVC整合WebSocket通信 目录 ========================================================================= ...

  4. Spring Boot 整合 Elasticsearch,实现 function score query 权重分查询

    摘要: 原创出处 www.bysocket.com 「泥瓦匠BYSocket 」欢迎转载,保留摘要,谢谢! 『 预见未来最好的方式就是亲手创造未来 – <史蒂夫·乔布斯传> 』 运行环境: ...

  5. spring boot整合jsp的那些坑(spring boot 学习笔记之三)

    Spring Boot 整合 Jsp 步骤: 1.新建一个spring boot项目 2.修改pom文件 <dependency>            <groupId>or ...

  6. spring boot 系列之四:spring boot 整合JPA

    上一篇我们讲了spring boot 整合JdbcTemplate来进行数据的持久化, 这篇我们来说下怎么通过spring boot 整合JPA来实现数据的持久化. 一.代码实现 修改pom,引入依赖 ...

  7. Spring Kafka和Spring Boot整合实现消息发送与消费简单案例

    本文主要分享下Spring Boot和Spring Kafka如何配置整合,实现发送和接收来自Spring Kafka的消息. 先前我已经分享了Kafka的基本介绍与集群环境搭建方法.关于Kafka的 ...

  8. Spring Boot之WebSocket

    一.项目说明 1.项目地址:https://github.com/hqzmss/test01-springboot-websocket.git 2.IDE:IntelliJ IDEA 2018.1.1 ...

  9. Spring Boot整合Mybatis并完成CRUD操作

    MyBatis 是一款优秀的持久层框架,被各大互联网公司使用,本文使用Spring Boot整合Mybatis,并完成CRUD操作. 为什么要使用Mybatis?我们需要掌握Mybatis吗? 说的官 ...

随机推荐

  1. CDNI - RFC7336翻译

    CDNI框架 摘要 本文档提出了CDNI的一个框架.框架的目的是提供对CDNI问题空间的总体描述,和描述CDN互连所需的各种组件之间的 关系.CDNI需要指定接口和机制解决诸如请求路由,分发交换元数据 ...

  2. 级联Cascade

    cascade分为两类: 1.JPA标准 2.Hibernate标准 JPA的方法使用JPA规范 如@OneToMany(cascade=CascadeType.ALL,mappedBy=" ...

  3. SpringBoot+Mybatis实现关联查询

    SpringBoot+Mybatis实现关联查询 今天学习了下Mybatis的动态查询,然后接着上次的Demo改造了下实现表的关联查询. 话不多说,开始今天的小Demo 首先接着上次的项目 https ...

  4. oracle入坑日记<六>自增列创建和清除(含序列和触发器的基础用法)

    0   前言 用过 SQLserver 和 MySQL 的自增列(auto_increment),然而 Oracle 在建表设置列时却没有自增列. 查阅资料后发现 Oracle 的自增列需要手动编写. ...

  5. H5判断手机是否存在应用和打开应用

    伪命题,其实js无法判断您的手机是否存在此应用,遇到这样的需求我们应该跟ios和Android开发的同事商量,需要他们给你一个url尝试打开,如果能打开就表示手机中有该应用,如果不能打开就表示手机没有 ...

  6. HTML5+CSS3(2)

    一.视频与音频 1.用JavaScript检测音频格式支持 <!DOCTYPE html> <html> <head> <meta charset=" ...

  7. visual studio 2013 几个测试工具(Nunit 3、xUnit)

    一.Nunit 3 1.在解决方案里添加一个类库——引用——右键(如下图)) 3.搜索nunit 并安装(如图) 3.注意引入命名空间并给测试类和测试方法添加特性(如图) 4.如果测试通过则为绿色(如 ...

  8. 比较C#中几种常见的复制字节数组方法的效率

    在日常编程过程中,我们可能经常需要Copy各种数组,一般来说有以下几种常见的方法:Array.Copy,IList<T>.Copy,BinaryReader.ReadBytes,Buffe ...

  9. Linux部署笔记分享

    # Linux部署 ## 安装lrzsz1. 安装lrzsz: yum -y install lrzsz2. 进入tmp目录3. rz 上传安装文件 jdk-8u65-linux-x64.tar.gz ...

  10. Linux安装Tomcat,运行Eclipse,web项目

    到官网下载:https://tomcat.apache.org/download-80.cgi  在这里是8.5.39版本 下载tar,gz 提取解压后,我这里是放到opt目录下 cd  切换目录 / ...