I'm doing an HTML5 Game using node.js and socket.io
I decided to host it on Heroku.
Heroku isn't allowing the use of WebSockets, so I have to setup xhr-polling instead. (Socket-io on Heroku)
io.configure( function() {
io.set( "transports", ["xhr-polling"] );
io.set( "polling duration", 10 );
} );
Before, I was using web-sockets only
io.set( "transports", ["websocket"] );
Now, when a client disconnect (close his window or refresh his page) the event "disconnect" isn't trigger immediatly on the server (it looks like it's waiting for the client heartbeat to time out).
client.on( "disconnect", onClientDisconnect );
If the client reloads, I get multiple connection events before disconnect is fired.
My problem is here.
Do you have any ideas, why xhr-polling doesn't fire the disconnect event ?
Is this a bad configuration of socket.io ?
Thanks.
It says here that you can configure the heartbeat. To properly configure it, you must adjust the heartbeat both on the server and the client side (which is given here).
Try lowering the heartbeat. It may solve your problem.
On other note, appfog seems to support websockets.
Just configure session auth and you can always know what client has connected. E.g.
io.set('authorization', function(handshakeData, ack) {
var cookies = require(...);
var signedCookies = parseCookies(cookies, secret);
sessionStore.get(signedCookies['connect.sid'], function(err, sessionData) {
handshakeData.session = sessionData || {};
handshakeData.sid = signedCookies['connect.sid'] || null;
ack(err, err ? false : true);
});
});
Related
i'd like to ask some question about how to close a websocket client when offline/switched network.
when i try to close the socket for the 2 case in chrome, after i call websocket.close, i cannot recieve onclose event for a long time (around 60s), then i can recieve it finally.
after i check the readystate, i found that in the coming 60s, the state is 2(CLOSEING), not turned to 3(CLOSED).
so i'd like to know is there any steps i missed when i call websocket.close() in offline/switched network condition. while it runs well when the network is normal.
what's your back-end framework?
If you try to handle client's network that suddenly turned offline, there're two way you can try to close websocket from client as follows.
Kindly refer to source code here.
Using the js offline event handle
If we would like to detect if the user went offline we simply add websocket close function into offline event function.
front-end
function closeWebSocket() {
websocket.close();
}
$(window).on('beforeunload offline', event => {
closeWebSocket();
});
back-end (WebSocketServer)
#OnClose
public void onClose(Session session) {
CURRENT_CLIENTS.remove(session.getId());
}
Using Ping interval on the client-side and decrease the websocket timeout on server-side
If websocket server doesn't receive any message in specific time, it will lead to a timeout. So we can use this mechanism to decrease the timeout to close session if the client doesn't send any ping due to offline.
front-end
// send ping to server every 3 seconds
const keepAlive = function (timeout = 20000) {
if (websocket.readyState === websocket.OPEN) {
websocket.send('ping');
}
setTimeout(keepAlive, timeout);
};
back-end (WebSocketConfig)
#Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxSessionIdleTimeout(5000L);
return container;
}
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.
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.
How can I close the socket connection on the client side?
I am using:
socket.io 0.9
node.js 0.10.15
express 3.3.4
i.e.:
call localhost/test
-- server side
var test = io
.of('/test')
.on('connection', function (socket) {
console.log('open socket: ' + socket);
socket.on('disconnect', function () {
console.log('disconnected event');
//socket.manager.onClientDisconnect(socket.id); --> endless loop with this disconnect event on server side
//socket.disconnect(); --> same here
});
});
-- client side
var socket = io.connect('http://localhost:3000/test');
socket.on('disconnect', function () {
console.log('disconnect client event....');
});
socket.emit('getInitData', function (data) {
.. do something with data
});
If I load the test-page I need some values from the server (getInitData).
On the first page visit I get the data once, on a reload or second visit I get it twice and so on.
The connection on the server side is beeing closed automatically on page reload and if you leave the page.
But on the client side the connection is still open.
How can I close the connection on the client side or check if there is already a open connection?
UPDATE
I tried now the following: (client side)
window.onbeforeunload = function(e) {
socket.disconnect();
};
This triggers on the client side the disconnect event, but I still get the twice or tripple response.
Did you try:
socket.disconnect()
on client?
For socket.io version 1.4.5:
On server:
socket.on('end', function (){
socket.disconnect(0);
});
On client:
var socket = io();
socket.emit('end');
There is no such thing as connection on server side and/or browser side. There is only one connection. If one of the sides closes it, then it is closed (and you cannot push data to a connection that is closed obviously).
Now a browser closes the connection when you leave the page (it does not depend on the library/language/OS you are using on the sever-side). This is at least true for WebSockets (it might not be true for long polling because of keep-alive but hopefuly socket.io handles this correctly).
If a problem like this happens, then I'm pretty sure that there's a bug in your own code (on the server side). Possibly you are stacking some event handlers where you should not.
socket.disconnect()
Only reboots the connection firing disconnect event on client side. But gets connected again.
socket.close()
Disconnect the connection from client. The client will keep trying to connect.
socket.disconnect() is a synonym to socket.close() which disconnect the socket manually.
When you type in client side :
const socket = io('http://localhost');
this will open a connection with autoConnect: true , so the lib will try to reconnect again when you disconnect the socket from server, to disable the autoConnection:
const socket = io('http://localhost', {autoConnect: false});
socket.open();// synonym to socket.connect()
And if you want you can manually reconnect:
socket.on('disconnect', () => {
socket.open();
});
I'm trying to close users connection in version 1.0 and found this method:
socket.conn.close()
The difference between this method and disconnect() is that the client will keep trying to reconnect to the server.
try this to close the connection:
socket.close();
and if you want to open it again:
socket.connect();
Just try socket.disconnect(true) on the server side by emitting any event from the client side.
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.