如何在 sharedworkers 中使用 html5 websockets

How to use html5 websockets within sharedworkers

在查看关于 Whosebug 的类似问题后,我没有发现任何对我想在我的项目中做的事情有帮助的东西。阅读和研究我成功地使应用程序与我的 Ratchet PHP websocket 服务器建立了多个连接,但我注意到每次用户重新加载页面或在新选项卡中打开 link 时,客户端 websocket 得到断开连接然后重新连接。

所以,我想知道如何在使用 Sharedworker 的 Web 应用程序中为多个用户只获得一个到 WebSocket 服务器的持久连接。

我在客户端的是这样的:

<script>
$(document).ready(function($) {
   let socket = new WebSocket("ws://realtime:8090");

   socket.onopen = function(e) {
      console.log("Browser client connected to websocket server");
      socket.send("Greetings from the browser!");
   };

   socket.onmessage = function(event) {
      console.log('Data received from server: ' + event.data);
   };

   socket.onclose = function(event) {
      if (event.wasClean) {
         console.log(`Connection closed cleanly, code=${event.code} reason=${event.reason}`);
      }
      else {
         // e.g. server process killed or network down
         // event.code is usually 1006 in this case
         console.log('Connection closed unexpectedly.');
      }
   };

   socket.onerror = function(error) {
      alert(error.message);
   };     
});
</script>

好的,在阅读、研究和尝试不同的东西和代码示例之后,我得出了这个解决方案:

客户端(浏览器)应该连接到 Sharedworker。 sharedworker 是一个单独的 javascript 文件,其中包含 sharedworker 的核心以及需要在其中执行的任何其他 JS 代码。 我首先测试了 sharedworker 在浏览器选项卡上的正常工作,计算每个用户打开的选项卡数量并将消息共享给一个用户,然后再共享给一组用户。 一旦浏览器和 Sharedworker 之间的通信通过了这些测试,我就将 websocket 代码添加到 Sharedworker JS 文件的主体中。

最后,客户端(浏览器)是这样的:

  <script>
   $(document).ready(function($) {
      var currentUser = "{{ Auth::user()->name }}";

      let worker = new SharedWorker('worker.js');
      worker.port.start();
      worker.port.postMessage({
         action: 'connect',
         username: currentUser
      });

      worker.port.onmessage = function(message) {
         console.log(message.data);
      };

   });
  </script>

Sharedworker 看起来像这样:

// All this code is executed only once, until the onconnect() function.
//---------------------------------------------------------------------

// The array AllPorts contains objects with the format {user:<string>, port:<MessagePort>}
let AllPorts = [];

var socket = new WebSocket("ws://ssa:8090");

// Called when the WebSocket Server accepts the connection.
socket.onopen = function(e) {
   //
};

// Event handler fired when the WebSocket Server sends a message to this client.
socket.onmessage = function(e) {
   var message = JSON.parse(e.data);
   // This loop sends a message to each tab opened by the given user.
   for (var i = 0; i < AllPorts.length; i++) {
      if (AllPorts[i].user == message.to) {
         AllPorts[i].port.postMessage(message.msg);
      }
   }
};

socket.onclose = function(event) {
   if (event.wasClean) {
      console.log('Connection closed normally');
   }
   else {
      console.log('Connection closed unexpectedly.');
   }
};

socket.onerror = function(error) {
   console.log(error.message);
};



// This event handler is fired every time a new tab is opened on the web browser.
onconnect = function(ev) {
   let port = ev.ports[0];

   port.onmessage = function(e) {
      console.log(e.data.action);
      let currentUser = e.data.username;
      let userIsConnected = false;

      switch (e.data.action) {
         case "connect":
            for (var i = 0; i < AllPorts.length; i++) {
               if (AllPorts[i].user == currentUser) {
                  userIsConnected = true;
               }
            }
            // Add new connected tab to AllPorts array.
            AllPorts.push({user: currentUser, port: port});
            if (!userIsConnected) {
               // New users are added to the list of the WebSocket Server.
               setTimeout(() => {
                  socket.send(JSON.stringify({action: 'connect', username: currentUser}));
               }, 600);
            }
            break;

         case "close":
            console.log(AllPorts);
            var index;
            // This is also executed when the user reloads the Tab.
            for (var i = 0; i < AllPorts.length; i++) {
               if (AllPorts[i].port == port) {
                  index = i;
                  currentUser = AllPorts[i].user;
               }
            }
            AllPorts.splice(index, 1);
            userIsConnected = false;
            // Check for any connected tab.
            for (var i = 0; i < AllPorts.length; i++) {
               if (AllPorts[i].user == currentUser) {
                  userIsConnected = true;
               }
            }
            if (!userIsConnected) {
               // User doen't have more tabs opened. Remove user from WebSocket Server.
               socket.send(JSON.stringify({action: 'disconnect', username: currentUser}));
            }
            break;

         case "notify":
            // Check if given user is connected.
            for (var i = 0; i < AllPorts.length; i++) {
               if (AllPorts[i].user == currentUser) {
                  userIsConnected = true;
               }
            }
            if (userIsConnected) {
               socket.send(JSON.stringify({action: 'notify', to: currentUser, message: e.data.message}));
            }
      } // switch
   } // port.onmessage
} // onconnect