Websocket establish bulletproof connection - javascript

My coffee code to establish websocket connection when it isn't present return an error:
socket = new WebSocket('ws://localhost:8080')
#=> Firefox can't establish a connection to the server at ws://localhost:8080/.
#=> socket = new WebSocket('ws://localhost:8080');
It's normal behaviour of WebSocket, but I want WebSockets work like this:
# pseudocode
if websocket.establish_connection()
# do thing #1
else
do thing no.2
I think about try...catch but I think it's dirty. Is there any other way of troubleshooting?

Its not actually throwing an Error, the program execution continues. Its simply logging the error (asynchronously) to the console. To handle connection failures, you can bind to the sockets onerror event.
Something like this could work:
function establishWSConnection(url) {
return new Promise(function(res, rej) {
var ws = new WebSocket(url);
ws.onerror = rej;
ws.onopen = function () {
ws.onerror = null;
res(ws);
}
});
}
establishWSConnection('ws://localhost:1234').then(function(ws) {
// do stuff
}).catch(function(err){
console.log(err);
});

Related

Websocket won't reconnect unless I close the browser tab and restart it

I have a webserver with websockets set up on an ESP8266. The application runs fine on both client and server sides, sending and receiving data. However, if the server side disconnects (power cycle or upload new code), the client (Chrome) won't reconnect to the websocket. I can reload/refresh the web page, and it claims (according to the console log) to be connecting to the websocket, but it does not. The only solution I have found that works is to close the tab, and then restart a new session.
My code is heavily based on this tutorial from Random Nerd Tutorials
var gateway = `ws://${window.location.hostname}/ws`;
var websocket;
function initWebSocket() {
console.log('Trying to open a WebSocket connection...');
websocket = new WebSocket(gateway);
websocket.onopen = onOpen;
websocket.onclose = onClose;
websocket.onmessage = onMessage; // <-- add this line
}
function onOpen(event) {
console.log('Connection opened');
}
function onClose(event) {
console.log('Connection closed');
setTimeout(initWebSocket, 2000);
}
Is there something that is missing from the code above to make it more reliable?
You probably need to use setInterval. Try this, you may have to tweek it a bit.
var gateway = `ws://${window.location.hostname}/ws`;
var websocket, sockTimer=null;
function initWebSocket() {
console.log('Trying to open a WebSocket connection...');
websocket = new WebSocket(gateway);
websocket.onopen = onOpen;
websocket.onerror = onError; //  new
websocket.onclose = onClose;
websocket.onmessage = onMessage; // <-- add this line
}
function onOpen(event) {
clearInterval(sockTimer) // <= better
console.log('Connection opened');
}
function onError() { // <= New
sockTimer = setInterval(init, 1000 * 60);
};
function onClose(event) {
console.log('Connection closed');
//setTimeout(initWebSocket, 2000);
sockTimer = setInterval(initWebSocket, 1000 * 60); // <=new
}

C# Websocket The remote party closed the WebSocket connection without completing the close handshake

Hello I'm currently implementing a websocket component to my very basic site. I'm running .NET Core 3.1 HTTP Listener for serving html, I've been stumped by implementing websockets.
I've worked with TCP in C# before and understand the flow of everything but websockets are a new thing to me. Here is the C# code for accepting websockets
[Route("/socket", "GET")]
public static async Task upgrade(HttpListenerContext c)
{
if (!c.Request.IsWebSocketRequest)
{
c.Response.StatusCode = 400;
c.Response.Close();
return;
}
try
{
var sock = (await c.AcceptWebSocketAsync(null)).WebSocket;
byte[] buff = new byte[1024];
var r = await sock.ReceiveAsync(buff, System.Threading.CancellationToken.None);
}
catch (Exception x)
{
Console.WriteLine($"Got exception: {x}");
}
//WebSocketHandler.AddSocket(sock);
}
I've added var r = await sock.ReceiveAsync(buff, System.Threading.CancellationToken.None); to this function because originally I was getting the exception in my WebSocketHandler class, so I moved the code to the one function to test.
Here is the client:
<script>
let socket = new WebSocket("ws://localhost:3000/socket");
socket.onopen = function (event) {
console.log("[ OPENED ] - Opened Websocket!");
};
socket.onclose = function (event) {
console.log("[ CLOSED ] - Socket closed");
};
socket.onerror = function (error) {
console.log("[ ERROR ] - Got websocket error:");
console.error(error);
};
socket.onmessage = function (event) {
// This function will be responsible for handling events
console.log("[ MESSAGE ] - Message received: ");
const content = JSON.parse(event.data);
console.log(content);
};
</script>
Here is the output in the console for the client:
Navigated to http://127.0.0.1:5500/index.html
index.html:556 [ OPENED ] - Opened Websocket!
index.html:569 [ CLOSED ] - Socket closed
And here is the exception from the C# server:
Got exception: System.Net.WebSockets.WebSocketException (997): The remote party closed the WebSocket connection without completing the close handshake.
at System.Net.WebSockets.WebSocketBase.WebSocketOperation.Process(Nullable`1 buffer, CancellationToken cancellationToken)
at System.Net.WebSockets.WebSocketBase.ReceiveAsyncCore(ArraySegment`1 buffer, CancellationToken cancellationToken)
at SwissbotCore.HTTP.Routes.UpgradeWebsocket.upgrade(HttpListenerContext c) in C:\Users\plynch\source\repos\SwissbotCore\SwissbotCore\HTTP\Routes\UpgradeWebsocket.cs:line 29
I can provide the http requests that the client sends if need be but I am completely stumped on this, any help will be greatly appreciated.
You might want to read up on The Close Handshake in Section 1.4 in RFC 6455 and also Close the WebSocket Connection in Section 7.1.1 in RFC 6455.
Essentially, you need to let the WebSocket endpoint know you are going to close the socket, before you terminate the socket.
For your server side, you should probably be catching this exception, as this can also happen in production scenarios when network issues occur.
I'm not sure why, but, if you change the code inside try block to this:
try
{
var sock = (await c.AcceptWebSocketAsync(null)).WebSocket;
byte[] buff = new byte[1024];
await Listen(sock);
}
catch (Exception x)
{
Console.WriteLine($"Got exception: {x}");
}
private async Task Listen(WebSocket sock)
{
return new Task(async () =>
{
while(sock.State == WebSocketState.Open)
{
var r = await sock.ReceiveAsync(buff, System.Threading.CancellationToken.None);
}
});
}
it's gonna work out fine.

