Node JS socket.on() client side glitch? - javascript

I am learning Node JS and have come across some strange behavior which perhaps someone knows the answer to. A lot of tutorials posts I have come across are saying to use "socket.on('connect', function(){ blah blah }); on the client side to send a request to the server. Esentially this is supposed to stop a call from being sent to the server until the server has established a connection with the client. However when I test the code, it works as long as the client html is run first, and then node js is launched afterwards. If node js is running the server code first, and then the client side html is launched, the emit function doesn't call out to the server for some reason. If I omit the socket.on function and go straight to my socket.emit function, the server receives the emit function from the client. If node js is not running first, the client side html continues to run until node js launches the server script, at which point the client connects, and emits the request automatically. Does anyone know why this is happening? As I said, I am still learning Sockets and Node JS, so I would rather understand the behavior instead of just continuing on with my coding, even though I got it to work. Any information you provide would be extremely valuable. Thanks.:)
// Server side
var express = require('express');
var socket = require('socket.io');
var app = express();
var server = app.listen(1111, function(){ console.log("Server Listening"); });
var io = socket(server);
io.on('connect', function(socket) {
socket.on('Contact', function(data) {
socket.join(data);
console.log(data); // just to test if data is sent
});
});
// Client in head section
<script src = "https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.7.3/socket.io.js"></script>
<script type = "text/javascript">
var socket = io.connect("http://localhost:1111");
function Setup() { // called when page has loaded
//blah blah blah lots and lots of code
var data = "blahblah"; // room to join
socket.on('connect', function(){
socket.emit('Contact', data);
});
}
// If I remove the socket.on function it works flawlessly everytime. Left in, it will only fire if the
// node js server file is launched after the client launches

It seems like you're calling the Setup() function manually some time later (in the page load event perhaps, based on the comment). This means you're creating a race condition: If the server connects before the page load event, the connect event happens too fast and you attach your listener only after it happens.
So essentially what's happening is this:
First Case (server starts after client)
Client HTML loads, script executes
Client starts attempting connection to server
Client assets finish loading, Setup() is called
Client connect listener attached to socket
Server starts up, starts accepting connections
Server accepts client connection
Client connect listener fires, emits Contact event
Second Case (client starts after server)
Server starts up, starts accepting connections
Client HTML loads, script executes
Server accepts client connection
There is currently no connect listener, so nothing happens
Client assets finish loading, Setup() is called
Client connect listener attached to socket
Silence...
To fix, you should only call io.connect(...) in the same scope that you add listeners to it. This means either moving the socket.on('connect', ...) outside of Setup() or moving the io.connect() inside Setup() (probably better).

Related

Closing single node server connection closes them all

I barely ask any questions on Stack Overflow, but this one is beyond me. I guess I'm missing something basic as I'm pretty new to Node server.
Our application is pretty basic. The server is supposed to receive a handful of text lines (data), merge and parse them, and once the connection is closed (data sending is over) it sends the data to the api.
var net = require('net');
var fs = require('fs');
const axios = require('axios')
const server = new net.Server();
server.listen(PORT, IP);
server.on("connection", client => {
client.write("Hello\n");
console.log('connected');
let received = "";
client.on("data", data => {
received += data
console.log("Partial data is: " + data);
});
client.on("close", () => {
received = received.toString('utf8');
fs.appendFile('log.txt', received, function (err) {});
received = received.replace(/(?:\r\n|\r|\n)/g, "||");
axios.post(APIADDRESS, {data: received});
console.log('Full data is: '+ {data: received});
});
});
To send the data I'm simply running a netcat or nc using the netcat ipaddress port, that's not a problem. It's connecting fine, status message is received.
The thing is - once I open two or more connections from two DIFFERENT SSh servers something weird happens. I can send the line after line just fine. The server reports back "partial data" debug without problem, for both of them.
However, once I close one of the connections (ctrl+c) they BOTH close.
In the end, only the data from the manually closed connection is received. The other one, from a separate nc on a separate ssh server never reaches the client.on("close") part, it seems. It's just terminated for no reason.
Any ideas? I don't even know where to start.
//EDIT
Just tested it from my pc and some ssh mobile app using separated SSH servers. As soon as ctrl+c is sent at any device it closes the connection for all clients.
//Forgot to mention I'm running pm2 to keep the server up. Once I turned on the script by hand, ignoring pm2 - it works fine. Weird. It is happening because of PM2.
I would guess that you have Putty configured to ‘Share SSH connections if possible’. Per some doc, when doing so:
When this mode is in use, the first PuTTY that connected to a given server becomes the ‘upstream’, which means that it is the one managing the real SSH connection. All subsequent PuTTYs which reuse the connection are referred to as ‘downstreams’: they do not connect to the real server at all, but instead connect to the upstream PuTTY via local inter-process communication methods.
So, if you Ctrl+C the PuTTY session that is managing the actual shared connection, they both lose their connection.
You could presumably disable this shared connection feature at either the client or server end of things since both must be enabled for sharing to occur.
To anyone coming here in the future.
If you are using pm2 with --watch enabled and the text log file is in the same folder as your main server script... That's the reason why it drops the connection after a single client disconnects. It just detects that the log has changed.
I'm not facepalming, that's not even funny.

