How to close a websocket in Angular 2/4+ and Socket.io - javascript

I am trying to create an app that uses websockets, but am running into the dreaded "multiple connection" issue where everytime the page is reloaded rather than closing and reopening a new websocket, another connection is simply added to the list. My question is if anyone knows the best/proper way to close a Socket.io websocket from Angular 2/4+.
Here's what I have as far as code:
service.ts
getUserSocket(userID: string): Observable<any> {
return new Observable(_Observer => {
// Setup the socket namespace subscription
this.UserSocket = io(NTC_API_URL + `/user/${userID}`, { secure: true });
this.UserSocket.on('message', _Message => {
console.log(_Message);
})
})
}
closeUserSocket() {
this.UserSocket.disconnect();
this.UserSocket.close();
}
component.ts
ngOninit() {
// Setup the User Socket
this.UserSocket = this._UsersService.getUserSocket(this.currentUser.userID)
.subscribe(_UserSocketMessage => {
console.log(_UserSocketMessage);
})
}
ngOnDestroy() {
this.UserSocket.unsubscribe();
this._UsersService.closeUserSocket();
}
This doesn't seem to be working as I can still watch the connections pile up just by logging in an out of the application which I have confirmed calls the component to be destroyed.
Another option I tried was using the once listener instead of the on listener, which worked, but I am unsure about the side effects and have read several places that it's not necessarily a fix for not closing the connection, which I can understand.

After quite a bit of tinkering and research it occurred to me that web sockets are made to be resilient. In other words, since they were engineered to reconnect if possible, focusing on disconnect and close was the wrong approach. It was far easier to enforce policies with regard to the Creation of web socket connections.
For starters, I had to refactor a few things on the server side to make sure particular namespaces were only initialized once. This wasn't achieved so much by altering initialization code as it was by making sure that web socket initialization only occurred in places that either only happened on startup, or during processes that are only highly likely to happen once such as a particular user being created.
On the angular side it was also pretty straightforward in that all I had to do was assign the socket to a property within the service, then check if it existed before making the socket connection. Hence only creating the connection if no connection existed previously.
service.ts
// Setup the socket namespace subscription
if (!this.UserSocket) {
// Initialize user permissions socket
this.UserSocket = io(NTC_API_URL + `/user/${this.LocalUser.userID}`, { secure: true });
}

Related

how to emit event on nuxt-socket.io from client to server

im learning to use Nuxt in the past i have worked with socket.io and vue but this time i am using nuxt-socket-io
https://nuxt-socket-io.netlify.app/
My problem is that I cannot send events from client to server
here an example:
in socket-io-server.js
io.emit ("eventTry")
at homepage.vue
mounted () {
this.socket = this. $ nuxtSocket ({
name: "main",
});
this.socket.on ("eventTry", () => {
console.log ("this Work Good");
})
},
and this works fine
but when I try to send from the client to the server nothing works example:
at homepage.vue
this.socket.emit ("eventTryClient");
and in socket-io-serve.js:
io.on ("eventTryClient", () => {
console.log ("something should happen here")
})
and this DONT WORK
I've really looked everywhere and I can't find a solution to this problem that should be so simple, could someone help me? I need to have a bidirectional communication, which is the best of socket.io I think
Thanks in advance for your help <3
You need to listen for events on the socket. Listening on a namespace, e.g. your io variable, won't actually listen to events sent by the client. More info here, although it doesn't explicitly state to listen on the client socket.
I do not know where I saw this solution, but I will share it because surely there is someone else who is interested in this... The only thing necessary to establish the connection between client and server is the following:
in your client (example homepage.vue):
import io from "socket.io-client";
const ioClient = io.connect("http://localhost:3001"); // replace it with the url of your socket
The URL it's in nuxt.config.js, and you probably have something like this:
// socket.io configuration
io: {
sockets: [{
default: true, // make this the default socket
name: 'main', // give it a name that we can later use to choose this socket in the .vue file
url: 'http://localhost:3001' // URL wherever your socket IO server runs
}]
},
With this you can use ioClient as your connection on the client for example:
ioClient.on("connect", () => {
console.log("socket-io-client conected")
ioClient.emit('chatmessage', this.message); //now this works and you can receive it on your server normally
})
I dont know if this is the better way to do that, but i dont really saw solutions for this in the documentation ... maybe im blind, or no body answer that

