websocket

作者: 鹏程1995 | 来源:发表于2020-02-11 17:22 被阅读0次

    简述

    大四狗,正在外边实习,前一段时间部门轮流做技术分享,轮到我了。恰好有一个项目要加功能,大概需求是任务发布者A向另一个人B发布任务。

    • 如果B在线,则应该及时收到新任务提醒;
    • 如果B不在,则在登陆时应显示新任务提醒。所有的提示均为系统级弹窗。

    大概的要求就是这些,第二点很简单,登陆后扫一遍就OK。第一个就有些问题了,在不刷新页面的情况下获得通知,在以前都是后台使用ajax轮询的,对服务器的压力比较大。boss说用WebSocket搞一下。我在9月中旬看了两天的websocket,后来就放下了,这几天又捡了捡。下面将WebSocket和轮询作比较,从为什么用WebSocketWebSocket工作原理,基于Spring MVC的WebSocket搭建三个方面说一下。

    正题

    为什么用WebSocket

    几种解决方案

    轮询

    轮询分为长轮询和短轮询。

    • 短轮询指的是浏览器端每隔一定时间向服务器发出ajax请求,查询状态有没有变化。服务器直接返回结果。
    • 长轮询指的是浏览器发出ajax请求查询状态,如果状态没有变化【没有新消息】,服务器不立刻返回,而是保持连接直到超时之前,在这一段时间如果有新消息则立刻返回,如果没有新消息,则在超时之前返回一个没有新消息的response。浏览器端在每接收到一个回复后都立刻发起下一次ajax查询,以此来完成监听。

    短轮询过程图

    1.png

    长轮询过程图

    2.png

    长连接

    • 长连接是和短连接对应的。
    • 长连接指的是在一次TCP连接中传输多个数据包,在连接保持期间,如果没有数据包发送,需要双方发送链路检测包【又叫心跳包】
    • 短连接指的是当双方有数据交互时,就建立连接,数据发送完成后马上断开连接。也就是说每次连接只完成一项业务

    通俗的来说,长连接的连接是要保持一段时间的,短连接用完就丢。长短连接和上边的长短轮询是不一样的:

    长短轮询指的是服务器端是否及时返回浏览器端消息。长短连接指的是建立的TCP连接是否保持。轮询是长还是短主要取决于服务器端的处理策略;连接是长是短取决于浏览器和服务器双方,这个要看通讯协议的。

    举个栗子,拿HTTP1.0和HTTP1.1来说吧,在HTTP1.1中新加了一个keep-alive字段。在传统的HTTP1.0通讯协议中,用户发起HTTP请求时会经历一下过程:

    1. 用户和服务器三次握手,建立TCP连接。
    2. 用户发送HTTP请求request
    3. 服务器返回响应response
    4. 用户和服务器四次握手,断开TCP连接。

    HTTP1.0时序图


    3.png

    在HTTP1.1中,有一个keep-alive,默认情况下,采用是长连接,我们不妨假设HTTP1.1的keep-alive设置的时间是m,对应的TCP也有一个keep-alive,设置的时间是n。此时用户发起HTTP请求的过程就变成了下面这样:

    1. 用户和服务器三次握手,建立TCP连接。
    2. 用户发送HTTP请求request。
    3. 服务器返回响应response。
    4. 从此开始计时,m时间内如果没有HTTP请求则四次握手断开TCP连接。如果之前已经有到计时了,则刷新计时为m
    5. 用户有新的HTTP请求,直接跳到第2步。

    在2,3,4,5过程中,连接如果长时间没有发送或者接收消息,服务器和浏览器为了确定互相都在线,就在TCP中约定了一个keep-alive字段。上边设置keep-alive为n,也就是说当TCP连接在n时间内没有通讯的话,就用心跳包来保活。

    HTTP1.1时序图


    4.png

    WebSocket工作原理

    其实上边已经解释的差不多了,HTTP1.1虽然有keep-alive字段,但是长连接只是保持了TCP不立刻断开,在短时间内多次请求的情况下是TCP连接可以复用。但是HTTP通讯协议服务器只能被动接受,还是不能主动发送消息。

    于是在H5规范中就提出来一个新的通讯协议:WebSocket协议。WebSocket是一个基于TCP连接的全双工的通讯协议,在2011年被列入标准。有人把WebSocker当作HTTP协议长连接的“大补丁”。

    WebSocket,HTTP,TCP的关系

    5.png

    图中WebSocket和HTTP之间有一些交互,这是因为WebSocket是通过HTTP协议进行一次握手。握手成功后就可以正常通讯了。

    这里还有一个问题,也是学习了WebSocket后大家都经常问你的问题:

    WebSocket和Socket的区别是什么?

    网络分为七层:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。

    IP是工作在网络层的,通过IP可以唯一确定网络中的一台主机,TCP/UDP工作在传输层,通过协议和端口号可以唯一确定主机中的一个进程。所以有了TCP/IP协议进行不同主机的进程间的通讯。但是TCP/IP协议很复杂,所有有了socket,把下层的复杂的TCP/IP协议屏蔽掉,向上层提供简单的接口。

    WebSocket工作在应用层,是一个完整的通讯协议。

    两者没有任何关系。

    基于Spring MVC的WebSocket搭建

    创建WebSocket处理类

    Spring在org.springframework.web.socket包里提供了WebSocketHandeler接口。

    并在org.springframework.web.socket.handler包里提供了实现了此接口的可以继承的类:

    TextWebSocketHandler                   
        //    ----------文本收发,可用来传信息
    BinaryWebSocketHandler           
        //    ----------二进制文件收发,可用作上传下载
    AbstractWebSocketHandler        
        //    ----------前两者的父类,实现了WebSocketHandeler接口,并列出了前两者的所有方法
    
    //WebSocketHandeler接口
    package org.springframework.web.socket;
    
    public interface WebSocketHandler {
        void afterConnectionEstablished(WebSocketSession var1) throws Exception;
    
        void handleMessage(WebSocketSession var1, WebSocketMessage<?> var2) throws Exception;
    
        void handleTransportError(WebSocketSession var1, Throwable var2) throws Exception;
    
        void afterConnectionClosed(WebSocketSession var1, CloseStatus var2) throws Exception;
    
        boolean supportsPartialMessages();
    }
    

    创建握手接口

    Spring在org.springframework.web.socket包里提供了HandshakeInterceptor接口。

    并在org.springframework.web.socket.server.support包里提供了实现了此接口的可以继承的类:

    <pre>HttpSessionHandshakeInterceptor
    // ----------通过http握手时使用,
    可以用来查验httpSession中的登陆数据
    OriginHandshakeInterceptor
    // ----------面向可访问来源的筛选</pre>

    下面是HandshakeInterceptor的接口

    <pre>public interface HandshakeInterceptor {
    boolean beforeHandshake(ServerHttpRequest var1, ServerHttpResponse var2, WebSocketHandler var3, Map<String, Object> var4) throws Exception;
    void afterHandshake(ServerHttpRequest var1, ServerHttpResponse var2, WebSocketHandler var3, Exception var4);}</pre>

    处理类和握手协议的配置

    Spring在org.springframework.web.socket.config.annotation包里提供了WebSocketConfigurer接口。实现这个接口并实现registerWebSocketHandlers(WebSocketHandlerRegistrywebSocketHandlerRegistry)方法,配置websocket入口,允许访问的域、注册Handler、SockJs支持和拦截器。

    public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
            webSocketHandlerRegistry
                    .addHandler(handler,"/aWebSocket")//添加一个处理器还有定义处理器的处理路径
                    .addInterceptors(handlerShakerInceptor)//添加一个过滤器
                    .setAllowedOrigins("*");
    
        }
    

    附录里有完整的项目代码。给整个项目我觉得比在这里粘代码要好得多,就不粘了。

    相关文章

      网友评论

          本文标题:websocket

          本文链接:https://www.haomeiwen.com/subject/yybxfhtx.html