44

我正在尝试使用回退到轮询的 WebSocket 来实现。如果 WebSocket 连接成功,readyState则变为 1,但如果失败,readyState则为 3,我应该开始轮询。

我试过这样的事情:

var socket = new WebSocket(url);
socket.onmessage = onmsg;
while (socket.readyState == 0)
{
}
if (socket.readyState != 1)
{
    // fall back to polling
    setInterval(poll, interval);
}

我期待socket.readyState异步更新,并允许我立即阅读。但是,当我运行它时,我的浏览器冻结了(我在放弃之前将它打开了大约半分钟)。

我想也许有一个onreadyStateChanged事件,但我没有在 MDN 参考中看到一个事件。

我应该如何实施?显然空循环不起作用,并且没有事件。

4

9 回答 9

43

这很简单,而且效果很好......您可以添加关于最大时间的条件,或尝试次数以使其更健壮......

function sendMessage(msg){
    // Wait until the state of the socket is not ready and send the message when it is...
    waitForSocketConnection(ws, function(){
        console.log("message sent!!!");
        ws.send(msg);
    });
}

// Make the function wait until the connection is made...
function waitForSocketConnection(socket, callback){
    setTimeout(
        function () {
            if (socket.readyState === 1) {
                console.log("Connection is made")
                if (callback != null){
                    callback();
                }
            } else {
                console.log("wait for connection...")
                waitForSocketConnection(socket, callback);
            }

        }, 5); // wait 5 milisecond for the connection...
}
于 2014-01-28T00:38:21.523 回答
38

这是一个更详细的解释。首先,检查特定的浏览器 API,因为并非所有浏览器都使用最新的 RFC。您可以咨询

您不想运行循环来不断检查就绪状态,这是您不需要的额外开销。更好的方法是了解与就绪状态更改相关的所有事件,然后将它们适当地连接起来。它们如下:

onclose当 WebSocket 连接的 readyState 更改为 CLOSED 时要调用的事件侦听器。侦听器收到一个名为“close”的 CloseEvent。

onerror发生错误时要调用的事件侦听器。这是一个名为“错误”的简单事件。

onmessage从服务器接收到消息时要调用的事件侦听器。侦听器收到一个名为“消息”的 MessageEvent。

onopen当 WebSocket 连接的 readyState 变为 OPEN 时调用的事件监听器;这表明连接已准备好发送和接收数据。该事件是一个名称为“open”的简单事件。

JS 完全是事件驱动的,因此您只需连接所有这些事件并检查就绪状态,这样您就可以相应地从 WS 切换到轮询。

我建议您查看 Mozilla 参考资料,它比 RFC 文档更易于阅读,并且可以很好地概述 API 及其工作原理(链接)。

如果失败并轮询,请不要忘记为重试进行回调,直到触发成功重新连接的回调。

于 2012-11-27T18:20:45.410 回答
14

我根本没有使用池。相反,我使用排队。首先我创建新的发送函数和一个队列:

var msgs = []
function send (msg) {
  if (ws.readyState !== 1) {
    msgs.push(msg)
  } else {
    ws.send(msg)
  }
}

然后我需要在首次建立连接时读取并发送:

function my_element_click () {
  if (ws == null){
    ws = new WebSocket(websocket_url)
    ws.onopen = function () {
      while (msgs.length > 0) {
        ws.send(msgs.pop())
      }
    }
    ws.onerror = function(error) {
      // do sth on error
    }
  } 
  msg = {type: 'mymessage', data: my_element.value}
  send(JSON.stringify(msg))
}

此示例中的 WebSocket 连接仅在第一次单击时创建。通常,在第二个消息开始直接发送。

于 2018-01-05T13:10:01.523 回答
7

看看http://dev.w3.org/html5/websockets/

搜索“事件处理程序”并找到表。

onopen -> 打开
onmessage -> message
onerror ->error
onclose ->close

function update(e){ /*Do Something*/};
var ws = new WebSocket("ws://localhost:9999/");

ws.onmessage = update;
于 2013-05-15T12:07:36.543 回答
3

如果您使用async/await并且只想等到连接可用,我建议您使用此功能:

