How can i handle Close event in Socket.io? - node.js

I'm making simple online game which based on Web.
the game uses Socket.io for netwoking each other.
but I encountered the problem.
think about following situation .
I ran Socket.io server.
one player making the room , and other player join the room.
they played game some time ..
but one player so angry and close the game tab.
in this situation , how can I get the event which one client have been closed the browser in server-side ?
according to googling , peoples say like this : "use browser-close event like onBeforeUnload"
but I know that All browser don't support onBeforeUnload event. so i want solution about
checking the client disconnection event in SERVER SIDE.
in Socket.io ( nodeJS ) server-side console , when client's connection closed , the console say like following :
debug - discarding transport
My nodeJS version is 0.4.10 and Socket.io version is 0.8.7. and both are running on Linux.
Anyone can help please ?
shortend codes are here :
var io = require ( "socket.io" ).listen ( 3335 );
io.sockets.on ( "connection" , function ( socket )
{
socket.on ( "req_create_room" , function ( roomId )
{
var socketInstance = io
.of ( "/" + roomId )
.on ( "connection" , function ( sock )
{
sock.on ( "disconnect" , function ()
{
// i want this socket data always displayed...
// but first-connected-client doesn't fire this event ..
console.log ( sock );
}
});
});
});

Update: I created a blog post for this solution. Any feedback is welcome!
I recommend using the 'sync disconnect on unload' option for Socket IO. I was having similar problems, and this really helped me out.
On the client:
var socket = io.connect(<your_url>, {
'sync disconnect on unload': true });
No need to wire in any unload or beforeunload events. Tried this out in several browsers, and its worked perfectly so far.

There's an event disconnect which fires whenever a socket.io connection dies (note that you need this, because you may still have a wep page open, but what if your internet connection dies?). Try this:
var io = require('socket.io').listen(80);
io.sockets.on('connection', function (socket) {
socket.on('disconnect', function () {
io.sockets.emit('user disconnected');
});
});
at your server. Taken from Socket.IO website.
//EDIT
So I looked at your code and did some tests at my place. I obtained the very same results as you and here's my explanation. You are trying to make Socket.IO very dynamic by dynamically forcing it to listen to different urls (which are added at runtime). But when the first connection is made, at that moment the server does not listen to the other url. It seems that exactly at that point (when connection is accepted) the disconnect handler is set for the first connection and it is not updated later (note that Socket.IO does not create new connections when you call io.connect many times at the client side). All in all this seems to be a bug! Or perhaps there is some fancy explanation why this behaviour should be as it is but I do not know it.
Let me tell you some other things. First of all this dynamical creation of listeners does not seem to be a good way. In my opinion you should do the following: store the existing rooms and use one url for all of them. Hold the ID of a room and when you emit for example message event from client add the ID of a room to the data and handle this with one message handler at the server. I think you get the idea. Push the dynamic part into the data, not urls. But I might be wrong, at least that's my opinion.
Another thing is that the code you wrote seems to be bad. Note that running .on('connection', handler) many times will make it fire many times. Handlers stack one onto another, they do not replace each other. So this is how I would implement this:
var io = require("socket.io").listen(app);
var roomIds = [];
function update_listeners(id) {
io.of("/"+id).on("connection", function(socket) {
console.log("I'm in room " + id);
socket.on("disconnect", function(s) {
console.log("Disconnected from " + roomId);
});
});
}
var test = io.sockets.on("connection", function(socket) {
console.log("I'm in global connection handler");
socket.on("req_create_room", function(data) {
if (roomIds.indexOf(data.roomId) == -1 ) {
roomIds.push(data.roomId);
update_listeners(data.roomId);
}
test.emit("room_created", {ok:true});
});
socket.on("disconnect", function(s) {
console.log("Disconnected from global handler");
});
});
Keep in mind that the problem with creating connections before the listeners are defined will still occure.

Related

Socket: When player in a room closes browser, how server can correctly emit to other sockets in that room?

Please, read to the end, help me!
I'm developing a multiroom game using SocketIO, with a client-side (React) and a server-side (Node). I'm having a problem on handling this situation:
Consider that at least two clients are inside a Room, after have joining it with socket.join(roomId) (at server-side).
Situation 1: When a player leaves the room manually (clicking a button in the page), the client-side does socket.emit("playerLeaving", someVar) to the server-side listener socket.on("playerLeaving", doStuff) that do some important things with the database (let's call it X listener). Then this listener fires back an event to the other client sockets in that room, with io.sockets.in(roomID).emit("playerLeft", someVar). No problem here.
Situation 2: Instead of manually leaving that room, that user just close the tab/browser, which fires a "disconnect" event do the server-side. Event in that case, the server must do the same procedure that he did on 1st situation, inside that X listener, so I just tried to emit this event socket.emit("playerLeaving", roomID) from inside the "disconnect" handler at server-side, so the X listener would receive it, do his stuffs, and broadcast to the other players that someone has left that room.
THE PROBLEM: the "disconnect" handler (at server-side) emits to X listener (also server-side), but this one doesn't receives it. Summin up, when a player in a room closes the tab/browser, the server isn't handling it correctly and consequently the other players in that room doesn't even get to know about that.
MY CODE:
SERVER-SIDE:
io.sockets.on('connection', (socket) => {
socket.on("playerJoiningRoom", (roomID) => {
socket.join(roomID); // to sign socket to a room
// .... some code
io.broadcast.to(roomID).emit("someoneJoined", someVar); // this works fine
})
// X-listener
socket.on("playerLeaving", (someVar) => {
// ... do stuff in database
io.sockets.in(roomID).emit("playerLeft", otherVar);
}
socket.on("disconnect", () => {
// ...some code here
// then, emit to the listener above
// example: socket.emit("playerLeaving", someVar);
});
})
}
CLIENT-SIDE
function socketEnteredRoom(socket, roomID) {
// notify server
socket.emit("playerJoiningRoom", roomID);
// listen to other players joining the room
socket.on("someoneJoined", someVar);
// listen to other players leaving the room. This listener works when some player leave manually (leave button), but don't work when a player closes tab/browser
socket.on("someoneLeft", someVar);
}
// when player wants to exit room
function handlerExitRoom() {
socket.emit("playerLeaving", someVar);
}
Waiting for your help! Thank you!
(sorry if this is a repeated question, but I couldn't find a answer that worked)
I'm not sure since I can't see your full code, but a common issue is people try to emit to rooms their socket is in in disconnect but the socket is no longer in any rooms at this point. Try changing to disconnect to disconnecting.
Try this,
//server-side
socket.on("disconnect", () => {
io.emit("playerLeaving", someVar);
});
//client-side
socket.emit("disconnect", () => {
});

socket.on event gets triggered multiple times

var express = require('express');
var app = express();
var server = app.listen(3000);
var replyFromBot;
app.use(express.static('public'));
var socket = require('socket.io');
var io = socket(server);
io.sockets.on('connection' , newConnection);
function newConnection(socket) {
console.log(socket.id);
listen = true;
socket.on('Quest' ,reply);
function reply(data) {
replyFromBot = bot.reply("local-user", data);
console.log(socket.id+ " "+replyFromBot);
socket.emit('Ans' , replyFromBot);
}
}
i've created a server based chat-bot application using node.js socket.io and express but the thing is for first time when i call socket.on it gets executed once and for 2nd time it gets executed twice for 3rd thrice and so on i've tackled this issue by setting a flag on my client so that it would display only once. i just wants to know is my code logically correct i mean is this a good code? because if the client ask a question for 10th time than listeners array will have 10+9+8....+1 listeners it would go on increasing depending upon number of questions clients asked. which is not good
i tried using removeListener it just removes listener once and it dosent call back for 2nd time. what do you guys recommend? do i go with this or is there any other way to add the listener when socket.on called and remove it when it gets executed and again add listener for the next time it gets called
thank-you.
client code:
function reply() {
socket.emit('Quest' , Quest);
flag = true;
audio.play();
socket.on('Ans', function(replyFromBot) {
if(flag) {
console.log("hi");
var para = document.createElement("p2");
x = document.getElementById("MiddleBox");
para.appendChild(document.createTextNode(replyFromBot));
x.appendChild(para);
x.scrollTop = x.scrollHeight;
flag = false;
}
});
}
The problem is caused by your client code. Each time you call the reply() function in the client you set up an additional socket.on('Ans', ...) event handler which means they accumulate. You can change that to socket.once() and it will remove itself each time after it get the Ans message. You can then also remove your flag variable.
function reply() {
socket.emit('Quest' , Quest);
audio.play();
// change this to .once()
socket.once('Ans', function(replyFromBot) {
console.log("hi");
var para = document.createElement("p2");
x = document.getElementById("MiddleBox");
para.appendChild(document.createTextNode(replyFromBot));
x.appendChild(para);
x.scrollTop = x.scrollHeight;
});
}
Socket.io is not really built as a request/response system which is what you are trying to use it as. An even better way to implement this would be to use the ack capability that socket.io has so you can get a direct response back to your Quest message you send.
You also need to fix your shared variables replyFromBot and listen on your server because those are concurrency problems waiting to happen as soon as you have multiple users using your server.
Better Solution
A better solution would be to use the ack capability that socket.io has to get a direct response to a message you sent. To do that, you'd change your server to this:
function newConnection(socket) {
console.log(socket.id);
socket.on('Quest', function(data, fn) {
let replyFromBot = bot.reply("local-user", data);
console.log(socket.id+ " "+replyFromBot);
// send ack response
fn(replyFromBot);
});
}
And, change your client code to this:
function reply() {
audio.play();
socket.emit('Quest', Quest, function(replyFromBot) {
console.log("hi");
var para = document.createElement("p2");
x = document.getElementById("MiddleBox");
para.appendChild(document.createTextNode(replyFromBot));
x.appendChild(para);
x.scrollTop = x.scrollHeight;
});
}
Doing it this way, you're hooking into a direct reply from the message so it works as request/response much better than the way you were doing it.
Instead of socket.on('Quest' ,reply); try socket.once('Quest' ,reply);
The bug in your code is that each time newConnection() is called node registers a event listener 'Quest'. So first time newConnection() is called the number of event listener with event 'Quest' is one, the second time function is called, number of event listener increases to two and so on
socket.once() ensures that number of event listener bound to socket with event 'Quest' registered is exactly one

I do not want to run socket.emit until I am ready to use it

So I don't want to fetch from the server socket.emit until I need it. Because everything is not prepared fully until then. How do I have socket.emit sit quietly until I am ready for it?
Server
io.sockets.on('connection', function (socket) {
socket.on('adduser', function(player){
........
}
socket.emit('getQ', socket.Ques[0].question);
]
Client
var retrieveQ = function (){
socket.on('getQ', function(quesPick){
quesObj = quesPick;
})
The client is retrieveQ much later on its not suppose to be done at start, yet socket is emitting almost right away and giving me errors because socket.Ques is not made until later on.
Simple, emit getQ on response to a client request after client socket connects. Something like this:
io.sockets.on('connection', function (socket) {
socket.on('adduser', function(player){
........
}
socket.on('askGetQ', function(zzz){
socket.emit('getQ', socket.Ques[0].question);
}
}
On the client do:
io.on('connection', function(socket){
socket.emit('getQ', 'something');
});
node.js is an event driven environment. So, sometime in the future there will be some sort of event on your server that will cause you to want to send this .emit(). At that point, you need to be able to find the appropriate socket object and then use it to send the .emit().
Since you haven't said exactly what event you want to use to send this emit, we can't really help more specifically. If you care to explain that, we could probably offer specific code.
On the client you should do:
const chat = io.connect('http://localhost/chat')
chat.on('connect', function () {
chat.emit('getQ', socket.Ques[0].question);
});

node.js + socket.IO - socket not reconnecting?

I know socket.io has a built in feature for reconnecting and everything, however I don't think that it is working - as I have seen from others it's also not working for them either.
If a user puts their computer to sleep, it disconnects them, and then when they open it back up they are no longer connected so they don't any of the notifications or anything until they refresh the page. Perhaps it's just something that I'm not doing correctly?
var io = require('socket.io').listen(8080);
var users = {};
////////////////USER CONNECTED/////
console.log("Sever is now running");
io.sockets.on('connection', function (socket) {
//Tell the client that they are connected
socket.emit('connected');
//Once the users session is recieved
socket.on('session', function (session) {
//Add users to users variable
users[socket.id] = {userID:session, socketID:socket};
//When user disconnects
socket.on('disconnect', function () {
//socket.socket.connect();
var count= 0;
for(var key in users){
if(users[key].userID==session)++count;
if(count== 2) break;
}
if(count== 1){
socket.broadcast.emit('disconnect', { data : session});
}
//Remove users session id from users variable
delete users[socket.id];
});
socket.on('error', function (err) {
//socket.socket.connect();
});
socket.emit("connection") needs to be called when the user reconnects, or at least the events that happen in that event need to be called.
Also socket.socket.connect(); doesn't work, it returns with an error and it shuts the socket server down with an error of "connect doesn't exist".
The problem is related to io.connect params.
Look at this client code (it will try to reconnect forever, with max delay between attempts 3sec):
ioParams = {'reconnection limit': 3000, 'max reconnection attempts': Number.MAX_VALUE, 'connect timeout':7000}
socketAddress = null
socket = io.connect(socketAddress, ioParams)
There are two important parameters out there, related to your problem:
reconnection limit - limit the upper time of delay between reconnect attemts. Normally it's getting bigger and bigger in time of server outage
max reconnection attempts - how many times you want to try. Default is 10. In most cases this is the problem why the client stops trying.

Socket.io: Connect from one server to another

I'm trying to make a nodejs(socket.io) server to communicate with another one.
So the client emits an event to the 'hub' server and this server emits an event to some second server for processing the action.
I tried to do:
var io_client = require( 'socket.io-client' );
and then,
io_client.connect( "second_server_host" );
it seems to work for connection but you can't do anything with this:
debug - set close timeout for client 15988842591410188424
info - socket error Error: write ECONNABORTED
at errnoException (net.js:642:11)
at Socket._write (net.js:459:18)
at Socket.write (net.js:446:15)
I guess I'm doing it wrong and missing something obvious.
Any suggestions?
Just came across this question, and another just like it with a much better answer.
https://stackoverflow.com/a/14118102/1068746
You can do server to server. The "client" code remains the same as if it was on the browser. Amazing isn't it?
I just tried it myself, and it works fine..
I ran 2 servers - using the same exact code - once on port 3000 as server, and another on port 3001 as client. The code looks like this:
, io = require('socket.io')
, ioClient = require('socket.io-client')
....
if ( app.get('port') == 3000 ){
io.listen(server).sockets.on('connection', function (socket) {
socket.on('my other event', function (data) {
console.log(data);
});
});
}else{
function emitMessage( socket ){
socket.emit('my other event', { my: 'data' });
setTimeout(function(){emitMessage(socket)}, 1000);
}
var socket = ioClient.connect("http://localhost:3000");
emitMessage(socket);
}
And if you see on the server side a "{my:data}" print every second, everything works great. Just make sure to run the client (port 3001) after the server (port 3000).
For anyone searching for a short working example, see below. This example works with socket.io#0.9.16 and socket.io-client#0.9.16.
var port = 3011;
var server = require( 'http' ).createServer( ).listen( port, function () {
console.log( "Express server listening on port " + port );
} );
var io = require( 'socket.io' ).listen( server ).set( "log level", 0 );
io.sockets.on( "connection", function ( socket ) {
console.log( 'Server: Incoming connection.' );
socket.on( "echo", function ( msg, callback ) {
callback( msg );
} );
} );
var ioc = require( 'socket.io-client' );
var client = ioc.connect( "http://localhost:" + port );
client.once( "connect", function () {
console.log( 'Client: Connected to port ' + port );
client.emit( "echo", "Hello World", function ( message ) {
console.log( 'Echo received: ', message );
client.disconnect();
server.close();
} );
} );
For Server to Server or App to App communication, I think you should look into Redis Pub-sub. Its capable of very good speeds, and can handle the entire message queuing architecture of a big app.
Here is a slightly complex but quite understandable example for using Redis Pub Sub:
Redis Pub Sub Example
For anyone looking to do this on a MeteorJS app, I created a new Meteor package joncursi:socket-io-client to solve this problem. Please see https://atmospherejs.com/joncursi/socket-io-client for more detail and example usage. Since I've bundled the NPM binaries into a package for you, you don't have to worry about installing NPM packages, declaring NPM.require() dependencies, etc. And best of all, you can deploy to .meteor.com without a hitch.
ECONNABORTED means that the connection have been closed by "the other side".
For example, lets say we have two programs, A and B. Program A connects to program B, and they start to send data back and forth. Program B closes the connection for some reason. After the connection was closed program A tries to write to program B, but since the connection is closed program A will get the error ECONNABORTED.
One of your programs have closed the connection, and the other doesn't know about it and tries to write to the socket, resulting in an error.
The native Node TCP module is probably what you want - I wanted to do what you're trying to do but it seems that the fact that WebSockets are strictly many-browser to server, or browser to many-server.
You can weave a tcp strategy into your websocket logic.
Using tcp:
var net = require('net');
var tcp = net.connect({port: 3000, host: 'localhost'});
tcp.on('connect', function(){
var buffer = new Buffer(16).fill(0);
buffer.write('some stuff');
tcp.write(buffer);
});
tcp.on('data', function(data){console.log('data is:', data)});
tcp.on('end', cb);
tcp.on('error', cb);
I would use a bridge pattern with this:
https://www.google.com/search?q=javascript+bridge+pattern&aq=f&oq=javascript+bridge+pattern&aqs=chrome.0.57.6617&sourceid=chrome&ie=UTF-8
OR, use the Node Module https://npmjs.org/package/ws-tcp-bridge
I also heard that using redis can be quite helpful - Socket.io uses this as a fallback.
Hope this helps...
Cheers

Resources