Weird JavaScript in a Express/Socket.io app I am working on

I am trying to develop a SCADA like app using Brian Ford's excellent https://github.com/btford/angular-socket-io-seed as a starting point, but I have run into some some JavaScript code that I just don't understand. Worse I don't even know what to search for.
Every example I find via Google uses the second syntax which here at least does not work.
This code in the main app.js works, but I need access to the socket object so I can pass it to my under development simulation module, so I need to change it. But when I change the socket connect call back the module no longer gets loaded. I know when code in routes/socket.js gets run because the log line MET3: is from it.
Can somebody give me a clue what the original line is doing so I can make changes to it?
Is this some cool new shorthand I should be using?
Not certain if it is relevant but I am running socket.io 0.9.16 and node.js 0.10.29.
var io = require('socket.io').listen(server);
// many lines latter
io.sockets.on('connection', require('./routes/socket.js')); // What is this?
Working Output
Express server listening on port 3000
debug - client authorized
info - handshake authorized hOEv8Iv7pPO1xLdxdq1V
MET1: routes/index.js index()
debug - setting request GET /socket.io/1/websocket/hOEv8Iv7pPO1xLdxdq1V
**MET3:** routes/socket.js Socket ID [hOEv8Iv7pPO1xLdxdq1V] connected
debug - websocket writing 5:::{"name":"send:name","args":[{"sockName":"JohnDoe","sockPage":"RETS"}]}
debug - websocket writing 5:::{"name":"send:time","args":[{"time":"Fri Jul 25 2014 17:39:32 GMT-0400 (EDT)"}]}
var io = require('socket.io').listen(server);
// many lines latter
io.sockets.on('connection', function (socket) {
console.log ("MET0: app.js io.sockets.on() running");
require('./routes/socket.js'); // This should work
});
Broken Output
Express server listening on port 3000
debug - client authorized
info - handshake authorized Hn9It34K2OCT6o8ceinF
**MET0:** app.js io.sockets.on() running
MET1: routes/index.js index()
debug - emitting heartbeat for client Hn9It34K2OCT6o8ceinF
debug - websocket writing 2::
debug - set heartbeat timeout for client Hn9It34K2OCT6o8ceinF
debug - got heartbeat packet
debug - cleared heartbeat timeout for client Hn9It34K2OCT6o8ceinF
io.sockets.on('connection', require('./routes/socket.js')); // What is this?
Essentially this says pass whatever is returned from require('./routes/socket.js') to the sockets function. If you aren't familiar with how requiring modules work in node, it is likely that /routes/socket.js contains something like the following:
module.exports = function() {
// Do some work
};
This means that the function above will be returned by the require call. Have a look inside socket.js and see what is returned.

Trigger Event On Server Shutdown (NodeJS)

