Angular: An Issue about Websocket reconnection - javascript

I set a timeout function to wait and trigger reconnection after the connection is broken.
My Angular version is 11.
The following is my WebsocketService code:
import { Injectable } from '#angular/core'
#Injectable()
export class WebSocketService {
constructor() {
this.init();
}
init() {
var ws = new WebSocket('ws://localhost:8080');
ws.onopen = (event) => {
console.log('WebSocket connected');
}
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log(data);
}
ws.onclose = (event) => {
console.log('Close WebSocket connection');
setTimeout(this.init, 1000);
}
ws.onerror = (event) => {
console.log(`WebSocket connection error: ${event}`);
ws.close();
}
}
}
My problem is that setTimeout function always be triggered twice.
Then it will not work anymore.
Have any suggestion?
Thanks!

Related

Correct way of connecting Websocket events to update my React Component

So I'm primarily a C++ backend developer but I'm learning React on the side and I have this really simple file to use websockets.
import React, { useState } from "react";
var serverMessage = "";
var webSocketReady = false;
function connect() {
webSocket = new WebSocket("ws://127.0.0.1:3000/ws")
webSocket.onopen = (event) => {
webSocketReady = true;
};
webSocket.onmessage = function (event) {
serverMessage = JSON.parse(event.data);
};
webSocket.onclose = function (event)
{
webSocketReady = false;
setTimeout(function() {
connect();
}, 1000);
};
webSocket.onerror = function (err)
{
console.log('Socket encountered error: ', err.message, 'Closing socket')
webSocket.close();
};
}
connect();
export default function MyTestComponent({
...props
}) {
const [varThatNeedHooks, setVar] = useState({});
if (!webSocketReady)
{
return (<h1>Could not connect to server retrying ...</h1>);
}
else if (serverMessage == "")
{
return (<h1>Waiting for message from server ...</h1>);
}
else
{
// do stuff with varThatNeedHooks
}
}
I'm looking at the react documentation and I realized this way that I'm connecting the websocket to MyTestComponent has got to be wrong. But I'm not sure what is the "React" way of hooking up the events from the websocket to change what gets rendered. Anyone have any good documents or examples I can follow?
Typically you'd make serverMessage and webSocketReady part of the React state so that when they are updated it triggers a component rerender. Use an useEffect hook to manage the socket instance.
Example:
import React, { useEffect, useState, useRef } from "react";
export default function MyTestComponent({ ...props }) {
const [varThatNeedHooks, setVar] = useState({});
const [serverMessage, setServerMessage] = useState("");
const [webSocketReady, setWebSocketReady] = useState(false);
const [webSocket, setWebSocket] = useState(new WebSocket("ws://127.0.0.1:3000/ws"));
useEffect(() => {
webSocket.onopen = (event) => {
setWebSocketReady(true);
};
webSocket.onmessage = function (event) {
setServerMessage(JSON.parse(event.data));
};
webSocket.onclose = function (event) {
setWebSocketReady(false);
setTimeout(() => {
setWebSocket(new WebSocket("ws://127.0.0.1:3000/ws"));
}, 1000);
};
webSocket.onerror = function (err) {
console.log('Socket encountered error: ', err.message, 'Closing socket');
setWebSocketReady(false);
webSocket.close();
};
return () => {
webSocket.close();
};
}, [webSocket]);
if (!webSocketReady) {
return <h1>Could not connect to server retrying ...</h1>;
} else if (serverMessage == "") {
return <h1>Waiting for message from server ...</h1>;
} else {
// do stuff with varThatNeedHooks
}
}

webSocket.send() is not executed in react js?