Reconnect to Laravel Echo server after session disconnection

I am attempting to write an web application with a persistent echo connection to a laravel-echo-server instance, which needs to detect disconnections and attempt to reconnect gracefully. The scenario I am attempting to overcome now is a user's machine has gone to sleep / reawoke and their session key has been invalidated (echo server requires an active session in our app). Detecting this situation from an HTTP perspective is solved - I setup a regular keepAlive, and if that keepAlive detects a 400-level error, it reconnects and updates the session auth_token.
When my Laravel session dies, I cannot tell that has happened from an echo perspective. The best I've found is I can attach to the 'disconnect' event, but that only gets triggered if the server-side laravel-echo-server process dies, rather than the session is invalid:
this.echoConnection.connector.socket.on('connect', function() {
logger.log('info', `Echo server running`);
})
this.echoConnection.connector.socket.on('disconnect', function() {
logger.log('warn', `Echo server disconnected`);
});
On the laravel-echo-server side, I can tell that the connection is dead - it will show this error:
⚠ [7:03:30 PM] - 5TwHN2qUys5VEFP5AAAG could not be authenticated to private.1
I cannot figure out how to catch this failure event programmatically from the client. Is there a way to capture it? Again, I can tell the session is dead eventually because I poll the server regularly via a http keepAlive function, but I would definitely also like to tell directly from the echo connection if possible, as it polls at a much higher natural rate.
As a second (more important) question, if I detect that my session has died, what should I do to recycle the echo connection (after I have logged in again via HTTP and gotten a new auth_token)? Is there anything specific I should call / etc? I've had some success calling disconnect() then setting up the connection again from scratch, but I do see errors such as:
websocket.js:201 WebSocket is already in CLOSING or CLOSED state.
Here is my current (naive) reconnection code, which is my initial connection code with an attempt to disconnect first stapled onto it:
async attemptEchoReconnect() {
if (this.echoConnection !== null) {
this.echoConnection.disconnect();
this.echoConnection = null;
}
const thisConnectionParams = this.props.connections[this.connectionName];
const curThis = this;
this.echoConnection = new Echo({
broadcaster: 'socket.io',
host: thisConnectionParams.echoHost,
authEndpoint: 'api/broadcasting/auth',
auth: {
headers: {
Authorization: `Bearer ` + thisConnectionParams.authToken
}
}
});
this.echoConnection.connector.socket.on('connect', function() {
logger.log('info', `Echo server running`);
})
this.echoConnection.connector.socket.on('disconnect', function() {
logger.log('warn', `Echo server disconnected`);
});
this.echoConnection.join('everywhere')
.here(users => {
logger.log('info', `Rejoined presence channel`);
});
this.echoConnection.private(`private.${this.props.id}`)
.listen(...);
setTimeout(() => { this.keepAlive() }, 120 * 1000);
}
Any help would be so great - these APIs are not well documented to the end that I really want, and I am hoping I can get some stability with this connection rather than having to do something ugly like force restart.
For anyone who needs help with this problem, my above echo reconnection code seems to be pretty stable, along with a keepAlive function to determine the state of the HTTP connection. I am still a bit uncertain of the origin of the console errors I am seeing, but I suspect they have to do with connection loss during a sleep cycle, which is not something I am particularly worried about.
I'd still be interested in hearing other thoughts if anyone has any. I am somewhat inclined to believe long-term stability of an echo connection is possible, though it does appear you have to proactively monitor it with what tools you have available.

Pusher subscribe and un-subscribe process

