AsuntoCurio
Recientemente configuré un servidor WebSocket local que funciona bien, sin embargo, tengo algunos problemas para entender cómo debo manejar una pérdida repentina de conexión que ni el cliente ni el servidor iniciaron intencionalmente, es decir: el servidor pierde energía, cables de ethernet extraído, etc. Necesito que el cliente sepa si la conexión se ha perdido en ~ 10 segundos.
Del lado del cliente, la conexión es simplemente:
var websocket_conn = new WebSocket('ws://192.168.0.5:3000');
websocket_conn.onopen = function(e) {
console.log('Connected!');
};
websocket_conn.onclose = function(e) {
console.log('Disconnected!');
};
Puedo activar manualmente la desconexión de la conexión que funciona bien,
websocket_conn.close();
Pero si simplemente saqué el cable ethernet de la parte posterior de la computadora o deshabilité la conexión, onclose
no se llama. He leído en otra publicación que eventualmente se llamará cuando TCP detecte pérdida de conectividad, pero no es en el momento oportuno que necesito, ya que el valor predeterminado para Firefox creo que es de 10 minutos, y realmente no quiero ir alrededor de cientos de computadoras about:config
cambiando este valor. La única otra sugerencia que he leído es usar un método de estilo de sondeo “keep-alive” ‘ping/pong’ que parece contradictorio con la idea de websockets.
¿Existe una manera más fácil de detectar este tipo de comportamiento de desconexión? ¿Las publicaciones antiguas que estoy leyendo todavía están actualizadas desde un punto técnico, y el mejor método sigue siendo el estilo ‘ping/pong’?
Tienes que agregar el método de ping pong
Crear un código en el servidor al recibir __silbido__ enviar __apestar__ atrás
El código JavaScript se da a continuación
function ping() {
ws.send('__ping__');
tm = setTimeout(function () {
/// ---connection closed ///
}, 5000);
}
function pong() {
clearTimeout(tm);
}
websocket_conn.onopen = function () {
setInterval(ping, 30000);
}
websocket_conn.onmessage = function (evt) {
var msg = evt.data;
if (msg == '__pong__') {
pong();
return;
}
//////-- other operation --//
}
-
Esto fue realmente útil para mí cuando se combinó con esta implementación de websocket de reconexión aquí: github.com/joewalnes/reconexión-websocket con una advertencia: también tuve que asignar el temporizador de intervalos a una variable y borrarlo dentro de la función de tiempo de espera de ping.
– León
31 de julio de 2019 a las 12:40
-
¿Cuál es el punto de websocket si estás haciendo encuestas con él 🙂
–Steve Moretz
18 de octubre de 2021 a las 7:51
-
@stevemoretz en realidad me estoy preguntando eso
– manudicri
20 de enero de 2022 a las 21:58
-
Websockets parece una capa muy delgada para TCP/IP, por lo que si necesita implementar una función adicional más allá de la implementación actual de TCP/IP, deberá agregarla usted mismo. Por lo tanto, el que respondió dijo que tiene que agregar ping/ping usted mismo. Sin embargo, sería bueno si websockets tuviera esto incorporado para que pudiera ser configurable y más genérico, ya que parece una molestia común.
– LeanMan
27 de enero de 2022 a las 9:15
AsuntoCurio
Esta fue la solución que terminé haciendo, que parece funcionar bien por el momento, es completamente específica para la configuración de mi proyecto y se basa en criterios establecidos que no se mencionaron originalmente en mi pregunta, pero podría ser útil para otra persona si sucede que están haciendo lo mismo.
La conexión al servidor websocket se produce dentro de un complemento de Firefox y, de forma predeterminada, la configuración de TCP de Firefox tiene un tiempo de espera de 10 minutos. Puede ver detalles adicionales con about:config
y buscando TCP.
Los complementos de Firefox pueden acceder a estos parámetros
var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
y también cambie estos parámetros especificando la rama y la preferencia junto con el nuevo valor
prefs.getBranch("network.http.tcp_keepalive.").setIntPref('long_lived_idle_time', 10);
Entonces, ahora, cualquier computadora con el complemento instalado tiene un tiempo de espera de 10 segundos para las conexiones TCP. Si se pierde la conexión, el onclose
se desencadena un evento que muestra una alerta y también intenta restablecer la conexión
websocket_conn.onclose = function (e) {
document.getElementById('websocket_no_connection').style.display = 'block';
setTimeout(my_extension.setup_websockets, 10000);
};
-
Como difícilmente puede haber una respuesta más adecuada ahora, el mejor curso de acción aquí probablemente sería aceptar la suya.
– Denys Séguret
20 de noviembre de 2014 a las 12:20
Jade
Usé la idea de ping/pong y funciona muy bien. Aquí está mi implementación en mi archivo server.js:
var SOCKET_CONNECTING = 0;
var SOCKET_OPEN = 1;
var SOCKET_CLOSING = 2;
var SOCKET_CLOSED = 3;
var WebSocketServer = require('ws').Server
wss = new WebSocketServer({ port: 8081 });
//Send message to all the users
wss.broadcast = function broadcast(data,sentBy)
{
for (var i in this.clients)
{
this.clients[i].send(data);
}
};
var userList = [];
var keepAlive = null;
var keepAliveInterval = 5000; //5 seconds
//JSON string parser
function isJson(str)
{
try {
JSON.parse(str);
}
catch (e) {
return false;
}
return true;
}
//WebSocket connection open handler
wss.on('connection', function connection(ws) {
function ping(client) {
if (ws.readyState === SOCKET_OPEN) {
ws.send('__ping__');
} else {
console.log('Server - connection has been closed for client ' + client);
removeUser(client);
}
}
function removeUser(client) {
console.log('Server - removing user: ' + client)
var found = false;
for (var i = 0; i < userList.length; i++) {
if (userList[i].name === client) {
userList.splice(i, 1);
found = true;
}
}
//send out the updated users list
if (found) {
wss.broadcast(JSON.stringify({userList: userList}));
};
return found;
}
function pong(client) {
console.log('Server - ' + client + ' is still active');
clearTimeout(keepAlive);
setTimeout(function () {
ping(client);
}, keepAliveInterval);
}
//WebSocket message receive handler
ws.on('message', function incoming(message) {
if (isJson(message)) {
var obj = JSON.parse(message);
//client is responding to keepAlive
if (obj.keepAlive !== undefined) {
pong(obj.keepAlive.toLowerCase());
}
if (obj.action === 'join') {
console.log('Server - joining', obj);
//start pinging to keep alive
ping(obj.name.toLocaleLowerCase());
if (userList.filter(function(e) { return e.name == obj.name.toLowerCase(); }).length <= 0) {
userList.push({name: obj.name.toLowerCase()});
}
wss.broadcast(JSON.stringify({userList: userList}));
console.log('Server - broadcasting user list', userList);
}
}
console.log('Server - received: %s', message.toString());
return false;
});
});
Aquí está mi archivo index.html:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Socket Test</title>
</head>
<body>
<div id="loading" style="display: none">
<p align="center">
LOADING...
</p>
</div>
<div id="login">
<p align="center">
<label for="name">Enter Your Name:</label>
<input type="text" id="name" />
<select id="role">
<option value="0">Attendee</option>
<option value="1">Presenter</option>
</select>
<button type="submit" onClick="login(document.getElementById('name').value, document.getElementById('role').value)">
Join
</button>
</p>
</div>
<div id="presentation" style="display: none">
<div class="slides">
<section>Slide 1</section>
<section>Slide 2</section>
</div>
<div id="online" style="font-size: 12px; width: 200px">
<strong>Users Online</strong>
<div id="userList">
</div>
</div>
</div>
<script>
function isJson(str) {
try {
JSON.parse(str);
}
catch (e) {
return false;
}
return true;
}
var ws;
var isChangedByMe = true;
var name = document.getElementById('name').value;
var role = document.getElementById('role').value;
function init()
{
loading = true;
ws = new WebSocket('wss://web-sockets-design1online.c9users.io:8081');
//Connection open event handler
ws.onopen = function(evt)
{
ws.send(JSON.stringify({action: 'connect', name: name, role: role}));
}
ws.onerror = function (msg) {
alert('socket error:' + msg.toString());
}
//if their socket closes unexpectedly, re-establish the connection
ws.onclose = function() {
init();
}
//Event Handler to receive messages from server
ws.onmessage = function(message)
{
console.log('Client - received socket message: '+ message.data.toString());
document.getElementById('loading').style.display = 'none';
if (message.data) {
obj = message.data;
if (obj.userList) {
//remove the current users in the list
userListElement = document.getElementById('userList');
while (userListElement.hasChildNodes()) {
userListElement.removeChild(userListElement.lastChild);
}
//add on the new users to the list
for (var i = 0; i < obj.userList.length; i++) {
var span = document.createElement('span');
span.className="user";
span.style.display = 'block';
span.innerHTML = obj.userList[i].name;
userListElement.appendChild(span);
}
}
}
if (message.data === '__ping__') {
ws.send(JSON.stringify({keepAlive: name}));
}
return false;
}
}
function login(userName, userRole) {
if (!userName) {
alert('You must enter a name.');
return false;
}
//set the global variables
name = userName;
role = userRole;
document.getElementById('loading').style.display = 'block';
document.getElementById('presentation').style.display = 'none';
document.getElementById('login').style.display = 'none';
init();
}
</script>
</body>
</html>
Aquí hay un enlace a la zona de pruebas de Cloud 9 si desea probarlo usted mismo: https://ide.c9.io/design1online/web-sockets
-
Creo que te estás perdiendo un puñado de variables y líneas de código. obj.action === ‘unirse’ ¿de dónde viene eso? Y creo que falta su variable para borrar su setTimeout.
–Brad Vanderbush
30 de mayo de 2020 a las 3:00
-
el obj.action === unirse proviene de presionar el botón unirse. Se hace referencia a setTimeout en el archivo keepAlive.
– Jade
16 de junio de 2020 a las 17:16
-
¿Por qué definir wss.broadcast dos veces?
– temirbek
9 de noviembre de 2021 a las 9:25
-
eso es solo un error tipográfico, arreglado, ty
– Jade
22 de noviembre de 2021 a las 18:22
vtortola
El protocolo websocket define marcos de control para ping y pong. Básicamente, si el servidor envía un ping, el navegador responderá con un pong, y también debería funcionar al revés. Probablemente el servidor WebSocket que utilice los implemente, y puede definir un tiempo de espera en el que el navegador debe responder o se considerará muerto. Esto debería ser transparente para su implementación tanto en el navegador como en el servidor.
Puede usarlos para detectar conexiones medio abiertas: http://blog.stephencleary.com/2009/05/detection-of-half-open-dropped.html
También relevante: WebSockets ping/pong, ¿por qué no TCP keepalive?
Está bien, llegué tarde a la fiesta, pero espero poder agregar algo de valor aquí. Mi implementación de TypeScript en un Angular
aplicación para manejar WebSocket
Conexión perdida. esto no usa PING PONG
estrategia para evitar mantener el servidor ocupado todo el tiempo. Esto comienza a intentar establecer la conexión solo después de que se pierde una conexión y continúa intentándolo cada 5 segundos hasta que se conecta correctamente. Así que, aquí vamos:
export class WebSocketClientComponent implements OnInit {
webSocket?: WebSocket;
selfClosing = false;
reconnectTimeout: any;
ngOnInit(): void {
this.establishWebSocketConnection();
}
establishWebSocketConnection() {
this.webSocket = new WebSocket('YourServerURlHere');
this.webSocket.onopen = (ev: any) => {
if (this.reconnectTimeout) {
clearTimeout(this.reconnectTimeout);
}
}
this.webSocket.onclose = (ev: CloseEvent) => {
if (this.selfClosing === false) {
this.startAttemptingToEstablishConnection();
}
}
}
private startAttemptingToEstablishConnection() {
this.reconnectTimeout = setTimeout(() => this.establishWebSocketConnection(), 5000);
}
}
Y eso es. Si desea cerrar la conexión websocket en algún momento, establezca selfClosing = true
. Esto dejaría de intentar volver a conectarse. Espero que esto ayude. Estoy seguro, el mismo código se puede usar en nativo JS
también.
-
Eso es muy útil para la reconexión, pero creo que el problema del OP fue que tomó mucho tiempo detectar que se perdió la conexión.
–Andrew W. Phillips
1 de septiembre de 2022 a las 7:09
-
Eso es muy útil para la reconexión, pero creo que el problema del OP fue que tomó mucho tiempo detectar que se perdió la conexión.
–Andrew W. Phillips
1 de septiembre de 2022 a las 7:09
Si no desea lidiar con tokens de mantenimiento de vida, planificar intentos de reconexión, etc., entonces debe usar una biblioteca comprobada. socket.io me viene a la mente.
– Denys Séguret
17 de noviembre de 2014 a las 10:55