同一SPA和网络服务器上的两个标签可以共享一个共同的网络套接字吗?

时间:2019-02-20 14:49:17

标签: linux web websocket

假设一个Web应用程序(以我为例,bismon;一个正在进行中的GPLv3 +程序,处于alpha状态,甚至没有发布;我正在逐步编写bismon-chariot-doc.pdf,它是一个草稿报告进行描述;请跳过前几个官僚页面。)它是(或应该是)单个网页应用程序。该Web应用程序将Web套接字用于从Web应用程序(Web“服务器”)发送到同一页面的所有选项卡的异步消息。该websocket不用于从Web浏览器选项卡到Web服务器的通信。有一个bismon多线程服务器进程(在Linux上运行),它是一台专用的Web服务器(问题中的两个选项卡都相同)。

假设用户正在Linux上使用最新的网络浏览器(例如Firefox 60.5以上)。他/她正在某些超链接(指向相同的Web应用程序服务器进程和相同页面)上执行“在新选项卡中打开”。我的猜测是,两个选项卡之间可能共享现有的 WebSocket(如果不可能的话,为什么?)。然后,当Web服务器在该WebSocket上发送JSON消息时会发生什么?这两个标签是否“并行”显示?为什么以及如何?如果那不可能,为什么以及如何?

我拥有的最接近的MCVE是我在github上的onionwebsocket.c示例(对libonionexamples/websockets/websockets.c代码的改编)。下面给出的代码,当然需要最新的版本(HTTP服务器库)。每个选项卡均由其自己的线程处理。它不会在标签之间共享websocket,但是我什至不知道该怎么做。

我希望websocket(数据从服务器到浏览器异步流动)隐含地对每个WebSocket消息进行“锁定”(或序列化)。我什至不确定这是否可行或有意义。浏览器在实践中如何运作? AFAIK,Firefox的每个选项卡都有其自己的 process (但实际上不是,它看上去对应于浏览器pthread),那么这些选项卡将如何共享WebSocket之类的“状态”?同一浏览器中的两个标签页如何共享数据和WebSocket?他们如何“同步”?当某个线程向相同 WebSocket写入内容,并且浏览器具有两个标签来处理来自其中的传入消息时,bismon内部发生了什么事情?

为完整起见,这是我获得的onionwebsocket.c代码(但我不了解WebSocket的所有细微之处-我发现它们的规范太简短了。)

/**
  onionwebsocket.c: my adaptation of Libonion's
  examples/websockets/websockets.c to learn more about websockets.

  Libonion is on  https://github.com/davidmoreno/onion

  Copyright (C) 2010-2019 David Moreno Montero, Basile Starynkevitch
  and others

  This [library] example is free software; you can redistribute it
  and/or modify it under the terms of, at your choice:

  a. the Apache License Version 2.0.

  b. the GNU General Public License as published by the
  Free Software Foundation; either version 2.0 of the License,
  or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of both licenses, if not see
  <http://www.gnu.org/licenses/> and
  <http://www.apache.org/licenses/LICENSE-2.0>.
*/

#include <features.h>
#include <onion/log.h>
#include <onion/onion.h>
#include <onion/websocket.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <stdatomic.h>
#include <stdio.h>

#ifndef HAVE_GNUTLS
// on Debian, install libcurl4-gnutls-dev & gnutls-dev pacakges before
// building libonion
#error without HAVE_GNUTLS but onion needs it
#endif

onion_connection_status websocket_example_cont (void *data,
                        onion_websocket * ws,
                        ssize_t data_ready_len);