I'm getting a Pusher subscribe/un-subscribe problem with presence groups on a fairly simple chat application. There should be two subscribed channels at a given time. Users switch between channels when navigating between backbone routes, so there are no hard page reloads.
Pusher appears to function most of the time, but I get intermittent errors for channel subscriptions.
I wrote two join channel methods that unsubscribe if one has been joined by a previous route. I'm worried that there is something async happening within Pusher that is breaking things.
My pusher related code for a single channel:
window.pusher = new Pusher('<%= Pusher.key %>', {
authEndpoint: 'api/pusher/auth'
});
Route:
this.groupFeed = this._pusherSubscribeGroup(group_id);
this.groupFeed.bind('new_conversation', function(data) {
var newConv = new App.Models.Conversation(data);
this.group.conversations().add(newConv);
}.bind(this));
Unsubscribe helper:
_pusherSubscribeGroup: function (group_id) {
if (this._groupChannelName) {
window.pusher.unsubscribe(this._groupChannelName);
}
this._groupChannelName = 'presence-group-' + group_id;
return window.pusher.subscribe(this._groupChannelName);
}
Console error:
Pusher : Error : {"type":"WebSocketError","error":{"type":"PusherError","data":{"code":null,"message":"Existing subscription to channel presence-group-1"}}}
Pusher : Error : {"type":"WebSocketError","error":{"type":"PusherError","data":{"code":null,"message":"Existing subscription to channel presence-group-1"}}}
The message is telling you that - as far as the Pusher service is concerned - this particular client identified by a socket_id is already subscribed to the presence-group-1 channel. From the point of view of the client this error normally isn't something you actually need to worry about.
However, if the user is quickly subscribing/unsubscribing by navigating routes it would be worth getting some more information to be completely sure. From the supplied code and description, it's not possible to determine if the problem is within the Pusher service or the client application.
Providing the output of the Pusher JavaScript library logging will provide more detail that could help determine the cause.

Listen for socket.io connection inside chrome extension

I'm able to send socket.io connections from my extension to my server, but I cannot hear emits from my server inside my extension. I've found conflicting answers regarding this question:
Opening a Socket.IO connection in a google chrome extension says this can't be done; and
Cross-domain connection in Socket.IO says it can.
Is there any special configuration I must change in order to accept emits from my socket server?
EDIT:
(Note: I'm using AngularJS, but it shouldn't be relevant to this question)
socketFactory.js:
myApp.factory('socketFactory', function($rootScope) {
var socket = io.connect('//dev.mydomain.com', {'path': '/api/socket'});
return socket;
}
inject.js:
var packetData = { 'some':'data', 'roomId':'123abc' };
socketFactory.emit('room:join', packetData);
...
socketFactory.on('room:update', function (data) {
console.log('Received data from socket server');
console.log(data)
}
socket.js (server-side):
socket.on('room:join', function ( data ) {
// Setting socketId to detect disconnect
data.user.socketId = socket.id;
socket.join( data.roomId, function() {
// Some code ...
io.sockets.to(data.roomId).emit('room:update', {'some':'data', 'roomId': '123abc'});
}
});
That's the basic setup of my connection. This system works perfectly when I launch the app in non-extension mode (we're making an extension to emulate our webapp), but when in the extension, room:update is never triggered.
EDIT 2:
We did a console.log on the socket object (generated on connect) in socket.js. Inside the headers, it appears the host is dev.mydomain.com, while the referrer is www.othersite.com. Could this be the problem? What does "host" refer to? Host of the socket server, or host of the socket listener? In the latter case, it would make sense it's not reaching www.othersite.com over which we have the extension running.
EDIT 3: ...And it started working out of nowhere. Must be a race condition somewhere. Closing the question as no longer relevant.
It suddenly works. Probably a race condition.

websocket error/network management

I am working on Backbone based application (It is also an tablet application), which uses websockets, .
Websockets are working well, but Application doesn't have any logic for error/network handing.
App should show message to user that he is disconnected, and app should retry to connect again, and once its connected back, things starts working again, like gmail.
I haven't written any server-side websocket code, and I am new to Websockets.
Is there any good article on how to handle network disconnection, reconnection for Websockets?
I am unable to find how to set timeout on Websockets, Or how to reconnect again etc.
As its an tablet app, so there will be frequent network disconnections, also App will be getting into sleep mode. Is there any special considerations or practices ?
According to this article, you can use try/catch to treat erroneous cases, like not being able to connect:
function connect(){
try{
var socket;
var host = "ws://localhost:8000/socket/server/startDaemon.php";
var socket = new WebSocket(host);
message('<p class="event">Socket Status: '+socket.readyState);
socket.onopen = function(){
message('<p class="event">Socket Status: '+socket.readyState+' (open)');
}
socket.onmessage = function(msg){
message('<p class="message">Received: '+msg.data);
}
socket.onclose = function(){
message('<p class="event">Socket Status: '+socket.readyState+' (Closed)');
}
} catch(exception){
message('<p>Error'+exception);
}
}
I couldn't find any mention of setting timeout duration even in the WebSocket spec, that might not be possible.

Categories

Resources