zoukankan      html  css  js  c++  java
  • websocket技术分享

    开发环境

    spring3+tomcat7+spring-websocket4 + spring security3

    运行环境

    windows、Linux

    一、背景:

    产品将要发布的消息或其他需要让客户提前知道的消息,在客户端和服务端建立连接后推送

    给客户端。

    二、WebSocket是什么

    WebSocket协议是基于TCP的一种新的网络协议。

    三、WebSocket身世挖掘

    1、WebSocket是HTML5出的东西(协议),跟HTTP协议基本没有关系,只是为了兼容现有浏览器的握手规范而已

    2、HTTP有1.1和1.0之说,也就是所谓的keep-alive,把多个HTTP请求合并为一个

    3、Websocket是一个持久化的协议,相对于HTTP这种非持久的协议来说

    4、典型的Websocket握手

    Host: server.example.com
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
    Sec-WebSocket-Version: 13
    Origin: http://example.com
     
    5、实现了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+
     

    四、WebSocket的特点

    1、长连接,实现双向通信, 具有底层socket的特点,实现真正意义上的推送功能

    2、是HTML5的技术之一,具有巨大的应用前景

    3、节约带宽,节省服务器资源

    缺点:

    少部分浏览器不支持,浏览器支持的程度与方式有区别

    五、WebSocket如何用

    前端代码:

     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 }

    六、WebSocket应用场景

    在线聊天室、在线客服系统、评论系统、WebIM等

    七、WebSocket的实现原理

    在实现websocket连线过程中,需要通过浏览器发出websocket连线请求,然后服务器发出回应,这个过程通常称为“握手” (handshaking)。
    在 WebSocket API,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数
    据互相传送。在此WebSocket 协议中,为我们实现即时服务带来了两大好处:
    1. Header
    互相沟通的Header是很小的-大概只有 2 Bytes
    2. Server Push
    服务器的推送,服务器不再被动的接收到浏览器的request之后才返回数据,而是在有新数据时就主动推送给浏览器。
     

    WebSocket和传统的HTTP交互方式的区别如下图:

     

    八、遇到的错误

     
    2、Message will not be sent because the WebSocket session has been closed
    服务端不正常关闭后,在后台的OnError的方法中抛出的异常,可以不抛出异常
     
    3、spring security3与WebSocket结合使用时报302循环重定向错误
    原因
    springsecurity的过滤器DelegatingFilterProxy和springMVC的过滤器DispatcherServlet
    冲突所导致的。
    解决方案(只将spring security的url-pattern中的/*改为具体的对指定的相关请求进行拦截就可以了)
     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> 

     九、头脑风暴

    1、在同一浏览器同时打开同一产品带有ws协议请求的多个标签页,是否都会走onClose方法(即删除相应的session)


       经测试是会的

    2、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()),非线程安全集合均可表现为线程安全的

     
     
     
  • 相关阅读:
    设置matplotlib.pyplot设置画图的坐标系
    [leetcode]238. 除自身以外数组的乘积
    彩色图到灰度图究竟是怎么变换的
    1.1 PIL:Python图像处理类库
    基于GoogLeNet的不同花分类微调训练案例
    消息队列 ActiveMQ的简单了解以及点对点与发布订阅的方法实现ActiveMQ
    解决session共享问题
    linux安装Nginx 以及 keepalived 管理Nginx
    nginx解决服务器宕机、解决跨域问题、配置防盗链、防止DDOS流量攻击
    Nginx实现负载均衡
  • 原文地址:https://www.cnblogs.com/yanduanduan/p/6744110.html
Copyright © 2011-2022 走看看