async connection (socket, timeout = 10000) {
  const isOpened = () => (socket.readyState === WebSocket.OPEN)

  if (socket.readyState !== WebSocket.CONNECTING) {
    return isOpened()
  }
  else {
    const intrasleep = 100
    const ttl = timeout / intrasleep // time to loop
    let loop = 0
    while (socket.readyState === WebSocket.CONNECTING && loop < ttl) {
      await new Promise(resolve => setTimeout(resolve, intrasleep))
      loop++
    }
    return isOpened()
  }
}

用法(在async函数中):

const websocket = new WebSocket('...')
const opened = await connection(websocket)
if (opened) {
  websocket.send('hello')
}
else {
  console.log("the socket is closed OR couldn't have the socket in time, program crashed");
  return
}
于 2019-10-30T18:48:07.873 回答
2

tl;博士

一个简单的代理包装器,用于向WebSocketstate添加事件,该事件将在其更改时发出:readyState

const WebSocketProxy = new Proxy(WebSocket, {
    construct: function(target, args) {
        // create WebSocket instance
        const instance = new target(...args);

        //internal function to dispatch 'state' event when readyState changed
        function _dispatchStateChangedEvent() {
            instance.dispatchEvent(new Event('state'));
            if (instance.onstate && typeof instance.onstate === 'function') {
                instance.onstate();
            }
        }

        //dispatch event immediately after websocket was initiated
        //obviously it will be CONNECTING event
        setTimeout(function () {
            _dispatchStateChangedEvent();
        }, 0);

        // WebSocket "onopen" handler
        const openHandler = () => {
            _dispatchStateChangedEvent();
        };

        // WebSocket "onclose" handler
        const closeHandler = () => {
            _dispatchStateChangedEvent();
            instance.removeEventListener('open', openHandler);
            instance.removeEventListener('close', closeHandler);
        };

        // add event listeners
        instance.addEventListener('open', openHandler);
        instance.addEventListener('close', closeHandler);

        return instance;
    }
});

很长的解释

您可以使用Proxy对象来监视内部WebSocket状态。

这是一篇很好的文章,它解释了如何使用 JS 代理对象调试 WebSockets

这是上面文章中的代码片段示例,以防将来该站点不可用:

// proxy the window.WebSocket object
var WebSocketProxy = new Proxy(window.WebSocket, {  
  construct: function(target, args) {
    // create WebSocket instance
    const instance = new target(...args);

    // WebSocket "onopen" handler
    const openHandler = (event) => {
      console.log('Open', event);
    };

    // WebSocket "onmessage" handler
    const messageHandler = (event) => {
      console.log('Message', event);
    };

    // WebSocket "onclose" handler
    const closeHandler = (event) => {
      console.log('Close', event);
      // remove event listeners
      instance.removeEventListener('open', openHandler);
      instance.removeEventListener('message', messageHandler);
      instance.removeEventListener('close', closeHandler);
    };  

    // add event listeners
    instance.addEventListener('open', openHandler);
    instance.addEventListener('message', messageHandler);
    instance.addEventListener('close', closeHandler);

    // proxy the WebSocket.send() function
    const sendProxy = new Proxy(instance.send, {
      apply: function(target, thisArg, args) {
        console.log('Send', args);
        target.apply(thisArg, args);
      }
    });

    // replace the native send function with the proxy
    instance.send = sendProxy;

    // return the WebSocket instance
    return instance;
  }
});

// replace the native WebSocket with the proxy
window.WebSocket = WebSocketProxy; 
于 2020-05-20T10:24:15.803 回答
0

就像您定义了一个onmessage处理程序一样,您也可以定义一个onerror处理程序。连接失败时将调用这个。

var socket = new WebSocket(url);
socket.onmessage = onmsg;
socket.onerror = function(error) {
    // connection failed - try polling
}
于 2012-11-24T23:57:21.670 回答
0

您的 while 循环可能会锁定您的线程。尝试使用:

setTimeout(function(){
    if(socket.readyState === 0) {
        //do nothing
    } else if (socket.readyState !=1) {
        //fallback
        setInterval(poll, interval);
    }
}, 50);
于 2012-11-24T22:51:31.970 回答
0

在我的用例中,如果连接失败,我想在屏幕上显示错误。

let $connectionError = document.getElementById("connection-error");

setTimeout( () => {
  if (ws.readyState !== 1) {
    $connectionError.classList.add( "show" );
  }
}, 100 );  // ms

请注意,在 Safari (9.1.2) 中不会error触发任何事件 - 否则我会将其放在错误处理程序中。

于 2016-08-03T08:40:01.330 回答