I am working with WebSocket in reactjs, but WebSocket.send is not executing on the onClick event.
here I create WebSocket
const ws = new WebSocket("ws://192.168.18.112:8000/ws/notification/");
connect to WebSocket
ws.onopen = (e) => {
console.log("connect");
};
here is my onClick function
const sendNotification = (e) => {
console.log("click");
if (ws.readyState === WebSocket.OPEN && message !== "") {
ws.send("message");
console.log("Sending message"); // this console is execute
} else if (ws.readyState === WebSocket.CONNECTING) {
ws.addEventListener("open", () => sendNotification());
console.log("Connecting state");
console.log("Not Send");
} else {
console.log("nothing happen");
}
};
so here is my problem I am unable to find bug or problem in my code.
try this W3CWebSocket. Also i don't think there si a "/" after notification after ws/notification double check if your end point correct or not.
mport React, { Component } from 'react';
import { w3cwebsocket as W3CWebSocket } from "websocket";
const client = new W3CWebSocket('ws://192.168.18.112:8000/ws/notification/');
class App extends Component {
componentWillMount() {
client.onopen = () => {
console.log('WebSocket Client Connected');
};
client.onmessage = (message) => {
console.log(message);
};
}
render() {
return (
<div>
Practical Intro To WebSockets.
</div>
);
}
}
export default App;

Invalid state Error on websockets when sending message

I'm working on app which send message via websockets (managed by django channels) and in return it receives json from django db as a message and renders frontend based on that json.
I have Invalid State Error when I try to send message by websocket, why? Messages send are usually Json. I works properly all the time but commented part doesn't and I don't know why please explain me.
function main() {
configGame();
}
function configGame() {
const socket = "ws://" + window.location.host + window.location.pathname;
const websocket = new WebSocket(socket);
const playerName = document.querySelector(".playerName_header").textContent;
function asignEvents() {
const ready_btn = document.querySelector(".--ready_btn");
const start_btn = document.querySelector(".--start_btn");
ready_btn.addEventListener("click", () => {
let mess = JSON.stringify({
player: playerName,
action: "ready",
});
sendMess(mess);
});
start_btn.addEventListener("click", () => {
let mess = JSON.stringify({
player: playerName,
action: "start",
});
sendMess(mess);
});
}
function openWebsocket() {
console.log("Establishing Websocket Connection...");
websocket.onopen = () => {
console.log("Websocket Connection Established!");
};
}
function setWebsocket() {
websocket.onmessage = (mess) => {
console.log(`Message: ${mess.data}`);
dataJson = JSON.parse(mess.data);
dataJson = JSON.parse(dataJson.message);
//Player Ready (jeszcze z max_players zrobic kontrolke)
if (dataJson.action === "player_ready") {
const playersReadyText = document.querySelector(".players_ready_text");
playersReadyText.textContent = `Players ready: ${dataJson.players_ready}`;
}
};
websocket.onclose = () => {
console.log("Websocket Connection Terminated!");
};
}
/*
function checkState() {
let mess = JSON.stringify({
player: playerName,
action: "game state",
});
sendMess(mess);
}
*/
function sendMess(messText) {
websocket.send(messText);
}
openWebsocket();
checkState(); //This one doesn't work
asignEvents();
setWebsocket();
}
// Asigning Event Listneres to DOM ELEMENTS
function asignEvents() {
const ready_btn = document.querySelector(".--ready_btn");
const start_btn = document.querySelector(".--start_btn");
ready_btn.addEventListener("click", () => {
console.log("Ready");
});
start_btn.addEventListener("click", () => {
console.log("Start");
});
}
main();
Error:
Console (Safari) returns InvalidState error and points to
method checkState and sendMess.
InvalidStateError: The object is in an invalid state.
Is the websocket connected?
sendMess(messText) {
if (websocket.readyState === WebSocket.OPEN) {
websocket.send(messText);
} else {
console.warn("websocket is not connected");
}
}

connecting to websocket make webworker un-responsive in Microsoft edge