onion_connection_status
websocket_example (void *data, onion_request * req, onion_response * res)
{
  onion_websocket *ws = onion_websocket_new (req, res);
  if (!ws)
    {
      time_t nowtim = time (NULL);
      char timbuf[80];
      static atomic_int acnt;
      atomic_fetch_add (&acnt, 1);
      memset (timbuf, 0, sizeof (timbuf));
      strftime (timbuf, sizeof (timbuf), "%c", localtime (&nowtim));
      onion_response_write0 (res,
                 "<html><body><h1 id='h1id'>Easy echo</h1>\n");
      onion_response_printf (res,
                 "<p>Generated <small><tt>%s</tt></small>, count %d, pid %d</p>\n",
                 timbuf, acnt, (int) getpid ());
      onion_response_write0 (res,
                 "<pre id=\"chat\"></pre>"
                 " <script>\ninit = function(){\n"
                 "msg=document.getElementById('msg');\n"
                 "msg.focus();\n\n"
                 "ws=new WebSocket('ws://'+window.location.host);\n"
                 "ws.onmessage=function(ev){\n document.getElementById('chat').textContent+=ev.data+'\\n';\n"
                 "};}\n"
                 "window.addEventListener('load', init, false);\n</script>\n"
                 "<input type=\"text\" id=\"msg\" onchange=\"javascript:ws.send(msg.value); msg.select(); msg.focus();\"/><br/>\n"
                 "<button onclick='ws.close(1000);'>Close connection</button>\n"
                 "<p>To <a href='#h1id'>top</a>.\n"
                 "Try to <i>open in new tab</i> that link</p>\n"
                 "</body></html>");
      printf ("websocket created acnt=%d timbuf=%s\n", acnt, timbuf);
      fflush (NULL);
      return OCS_PROCESSED;
    }

  onion_websocket_printf (ws,
              "Hello from server. Write something to echo it. ws@%p",
              ws);
  onion_websocket_set_callback (ws, websocket_example_cont);

  return OCS_WEBSOCKET;
}

onion_connection_status
websocket_example_cont (void *data, onion_websocket * ws,
            ssize_t data_ready_len)
{
  char tmp[256];
  if (data_ready_len > sizeof (tmp))
    data_ready_len = sizeof (tmp) - 1;

  int len = onion_websocket_read (ws, tmp, data_ready_len);
  if (len <= 0)
    {
      ONION_ERROR ("Error reading data: %d: %s (%d)", errno, strerror (errno),
           data_ready_len);
      return OCS_NEED_MORE_DATA;
    }
  tmp[len] = 0;
  onion_websocket_printf (ws, "Echo: %s (ws@%p)", tmp, ws);

  ONION_INFO ("Read from websocket ws@%p: %d: %s", ws, len, tmp);

  return OCS_NEED_MORE_DATA;
}

int
main ()
{
  onion *o = onion_new (O_THREADED);
  onion_set_port (o, "8087");
  onion_set_hostname (o, "localhost");

  onion_url *urls = onion_root_url (o);

  onion_url_add (urls, "", websocket_example);

  onion_listen (o);

  onion_free (o);
  return 0;
}


/****************
 **                           for Emacs...
 ** Local Variables: ;;
 ** compile-command: "gcc -o onionwebsocket -Wall -rdynamic -I/usr/local/include/ -O -g -DHAVE_GNUTLS onionwebsocket.c -L /usr/local/lib $(pkg-config --cflags --libs gnutls) -lonion" ;;
 ** End: ;;
 ****************/

要运行该示例,请在启动http://localhost:8087/程序(其编译命令接近代码结尾)之后,在Linux浏览器中使用./onionwebsocket

理想情况下,我希望所有选项卡共享同一websocket连接,并且希望网络服务器向同一浏览器的所有选项卡发送“广播”消息(希望实际的“广播”或“多播”发生在浏览器上侧)。但是我相信这是不可能的(但是我不明白为什么)。

我正在读here,“ Web Content进程选项卡”。我不明白那是什么意思。在我的Debian / Instable上我没有,但是我确实使用ps auxw | grep firefox 几个 firefox-esr进程进行了观察,例如带有类似于-contentproc -childID 1 -isForBrowser的参数,是Firefox浏览器的 undocumented 选项。

answer可能是相关的,但我不知道为什么以及如何。

换句话说,如果两个标签使用 same WebSocket并且接收(浏览器侧)相同的JSON消息,那该怎么办?还是那不可能?为什么?

PS。我承认我太am脚,无法深入到firefox

的50MLOC中

0 个答案:

没有答案