• cometd源码阅读WebSocketTransport初始化(六)


    说明

    comted的websocket实现是使用jetty的文档地址:jetty文档

    Transprot初始化时机参考cometd源码阅读-初始化(二) <1>处 会调用transport 的init方法

    org.cometd.server.BayeuxServerImpl#initializeServerTransports

    protected void initializeServerTransports() {
            if (_transports.isEmpty()) {
                //初始化Transport 没重定义则创建默认 指定了则创建指定的 注:反射创建 会传入bayeux
                String option = (String)getOption(TRANSPORTS_OPTION);
                if (option == null) {
                    //未定义则初始化处理websocket 和长轮询的Transport处理器 JSONP的处理器
                    // Order is important, see #findHttpTransport()
                    //
                    ServerTransport transport = newWebSocketTransport();
                    if (transport != null) {
                        addTransport(transport);
                    }
                    addTransport(newJSONTransport());
                    addTransport(new JSONPTransport(this));
                } else {
                    //如果有进行类的全名称配置 根据累的全名称创建
                    for (String className : option.split(",")) {
                        ServerTransport transport = newServerTransport(className.trim());
                        if (transport != null) {
                            addTransport(transport);
                        }
                    }
    
                    if (_transports.isEmpty()) {
                        throw new IllegalArgumentException("Option '" + TRANSPORTS_OPTION +
                                "' does not contain a valid list of server transport class names");
                    }
                }
            }
    
            //如果没有配置_allowedTransports 将transport加入到 _allowedTransports//liqiangtodo 暂时不晓得干嘛的
            if (_allowedTransports.isEmpty()) {
                String option = (String)getOption(ALLOWED_TRANSPORTS_OPTION);
                if (option == null) {
                    _allowedTransports.addAll(_transports.keySet());
                } else {
                    for (String transportName : option.split(",")) {
                        if (_transports.containsKey(transportName)) {
                            _allowedTransports.add(transportName);
                        }
                    }
    
                    if (_allowedTransports.isEmpty()) {
                        throw new IllegalArgumentException("Option '" + ALLOWED_TRANSPORTS_OPTION +
                                "' does not contain at least one configured server transport name");
                    }
                }
            }
    
            //逐个调用transport init方法完成Transport的初始化 Transport 内部的相关参数自定义配置可以通过Option拿到
            List<String> activeTransports = new ArrayList<>();
            for (String transportName : _allowedTransports) {
                ServerTransport serverTransport = getTransport(transportName);
                if (serverTransport instanceof AbstractServerTransport) {
                    //调用init方法进行初始化<1>
                    ((AbstractServerTransport)serverTransport).init();
                    //加入到已激活的transpor
                    activeTransports.add(serverTransport.getName());
                }
            }
            if (_logger.isDebugEnabled()) {
                _logger.debug("Active transports: {}", activeTransports);
            }
        }

    源码

    <1>

    org.cometd.server.websocket.javax.WebSocketTransport#init

    
    
    //配置的websoket连接地址可参考点击跳转>
    public static
    final String COMETD_URL_MAPPING_OPTION = "cometdURLMapping";
    public static final String IDLE_TIMEOUT_OPTION = "idleTimeout";
    @Override
        public void init() {
    //<2>先调用父类的init方法
    super.init(); ServletContext context = (ServletContext)getOption(ServletContext.class.getName()); if (context == null) { throw new IllegalArgumentException("Missing ServletContext"); } String cometdURLMapping = (String)getOption(COMETD_URL_MAPPING_OPTION); if (cometdURLMapping == null) { throw new IllegalArgumentException("Missing '" + COMETD_URL_MAPPING_OPTION + "' parameter"); } /** * org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer.initialize * jetty的webSocket模块 */ ServerContainer container = (ServerContainer)context.getAttribute(ServerContainer.class.getName()); if (container == null) { throw new IllegalArgumentException("Missing WebSocket ServerContainer"); } // JSR 356 does not support a input buffer size option //从option配置获取最大接收文本的缓冲区大小 从option获取 int maxMessageSize = getMaxMessageSize(); if (maxMessageSize < 0) { maxMessageSize = container.getDefaultMaxTextMessageBufferSize(); } container.setDefaultMaxTextMessageBufferSize(maxMessageSize); //从option获取在多久没收收到输入后链接关闭 long idleTimeout = getOption(IDLE_TIMEOUT_OPTION, container.getDefaultMaxSessionIdleTimeout()); container.setDefaultMaxSessionIdleTimeout(idleTimeout); String protocol = getProtocol(); List<String> protocols = protocol == null ? null : Collections.singletonList(protocol); //<4> Configurator configurator = new Configurator(context); //根据configurator 自定义配置处理器用于设置websocket mapping 以及接收连接 关闭 消息 等事件 org.cometd.server.websocket.javax.WebSocketEndPoint //遍历注册端点 ,号隔开可配置多个
    for (String mapping : normalizeURLMapping(cometdURLMapping)) {
    //<5> ServerEndpointConfig config
    = ServerEndpointConfig.Builder.create(WebSocketEndPoint.class, mapping) .subprotocols(protocols) .configurator(configurator) .build(); try { container.addEndpoint(config); } catch (DeploymentException x) { throw new RuntimeException(x); } } }

    <2>

    org.cometd.server.websocket.common.AbstractWebSocketTransport#init

        public static final String PROTOCOL_OPTION = "protocol";
        public static final String MESSAGES_PER_FRAME_OPTION = "messagesPerFrame";
        public static final String REQUIRE_HANDSHAKE_PER_CONNECTION_OPTION = "requireHandshakePerConnection";
        @Override
        public void init() {
            //<3>调用父类的init方法
            super.init();
            _protocol = getOption(PROTOCOL_OPTION, null);
            _messagesPerFrame = getOption(MESSAGES_PER_FRAME_OPTION, 1);
            _requireHandshakePerConnection = getOption(REQUIRE_HANDSHAKE_PER_CONNECTION_OPTION, false);
        }

    <3>

    org.cometd.server.AbstractServerTransport#init

    public static final String TIMEOUT_OPTION = "timeout";
    public static final String INTERVAL_OPTION = "interval";
    public static final String MAX_INTERVAL_OPTION = "maxInterval";
    public static final String MAX_PROCESSING_OPTION = "maxProcessing";
    public static final String MAX_LAZY_TIMEOUT_OPTION = "maxLazyTimeout";
    public static final String META_CONNECT_DELIVERY_OPTION = "metaConnectDeliverOnly";
    public static final String MAX_QUEUE_OPTION = "maxQueue";
    public static final String JSON_CONTEXT_OPTION = "jsonContext";
    public static final String HANDSHAKE_RECONNECT_OPTION = "handshakeReconnect";
    public static final String ALLOW_MESSAGE_DELIVERY_DURING_HANDSHAKE = "allowMessageDeliveryDuringHandshake";
     /**
         * Initializes the transport, resolving default and direct options.
         *初始化传输,解析默认和直接选项。
         */
        public void init() {
            _interval = getOption(INTERVAL_OPTION, _interval);
            _maxInterval = getOption(MAX_INTERVAL_OPTION, _maxInterval);
            _timeout = getOption(TIMEOUT_OPTION, _timeout);
            _maxLazyTimeout = getOption(MAX_LAZY_TIMEOUT_OPTION, _maxLazyTimeout);
            _metaConnectDeliveryOnly = getOption(META_CONNECT_DELIVERY_OPTION, _metaConnectDeliveryOnly);
            _jsonContext = (JSONContextServer)getOption(JSON_CONTEXT_OPTION);
            _handshakeReconnect = getOption(HANDSHAKE_RECONNECT_OPTION, false);
            _allowHandshakeDelivery = getOption(ALLOW_MESSAGE_DELIVERY_DURING_HANDSHAKE, false);
            _maxMessageSize = getOption(MAX_MESSAGE_SIZE_OPTION, -1);
        }

    <4>

    org.cometd.server.websocket.javax.WebSocketTransport.Configurator

      private class Configurator extends ServerEndpointConfig.Configurator {
            private final ServletContext servletContext;
    
            private Configurator(ServletContext servletContext) {
                this.servletContext = servletContext;
            }
    
            /**
             * websocket握手 我们可以自定义请求头
             * sec.put("trace_id",uuid)
             * response.put("trace_id,uuid)
             * @param sec
             * @param request
             * @param response
             */
            @Override
            public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
                BayeuxContextHolder context = provideContext();
                context.bayeuxContext = new WebSocketContext(servletContext, request, sec.getUserProperties());
                //WebSocketTransport钩子方法
                WebSocketTransport.this.modifyHandshake(request, response);
            }
    
            /**
             * 用于判断请求是否websocket接口
             * @param originHeaderValue 为请求头的org Origin: http://localhost:8080
             * @return
             */
            @Override
            public boolean checkOrigin(String originHeaderValue) {
                //内部写死的传的true
                return WebSocketTransport.this.checkOrigin(originHeaderValue);
            }
    
            /**
             * 如果提供了子协议,它将用于 WebSocket 升级响应标头Sec-WebSocket-Protocol
             * supported为请求头Sec-WebSocket-Protocol
             * 应是用来判断是否支持此协议
             * @param supported
             * @param requested
             * @return
             */
            @Override
            public String getNegotiatedSubprotocol(List<String> supported, List<String> requested) {
                BayeuxContextHolder context = provideContext();
                context.protocolMatches = checkProtocol(supported, requested);
                if (context.protocolMatches) {
                    return super.getNegotiatedSubprotocol(supported, requested);
                }
                LOGGER.warn("Could not negotiate WebSocket SubProtocols: server{} != client{}", supported, requested);
                return null;
            }
    
            /**
             * 返回的值用于 WebSocket 升级响应标头Sec-WebSocket-Extensions
             * @param installed
             * @param requested
             * @return
             */
            @Override
            public List<Extension> getNegotiatedExtensions(List<Extension> installed, List<Extension> requested) {
                Set<Extension> negotiated = new LinkedHashSet<>();
                for (Extension requestedExtension : requested) {
                    String name = requestedExtension.getName();
                    boolean option = getOption(ENABLE_EXTENSION_PREFIX_OPTION + name, true);
                    if (option) {
                        for (Extension installedExtension : installed) {
                            if (installedExtension.getName().equals(name)) {
                                negotiated.add(requestedExtension);
                                break;
                            }
                        }
                    }
                }
                return new ArrayList<>(negotiated);
            }
    
            /**
             * 这也是cometd的切入点 当客户端 申请建立连接 创建的处理器 这个处理器处理器可以接收连接打开  关闭 关闭等事件 多例每次创建链接都会调用一次
             * 如果在任何时候您不希望升级此请求,只需从getEndpointInstance类中抛出异常即可。我推荐一个 java.lang.InstantiationException。这将导致 Jetty 不执行升级并将请求发送到 servlet 处理链。
             * @param endpointClass
             * @param <T>
             * @return
             * @throws InstantiationException
             */
            @Override
            @SuppressWarnings("unchecked")
            public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException {
                //初始化BayeuxContextHolder  后续线程共享
                BayeuxContextHolder holder = provideContext();
                if (!getBayeux().getAllowedTransports().contains(getName())) {
                    throw new InstantiationException("Transport not allowed");
                }
                if (!holder.protocolMatches) {
                    throw new InstantiationException("Could not negotiate WebSocket SubProtocols");
                }
                T instance = (T)newWebSocketEndPoint(holder.bayeuxContext);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Created {}", instance);
                }
                holder.clear();
                return instance;
            }
    
            private boolean checkProtocol(List<String> serverProtocols, List<String> clientProtocols) {
                if (serverProtocols.isEmpty()) {
                    return true;
                }
    
                for (String clientProtocol : clientProtocols) {
                    if (serverProtocols.contains(clientProtocol)) {
                        return true;
                    }
                }
                return false;
            }
    
            private BayeuxContextHolder provideContext() {
                BayeuxContextHolder holder = BayeuxContextHolder.holder.get();
                if (holder == null) {
                    holder = new BayeuxContextHolder();
                    holder.clear();
                    BayeuxContextHolder.holder.set(holder);
                }
                return holder;
            }
        }
    
        private static class BayeuxContextHolder {
            private static final ThreadLocal<BayeuxContextHolder> holder = new ThreadLocal<>();
            private WebSocketContext bayeuxContext;
            private boolean protocolMatches;
    
            public void clear() {
                BayeuxContextHolder.holder.set(null);
                bayeuxContext = null;
                // Use a sensible default in case getNegotiatedSubprotocol() is not invoked.
                protocolMatches = true;
            }
        }
    
        private class EndPoint extends WebSocketEndPoint {
            private EndPoint(BayeuxContext bayeuxContext) {
                super(WebSocketTransport.this, bayeuxContext);
            }
    
            @Override
            protected void writeComplete(AbstractWebSocketEndPoint.Context context, List<ServerMessage> messages) {
                WebSocketTransport.this.writeComplete(context, messages);
            }
        }

    <5>

    基于jetty的websoket扩展 最终对应事件转发到comted内部做处理

    org.cometd.server.websocket.javax.WebSocketEndPoint

    public class WebSocketEndPoint extends Endpoint implements MessageHandler.Whole<String> {
        private final Logger _logger = LoggerFactory.getLogger(getClass());
        private final AbstractWebSocketEndPoint _delegate;
        private volatile Session _wsSession;
    
        public WebSocketEndPoint(AbstractWebSocketTransport transport, BayeuxContext bayeuxContext) {
            //通过delegate封装transport和bayeuxContext 相关相关监听处理委托给deletegate
            _delegate = new Delegate(transport, bayeuxContext);
        }
    
        @Override
        public void onOpen(Session wsSession, EndpointConfig config) {
            _wsSession = wsSession;
            //设置当前session的处理handle 监听消息,关闭事件
            wsSession.addMessageHandler(this);
        }
    
        @Override
        public void onMessage(String data) {
            if (_logger.isDebugEnabled()) {
                _logger.debug("WebSocket Text message on {}", this);
            }
            try {
                try {
                    Promise.Completable<Void> completable = new Promise.Completable<>();
                    _delegate.onMessage(data, completable);
                    // Wait, to apply backpressure to the client.
                    completable.get();
                } catch (ExecutionException x) {
                    throw x.getCause();
                }
            } catch (Throwable failure) {
                if (_logger.isDebugEnabled()) {
                    _logger.debug("", failure);
                }
                _delegate.close(1011, failure.toString());
            }
        }
    
        @Override
        public void onClose(Session wsSession, CloseReason closeReason) {
            _delegate.onClose(closeReason.getCloseCode().getCode(), closeReason.getReasonPhrase());
        }
    
        @Override
        public void onError(Session wsSession, Throwable failure) {
            _delegate.onError(failure);
        }
    
        protected void writeComplete(AbstractWebSocketEndPoint.Context context, List<ServerMessage> messages) {
        }
    
        @Override
        public String toString() {
            return String.format("%s@%x[%s]", getClass().getSimpleName(), hashCode(), _delegate);
        }
    
        private class Delegate extends AbstractWebSocketEndPoint {
            public Delegate(AbstractWebSocketTransport transport, BayeuxContext bayeuxContext) {
                super(transport, bayeuxContext);
            }
    
            @Override
            protected void send(ServerSession session, String data, Callback callback) {
                if (_logger.isDebugEnabled()) {
                    _logger.debug("Sending {} on {}", data, this);
                }
                // Async write.
                _wsSession.getAsyncRemote().sendText(data, result -> {
                    Throwable failure = result.getException();
                    if (failure == null) {
                        callback.succeeded();
                    } else {
                        callback.failed(failure);
                    }
                });
            }
    
            @Override
            public void close(int code, String reason) {
                try {
                    // Limits of the WebSocket APIs, otherwise an exception is thrown.
                    reason = reason.substring(0, Math.min(reason.length(), 30));
                    if (_logger.isDebugEnabled()) {
                        _logger.debug("Closing {}/{} on {}", code, reason, this);
                    }
                    _wsSession.close(new CloseReason(CloseReason.CloseCodes.getCloseCode(code), reason));
                } catch (Throwable x) {
                    _logger.trace("Could not close WebSocket session on {}", this, x);
                }
            }
    
            @Override
            protected void writeComplete(Context context, List<ServerMessage> messages) {
                WebSocketEndPoint.this.writeComplete(context, messages);
            }
    
            @Override
            public String toString() {
                return String.format("%s[%s]", super.toString(), _wsSession);
            }
        }
    }
  • 相关阅读:
    Overview | POCO C++ Libraries
    simple.c
    Classes for Writing HTTP Clients in C++ CodeProject
    APScheduler 2.0.3 : Python Package Index
    neon HTTP and WebDAV client library
    HTTP Client C API
    vi编辑器的学习使用(二十一)
    自由软件的定义
    vi编辑器的学习使用(二十三))
    vi编辑器的学习使用(二十二)
  • 原文地址:https://www.cnblogs.com/LQBlog/p/16575739.html
Copyright © 2020-2023  润新知