zoukankan      html  css  js  c++  java
  • websocket服务器推送

    1.1 服务器推送

    WebSocket作为一种通信协议,属于服务器推送技术的一种,IE10+支持。

    服务器推送技术不止一种,有短轮询、长轮询、WebSocket、Server-sent Events(SSE)等,他们各有优缺点

    #短轮询长轮询Websocketsse
    通讯方式 http http 基于TCP长连接通讯 http
    触发方式 轮询 轮询 事件 事件
    优点 兼容性好容错性强,实现简单 比短轮询节约资源 全双工通讯协议,性能开销小、安全性高,有一定可扩展性 实现简便,开发成本低
    缺点 安全性差,占较多的内存资源与请求数 安全性差,占较多的内存资源与请求数 传输数据需要进行二次解析,增加开发成本及难度 只适用高级浏览器
    适用范围 b/s服务 b/s服务 网络游戏、银行交互和支付 服务端到客户端单向推送

    短轮询最简单,在一些简单的场景也会经常使用,就是隔一段时间就发起一个ajax请求。那么长轮询是什么呢?

    长轮询(Long Polling)是在Ajax轮询基础上做的一些改进,在没有更新的时候不再返回空响应,而且把连接保持到有更新的时候,客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。它是一个解决方案,但不是最佳的技术方案。

    如果说短轮询是客户端不断打电话问服务端有没有消息,服务端回复后立刻挂断,等待下次再打;长轮询是客户端一直打电话,服务端接到电话不挂断,有消息的时候再回复客户端并挂断。

    SSE(Server-Sent Events)与长轮询机制类似,区别是每个连接不只发送一个消息。客户端发送一个请求,服务端保持这个连接直到有新消息发送回客户端,仍然保持着连接,这样连接就可以支持消息的再次发送,由服务器单向发送给客户端。然而IE直到11都不支持,不多说了....

    1.2 WebSocket的特点

    为什么已经有了轮询还要WebSocket呢,是因为短轮询和长轮询有个缺陷:通信只能由客户端发起。

    那么如果后端想往前端推送消息需要前端去轮询,不断查询后端是否有新消息,而轮询的效率低且浪费资源(必须不停 setInterval 或 setTimeout 去连接,或者 HTTP 连接始终打开),WebSocket提供了一个文明优雅的全双工通信方案。一般适合于对数据的实时性要求比较强的场景,如通信、股票、直播、共享桌面,特别适合于客户端与服务频繁交互的情况下,如聊天室、实时共享、多人协作等平台。

    特点

    1. 建立在 TCP 协议之上,服务器端的实现比较容易。
    2. 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
    3. 数据格式比较轻量,性能开销小,通信高效。服务器与客户端之间交换的标头信息大概只有2字节;
    4. 可以发送文本,也可以发送二进制数据。
    5. 没有同源限制,客户端可以与任意服务器通信。
    6. 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。ex:ws://example.com:80/some/path
    7. 不用频繁创建及销毁TCP请求,减少网络带宽资源的占用,同时也节省服务器资源;
    8. WebSocket是纯事件驱动的,一旦连接建立,通过监听事件可以处理到来的数据和改变的连接状态,数据都以帧序列的形式传输。服务端发送数据后,消息和事件会异步到达。
    9. 无超时处理。

    HTTP与WS协议结构

    WebSocket协议标识符用ws表示。`wss协议表示加密的WebSocket协议,对应HTTPs协议。结构如下:

    • HTTP: TCP > HTTP
    • HTTPS: TCP > TLS > HTTP
    • WS: TCP > WS
    • WSS: TCP > TLS > WS

    以下是简单代码记录,亲测可用,基于springMVC的服务器推送技术websocket。

    1.gradle构建,websocket引入的包:"org.springframework:spring-websocket:4.2.4.RELEASE"

    2.context.xml配置需要加的地方

    xmlns:websocket="http://www.springframework.org/schema/websocket"

    http://www.springframework.org/schema/websocket
    http://www.springframework.org/schema/websocket/spring-websocket-4.1.xsd

    3.websocket的配置类

     4.websocke接受消息以及处理的类

    5.页面js代码

    6.简单的运行效果

     

     涉及到的代码就这几个,拷贝到springmvc项目里就可以。

      1 package com.kitty.activity.common.websocket;
      2 
      3 import org.springframework.stereotype.Service;
      4 import org.springframework.web.socket.CloseStatus;
      5 import org.springframework.web.socket.TextMessage;
      6 import org.springframework.web.socket.WebSocketMessage;
      7 import org.springframework.web.socket.WebSocketSession;
      8 import org.springframework.web.socket.handler.TextWebSocketHandler;
      9 
     10 import java.io.IOException;
     11 import java.util.HashMap;
     12 import java.util.Map;
     13 import java.util.Set;
     14 
     15 /**
     16  * Created by liuxn on 2018/5/22 0022.
     17  */
     18 @Service
     19 public class MyHandler extends TextWebSocketHandler {
     20     //在线用户列表
     21     private static final Map<Integer, WebSocketSession> users;
     22     //用户标识
     23     private static final String CLIENT_ID = "userId";
     24 
     25     static {
     26         users = new HashMap<>();
     27     }
     28 
     29     @Override
     30     public void afterConnectionEstablished(WebSocketSession session) throws Exception {
     31         System.out.println("成功建立连接");
     32         Integer userId = getClientId(session);
     33         if (userId != null) {
     34             users.put(userId, session);
     35             session.sendMessage(new TextMessage("你已成功建立socket连接"));
     36             System.out.println(userId);
     37             System.out.println(session);
     38         }
     39     }
     40 
     41     @Override
     42     public void handleTextMessage(WebSocketSession session, TextMessage message) {
     43         // ...
     44         System.out.println("收到客户端消息:"+message.getPayload());
     45 
     46         WebSocketMessage message1 = new TextMessage("server:"+message);
     47         try {
     48             session.sendMessage(message1);
     49         } catch (IOException e) {
     50             e.printStackTrace();
     51         }
     52     }
     53 
     54     @Override
     55     public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
     56         if (session.isOpen()) {
     57             session.close();
     58         }
     59         System.out.println("连接出错");
     60         users.remove(getClientId(session));
     61     }
     62 
     63     @Override
     64     public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
     65         System.out.println("连接已关闭:" + status);
     66         users.remove(getClientId(session));
     67     }
     68 
     69     /**
     70      * 发送信息给指定用户
     71      * @param clientId
     72      * @param message
     73      * @return
     74      */
     75     public boolean sendMessageToUser(Integer clientId, TextMessage message) {
     76         if (users.get(clientId) == null) return false;
     77         WebSocketSession session = users.get(clientId);
     78         System.out.println("sendMessage:" + session+",msg:"+message.getPayload());
     79         if (!session.isOpen()) return false;
     80         try {
     81             session.sendMessage(message);
     82         } catch (IOException e) {
     83             e.printStackTrace();
     84             return false;
     85         }
     86         return true;
     87     }
     88 
     89     /**
     90      * 广播信息
     91      * @param message
     92      * @return
     93      */
     94     public boolean sendMessageToAllUsers(TextMessage message) {
     95         boolean allSendSuccess = true;
     96         Set<Integer> clientIds = users.keySet();
     97         WebSocketSession session = null;
     98         for (Integer clientId : clientIds) {
     99             try {
    100                 session = users.get(clientId);
    101                 if (session.isOpen()) {
    102                     session.sendMessage(message);
    103                 }
    104             } catch (IOException e) {
    105                 e.printStackTrace();
    106                 allSendSuccess = false;
    107             }
    108         }
    109 
    110         return  allSendSuccess;
    111     }
    112 
    113     @Override
    114     public boolean supportsPartialMessages() {
    115         return false;
    116     }
    117 
    118     /**
    119      * 获取用户标识
    120      * @param session
    121      * @return
    122      */
    123     private Integer getClientId(WebSocketSession session) {
    124         try {
    125             Integer clientId = (Integer) session.getAttributes().get(CLIENT_ID);
    126             return clientId;
    127         } catch (Exception e) {
    128             return null;
    129         }
    130     }
    131 }
    MyHandler
     1 package com.kitty.activity.common.websocket;
     2 
     3 import org.springframework.beans.factory.annotation.Autowired;
     4 import org.springframework.stereotype.Controller;
     5 import org.springframework.web.bind.annotation.PathVariable;
     6 import org.springframework.web.bind.annotation.RequestMapping;
     7 import org.springframework.web.bind.annotation.ResponseBody;
     8 import org.springframework.web.servlet.ModelAndView;
     9 import org.springframework.web.socket.TextMessage;
    10 
    11 import javax.servlet.http.HttpSession;
    12 
    13  
    14 @Controller
    15 public class SocketController {
    16 
    17     @Autowired
    18     MyHandler handler;
    19 
    20     //玩家登录
    21     @RequestMapping("/external/login/{userId}")
    22     public ModelAndView login(HttpSession session, @PathVariable("userId") Integer userId) {
    23         System.out.println("登录接口,userId=" + userId);
    24         session.setAttribute("userId", userId);
    25         System.out.println(session.getAttribute("userId"));
    26 
    27         return new ModelAndView("phone/websocket_test");
    28     }
    29 
    30     //模拟给指定玩家发消息
    31     @RequestMapping("/external/message")
    32     @ResponseBody
    33     public String sendMessage(Integer userId,String message) {
    34         boolean hasSend = handler.sendMessageToUser(userId, new TextMessage(message));
    35         System.out.println(hasSend);
    36         return "success";
    37     }
    38 
    39 
    40     //模拟给所有玩家发消息
    41     @RequestMapping("/external/message/all")
    42     @ResponseBody
    43     public String sendAll(String message) {
    44         boolean hasSend = handler.sendMessageToAllUsers(new TextMessage(message));
    45         System.out.println(hasSend);
    46         return "success";
    47     }
    48 
    49 }
    SocketController
     1 package com.kitty.activity.common.websocket;
     2 
     3 import org.springframework.context.annotation.Bean;
     4 import org.springframework.context.annotation.Configuration;
     5 import org.springframework.web.socket.WebSocketHandler;
     6 import org.springframework.web.socket.config.annotation.EnableWebSocket;
     7 import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
     8 import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
     9 
    10  
    11 @Configuration
    12 @EnableWebSocket
    13 public class WebSocketConfig implements WebSocketConfigurer {
    14 
    15     @Override
    16     public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
    17         registry.addHandler(myHandler(), "/external/myHandler").addInterceptors(new WebSocketInterceptor());
    18     }
    19 
    20     @Bean
    21     public WebSocketHandler myHandler() {
    22         return new MyHandler();
    23     }
    24 }
    WebSocketConfig
     1 package com.kitty.activity.common.websocket;
     2 
     3 import org.springframework.http.server.ServerHttpRequest;
     4 import org.springframework.http.server.ServerHttpResponse;
     5 import org.springframework.http.server.ServletServerHttpRequest;
     6 import org.springframework.web.socket.WebSocketHandler;
     7 import org.springframework.web.socket.server.HandshakeInterceptor;
     8 
     9 import javax.servlet.http.HttpSession;
    10 import java.util.Map;
    11 
    12 /**
    13  * 
    14  */
    15 public class WebSocketInterceptor implements HandshakeInterceptor {
    16 
    17     @Override
    18     public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler, Map<String, Object> map) throws Exception {
    19         if (request instanceof ServletServerHttpRequest) {
    20             System.out.println("*****beforeHandshake******");
    21             ServletServerHttpRequest serverHttpRequest = (ServletServerHttpRequest) request;
    22             HttpSession session = serverHttpRequest.getServletRequest().getSession();
    23 //            Map parameterMap = serverHttpRequest.getServletRequest().getParameterMap();
    24 //            System.out.println(parameterMap);
    25             if (session != null) {
    26                 map.put("userId", session.getAttribute("userId"));
    27             }
    28 
    29         }
    30         return true;
    31     }
    32 
    33     @Override
    34     public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
    35         System.out.println("******afterHandshake******");
    36     }
    37 }
    WebSocketInterceptor
     1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
     2 <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
     3 <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
     4 <c:set var="ctx" value="${pageContext.request.contextPath}"/>
     5 <html>
     6 <head>
     7     <meta charset="utf-8">
     8     <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
     9     <meta content="yes" name="apple-mobile-web-app-capable">
    10     <meta content="black" name="apple-mobile-web-app-status-bar-style">
    11     <meta content="telephone=no" name="format-detection">
    12     <meta content="email=no" name="format-detection">
    13     <meta name="baseUrl" content="${ctx}"/>
    14     <meta name="roleId" content="${roleId}"/>
    15     <title>websocket</title>
    16     <script src="${ctx}/assets/js/jquery.min.js"></script>
    17     <script src="http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js"></script>
    18 
    19 </head>
    20 
    21 <body>
    22 <div class="act-outline" width="100%">
    23     it is work.
    24 </div>
    25 <script>
    26     //websocket
    27     $(function () {
    28         var baseUrl = $('meta[name="baseUrl"]').attr("content");
    29         //判断当前浏览器是否支持WebSocket
    30         var websocket;
    31         if ('WebSocket' in window) {
    32             websocket = new WebSocket('ws://' + window.location.host + '/activity/external/myHandler');
    33             //      var ws = new WebSocket('ws://192.168.3.26:8999/activity/external/myHandler') //也可以指定ip
    34         } else if ('MozWebSocket' in window) {
    35             websocket = new MozWebSocket("ws://" + window.location.host + '/activity/external/myHandler'); //未测试
    36         } else {
    37             websocket = new SockJS("http://" + window.location.host + '/activity/external/myHandler'); //未测试
    38         }
    39 
    40         websocket.onopen = function () {
    41                     console.log("正在打开连接,准备发消息给服务器...");
    42             websocket.send("{text:hello}");
    43         }
    44         websocket.onclose = function () {
    45                     console.log("服务器关闭连接:onclose");
    46         }
    47 
    48         websocket.onmessage = function (msg) {
    49                     console.log("收到服务器推送数据:"+msg.data);
    50         }
    51 
    52 
    53     })
    54 </script>
    55 </body>
    56 </html>
    jsp脚本页面

     备注:http://ip:port/activity 是项目根目录。

    参考链接地址:

    https://blog.csdn.net/u014520745/article/details/62046396

    https://www.cnblogs.com/interdrp/p/7903736.html

  • 相关阅读:
    Eclipse安装Hadoop插件
    (转)Ubuntu14.0.4中hadoop2.4.0伪分布模式配置
    Hadoop--DataNode无法启动
    启动与关闭hadoop
    hadoop中执行命令时发生错误
    strings命令
    Deriving data from ElasticSearch Engine
    elasticsearch data importing
    reading words in your computer and changing to female voice, linux festival text2wave saving wav files
    DDNS client on a Linux machine
  • 原文地址:https://www.cnblogs.com/shown/p/9076113.html
Copyright © 2011-2022 走看看