zoukankan      html  css  js  c++  java
  • websocket初探

    过去都是使用浏览器端插件(如flash和java applet)与服务器建立套接字通信,这些插件技术曾经广泛应用在网页聊天和网页游戏,html5提供的websocket有取代这些插件的趋势。

    在websocket之前,实现服务器向浏览器推送消息有两种方式:ajax轮询和comet长轮询。

    • ajax轮询(polling):浏览器端不停地向服务器发起请求,问:“有新数据没有?”,不管有没有数据它都会返回来。这种方式不管对客户端还是对服务器都是巨大的压力。
    • comet技术(long-polling):浏览器发起一次请求,与服务器建立一个长连接,直到数据来临时才回复浏览器。一个请求结束之后,浏览器会马上发起另一个请求。这种技术的缺点是需要服务器维护的请求数很多。相当于有很多请求在同时发生。这种技术依旧流行。

    一、Java中的websocket库

    websocket是java标准库的一部分,位于javax包下,但它只是定义一些接口。
    websocket有不同的实现,如Tomcat的,jetty的,Spring的,还有一个名叫TooTallNate组织发布的java-websocket库,atmosphere库,socket.io的java版本等。

    二、Python中的websocket库

    websocket协议本身并不复杂,100行Python代码可以实现简单的websocket协议。
    Python中最基础的websocket库是gevent.websocket。使用Flask的人们对这个库进行了一下封装,让它变得更好用,这个库名叫flask-sockets。
    还有一个封装得比较重的库flask-socketio。
    但是我运行flask-socketio和flask-sockets这两个库都失败了,gevent-websocket运行成功了。

    在JS中有一个库socketio,这个库能够兼容各个版本的浏览器,是对websocket的封装,在网页端用这个库应该是最佳选择。

    三、旧版的tomcat的websocket

    在javax.websocket接口出来之前,tomcat7就已经对websocket提供支持了。于是在javax.websocket出来之后,tomcat8就开始废弃tomcat7中定义的websocket,tomcat7关于websocket的包位于org.apache.catalina.websocket中。

    包org.apache.catalina.websocket中的这些类为WebSocket开发服务端提供了支持,这些类的主要功能简述如下:

    1、Constants:包org.apache.catalina.websocket中用到的常数定义在这个类中,它只包含静态常数定义,无任何逻辑实现。
    2、MessageInbound:基于消息的WebSocket实现类(带内消息),应用程序应当扩展这个类并实现其抽象方法onBinaryMessage和onTextMessage。
    3、StreamInbound:基于流的WebSocket实现类(带内流),应用程序应当扩展这个类并实现其抽象方法onBinaryData和onTextData。
    4、WebSocketServlet:提供遵循RFC6455的WebSocket连接的Servlet基本实现。客户端使用WebSocket连接服务端时,需要将WebSocketServlet的子类作为连接入口。同时,该子类应当实现WebSocketServlet的抽象方法createWebSocketInbound,以便创建一个inbound实例(MessageInbound或StreamInbound)。
    5、WsFrame:代表完整的WebSocket框架。
    6、WsHttpServletRequestWrapper:包装过的HttpServletRequest对象。
    7、WsInputStream:基于WebSocket框架底层的socket的输入流。
    8、 WsOutbound:提供发送消息到客户端的功能。它提供的所有向客户端的写方法都是同步的,可以防止多线程同时向客户端写入数据。

    它的典型写法如下:

    public class ChatWebSocketServlet extends WebSocketServlet {  
      
        private static final long serialVersionUID = 1L;  
      
        private static final String GUEST_PREFIX = "Guest";  
      
        private final AtomicInteger connectionIds = new AtomicInteger(0);  
        private final Set<ChatMessageInbound> connections =  
                new CopyOnWriteArraySet<ChatMessageInbound>();  
        // 创建Inbound实例,WebSocketServlet子类必须实现的方法  
        @Override  
        protected StreamInbound createWebSocketInbound(String subProtocol,  
                HttpServletRequest request) {  
            return new ChatMessageInbound(connectionIds.incrementAndGet());  
        }  
        // MessageInbound子类,完成收到WebSocket消息后的逻辑处理  
        private final class ChatMessageInbound extends MessageInbound {  
      
            private final String nickname;  
      
            private ChatMessageInbound(int id) {  
                this.nickname = GUEST_PREFIX + id;  
            }  
            // Open事件  
            @Override  
            protected void onOpen(WsOutbound outbound) {  
                connections.add(this);  
                String message = String.format("* %s %s",  
                        nickname, "has joined.");  
                broadcast(message);  
            }  
            // Close事件  
            @Override  
            protected void onClose(int status) {  
                connections.remove(this);  
                String message = String.format("* %s %s",  
                        nickname, "has disconnected.");  
                broadcast(message);  
            }  
            // 二进制消息事件  
            @Override  
            protected void onBinaryMessage(ByteBuffer message) throws IOException {  
                throw new UnsupportedOperationException(  
                        "Binary message not supported.");  
            }  
            // 文本消息事件  
            @Override  
            protected void onTextMessage(CharBuffer message) throws IOException {  
                // Never trust the client  
                String filteredMessage = String.format("%s: %s",  
                        nickname, HTMLFilter.filter(message.toString()));  
                broadcast(filteredMessage);  
            }  
            // 向所有已连接的客户端发送文本消息(广播)  
            private void broadcast(String message) {  
                for (ChatMessageInbound connection : connections) {  
                    try {  
                        CharBuffer buffer = CharBuffer.wrap(message);  
                        connection.getWsOutbound().writeTextMessage(buffer);  
                    } catch (IOException ignore) {  
                        // Ignore  
                    }  
                }  
            }  
        }  
    

    四、javax.websocket定义的函数参数

    IllegalArgumentException
    No payload parameter present on the method[message]
    

    意思是该有的参数没有,比如

    • onError()必须有Throwable参数
    • onMessage()必须有String message参数或者ByteBuffer类型的参数来接受消息

    沿着抛出这个异常的异常栈逐个打开源代码,会看见容器初始化ServerEndPoint的每个细节,以及对其函数的解析.
    onOpen(EndpointConfig)
    onClose(CloseReason)
    onError(Throwable)
    onMessage(PhongMessage | InputStream | byte[] | ByteBuffer | Reader | String,boolean isLastMessage)

    上面这些是有且仅能包含的参数,其中onMessage必须接受一种数据类型的数据,可以是Reader(接受文本),也可以是InputStream(二进制).PhongMessage是处理ping信息的.byte[]和ByteBuffer都是对InputStream进行了一下读取,String是对Reader进行了一下读取.
    OnOpen和OnError函数不能有String类型的参数,因为它们只能包含以上类型的参数,如果OnOpen和OnError有String类型的参数,则只能是@PathParam注解的String类型的参数,否则报错A parameter of type [class java.lang.String] was found on method[error] of class [java.lang.reflect.Method] that did not have a @PathParam annotation

    五、TooTallNate-java-websocket

    这个库100%用Java实现,基于nio。它包含了一个websocket服务器和一个websocket客户端。官方仓库包含丰富的代码示例。这个库并不符合javax.websocket接口,它纯粹是民间的websocket实现。
    服务器端需要实例化WebSocketServer这个类,可以覆盖它的onOpen,onMessage等函数。
    客户端除了可以是网页,也可以是Java。用Java实现websocket客户端需要实例化WebSocketClient这个类。

    Tomcat的websocket实现能够很好地跟整个web应用融为一体,比如websocket和web应用可以共用8080端口。如果用TooTallNate-java-websocket,那就必须用两个端口了。

  • 相关阅读:
    深入理解yield from语法
    数据库事务并发问题,锁机制和对应的4种隔离级别
    同源策略与CORS跨域请求
    Restful 4 -- 认证组件、权限组件、频率组件、url注册器、响应器、分页器
    Restful 3 -- 序列化组件(GET/PUT/DELETE接口设计)、视图优化组件
    Restful 2 --DRF解析器,序列化组件使用(GET/POST接口设计)
    Restful 1 -- REST、DRF(View源码解读、APIView源码解读)及框架实现
    Vue(7)- vue-cookies、极验滑动验证geetest、vue-router的导航守卫
    Vue --6 router进阶、单页面应用(SPA)带来的问题
    Vue 5 -- axios、vuex
  • 原文地址:https://www.cnblogs.com/weiyinfu/p/6287117.html
Copyright © 2011-2022 走看看