As we all know, when you update a nodejs file and upload it to your server, it needs a restart to be applied -- but if you're using something like nodemon where it automatically restarts your server, your clients that are logged in would need to refresh the screen.
So I was wondering, is there a way I could trigger a "logout" when my server goes down/is restarted so that at the minimum it just refreshes the clients so they know what's happened?
As it stands they'll broadcast undefined errors because theyre session has been lost?
I tried to validate using a function I have called authenticate
function authenticate(req,res,next) {
if (!req.session.loggedIn) {
fs.readFile('./html/login.html', "utf8", function(err, html) {
res.send(html);
});
} else next();
}
and here's how I call the function:
app.io.route('move', authenticate, function(req) {
But my server crashes because the parameters are undefined at that point.
Basically think of it like an onbeforeunload event but instead of the browser, the server.
The approach I would take is have something like Socket.IO notify all connected clients that your server is performing a restart, this restart notice, once received, would cause the client to disconnect the socket and poll for the server to come back up. Once it's back up, you can do a hard or soft refresh.
Server Server
SIGINT/Exit received --> io.broadcast("server:restart") --
Client Client
-> receive "server:restart" --> disconnect socket, poll for server back up --
Client
-> load refreshed data
You can capture some signal events like shown here and based on some information here you can use the "exit" event.
process.on("exit", function() {
app.io.sockets.emit("server:shutdown");
});
This will only work if the emit process is syncrhonous (and it looks like it may be). That means you can't get a callback you can only broadcast the message and let the client do the rest.
If you want to "hard refresh" you can poll a "heartbeat" route that just returns if the server is good (and running, obviously) and then do a location.reload() call. For a soft refresh, ping a route that returns new page contents and then page swap ($("body").html(response)).

Establishing WebSocket Connection within and for a JS API

Aim: In order to provide a simple, easy-to-use JS library that communicates with a server I am trying to establish a WebSocket connection at the start of my API and define the library's functionality thereafter.
Problem: The issue is that my socket's readystate does not transition to 1 (ready) before the library is loaded and the user's script is run. Debugging this has confirmed this and I'm not sure how to ensure my WebSocket is established before proceeding with the user's script.
What I have:
(function(myObject) {
var socket = new WebSocket(url);
socket.emit = function() {
//do some tasks and create a var "message"
socket.send(message);
}
window.myObject = myObject.prototype = {
doSomething: function() {
socket.emit();
}
}
}(myObject = window.myObject || {}));
Above, I've defined a custom method (socket.emit()) on the socket object that, in turn, calls socket.send(). I do this to manipulate some data before sending it via socket.send(), reusing code that would otherwise be repeated.
Then I have some very basic html that includes this JS library and a simple script runs as follows:
$(document).ready(function() {
myObject.doSomething();
});
The error I get is: "Failed to execute 'send' on 'Websocket': already in CONNECTING state."
The library successfully loads but the Websocket does not complete connection before the client script is run. How can I ensure my WebSocket is connected (readystate == 1) before the API finishes loading?
Thanks for the help!
What you could do is buffer all messages in socket.emit as long as the socket is not connected and send them once you receive the onOpen callback.
While this will handle your current problem you will see that new ones will arise:
You might want to handle disconnects from your server and reconnects and therefore you might need to extend your library API anyhow to also expose connectionstate change events and force the library user to only send messages when connected.

Socket.io not listening

im using this simple code
Client.html
http://pastebin.com/c1C2F9MQ
Server.js
http://pastebin.com/Cqjhjfwm
Everything working fine till the last step of my client code
socket.on('add', function(data) {
socket.broadcast.emit('AAA');
});
It seems that the socket add never comes but on my server I have
socket.on('Text', function(data) {
socket.emit('add', data);
});
And I tested if socket text was coming and it does, I cant find the problem
Thanks
socket.broadcast.emit sends message to all sockets connected to server except the concerned socket. So most likely add does come to server but it broadcasts AAA which your client cannot get. Use io.sockets.emit to send to all connected sockets. Change this
socket.broadcast.emit('AAA');
to
io.sockets.emit('AAA');
Update
I too overlooked that you are calling socket.broadcast.emit from client not from server. It would have shown error on browser console, since broadcast is absent on client.
Currently your on('add') code on the client side is within the on('connect') event which is not correct...
You need to take it ouside there, so it becomes:
socket.on('connect', function () {
$('#button').click(function() {
var addtext = $('#text').val();
socket.emit('Text', addtext);
});
});
socket.on('add', function(data) {
socket.emit('AAA');
});
EDIT: I also just noticed you had socket.broadcast.emit() in your client side code. As far as I know there's no concept of broadcasting from the client. If you want to broadcast something then the client should send it to the server then the server broadcasts to the other clients.

Categories

Resources