javascript websockets - 控制初始连接/ onOpen何时绑定

时间:2011-12-05 22:56:11

标签: javascript html5 websocket

两个相关的问题可能更多地源于我对浏览器预解析javascript的方式缺乏了解:

var ws = new WebSocket("ws://ws.my.url.com");
ws.onOpen = function() { ... };

似乎无法直接控制WebSocket的初始化,除了将其包装在回调中之外,所以我假设一旦javascript代码被加载并创建到构造函数就会创建连接?

onOpen属性何时附加到ws?是否存在竞争条件的可能性(如果由于某种原因,您在套接字的定义和onOpen的定义之间有一些代码?),那么onOpen在连接之前/之后是不可判定地绑定的成立(我知道你可以选择ws.readyState)。补充一点,是WebSocket握手阻塞吗?

我意识到这一切都是目前的草案,可能依赖于实施,我可能已经错过了一些令人眼花缭乱的事情,但我在网上搜索没有看到任何特别相关/浏览W3c规范草案,所以任何帮助我非常感谢我对websockets / javascript内部工作的理解!

5 个答案:

答案 0 :(得分:30)

JavaScript是单线程的,这意味着在当前执行范围完成且网络执行有机会运行之前,无法建立网络连接。执行范围可以是当前函数(下例中的connect函数)。所以,如果你在使用setTimeout时很晚就绑定它,你可能会错过onopen事件。在这个例子中,你可以错过这个事件:

查看:http://jsbin.com/ulihup/edit#javascript,html,live

代码:

var ws = null;

function connect() {
  ws = new WebSocket('ws://ws.pusherapp.com:80/app/a42751cdeb5eb77a6889?client=js&version=1.10');
  setTimeout(bindEvents, 1000);
  setReadyState();
}

function bindEvents() {
  ws.onopen = function() {
    log('onopen called');
    setReadyState();
  };
}

function setReadyState() {
  log('ws.readyState: ' + ws.readyState);
}

function log(msg) {
  if(document.body) {
    var text = document.createTextNode(msg);
    document.body.appendChild(text);
  }
}

connect();

如果您运行该示例,您可能会看到永远不会输出'onopen called'日志行。这是因为我们错过了这个活动。

但是,如果您将new WebSocket(...)onopen事件的绑定保持在相同的执行范围内,那么您将无法错过该事件。

有关scope of execution及其排队,排程和处理方式的更多信息,请查看John Resig在Timers in JavaScript上的帖子。

答案 1 :(得分:2)

@ leggetter是对的,下面的代码按顺序执行:

(function(){
    ws = new WebSocket("ws://echo.websocket.org");
    ws.addEventListener('open', function(e){
        console.log('open', e);
        ws.send('test');
    });
    ws.addEventListener('message', function(e){console.log('msg', e)});

})();

但是,在W3C spec中有一条奇怪的路线:

  

返回一个新的WebSocket对象,并在后台继续执行这些步骤(不阻止脚本)。

当我学习浏览器api时,这对我来说很困惑。我假设用户代理忽略它,或者我误解了它。

答案 2 :(得分:2)

注意I / O可能在执行范围内发生的事实。 例如,在以下代码中

var ws = new WebSocket("ws://localhost:8080/WebSockets/example");
alert("Hi");
ws.onopen = function(){
    writeToScreen("Web Socket is connected!!" + "<br>");
};
function writeToScreen(message) {
    var div = document.getElementById('test');
    div.insertAdjacentHTML( 'beforeend', message );
}

,系统会显示或不显示消息"Web Socket is connected",具体取决于您关闭"Hi"提醒所需的时间

答案 3 :(得分:2)

TL; DR-标准声明可以在“ [JS]事件循环运行时”(例如,通过浏览器的C ++代码)打开连接,但是必须触发open事件被排队到JS事件循环中,这意味着即使在当前执行块仍在执行的情况下打开了连接,也保证可以执行与onOpen在同一执行块中注册的任何new WebSocket(...)回调。 / strong>


根据HTML标准(强调我的)中的The WebSocket Interface规范:

WebSocket(url, protocols)构造函数在被调用时必须运行以下步骤:

  1. urlRecord是将URL解析器应用于url的结果。
  2. 如果urlRecord失败,则抛出“ SyntaxErrorDOMException
  3. 如果urlRecord的方案不是“ ws”或“ wss”,则抛出“ SyntaxErrorDOMException
  4. 如果urlRecord的片段不为空,则抛出一个“ SyntaxErrorDOMException
  5. 如果protocols是字符串,请将protocols设置为仅包含该字符串的序列。
  6. 如果protocols中的任何一个值出现多次或以其他方式无法满足对组成 WebSocket协议定义的Sec-WebSocket-Protocol字段值的元素的要求>,然后抛出“ SyntaxErrorDOMException
  7. 并行运行此步骤:

    1. 在给定urlRecord,协议和条目设置对象的情况下建立WebSocket连接。 [FETCH]

    注意如果建立WebSocket连接算法失败,则会触发WebSocket连接算法失败,然后调用close WebSocket连接算法,然后确定WebSocket连接已关闭,从而触发关闭事件,如下所述。

  8. 返回一个新的WebSocket对象,其URL为urlRecord。

请注意,连接的建立是“并行”运行的,the specification further states表示“ ... 并行是指这些步骤要一步一步地运行,与标准中的其他逻辑同时(例如与事件循环同时)。该标准并未定义实现此目标的精确机制,例如分时协作多任务处理,纤维,线程,进程,使用不同的超线程,内核,CPU,机器等。”

这意味着理论上可以在onOpen之前打开连接 ,即使onOpen(...)是构造函数调用之后的下一条语句。

但是...标准继续在Feedback from the protocol下声明:

建立WebSocket连接后,用户代理必须将任务排队才能运行这些步骤

  1. readyState属性的值更改为OPEN(1)。
  2. 如果extensions属性的值不是null,请更改为正在使用的扩展名。 [WSP]
  3. protocol属性的值更改为正在使用的子协议(如果它不是null的值)。 [WSP]
  4. open对象上触发名为WebSocket的事件。

注意由于上述算法作为任务排队,因此在WebSocket连接建立和脚本为open事件设置事件侦听器之间没有竞争条件。

因此,在遵循HTML标准的浏览器或库中,可以确保执行注册到WebSocket.onOpen(...)的回调,如果,该回调是在执行块结束之前注册的。构造函数被调用,并且在同一块中释放事件循环的任何后续语句之前(例如await)。

答案 4 :(得分:1)

在脚本完成执行之后才会发生实际的I / O,因此不应存在竞争条件。