spring4.1.6 WebSocket实例
2015-07-14 21:33
633 查看
WebSocket protocol 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。
在 WebSocket API,浏览器和服务器只需要要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。在此WebSocket 协议中,为我们实现即时服务带来了两大好处:
1. Header
互相沟通的Header是很小的-大概只有 2 Bytes
2. Server Push
目前支持websocket的浏览器有:
对于那些不支持websocket的浏览器,可以使用SockJS来协调支持,该库目前提供的模拟方式已经支持绝大多是浏览器了。具体情况如下:
从spring4开始,加入了对websocket的支持,同时也加入了对SockJS、STOMP的支持,现在通过Spring可以非常方便的创建我们的WebSocket应用服务。
进入正题,使用maven搭建spring的webmvc环境。pom.xml的内容如下:
web.xml的配置(其中<async-supported>true</async-supported>配置在使用非W3C WebSocket连接的时候需要加上):
dispatcher-server.xml的配置如下:
WebSocketEndPoint代码:
继承TextWebSocketHandler,并复写一些方法。很好理解。
HandshakeInterceptor的源码如下:
主要做一些握手的之前的预处理工作。
最后就是写个HomeController加载客户端页面了。
websocket.jsp的代码如下:
完整实例下载地址:https://git.oschina.net/zhmlvft/ws_test
在 WebSocket API,浏览器和服务器只需要要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。在此WebSocket 协议中,为我们实现即时服务带来了两大好处:
1. Header
互相沟通的Header是很小的-大概只有 2 Bytes
2. Server Push
目前支持websocket的浏览器有:
Chrome | Supported in version 4+ |
Firefox | Supported in version 4+ |
Internet Explorer | Supported in version 10+ |
Opera | Supported in version 10+ |
Safari | Supported in version 5+ |
Browser | Websockets | Streaming | Polling |
---|---|---|---|
IE 6, 7 | no | no | jsonp-polling |
IE 8, 9 (cookies=no) | no | xdr-streaming † | xdr-polling † |
IE 8, 9 (cookies=yes) | no | iframe-htmlfile | iframe-xhr-polling |
IE 10 | rfc6455 | xhr-streaming | xhr-polling |
Chrome 6-13 | hixie-76 | xhr-streaming | xhr-polling |
Chrome 14+ | hybi-10 / rfc6455 | xhr-streaming | xhr-polling |
Firefox <10 | no ‡ | xhr-streaming | xhr-polling |
Firefox 10+ | hybi-10 / rfc6455 | xhr-streaming | xhr-polling |
Safari 5 | hixie-76 | xhr-streaming | xhr-polling |
Opera 10.70+ | no ‡ | iframe-eventsource | iframe-xhr-polling |
Opera 12.10+ | rfc6455 | xhr-streaming | xhr-polling |
Konqueror | no | no | jsonp-polling |
进入正题,使用maven搭建spring的webmvc环境。pom.xml的内容如下:
<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.zhm.spring</groupId> <artifactId>ws_test</artifactId> <packaging>war</packaging> <version>1.0-SNAPSHOT</version> <name>ws_test Maven Webapp</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>4.1.6.RELEASE</spring.version> <jackson.version>2.5.4</jackson.version> <jetty.version>6.1.23</jetty.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</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-websocket</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-messaging</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp.jstl</groupId> <artifactId>jstl-api</artifactId> <version>1.2</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>${jackson.version}</version> </dependency> </dependencies> <build> <finalName>ROOT</finalName> <plugins> <plugin> <artifactId>maven-war-plugin</artifactId> <version>2.6</version> <configuration> <packagingExcludes> %regex[WEB-INF/lib/.*] </packagingExcludes> </configuration> </plugin> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <port>9090</port> <path>/</path> <uriEncoding>UTF-8</uriEncoding> <finalName>ROOT</finalName> <server>tomcat7</server> </configuration> </plugin> </plugins> </build> </project>
web.xml的配置(其中<async-supported>true</async-supported>配置在使用非W3C WebSocket连接的时候需要加上):
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_1.xsd"> <filter> <filter-name>encodingFilter</filter-name> <filter-class> org.springframework.web.filter.CharacterEncodingFilter </filter-class> <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> <async-supported>true</async-supported> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <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:spring/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> <display-name>Archetype Created Web Application</display-name> </web-app>
dispatcher-server.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:context="http://www.springframework.org/schema/context" xmlns:websocket="http://www.springframework.org/schema/websocket" 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.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd"> <!-- websocket消息接收与处理类 --> <bean id="websocket" class="com.zhm.spring.ws.websocket.WebSocketEndPoint"/> <!-- 定义客户端与服务器握手的拦截器,可以做一些预处理 --> <!-- 该拦截器专门为SockJS客户端服务的 --> <websocket:handlers> <websocket:mapping path="/sockjs/websocket" handler="websocket"/> <websocket:handshake-interceptors> <bean class="com.zhm.spring.ws.websocket.HandshakeInterceptor"/> </websocket:handshake-interceptors> <!-- 开启sockjs支持 --> <websocket:sockjs /> </websocket:handlers> <!-- 定义客户端与服务器握手的拦截器,可以做一些预处理 --> <!-- 该拦截器专门为WebSocket客户端服务的 --> <websocket:handlers> <websocket:mapping path="/websocket" handler="websocket"/> <websocket:handshake-interceptors> <bean class="com.zhm.spring.ws.websocket.HandshakeInterceptor"/> </websocket:handshake-interceptors> </websocket:handlers> <mvc:view-resolvers> <mvc:jsp /> </mvc:view-resolvers> <mvc:annotation-driven /> <context:component-scan base-package="com.zhm.spring" /> </beans>
WebSocketEndPoint代码:
package com.zhm.spring.ws.websocket; import com.zhm.spring.ws.utils.Constants; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.handler.TextWebSocketHandler; import java.util.ArrayList; /** * Created by zhm on 2015/7/14. */ public class WebSocketEndPoint extends TextWebSocketHandler{ private static final ArrayList<WebSocketSession> users; static { users = new ArrayList<WebSocketSession>(); } @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { users.add(session); super.afterConnectionEstablished(session); } @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { users.remove(session); super.handleTransportError(session, exception); } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { users.remove(session); super.afterConnectionClosed(session, status); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { super.handleTextMessage(session, message); TextMessage returnMessage = new TextMessage(session.getAttributes().get(Constants.SESSION_USERNAME.value())+" : "+message.getPayload()); // session.sendMessage(returnMessage); sendToAllClients(returnMessage, session); } private void sendToAllClients(TextMessage msg,WebSocketSession curSession) { try { for(WebSocketSession user : users) { if(user.isOpen()) { if (!user.getId().equals(curSession.getId())){ user.sendMessage(msg); } } else { users.remove(user.getId()); } } }catch(Exception e) { e.printStackTrace(); } } }
继承TextWebSocketHandler,并复写一些方法。很好理解。
HandshakeInterceptor的源码如下:
package com.zhm.spring.ws.websocket; import com.zhm.spring.ws.utils.Constants; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; import javax.servlet.http.HttpSession; import java.util.Map; /** * Created by zhm on 2015/7/14. */ public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { if (request instanceof ServletServerHttpRequest) { ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request; HttpSession session = servletRequest.getServletRequest().getSession(false); if (session != null) { //使用userName区分WebSocketHandler,以便定向发送消息 String userName = (String) session.getAttribute(Constants.SESSION_USERNAME.value()); attributes.put(Constants.SESSION_USERNAME.value(),userName); } } return super.beforeHandshake(request, response, wsHandler, attributes); } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) { super.afterHandshake(request, response, wsHandler, ex); } }
主要做一些握手的之前的预处理工作。
最后就是写个HomeController加载客户端页面了。
package com.zhm.spring.ws.controller; import com.zhm.spring.ws.utils.Constants; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpSession; /** * Created by zhm on 2015/7/14. */ @Controller public class HomeController { @RequestMapping(value="/webchat/{username}") public String webchat(@PathVariable String username,HttpSession session){ session.setAttribute(Constants.SESSION_USERNAME.value(),username); return "websocket"; } }
websocket.jsp的代码如下:
<%@ page import="com.zhm.spring.ws.utils.Constants" %> <%-- Created by IntelliJ IDEA. User: zhm Date: 2015/7/14 Time: 10:35 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <!DOCTYPE html> <html> <head> <title>Spring4 websocket实例</title> <meta charset="utf-8"> <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: #999999; 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) { alert('请选择使用W3C的websocket还是SockJS'); return; } ws = (url.indexOf('sockjs') != -1) ? new SockJS(url, undefined, {protocols_whitelist: transports}) : new WebSocket(url); ws.onopen = function () { setConnected(true); log('Info: 连接成功.'); }; ws.onmessage = function (event) { log(event.data); }; ws.onclose = function (event) { setConnected(false); log('Info: 断开连接.'); 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('<%=session.getAttribute(Constants.SESSION_USERNAME.value())%>:' + message); ws.send(message); } else { alert('没有建立连接,请连接服务!'); } } 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) { alert(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 support Javascript! 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('/websocket');"> <label for="radio1">W3C WebSocket</label> <br> <input id="radio2" type="radio" name="group1" onclick="updateUrl('/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();">连接服务器</button> <button id="disconnect" disabled="disabled" onclick="disconnect();">断开连接</button> </div> <div> <textarea id="message" style="width: 350px">测试消息!</textarea> </div> <div> <button id="echo" onclick="echo();" disabled="disabled">发送消息</button> </div> </div> <div id="console-container"> <div id="console"></div> </div> </div> </body> </html>
完整实例下载地址:https://git.oschina.net/zhmlvft/ws_test
相关文章推荐
- java解决数字组合问题
- JavaWeb开发中form、ajax提交数据Model转化
- 【Java】POST简略
- 获得spring容器上下文
- java高新技术上《九》
- java中的println方法打印Date类型数据
- [LeetCode][Java] Pow(x, n)
- Java源码---HashMap的底层实现
- java io流类图
- Eclipse设置:背景与字体大小和xml文件中字体大小调整
- java web如何根据用户使用的浏览器来提示用户更新或更换浏览器
- #笔记#圣思园 JavaWeb 第39讲——Filter过滤器
- JFinal所集成的插件们
- 在线java编辑器
- JAVA线程池的分析和使用
- Eclipse中jsp、js文件编辑时,卡死现象解决汇总
- java字符串常量池知识
- myEclipse8.5下SSH2开发时如何删除antlr-2.7.2包
- [LeetCode][Java] Anagrams
- Java自带的线程池ThreadPoolExecutor详细介绍说明和实例应用