WebRTC more than one peer connection

I searched almost everywhere and can't seem to find any resource on this scenario for some reason. Any help would greatly be appreciated.
The problem is this:
I have 4 users. Lets say: A,B,C and D. I want to match them according to url. For example, if A and B connects to &room=1 and C and D connects to &room=2 I want to establish the connection between A and B pairs and C and D pairs.
Currently my code only establishes connection between A and B, and if one of the C or D users initiates the call, the connection between A and B gets disconnected and C cannot find D.
In order to solve this problem I tried to create an object like this:
{
room: 1,
peer: RTCPeerConnection
}
and store it in an array and get the peer connection according to room id and establish the connection based on RTCPeerConnection that is stored according to roomId. But that didn't work.
How would I go around fixing this without establishing a socket server that handles this communication?
I currently establish connections this way if it helps:
navigator.mediaDevices
.getUserMedia({
audio: true,
video: true
})
.then(function (stream) {
localStream = stream;
localVideo.srcObject = localStream;
try {
conn = new RTCPeerConnection(servers);
} catch (err) {
console.log("Can't establish connection");
return;
}
localStream.getTracks().forEach(function (track) {
conn.addTrack(track, localStream);
});
conn.onaddstream = function (event) {
setTimeout(() => {
callButton.click();
}, 2000);
remoteVideo.srcObject = event.stream;
};
conn.onicecandidate = function (event) {
if (event.candidate) {
chat.server.iceCandidate(
JSON.stringify({ candidate: event.candidate })
);
}
};
})
.catch(function (err) {
console.log("Error", err);
});
And this is my failed solution:
var connections = JSON.parse(localStorage.getItem("connections")) || [];
var connection;
if (connections) {
connection = connections.find(function (conn) {
return conn.id = roomId;
})
}
if (!connection) {
conn = new RTCPeerConnection(servers);
var data = {
id: roomId,
peer: conn
}
localStorage.removeItem("connections");
localStorage.setItem("connections", JSON.stringify(connections));
} else {
conn = JSON.parse(connection.peer);
conn.__proto__ = new RTCPeerConnection();
}
This fails because of course you can't store proto of an object in localStorage (RTCPeerConnection). When I stringify/parse it peer attribute comes as an empty object. If I try a global variable, instead of localStorage, it always comes empty. I'd very appreciate any tip about this issue.
It's like your messing up with the localStorage variable, for testing purpose and avoid the signaling server implementation, I advice to use the serverless-webrtc or an updated by jameshfisher
With this you can create the rooms (separate the clients) and show you a sdp message to copy and paste to the other client (without localStorage) to make the peer connection.
This will be better to test between browsers firefox <-> chrome, safari, etc.

Check if WebSocket Server is running (on localhost)