So this is weird, when i try to connect to websocket (this is only a Microsoft edge issue) it makes so on every second page refresh webworker will not accept messages onMessage wont trigger at all:
consider the following:
main.js
var worker = new Worker("webworker.js");
worker.postMessage({ type: 'INIT_SOCKET' });
worker.addEventListener('message', (event) => {
let data = event.data;
if (typeof data === 'string') {
data = JSON.parse(data);
}
if (data.type === 'SOCKET_INITIALIZED') {
console.log('inititalized');
}
});
webworker.js
var io = require('socket.io-client');
var socket;
onmessage = function(event) {
var data = event.data;
console.log('got a event');
if (typeof data === 'string') {
data = JSON.parse(data);
}
switch (data.type) {
case 'INIT_SOCKET':
try {
socket = io('xxxx', { transports: [ 'websocket' ], secure: true }); // this line causes the error
socket.on('connect', function () {
postMessage({
type: Consts.SOCKET_INITIALIZED
});
});
} catch(e) {
console.log('some error ', e);
}
break;
};
};
require does not appear to be defined at Worker context. Use importScripts() to import external scripts into DedicatedWorkerGlobalScope. For example
importScripts("socket.io.js");
Could not determine how to stop io() call from polling and getting error, probably due to 404 error
socket.io.js:7370 WebSocket connection to
'ws://echo.websocket.org/socket.io/?EIO=3&transport=websocket' failed:
Error during WebSocket handshake: Unexpected response code: 404
probably due to being unfamiliar, here, as to how io() is implemented. Though was able to define Socket object within Worker scope.
Approach using WebSocket returns expected result
const worker = new Worker("webworker.js");
worker.addEventListener('message', (event) => {
let data = event.data;
if (typeof data === 'string') {
console.log(data)
}
if (data.type === 'SOCKET_INITIALIZED') {
console.log('inititalized');
}
});
worker.postMessage({
type: 'INIT_SOCKET'
});
importScripts("socket.io.js");
let sock = io();
console.log(sock); // to demonstrate `Socket` is defined
sock.close(); // closing socket here to prevent `404` polling errors
self.socket = void 0;
self.onmessage = function(event) {
var data = event.data;
console.log('got a event');
if (typeof data === 'string') {
data = JSON.parse(data);
}
switch (data.type) {
case 'INIT_SOCKET':
if (!self.socket) {
try {
self.socket = new WebSocket("ws://echo.websocket.org/");
self.socket.onopen = function(e) {
socket.send("WebSocket rocks");
console.log("self.socket event.type:", e.type);
self.postMessage({
type: 'SOCKET_INITIALIZED'
});
};
self.socket.onmessage = function(e) {
console.log(e.data);
self.socket.close()
};
self.socket.onerror = function(e) {
console.log("self.socket error", e);
};
self.socket.onclose = function(e) {
console.log("self.socket event.type", e.type);
};
} catch (e) {
console.log('some error ', e);
}
break;
};
}
};
plnkr http://plnkr.co/edit/zVnLE6qG7Kf4yVSb0aJt?p=preview

Uncaught InvalidStateError: Failed to execute 'send' on 'WebSocket': Still in CONNECTING state

