开发环境:html
spring3+tomcat7+spring-websocket4 + spring security3前端
运行环境:java
windows、Linuxweb
产品将要发布的消息或其余须要让客户提早知道的消息,在客户端和服务端创建链接后推送spring
给客户端。windows
WebSocket协议是基于TCP的一种新的网络协议。后端
一、WebSocket是HTML5出的东西(协议),跟HTTP协议基本没有关系,只是为了兼容现有浏览器的握手规范而已浏览器
二、HTTP有1.1和1.0之说,也就是所谓的keep-alive,把多个HTTP请求合并为一个tomcat
三、Websocket是一个持久化的协议,相对于HTTP这种非持久的协议来讲安全
四、典型的Websocket握手
Chrome | Supported in version 4+ |
Firefox | Supported in version 4+ |
Opera | Supported in version 10+ |
Safari | Supported in version 5+ |
IE(Internet Explorer) | Supported in version 10+ |
一、长链接,实现双向通讯, 具备底层socket的特色,实现真正意义上的推送功能。
二、是HTML5的技术之一,具备巨大的应用前景
三、节约带宽,节省服务器资源
缺点:
少部分浏览器不支持,浏览器支持的程度与方式有区别
前端代码:
1 var DJCW_webSocket = (function(){ 2 var webSocket = null, 3 tryTime = 0, 4 initSocket; 5 6 initSocket = function(){ 7 debugger 8 var _marquee = "<marquee behavior='scroll' direction='left' behavior='scroll'>"; 9 10 if (!window.WebSocket) { 11 alert("您的浏览器不支持websocket!"); 12 return false; 13 } 14 15 webSocket = new WebSocket('ws://127.0.0.1:8082/projectname/websocket'); 16 17 // 收到服务端消息 18 webSocket.onmessage = function(msg) { 19 DJCW.messagesScroll(msg.data); 20 }; 21 22 // 异常 23 webSocket.onerror = function(event) {}; 24 25 // 创建链接 26 webSocket.onopen = function(event) {}; 27 28 // 断线重连 29 webSocket.onclose = function() { 30 // 重试10次,每次之间间隔10秒 31 if (tryTime < 10) { 32 setTimeout(function() { 33 webSocket = null; 34 tryTime++; 35 initSocket(); 36 }, 500); 37 } else { 38 tryTime = 0; 39 } 40 }; 41 }; 42 43 initModule = function() { 44 initSocket(); 45 }; 46 return { 47 initSocket:initSocket, 48 initModule : initModule 49 }; 50 })(); 51 52 $(function() { 53 DJCW_webSocket.initModule(); 54 window.onbeforeunload = function() { 55 // 离开页面时的其余操做 56 }; 57 });
后端代码:
1 import java.io.IOException; 2 import java.util.concurrent.CopyOnWriteArraySet; 3 4 import javax.websocket.OnClose; 5 import javax.websocket.OnError; 6 import javax.websocket.OnMessage; 7 import javax.websocket.OnOpen; 8 import javax.websocket.Session; 9 import javax.websocket.server.ServerEndpoint; 10 11 12 import org.springframework.stereotype.Service; 13 14 /** 15 * 功能说明:websocket处理类, 使用J2EE7的标准 切忌直接在该链接处理类中加入业务处理代码 做者:ydd(2017-04-12 15:29) 16 */ 17 // relationId和userCode是个人业务标识参数,websocket是链接的路径,能够自行定义 18 @ServerEndpoint("/websocket") 19 @Service 20 public class WebsocketEndPoint { 21 22 private static final Log LOG = Log.getLogger(WebsocketEndPoint.class); 23 24 private static CopyOnWriteArraySet<WebsocketEndPoint> sessions = new CopyOnWriteArraySet<WebsocketEndPoint>(); 25 private Session session; 26 27 28 public WebsocketEndPoint() { 29 } 30 31 /** 32 * @Title: onOpen 33 * @Description: (打开链接时触发) 34 * @param @param session 设定文件 35 * @return void 返回类型 36 * @throws 37 */ 38 @OnOpen 39 public void onOpen(Session session) { 40 this.session = session; 41 sessions.add(this); 42 } 43 44 45 /** 46 * @Title: onMessage 47 * @Description: (收到客户端消息时触发) 48 * @param @param message 49 * @param @return 设定文件 50 * @return String 返回类型 51 * @throws 52 */ 53 @OnMessage 54 public String onMessage(String message) { 55 return "Got your message (" + message + ").Thanks !"; 56 } 57 58 /** 59 * @Title: onError 60 * @Description: (异常时触发) 61 * @param @param throwable 62 * @param @param session 设定文件 63 * @return void 返回类型 64 * @throws 65 */ 66 @OnError 67 public void onError(Throwable throwable, Session session) { 68 LOG.e("Chat Error: " + throwable.toString()); 69 } 70 71 /** 72 * @Title: onClose 73 * @Description: (关闭链接时触发) 74 * @param @param session 设定文件 75 * @return void 返回类型 76 * @throws 77 */ 78 @OnClose 79 public void onClose(Session session) { 80 sessions.remove(this); 81 } 82 83 /** 84 * 向全部用户发送消息 85 * 86 * @param msg 87 */ 88 public void sendUser(String msg) { 89 try { 90 if (sessions.size() != 0) { 91 for (WebsocketEndPoint s : sessions) { 92 if (s != null) { 93 s.session.getBasicRemote().sendText(msg); 94 } 95 } 96 } 97 } catch (IOException e) { 98 e.printStackTrace(); 99 } 100 } 101 102 }
在线聊天室、在线客服系统、评论系统、WebIM等
WebSocket和传统的HTTP交互方式的区别以下图:
1 <!-- spring security --> 2 <filter> 3 <filter-name>springSecurityFilterChain</filter-name> 4 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 5 </filter> 6 <!-- 因为加入了websocket,不能写成/*,否则会出现302循环重定向,由于和 DispatcherServlet相冲突--> 7 <filter-mapping> 8 <filter-name>springSecurityFilterChain</filter-name> 9 <url-pattern>*.js</url-pattern> 10 <url-pattern>*.jsp</url-pattern> 11 <!--经过SecurityContextHolder.getContext().getAuthentication().getPrincipal()能够获取到用户名 --> 12 <url-pattern>/login/</url-pattern> 13 <!-- 登陆时springsecurity自带的类进行用户密码认证以及赋予相关权限 --> 14 <url-pattern>/j_spring_security_check</url-pattern> 15 <!-- 退出时经过springsecurity自带的url进行退出并清除session --> 16 <url-pattern>/j_spring_security_logout</url-pattern> 17 </filter-mapping>
一、在同一浏览器同时打开同一产品带有ws协议请求的多个标签页,是否都会走onClose方法(即删除相应的session)
经测试是会的
二、static CopyOnWriteArraySet<WebsocketEndPoint> sessions = new CopyOnWriteArraySet<WebsocketEndPoint>();
每次加载类的时候不会new出一个新的实例吗,这样的话怎么实现的将消息推送给全部用户呢
不会每次都new出一个新的实例
static变量
按照是否静态的对类成员变量进行分类可分两种:一种是被static修饰的变量,叫静态变量或类变量;另外一种是没有被static修饰的变量,叫实例变量。
二者的区别是:
对于静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,在加载类的过程当中完成静态变量的内存分配,可用类名直接访问(方便),
固然也能够经过对象来访问(可是这是不推荐的)。
对于实例变量,没建立一个实例,就会为实例变量分配一次内存,实例变量能够在内存中有多个拷贝,互不影响(灵活)。
CopyOnWriteArraySet介绍:
属于java.util.concurrent包下的,java.util.concurrent 包添加了多个新的线程安全集合类(ConcurrentHashMap、CopyOnWriteArrayList 和
CopyOnWriteArraySet)这些类的目的是提供高性能、高度可伸缩性、线程安全的基本集合类型版本,经过同步的封装工厂(Collections.synchronizedMap()、
synchronizedList() 和 synchronizedSet()),非线程安全集合都可表现为线程安全的