When I try to initialize a websocket connection to the server running on localhost with
var webSocket = new WebSocket("ws://localhost:8025/myContextRoot");
in javascript, but the server hasn't completed starting up yet, I get the error
SCRIPT12029: WebSocket Error: Network Error 12029, A connection with the server could not be established
How can I prevent this? I.e. how do I check if the server has already started or how can I force the WebSocket client to wait for the server?
What about:
var webSocketFactory = {
connectionTries: 3,
connect: function(url) {
var ws = new WebSocket(url);
ws.addEventListener("error", e => {
// readyState === 3 is CLOSED
if (e.target.readyState === 3) {
this.connectionTries--;
if (this.connectionTries > 0) {
setTimeout(() => this.connect(url), 5000);
} else {
throw new Error("Maximum number of connection trials has been reached");
}
}
});
}
};
var webSocket = webSocketFactory.connect("ws://localhost:8025/myContextRoot");
When you get a connection error, you can do a limited number of trial-errors to try to re-connect. Or you can endlessly try to reach the server.
The accepted answer is perfectly fine. I just would like to extend it a little bit further with promises.
var wsFactory = { tryCount: 3,
connect : function(url){
var ctx = this,
ws = new WebSocket(url);
return new Promise(function(v,x){
ws.onerror = e => { console.log(`WS connection attempt ${4-ctx.tryCount} -> Unsuccessful`);
e.target.readyState === 3 && --ctx.tryCount;
if (ctx.tryCount > 0) setTimeout(() => v(ctx.connect(url)), 1000);
else x(new Error("3 unsuccessfull connection attempts"));
};
ws.onopen = e => { console.log(`WS connection Status: ${e.target.readyState}`);
v(ws);
};
ws.onmessage = m => console.log(m.data);
});
}
};
wsFactory.connect("ws://localhost:8025/myContextRoot")
.then(ws => ws.send("Hey..! This is my first socket message"))
.catch(console.log);
You can't prevent (or put on hold) the WebSocket from starting / establish a connection. WebSocket automatically establishes a connection with the server when its declared. What you can do is place all your code inside onopen event handler that you want to execute on successful connection. So it would be like...
var webSocket = new WebSocket("ws://localhost:8025/myContextRoot");
webSocket.onopen = function() {
// code you want to execute
};
check this article to know more about WebSocket.
Hence the protocol can't get queried by the server if it is not started, the only option is trial and error.
Or you could let the WebSocket server create a simple textfile with the timestamp of the startup in your web space directory where the javascript can retrieve it and than try to establish a connection. You can retrieve the textfile with XMLHttpRequest.

Socket IO reconnect?

How to reconnect to socket io once disconnect has been called?
Here's the code
function initSocket(__bool){
if(__bool == true){
socket = io.connect('http://xxx.xxx.xxx.xxx:8081', {secure:false});
socket.on('connect', function(){console.log('connected')});
socket.on('disconnect', function (){console.log('disconnected')});
}else{
socket.disconnect();
socket = null;
}
}
If I do initSocket(true), it works. If I do initSocket(false), it disconnects. BUT THEN if I try to reconnect using initSocket(true), the connection does not work anymore. How can I get the connection to work?
Well, you have an option here ...
The first time you initialize the socket value you should connect with io.connect,
The next time ( after you've called disconnect once ), you should connect back with socket.socket.connect().
So your initSocket, should be something like
function initSocket(__bool){
if(__bool){
if ( !socket ) {
socket = io.connect('http://xxx.xxx.xxx.xxx:8081', {secure:false});
socket.on('connect', function(){console.log('connected')});
socket.on('disconnect', function (){console.log('disconnected')});
} else {
socket.socket.connect(); // Yep, socket.socket ( 2 times )
}
}else{
socket.disconnect();
// socket = null; <<< We don't need this anymore
}
}
I know you already have an answer, but I arrived here because the socket.IO client reconnection feature is broken in node at the moment.
Active bugs on the github repo show that lots of people aren't getting events on connect failure, and reconnect isn't happening automatically.
To work around this, you can create a manual reconnect loop as follows:
var socketClient = socketioClient.connect(socketHost)
var tryReconnect = function(){
if (socketClient.socket.connected === false &&
socketClient.socket.connecting === false) {
// use a connect() or reconnect() here if you want
socketClient.socket.connect()
}
}
var intervalID = setInterval(tryReconnect, 2000)
socketClient.on('connect', function () {
// once client connects, clear the reconnection interval function
clearInterval(intervalID)
//... do other stuff
})
You can reconnect by following client side config.
// 0.9 socket.io version
io.connect(SERVER_IP,{'force new connection':true });
// 1.0 socket.io version
io.connect(SERVER_IP,{'forceNew':true });
This is an old question, but I was struggling with this recently and stumbled here. Most recent versions of socket.io (>2.0) doesn't have the socket.socket property anymore as pointed out here.
I am using socket.io-client 2.2.0 and I was facing a situation where the socket seems to be connected (property socket.connected = true) but it wasn't communicating with the server.
So, to fix that, my solution was call socket.close()and socket.open. These commands force a disconnection and a new connection.
I had an issue with socket-io reconnect. May be this case will help someone. I had code like this:
var io = require('socket.io').listen(8080);
DB.connect(function () {
io.sockets.on('connection', function (socket) {
initSockets(socket);
});
});
this is wrong, becase there is a delay between open port assigned callbacks. Some of messages may be lost before DB gets initialized. The right way to fix it is:
var io = null;
DB.connect(function () {
io = require('socket.io').listen(8080);
io.sockets.on('connection', function (socket) {
console.log("On connection");
initSockets(socket);
});
});

Categories