我正在尝试使用回退来实现一个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参考中看到一个。
我应该如何实施?显然空循环不起作用,并且没有事件发生。
答案 0 :(得分:31)
这很简单,它完美地工作......你可以添加关于最大时间的条件,或尝试使它更健壮的数量......
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...
}
答案 1 :(得分:24)
这是一个更详细的解释。首先,检查特定的浏览器API,因为并非所有浏览器都在最新的RFC上。你可以参考
您不希望运行循环来不断检查readystate,这是您不需要的额外开销。更好的方法是了解与即时状态更改相关的所有事件,然后适当地连接它们。它们如下:
onclose
当WebSocket连接的readyState更改为CLOSED时要调用的事件侦听器。侦听器接收名为“close”的CloseEvent。
onerror
发生错误时要调用的事件侦听器。这是一个名为“error”的简单事件。
onmessage
从服务器收到消息时要调用的事件侦听器。侦听器接收名为“message”的MessageEvent。
onopen
当WebSocket连接的readyState更改为OPEN时要调用的事件侦听器;这表示连接已准备好发送和接收数据。该活动很简单,名称为“open”。
JS完全由事件驱动,所以你需要连接所有这些事件并检查readystate,这样你就可以从WS切换到相应的轮询。
我建议您查看Mozilla参考,它比RFC文档更容易阅读,它将为您提供API的良好概述及其工作原理:请参阅https://developer.mozilla.org/en-US/docs/WebSockets/WebSockets_reference/WebSocket
如果您遇到故障并进行轮询,请不要忘记进行重试回调,直到成功重新连接的回调被触发为止。
答案 2 :(得分:5)
查看http://dev.w3.org/html5/websockets/
搜索“事件处理程序”并找到表。
onopen - >开放
onmessage - >消息
onerror - >错误
onclose - >关闭
function update(e){ /*Do Something*/};
var ws = new WebSocket("ws://localhost:9999/");
ws.onmessage = update;
答案 3 :(得分:2)
我根本不使用池。相反,我使用排队。 首先,我创建了新的发送函数和一个队列:
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连接仅在第一次单击时创建。通常,第二条消息开始直接发送。
答案 4 :(得分:1)
tl; dr
一个简单的proxy包装器,可将state
事件添加到WebSocket,该事件将在其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;
}
});
详细说明:
这是一篇很好的文章,解释了如何进行Debugging WebSockets using JS Proxy Object
以下是上面文章中的代码段示例,以防将来无法使用该网站:
// 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;
答案 5 :(得分:0)
你的while循环可能会锁定你的线程。尝试使用:
setTimeout(function(){
if(socket.readyState === 0) {
//do nothing
} else if (socket.readyState !=1) {
//fallback
setInterval(poll, interval);
}
}, 50);
答案 6 :(得分:0)
在我的用例中,如果连接失败,我想在屏幕上显示错误。
let $connectionError = document.getElementById("connection-error");
setTimeout( () => {
if (ws.readyState !== 1) {
$connectionError.classList.add( "show" );
}
}, 100 ); // ms
请注意,在Safari(9.1.2)中,没有error
事件被触发 - 否则我会把它放在错误处理程序中。
答案 7 :(得分:0)
如果您使用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
}
答案 8 :(得分:-1)
就像你定义了一个onmessage
处理程序一样,你也可以定义一个onerror
处理程序。当连接失败时,将调用此文件。
var socket = new WebSocket(url);
socket.onmessage = onmsg;
socket.onerror = function(error) {
// connection failed - try polling
}