假设一个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示例(对libonion的examples/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