I'm playing around with Node, Socket.IO and BDD by creating a chat application. During one of the tests, I get a timeout error stating:
Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
The affected test is
it('#must be able to receive a message', function(done)
{
chatterServer.on('chatterMessage', function(data)
{
console.log('Incoming message!');
expect(data).to.have.property('message');
expect(data.message).to.be('Hello, world!');
done();
});
console.log('Sending message!');
chatterClient.send('chatterMessage', { message: 'Hello, world!' });
console.log('Sent!');
});
I found that the cause of this issue is that the chatterMessage event is not being caught by the server. Whilst I did specify it.
The console's output is:
Sending message!
Sent!
Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
I'm probably doing something wrong. I'm not too familiar with Node and Socket.IO, so I'm sorry if this question is very obvious.
I looked around Google with the search terms 'socket.io server not receiving from client', but from what I found, nothing helped me to solve my issue so far.
I did however try the solution in this question, but that didn't fix it for me.
I'm using Mocha and expect.js
The complete test is:
var util = require('util');
var Chatter = require('../src/index');
var ChatterServer = Chatter.Server;
var ChatterClient = Chatter.Client;
var express = require('express');
var expect = require('expect.js');
var socketIO = require('socket.io');
var socketIOClient = require('socket.io-client');
var host = 'http://localhost';
var port = 8080;
describe('Chatter', function()
{
'use strict';
var chatterServer;
var chatterClient;
var server;
before(function()
{
var app = express();
server = app.listen(port);
});
beforeEach(function()
{
chatterServer = new ChatterServer(socketIO(server));
chatterClient = new ChatterClient(socketIOClient, util.format('%s:%s', host, port.toString()));
});
...
it('#must be able to receive a message', function(done)
{
chatterServer.on('chatterMessage', function(data)
{
console.log('Incoming message!');
expect(data).to.have.property('message');
expect(data.message).to.be('Hello, world!');
done();
});
console.log('Sending message!');
chatterClient.send('chatterMessage', { message: 'Hello, world!' });
console.log('Sent!');
});
});
My Client (ChatterClient) is:
(function()
{
'use strict';
function Client(socketIO, url)
{
this.socketIO = socketIO(url);
}
Client.prototype.send = function(event, data)
{
this.socketIO.emit(event, data);
};
Client.prototype.on = function(event, callback)
{
this.socketIO.on(event, callback);
};
if (module !== undefined && module.hasOwnProperty('exports')) {
module.exports = Client;
} else {
window.Chatter = {
Client: Client,
};
}
}());
The Server (ChatterServer) is:
(function()
{
'use strict';
function Server(socketIO)
{
this.socketIO = socketIO;
this.connectedUsers = {};
this.on('connection', (function(user)
{
var userID = user.client.id;
this.connectedUsers[userID] = user;
user.emit('chatterConnectionAcknowledged', { id: userID });
}).bind(this));
}
Server.prototype.on = function(event, handler)
{
this.socketIO.on(event, handler);
};
module.exports = Server;
}());
You need to change your code in two sides.
First side, you will need to listen incoming socket connections on the socketIO object. (see the emphasized code below)
//.. some code
function Server(socketIO)
{
this.socketIO = socketIO;
this.connectedUsers = {};
this.socketIO.on('connection', (function(user)
{
var userID = user.client.id;
this.connectedUsers[userID] = user;
user.emit('chatterConnectionAcknowledged', { id: userID });
}).bind(this));
}
//.. some code
Second side, when you are adding new events to listen on the server, you need to bind those events to the sockets since they are ones that are going to listen when events are emitted from the socket clients.
Server.prototype.on = function (event, handler) {
Object.keys(this.connectedUsers).map(function (key) {
this.connectedUsers[key].on(event, handler);
}.bind(this));
};
Related
I am very beginner in NodeJS, I am taking data from S71200 PLC device using nodes7 library, I want to pass data using socket io emit but I can't pass data to socket io emit below my code
app.js
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var nodes7 = require('nodes7'); // This is the package name, if the repository is cloned you may need to require 'nodeS7' with uppercase S
var conn = new nodes7;
var doneReading = false;
var doneWriting = false;
var variables = {
TEST7: 'DB1,INT2.3',
TEST1: 'DB1,X0.0.3',
TEST2: 'DB1,INT2'
};
conn.initiateConnection({port: 102, host: '127.0.0.1', rack: 0, slot: 1}, connected);
function connected(err) {
if (typeof(err) !== "undefined") {
// We have an error. Maybe the PLC is not reachable.
console.log(err);
process.exit();
}
conn.setTranslationCB(function(tag) {return variables[tag];}); // This sets the "translation" to allow us to work with object names
conn.addItems(['TEST7','TEST1']);
//conn.writeItems('TEST2', 90, valuesWritten);
setInterval(function(){
conn.readAllItems(valuesReady);
},1000)
}
function valuesReady(anythingBad, values) {
if (anythingBad) { console.log("SOMETHING WENT WRONG READING VALUES!!!!"); }
console.log(values.TEST1[0],values.TEST1[1],values.TEST1[2],values.TEST7[0],values.TEST7[1],values.TEST7[2]);
//console.log( typeof(temp));
doneReading = true;
}
function valuesWritten(anythingBad) {
if (anythingBad) { console.log("SOMETHING WENT WRONG WRITING VALUES!!!!"); }
console.log("Done writing.");
doneWriting = true;
}
io.on('connection',function(socket){
console.log('one user connected '+socket.id);
socket.emit("channelname", {
message: "Passing S71200 data"
});
socket.on('disconnect',function(){
console.log('one user disconnected '+socket.id);
});
})
http.listen(3000,function(){
console.log('server listening on port 3000');
})
I am using interval function because every second data fetch from PLC device, I got all data from values.TEST1[0],values.TEST1[1],values.TEST1[2],values.TEST7[0],values.TEST7[1],values.TEST7[2] this data passing to
io.on('connection',function(socket){
console.log('one user connected '+socket.id);
socket.emit("channelname", {
message: "Passing S71200 data"
});
socket.on('disconnect',function(){
console.log('one user disconnected '+socket.id);
});
})
Help me to solve this problem
I know this question was asked already, but the solutions provided didn't work for me.
Here is the websocket server code
const https = require('https');
const fs = require('fs');
const path = require('path');
const WebSocket = require('ws');
let connectionsList = [];
/*
*/
var server = https.createServer({
cert: fs.readFileSync(path.resolve(__dirname, '../cert/certificate.crt')),
key: fs.readFileSync(path.resolve(__dirname, '../cert/private.key'))
}, function (request, response) {
console.log((new Date()) + ' Received request for ' + request.url);
response.writeHead(404);
response.end();
});
wsServer = new WebSocket.Server({ server });
function originIsAllowed(origin) {
// put logic here to detect whether the specified origin is allowed.
return true;
}
wsServer.on('connection', function (connection) {
//save new connections here
connectionsList.push(connection);
connection.on('message', function (message) {
const data = JSON.parse(message) || null;
if (data !== null && data.type === 'push') {
connectionsList.forEach((connection, index) => {
//Must Skip First Item This One Pumps Data To The Others
if (index > 0) {
if (connection.state === 'closed') {
ConnectionsList.splice(index);
}
connection.send(JSON.stringify(data.message));
}
})
}
});
});
wsServer.on("error", function(err){
console.log(err);
});
module.exports = server;
Here is the runner or starter
// A simple pid lookup
var exec = require('child_process').execFile;
const wss = require('./ws_server/wss');
const config = require('./config');
var fun = function () {
const process = exec(`${config.EXE.PATH}/${config.EXE.NAME}`, function () {
wss.close();
fun();
});
//if process is created, then makea websocket server
if (process.pid != null && process.pid !== undefined) {
try{
wss.on('error', function(error){
console.log(error);
});
wss.listen({port: config.PORT,host: config.HOST}, function () {
console.log((new Date()) + ` Server is listening on port ${config.PORT}`);
});
}
catch(err){
}
}
}
fun();
I keep having this error below even after I have checked and can't find anything using that port. I have tried all the approached mentioned from the here
How to fix Error: listen EADDRINUSE while using nodejs?
but nothing seems to work for me, please can anyone explain to me what's really the problem here. I am using windows server to run this nodejs script. thanks
How to fix Error: listen EADDRINUSE while using nodejs?
The issue is that the close is not awaited since:
wss.close is called
fun is executed in sync and the wss.listen execute before the closing has been completed
It should be necessary to run fun in the close callback
const process = exec(`${config.EXE.PATH}/${config.EXE.NAME}`, function () {
wss.close(function(){
// now the server is closed
fun();
});
});
I found the problem. It was a guess.
I updated the Node.js version in the computer and after that Node connected to the port and to the db successfully
I'm totally new to the whole nodeJS asynchronous-y callback-y programming so I need more like a guidance to understanding what I'm even doing. With that said, I have two files main.js and server.js
My main file looks like this:
var server=require('./server.js');
server();
function WhenUserClicksButton(){
server();
}
and my server file looks like this:
var net = require('net');
function server(){
net.createServer(function (socket) {
socket.write('\x16'); //SYN character
socket.on('data', function (data) {
//handle data from client
});
}).listen(33333);
}
First call of server(); starts the TCP server. Then function WhenUserClicksButton is called when user clicks button (duhh) in a GUI. But it attempts to start the server again so I get
Error: listen EADDRINUSE :::33333
I got why this is happening but I can't think of a solution for it. What I really need is:
Start the server and listen on 33333
When nothing is happening server and client just exchanges SYN and ACK characters every few seconds (I already have this part done, I just removed it from this example for clarity because it's not really topic of this question)
When user click button change socket.write('\x16'); to socket.write('something');
Then wait for server and client to exchange data and after everything is done return results back to main.js
As I said, I'm new to this and I believe my problem lies in not understanding fully of what I'm doing. Any help and explanations are welcome!
I think you're very near where you need to be. I would do something like this:
server.js
var net = require('net');
var netServer = null;
var netSocket = null;
function sendData(data) {
if (netServer && netSocket) {
console.log('Send data: sending: ', data);
netSocket.write(data);
}
}
function startServer(){
netServer = net.createServer(function (socket) {
netSocket = socket;
socket.write('\x16'); //SYN character
socket.on('data', function (data) {
console.log('Server: data from client: ', data);
if (data.length === 1 && data[0] === 0x16) {
// log and ignore SYN chars..
console.log('SYN received from client');
} else if (newDataCallback) {
newDataCallback(data);
};
});
});
console.log('Server listening on 33333..');
netServer.listen(33333);
}
var newDataCallback = null;
function setNewDataCallback(callback) {
newDataCallback = callback;
}
module.exports = {
sendData: sendData,
startServer: startServer,
setNewDataCallback: setNewDataCallback
};
main.js
var server = require('./server');
function newDataCallback(data) {
console.log('newDataCallback: New data from server: ', data);
}
server.setNewDataCallback(newDataCallback);
server.startServer();
function wheneverUserClicksButton() {
server.sendData('something');
}
testClient.js
var clientSocket = net.createConnection(33333, "127.0.0.1");
clientSocket.on('data', (someData) => {
console.log('Data received', someData);
});
clientSocket.on('connect', () => {
console.log('Client Socket connected ');
clientSocket.write('Hello from client');
});
I have a server
var connect = require('connect');
var serveStatic = require('serve-static');
var HTMLServer = function(path){
this.path = path;
this.server = connect().use(serveStatic(this.path));
this.startServer = function(callback){
this.server = this.server.listen(8080, callback);
};
this.stopServer = function(callback){
this.server.close(callback);
}
}
And I use it as follows:
var thisServer = new HTMLServer(__dirname);
thisServer.startServer(function(){
console.log('Server running on 8080...');
setTimeout(function(){
thisServer.stopServer(function(){
console.log('Server closed');
});
}, 3000);
});
As expected, server starts and after 3000 milliseconds it stops.
But, if within these 3000 milliseconds I make a request to this server, the stopServer is called, however the server is not closed.
I'm sure this line this.server.close(callback); gets executed, but doesn't close the server as I expect.
How can I fix that?
Is a request to the server changing the server instance in a way that needs a special handling?
Later edit:
I would like to add some precision now that I left the code running. It seems the server does get closed, however not instantly, but after an amount of time that I don't understand, no longer than 5 minutes.
So the close operation seems to be delayed. Can I make it instant somehow?
While #jfriend00 was correct that node.js keeps running until all exiting sockets are finished, the process.exit solution was a bit too radical for my use case and I needed a cleaner solution to close the server gracefully.
Looking into getConnections only added more confusion since it didn't function as expected. (for example it returned 2 connections even if I didn't make any request).
I also looked into server.listening but it returned false even if the server accepted more requests. Perhaps accepts connection from a client that made requests before closing the server.
Anyway, the solution for me was to use the http-shutdown lib which essentially adds the following .shutdown method to your server object.
function addShutdown(server) {
var connections = {};
var isShuttingDown = false;
var connectionCounter = 0;
function destroy(socket, force) {
if (force || (socket._isIdle && isShuttingDown)) {
socket.destroy();
delete connections[socket._connectionId];
}
};
function onConnection(socket) {
var id = connectionCounter++;
socket._isIdle = true;
socket._connectionId = id;
connections[id] = socket;
socket.on('close', function() {
delete connections[id];
});
};
server.on('request', function(req, res) {
req.socket._isIdle = false;
res.on('finish', function() {
req.socket._isIdle = true;
destroy(req.socket);
});
});
server.on('connection', onConnection);
server.on('secureConnection', onConnection);
function shutdown(force, cb) {
isShuttingDown = true;
server.close(function(err) {
if (cb) {
process.nextTick(function() { cb(err) });
}
});
Object.keys(connections).forEach(function(key) {
destroy(connections[key], force);
});
};
server.shutdown = function(cb) {
shutdown(false, cb);
};
server.forceShutdown = function(cb) {
shutdown(true, cb);
};
return server;
};
With this function, I can update my server as follows, and now stopServer works as expected:
var HTMLServer = function(path){
this.path = path;
this.server = connect().use(serveStatic(this.path));
this.startServer = function(callback){
this.server = addShutdown(this.server.listen(8080, callback));
};
this.stopServer = function(callback){
console.log("I was called");
this.server.shutdown(callback);
}
}
I'm using sockjs with standard configuration.
var ws = sockjs.createServer();
ws.on('connection', function(conn) {
conn.on('data', function(message) {
wsParser.parse(conn, message)
});
conn.on('close', function() {
});
});
var server = http.createServer(app);
ws.installHandlers(server, {prefix:'/ws'});
server.listen(config.server.port, config.server.host);
wsParser.parse function works like this:
function(conn, message) {
(...)
switch(message.action) {
case "titleAutocomplete":
titleAutocomplete(conn, message.data);
break;
(...) // a lot more of these
}
Each method called in switch sends back a message to client.
var titleAutocomplete = function(conn, data) {
redis.hgetall("titles:"+data.query, function(err, titles){
if(err) ERR(err);
if(titles) {
var response = JSON.stringify({"action": "titleAutocomplete", "data": {"titles": titles}});
conn.write(response);
}
})
};
Now my problem is that I'd like to make tests for my code (better late than never I guess) and I have no idea how to do it. I started writing normal http tests in with mocha + supertest but I just don't know how to handle websockets.
I'd like to have only one websocket connection to reuse through all tests, I'm binding the websocket connection with user session after first message and I want to test that persistence as well.
How do I make use of ws client's onmessage event and utilize it in my tests? How the tests can tell apart received messages and know which one they are supposed to wait for?
Collegue at work asked if it really needs to be a client connection or would it be possible to just mock it up. It turned out it was the way to go. I wrote a little helper class wsMockjs
var wsParser = require("../wsParser.js");
exports.createConnectionMock = function(id) {
return {
id: id,
cb: null,
write: function(message) {
this.cb(message);
},
send: function(action, data, cb) {
this.cb = cb;
var obj = {
action: action,
data: data
}
var message = JSON.stringify(obj);
wsParser.parse(this, message);
},
sendRaw: function(message, cb) {
this.cb = cb;
wsParser.parse(this, message);
}
}
}
Now in my mocha test I just do
var wsMock = require("./wsMock.js");
ws = wsMock.createConnectionMock("12345-67890-abcde-fghi-jklmn-opqrs-tuvwxyz");
(...)
describe('Websocket server', function () {
it('should set sessionId variable after handshake', function (done) {
ws.send('handshake', {token: data.token}, function (res) {
var msg = JSON.parse(res);
msg.action.should.equal('handshake');
msg.data.should.be.empty;
ws.should.have.property('sessionId');
ws.should.not.have.property('session');
done();
})
})
it('should not return error when making request after handshake', function (done) {
ws.send('titleAutocomplete', {query: "ter"}, function (res) {
var msg = JSON.parse(res);
msg.action.should.equal('titleAutocomplete');
msg.data.should.be.an.Object;
ws.should.have.property('session');
done();
})
})
})
It works like a charm and persist connection state and variables between requests.