1、简介
作为下一代的Web标准,HTML5拥有许多引人注目的新特性,如 Canvas、本地存储、多媒体编程接口、WebSocket等等。这其中有“Web的 TCP”之称的 WebSocket格外吸引开发人员的注意。WebSocket的出现使得浏览器提供对Socket的支持成为可能,从而在浏览器和服务器之间提供了一个基于TCP连接的双向通道。Web开发人员可以非常方便地使用WebSocket构建实时 web 应用,开发人员的手中从此又多了一柄神兵利器。
Web 应用的信息交互过程通常是客户端通过浏览器发出一个请求,服务器端接收和审核完请求后进行处理并返回结果给客户端,然后客户端浏览器将信息呈现出来,这种机制对于信息变化不是特别频繁的应用尚能相安无事,但是对于那些实时要求比较高的应用来说,比如说在线游戏、在线证券、设备监控、新闻在线播报、RSS订阅推送等等,当客户端浏览器准备呈现这些信息的时候,这些信息在服务器端可能已经过时了。所以保持客户端和服务器端的信息同步是实时Web应用的关键要素,对 Web开发人员来说也是一个难题。在 WebSocket规范出来之前,开发人员想实现这些实时的Web应用,不得不采用一些折衷的方案,其中最常用的就是轮询(Polling)和 Comet 技术,而Comet技术实际上是轮询技术的改进,又可细分为两种实现方式,一种是长轮询机制,一种称为流技术。
HTML5 WebSocket 设计出来的目的就是要取代轮询和 Comet技术,使客户端浏览器具备像 C/S架构下桌面系统的实时通讯能力。浏览器通过JavaScript向服务器发出建立 WebSocket连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP连接直接交换数据。因为 WebSocket连接本质上就是一个TCP连接,所以在数据传输的稳定性和数据传输量的大小方面,和轮询以及Comet技术比较,具有很大的性能优势。
但是鉴于web socket 对浏览器要求比较高,为了解决这个问题,推出了sockJS,SockJS是一个JavaScript库,提供跨浏览器javascript的API,创建了一个低延迟、全双工的浏览器和web服务器之间通信通道。
2、相关环境要求
Spring4.2.4(要选择4.0+),tomcat9.0.4版本,JDK1.8。
3、具体代码
(以下代码亲测可用!)
-----------------------------------------------
maven pom文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.cxw</groupId> <artifactId>testttt</artifactId> <packaging>war</packaging> <version>1.0-SNAPSHOT</version> <name>testttt Maven Webapp</name> <url>http://maven.apache.org</url> <properties> <spring.version>4.2.4.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <!-- websocket--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>4.0.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-messaging</artifactId> <version>4.0.1.RELEASE</version> </dependency> </dependencies> <build> <finalName>testttt</finalName> </build> </project>
---------------------------------默认地址WEB-INF下 applicationContext.xml配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd"> <!-- Springmvc框架,由springmvc管理controller。 --> <!-- <context:component-scan base-package="com.chris.controller" /> <context:property-placeholder location="classpath:conf/resource.properties" />--> <!-- 映射器和适配器(注解形式) --> <mvc:annotation-driven /> <!-- 视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean> <!--<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!– 设定默认编码 –> <property name="defaultEncoding" value="UTF-8"></property> <!– 设定文件上传的最大值5MB,5*1024*1024 –> <property name="maxUploadSize" value="5242880"></property> </bean> --> <mvc:resources location="/WEB-INF/js/" mapping="/js/**"/> <mvc:resources location="/WEB-INF/css/" mapping="/css/**"/> <!-- 引用dubbo服务 --> </beans>
(1)web.xml:
- <?xmlversionxmlversion="1.0"encoding="UTF-8"?>
- <web-app version="3.1"
- xmlns="http://xmlns.jcp.org/xml/ns/javaee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
- <description>用来测试WebSocket基础上推送的功能</description>
- <distributable/>
- <filter>
- <filter-name>encodingFilter</filter-name>
- <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
- <async-supported>true</async-supported>
- <init-param>
- <param-name>encoding</param-name>
- <param-value>UTF-8</param-value>
- </init-param>
- <init-param>
- <param-name>forceEncoding</param-name>
- <param-value>true</param-value>
- </init-param>
- </filter>
- <filter-mapping>
- <filter-name>encodingFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
- <!-- Spring 刷新Introspector防止内存泄露 -->
- <listener>
- <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
- </listener>
- <servlet>
- <servlet-name>dispatcher</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath:dispatcher-servlet.xml</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- <async-supported>true</async-supported>
- </servlet>
- <servlet-mapping>
- <servlet-name>dispatcher</servlet-name>
- <url-pattern>/</url-pattern>
- </servlet-mapping>
- <session-config>
- <session-timeout>
- 30
- </session-timeout>
- </session-config>
- <welcome-file-list>
- <welcome-file>testSocket.jsp</welcome-file>
- </welcome-file-list>
- </web-app>
(2) dispatcher-servlet.xml
- <?xmlversionxmlversion="1.0"encoding="UTF-8"?>
- <beans:beansxmlnsbeans:beansxmlns="http://www.springframework.org/schema/mvc"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:beans="http://www.springframework.org/schema/beans"
- xmlns:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
- http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
- <annotation-driven/>
- <!-- 自动扫描的包名 -->
- <context:component-scanbase-packagecontext:component-scanbase-package="zyy.sockjs.config"></context:component-scan>
- </beans:beans>
(3) HandshakeInterceptor.java
- package zyy.sockjs.config;
- import java.util.Map;
- import org.springframework.http.server.ServerHttpRequest;
- import org.springframework.http.server.ServerHttpResponse;
- import org.springframework.stereotype.Component;
- import org.springframework.web.socket.WebSocketHandler;
- import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
- @Component
- public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor {
- @Override
- public boolean beforeHandshake(ServerHttpRequest request,
- ServerHttpResponse response, WebSocketHandler wsHandler,
- Map<String, Object> attributes) throws Exception {
- System.out.println("Before Handshake");
- return super.beforeHandshake(request,response,wsHandler,attributes);
- }
- @Override
- public void afterHandshake(ServerHttpRequest request,
- ServerHttpResponse response, WebSocketHandler wsHandler,
- Exception ex) {
- System.out.println("After Handshake");
- super.afterHandshake(request,response,wsHandler,ex);
- }
- }
(4)InfoSocketEndPoint.java
- package zyy.sockjs.config;
- import org.springframework.stereotype.Component;
- import org.springframework.web.socket.TextMessage;
- import org.springframework.web.socket.WebSocketSession;
- import org.springframework.web.socket.handler.TextWebSocketHandler;
- @Component
- public class InfoSocketEndPoint extends TextWebSocketHandler {
- public InfoSocketEndPoint() {
- }
- @Override
- protected void handleTextMessage(WebSocketSession session,
- TextMessage message) throws Exception {
- super.handleTextMessage(session, message);
- TextMessage returnMessage = new TextMessage(message.getPayload()
- + " received at server");
- session.sendMessage(returnMessage);
- }
- }
(5)SystemWebSocketHandler.java
- package zyy.sockjs.config;
- import org.springframework.stereotype.Component;
- import org.springframework.web.socket.CloseStatus;
- import org.springframework.web.socket.TextMessage;
- import org.springframework.web.socket.WebSocketHandler;
- import org.springframework.web.socket.WebSocketMessage;
- import org.springframework.web.socket.WebSocketSession;
- /**
- *
- * @author dayu
- */
- @Component
- public class SystemWebSocketHandler implements WebSocketHandler {
- @Override
- public void afterConnectionEstablished(WebSocketSession session) throws Exception {
- System.out.println("connect to the websocket success......");
- session.sendMessage(new TextMessage("Server:connected OK!"));
- }
- @Override
- public void handleMessage(WebSocketSession wss, WebSocketMessage<?> wsm) throws Exception {
- TextMessage returnMessage = new TextMessage(wsm.getPayload()
- + " received at server");
- wss.sendMessage(returnMessage);
- }
- @Override
- public void handleTransportError(WebSocketSession wss, Throwable thrwbl) throws Exception {
- if(wss.isOpen()){
- wss.close();
- }
- System.out.println("websocket connection closed......");
- }
- @Override
- public void afterConnectionClosed(WebSocketSession wss, CloseStatus cs) throws Exception {
- System.out.println("websocket connection closed......");
- }
- @Override
- public boolean supportsPartialMessages() {
- return false;
- }
- }
(6)WebSocketConfig.java
- package zyy.sockjs.config;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.web.servlet.config.annotation.EnableWebMvc;
- import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
- import org.springframework.web.socket.WebSocketHandler;
- import org.springframework.web.socket.config.annotation.EnableWebSocket;
- import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
- import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
- @Configuration
- @EnableWebMvc
- @EnableWebSocket
- public class WebSocketConfig extends WebMvcConfigurerAdapter implements
- WebSocketConfigurer {
- public WebSocketConfig() {
- }
- @Override
- public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
- registry.addHandler(systemWebSocketHandler(), "/websck").addInterceptors(new HandshakeInterceptor());
- System.out.println("registed!");
- registry.addHandler(systemWebSocketHandler(), "/sockjs/websck/info").addInterceptors(new HandshakeInterceptor())
- .withSockJS();
- }
- @Bean
- public WebSocketHandler systemWebSocketHandler() {
- return new SystemWebSocketHandler();
- }
- }
(7)testSocket.jsp
- <%@ page language="java"contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
- <!DOCTYPE htmlPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
- <html>
- <head>
- <meta http-equiv="Content-Type"content="text/html; charset=ISO-8859-1">
- <title>WebSocket/SockJS Echo Sample (Adapted from Tomcat's echo sample)</title>
- <style type="text/css">
- #connect-container {
- float: left;
- width: 400px
- }
- #connect-container div {
- padding: 5px;
- }
- #console-container {
- float: left;
- margin-left: 15px;
- width: 400px;
- }
- #console {
- border:1px solid #CCCCCC;
- border-right-color:#33333333;
- border-bottom-color:#999999;
- height: 170px;
- overflow-y: scroll;
- padding: 5px;
- width: 100%;
- }
- #console p {
- padding: 0;
- margin: 0;
- }
- </style>
- <script src="http://cdn.sockjs.org/sockjs-0.3.min.js"></script>
- <script type="text/javascript">
- var ws = null;
- var url = null;
- var transports = [];
- function setConnected(connected) {
- document.getElementById('connect').disabled = connected;
- document.getElementById('disconnect').disabled = !connected;
- document.getElementById('echo').disabled = !connected;
- }
- function connect() {
- if (!url) {
- log('Select whether to use W3C WebSocket or SockJS');
- return;
- }
- //ws = (url.indexOf('sockjs') != -1) ?new SockJS(url, undefined, {protocols_whitelist: transports}) : new WebSocket(url);
- if ('WebSocket'in window) {
- ws= new WebSocket("ws://localhost:8080/SpringSocketJs/websck");
- }else {
- ws = new SockJS("http://localhost:8080/SpringSocketJs/sockjs/websck/info");
- }
- //websocket = new SockJS("http://localhost:8084/SpringWebSocketPush/sockjs/websck");
- ws.onopen = function () {
- setConnected(true);
- //log('Info: connection opened.');
- };
- ws.onmessage = function (event) {
- log('Received: ' + event.data);
- };
- ws.onclose = function (event) {
- setConnected(false);
- log('Info: connection closed.');
- log(event);
- };
- }
- function disconnect() {
- if (ws != null) {
- ws.close();
- ws = null;
- }
- setConnected(false);
- }
- function echo() {
- if (ws != null) {
- var message = document.getElementById('message').value;
- log('Sent: ' + message);
- ws.send(message);
- } else {
- alert('connection not established, please connect.');
- }
- }
- function updateUrl(urlPath) {
- if (urlPath.indexOf('sockjs') != -1) {
- url = urlPath;
- document.getElementById('sockJsTransportSelect').style.visibility ='visible';
- }
- else {
- if (window.location.protocol =='http:') {
- url = 'ws://' + window.location.host + urlPath;
- } else {
- url = 'wss://' + window.location.host + urlPath;
- }
- document.getElementById('sockJsTransportSelect').style.visibility ='hidden';
- }
- }
- function updateTransport(transport) {
- transports = (transport == 'all') ? [] : [transport];
- }
- function log(message) {
- var console = document.getElementById('console');
- var p = document.createElement('p');
- p.style.wordWrap = 'break-word';
- p.appendChild(document.createTextNode(message));
- console.appendChild(p);
- while (console.childNodes.length > 25) {
- console.removeChild(console.firstChild);
- }
- console.scrollTop = console.scrollHeight;
- }
- </script>
- </head>
- <body>
- <noscript><h2 style="color:#ff0000">Seems your browser doesn't supportJavascript!Websockets
- rely on Javascript being enabled. Please enable
- Javascript and reload this page!</h2></noscript>
- <div>
- <div id="connect-container">
- <input id="radio1"type="radio"name="group1"onclick="updateUrl('/SpringSocketJs/websocket');">
- <label for="radio1">W3C WebSocket</label>
- <br>
- <input id="radio2"type="radio"name="group1"onclick="updateUrl('/SpringSocketJs/sockjs/websocket');">
- <label for="radio2">SockJS</label>
- <div id="sockJsTransportSelect" style="visibility:hidden;">
- <span>SockJS transport:</span>
- <select onchange="updateTransport(this.value)">
- <option value="all">all</option>
- <option value="websocket">websocket</option>
- <option value="xhr-polling">xhr-polling</option>
- <option value="jsonp-polling">jsonp-polling</option>
- <option value="xhr-streaming">xhr-streaming</option>
- <option value="iframe-eventsource">iframe-eventsource</option>
- <option value="iframe-htmlfile">iframe-htmlfile</option>
- </select>
- </div>
- <div>
- <button id="connect"onclick="connect();">Connect</button>
- <button id="disconnect"disabled="disabled"onclick="disconnect();">Disconnect</button>
- </div>
- <div>
- <textarea id="message"style="width:350px">Here is a message!</textarea>
- </div>
- <div>
- <button id="echo"onclick="echo();"disabled="disabled">Echo message</button>
- </div>
- </div>
- <div id="console-container">
- <div id="console"></div>
- </div>
- </div>
- </body>
- </html>