• 多页签websocket 共享


    网上搜索类似的问题方法讲的都很不错,以下是一个简答说明

    问题来源

    主要是看到atmosphere 的js client 看到支持多页签websocket 共享,比较好奇,顺带看了下实现机制,发现居然是基于了
    localStorage的storage event 实现上还是比较巧妙,功能还是很强大的

    参考处理

     
    function _execute() {
                    // Shared across multiple tabs/windows.
                    if (_request.shared) {
                           // windows 以及tabs 共享
                        _localStorageService = _local(_request);
                        if (_localStorageService != null) {
                            if (_canLog('debug')) {
                                atmosphere.util.debug("Storage service available. All communication will be local");
                            }
     
                            if (_localStorageService.open(_request)) {
                                // Local connection.
                                return;
                            }
                        }
     
                        if (_canLog('debug')) {
                            atmosphere.util.debug("No Storage service available.");
                        }
                        // Wasn't local or an error occurred
                        _localStorageService = null;
                    }
     
     

    _local 的处理

    function _local(request) {
                    var trace, connector, orphan, name = "atmosphere-" + request.url, connectors = {
                        // 基于localstorage 的处理
                        storage: function () {
                            function onstorage(event) {
                                if (event.key === name && event.newValue) {
                                    // 处理消息
                                    listener(event.newValue);
                                }
                            }
     
                            if (!atmosphere.util.storage) {
                                return;
                            }
     
                            var storage = window.localStorage,
                                get = function (key) {
                                    var item = storage.getItem(name + "-" + key);
                                    return item === null ? [] : JSON.parse(item);
                                },
                                set = function (key, value) {
                                    storage.setItem(name + "-" + key, JSON.stringify(value));
                                };
     
                            return {
                                init: function () {
                                    set("children", get("children").concat([guid]));
                                    atmosphere.util.on(window, "storage", onstorage);
                                    return get("opened");
                                },
                                signal: function (type, data) {
                                    storage.setItem(name, JSON.stringify({
                                        target: "p",
                                        type: type,
                                        data: data
                                    }));
                                },
                                close: function () {
                                    var children = get("children");
     
                                    atmosphere.util.off(window, "storage", onstorage);
                                    if (children) {
                                        if (removeFromArray(children, request.id)) {
                                            set("children", children);
                                        }
                                    }
                                }
                            };
                        },
                        windowref: function () {
                            var win = window.open("", name.replace(/\W/g, ""));
     
                            if (!win || win.closed || !win.callbacks) {
                                return;
                            }
     
                            return {
                                init: function () {
                                    win.callbacks.push(listener);
                                    win.children.push(guid);
                                    return win.opened;
                                },
                                signal: function (type, data) {
                                    if (!win.closed && win.fire) {
                                        win.fire(JSON.stringify({
                                            target: "p",
                                            type: type,
                                            data: data
                                        }));
                                    }
                                },
                                close: function () {
                                    // Removes traces only if the parent is alive
                                    if (!orphan) {
                                        removeFromArray(win.callbacks, listener);
                                        removeFromArray(win.children, guid);
                                    }
                                }
     
                            };
                        }
                    };
     
                    function removeFromArray(array, val) {
                        var i, length = array.length;
     
                        for (i = 0; i < length; i++) {
                            if (array[i] === val) {
                                array.splice(i, 1);
                            }
                        }
     
                        return length !== array.length;
                    }
     
                    // Receives open, close and message command from the parent
                    function listener(string) {
                        var command = JSON.parse(string), data = command.data;
                        // 基于协议处理消息
                        if (command.target === "c") {
                            switch (command.type) {
                                case "open":
                                    _open("opening", 'local', _request);
                                    break;
                                case "close":
                                    if (!orphan) {
                                        orphan = true;
                                        if (data.reason === "aborted") {
                                            _close();
                                        } else {
                                            // Gives the heir some time to reconnect
                                            if (data.heir === guid) {
                                                _execute();
                                            } else {
                                                setTimeout(function () {
                                                    _execute();
                                                }, 100);
                                            }
                                        }
                                    }
                                    break;
                                // 消息处理
                                case "message":
                                   // 消息处理
                                    _prepareCallback(data, "messageReceived", 200, request.transport);
                                    break;
                                // 本地消息处理
                                case "localMessage":
                                    _localMessage(data);
                                    break;
                            }
                        }
                    }
     
                    function findTrace() {
                        var matcher = new RegExp("(?:^|; )(" + encodeURIComponent(name) + ")=([^;]*)").exec(document.cookie);
                        if (matcher) {
                            return JSON.parse(decodeURIComponent(matcher[2]));
                        }
                    }
     
                    // Finds and validates the parent socket's trace from the cookie
                    trace = findTrace();
                    if (!trace || atmosphere.util.now() - trace.ts > 1000) {
                        return;
                    }
     
                    // Chooses a connector
                    connector = connectors.storage() || connectors.windowref();
                    if (!connector) {
                        return;
                    }
     
                    return {
                        open: function () {
                            var parentOpened;
     
                            // Checks the shared one is alive
                            _traceTimer = setInterval(function () {
                                var oldTrace = trace;
                                trace = findTrace();
                                if (!trace || oldTrace.ts === trace.ts) {
                                    // Simulates a close signal
                                    listener(JSON.stringify({
                                        target: "c",
                                        type: "close",
                                        data: {
                                            reason: "error",
                                            heir: oldTrace.heir
                                        }
                                    }));
                                }
                            }, 1000);
     
                            parentOpened = connector.init();
                            if (parentOpened) {
                                // Firing the open event without delay robs the user of the opportunity to bind connecting event handlers
                                setTimeout(function () {
                                    _open("opening", 'local', request);
                                }, 50);
                            }
                            return parentOpened;
                        },
                        send: function (event) {
                            connector.signal("send", event);
                        },
                        localSend: function (event) {
                            connector.signal("localSend", JSON.stringify({
                                id: guid,
                                event: event
                            }));
                        },
                        close: function () {
                            // Do not signal the parent if this method is executed by the unload event handler
                            if (!_abortingConnection) {
                                clearInterval(_traceTimer);
                                connector.signal("close");
                                connector.close();
                            }
                        }
                    };
                }

    说明:以上代码处理还是比较复杂的,核心上还是利用了localStorage的storage event,因为消息框架的复杂性,以上代码做了不少消息协议的处理
    而且同时也包含了处理多windows的共享,很值得仔细研究下

    参考资料

    https://juejin.cn/post/6844904163533389837
    https://juejin.cn/post/6844903811232825357
    http://www.w3.org/TR/webstorage/#event-storage
    https://github.com/Atmosphere/atmosphere-javascript
    https://developer.mozilla.org/en-US/docs/web/api/window/open

  • 相关阅读:
    本周总结
    每日日报
    每日日报
    每日日报
    每日日报
    每日日报
    每日日报
    Hibernate中session.createCriteria的用法
    Spring AOP 源码分析
    TCP的三次握手四次挥手
  • 原文地址:https://www.cnblogs.com/rongfengliang/p/16187850.html
Copyright © 2020-2023  润新知