来自父窗口上下文中 iframe 的 Websocket 连接

And*_* P. 4 javascript websocket socket.io

我有一个页面和页面内的 iframe。两者托管在同一域上。我可以完全控制 iframe,但无法控制父页面。

图像

所以我需要建立从 iframe 建立一个 websocket 连接,但在父窗口的上下文中,并在我导航到 iframe 之外(导航到父页面的其他菜单项)时保持它的活动状态。

(见图)就像在 A.html 中建立连接一样,并在导航到 B 和 C 时保持连接。

那可能吗?

Mar*_*sev 5

简短回答:

直接将脚本注入到父窗口可能仍然是一个更优雅的解决方案。

长答案:

Websocket只是一种与服务器的连接。创建后,它会保留在浏览器的选项卡中,并且如果您卸载创建 websocket 的脚本,它也不会被破坏。

在 iframe 中创建 websocket 的两个问题:

  1. 您可能不想每次加载相同的 iframe 内容时都创建新的 websocket 连接。
  2. 一旦你的 iframe 被卸载 - 所有 websocket 的事件处理程序都会丢失(如 onopen、onclose、onmessage 等)。

您可以尝试在主窗口上创建一个 websocket 工厂。该工厂的实例将负责:

  1. 使用 iframe 提供的数据创建一个 websocket 并将其存储在内部集合属性中
  2. clone 提供了 websocket 事件处理程序,因此即使原始源已卸载,它们也可以访问。如果事件处理程序很简单,那么它会工作得很好。

函数克隆存在一些已知问题 - 即,如果它们使用 iframe 脚本中定义的外部闭包 - 该闭包将会丢失。您可能想对克隆库进行研究。

main.js(在主index.html中加载):

var socketsCollection = new SocketsCollection();

function SocketsCollection() {
    this.collection = {};

    this.add = function(key, obj) {
        if (this.exists(key)) return;

        // clone websocket event handlers
        // PS: this is not the best solution to clone a function. Need a better research here
        eval("var onopen = " + obj.onopen.toString());
        eval("var onclose = " + obj.onclose.toString());
        eval("var onmessage = " + obj.onmessage.toString());

        // create websocket
        var ws = new WebSocket(obj.url);
        ws.onopen = function(e) {
            onopen(e, key, ws);
        };
        ws.onclose = function(e) {
            onclose(e, key, ws);
        }
        ws.onmessage = function(e) {
            onmessage(e, key, ws);
        }

        this.collection[key] = {
            key: key,
            ws: ws
        };

        // test websocket is alive
        var self = this;
        var counter = 1;
        window.setInterval(function () {
            console.log('testing ' + key);
            self.collection[key].ws.send('ping # ' + counter + ' websocket ' + key);
            counter++;
        }, 2000);  
    }

    this.exists = function(key){
        return this.collection[key] !== undefined;
    }
}
Run Code Online (Sandbox Code Playgroud)

iframed.js:

function foo(window, socketKey) {
    if (window.socketsCollection.exists(socketKey)) return;

    var newSocketData = {
        url: "wss://echo.websocket.org/",
        onopen: function(e, key, ws) {
            console.log(key + ' is OPEN', ws.readyState)
            ws.send('Hello socket ' + key);
        },
        onclose: function(e, key, ws) {
            console.log(key + ' is CLOSED', ws.readyState)
        },
        onmessage: function (e, key, ws) {
            console.log(key + ' response: ', e.data);
        }
    };

    window.socketsCollection.add(socketKey, newSocketData);
}
Run Code Online (Sandbox Code Playgroud)

a.html:

<script src="iframed.js"></script>
<script>
    foo.call(window.parent, window.parent, 'A');
</script>
Run Code Online (Sandbox Code Playgroud)

b.html:

<script src="iframed.js"></script>
<script>
    foo.call(window.parent, window.parent, 'B');
</script>
Run Code Online (Sandbox Code Playgroud)