When my page loads, I try to send a message to the server to initiate a connection, but it's not working. This script block is near the top of my file:
var connection = new WrapperWS();
connection.ident();
// var autoIdent = window.addEventListener('load', connection.ident(), false);
Most of the time, I see the error in the title:
Uncaught InvalidStateError: Failed to execute 'send' on 'WebSocket': Still in CONNECTING state
So I tried to catch the exception, as you can see below, but now it seems InvalidStateError is not defined and that produces a ReferenceError.
Here's the wrapper object for my websocket connection:
// Define WrapperWS
function WrapperWS() {
if ("WebSocket" in window) {
var ws = new WebSocket("ws://server:8000/");
var self = this;
ws.onopen = function () {
console.log("Opening a connection...");
window.identified = false;
};
ws.onclose = function (evt) {
console.log("I'm sorry. Bye!");
};
ws.onmessage = function (evt) {
// handle messages here
};
ws.onerror = function (evt) {
console.log("ERR: " + evt.data);
};
this.write = function () {
if (!window.identified) {
connection.ident();
console.debug("Wasn't identified earlier. It is now.");
}
ws.send(theText.value);
};
this.ident = function () {
var session = "Test";
try {
ws.send(session);
} catch (error) {
if (error instanceof InvalidStateError) {
// possibly still 'CONNECTING'
if (ws.readyState !== 1) {
var waitSend = setInterval(ws.send(session), 1000);
}
}
}
window.identified = true;
theText.value = "Hello!";
say.click();
theText.disabled = false;
};
};
}
I am testing using Chromium on Ubuntu.
You could send messages via a proxy function that waits for the readyState to be 1.
this.send = function (message, callback) {
this.waitForConnection(function () {
ws.send(message);
if (typeof callback !== 'undefined') {
callback();
}
}, 1000);
};
this.waitForConnection = function (callback, interval) {
if (ws.readyState === 1) {
callback();
} else {
var that = this;
// optional: implement backoff for interval here
setTimeout(function () {
that.waitForConnection(callback, interval);
}, interval);
}
};
Then use this.send in place of ws.send, and put the code that should be run afterwards in a callback:
this.ident = function () {
var session = "Test";
this.send(session, function () {
window.identified = true;
theText.value = "Hello!";
say.click();
theText.disabled = false;
});
};
For something more streamlined you could look into promises.
This error is raised because you are sending your message before the WebSocket connection is established.
You can solve it by doing this simply:
conn.onopen = () => conn.send("Message");
This onopen function waits for your WebSocket connection to establish before sending your message.
if you use one websocket client object and connect from random app places then object can be in connecting mode (concurent access).
if you want to exchange through only one websoket then
create class with promise and keep it in property
class Ws {
get newClientPromise() {
return new Promise((resolve, reject) => {
let wsClient = new WebSocket("ws://demos.kaazing.com/echo");
console.log(wsClient)
wsClient.onopen = () => {
console.log("connected");
resolve(wsClient);
};
wsClient.onerror = error => reject(error);
})
}
get clientPromise() {
if (!this.promise) {
this.promise = this.newClientPromise
}
return this.promise;
}
}
create singleton
window.wsSingleton = new Ws()
use clientPromise property in any place of app
window.wsSingleton.clientPromise
.then( wsClient =>{wsClient.send('data'); console.log('sended')})
.catch( error => alert(error) )
http://jsfiddle.net/adqu7q58/11/
Method 1: Check connection
You can resolve a promise when socket is connected:
async function send(data) {
await checkConnection();
ws.send(data);
}
Implementation
This trick is implemented using an array of resolvers.
let ws = new WebSocket(url);
let connection_resolvers = [];
let checkConnection = () => {
return new Promise((resolve, reject) => {
if (ws.readyState === WebSocket.OPEN) {
resolve();
}
else {
connection_resolvers.push({resolve, reject});
}
});
}
ws.addEventListener('open', () => {
connection_resolvers.forEach(r => r.resolve())
});
Method 2: Wait for connection
You can resolve a promise when socket is not connected:
const MAX_RETRIES = 4;
async function send(data, retries = 0) {
try {
ws.send(data);
}
catch (error) {
if (retries < MAX_RETRIES error.name === "InvalidStateError") {
await waitForConnection();
send(data, retries + 1);
}
else {
throw error;
}
}
}
Implementation
This trick is implemented using an array of resolvers.
let ws = new WebSocket(url);
let connection_resolvers = [];
let waitForConnection = () => {
return new Promise((resolve, reject) => {
connection_resolvers.push({resolve, reject});
});
}
ws.addEventListener('open', () => {
connection_resolvers.forEach(r => r.resolve())
});
My opinion is that the second method has a little bit good performance!
It is possible to use functions and readyState with setTimeout.
function openSocket()
{
webSocket = new WebSocket("");
}
function sendData()
{
if(webSocket.readyState)
{
webSocket.send(JSON.stringify(
{
"event" : "",
"message" : ""
}));
}
else
{
setTimeout(sendData, 1000);
}
}
function eventHandler()
{
webSocket.onmessage = function(e)
{
data = JSON.parse(e.data);
event = data.event;
switch (event)
{...}
}
}
openSocket();
sendData();
enentHandler();

Categories