I have a simple TCP client. It makes a connection to a local data processing server. Sometimes the server will not be running, and that is ok. If it cannot connect, I would like it to fail gracefully with perhaps some console output, but not crash my process.
here is the code:
class EngineSwitchboard {
static routePacket(packet) {
if (gpsPackets.includes(packet.packetId)){
const gpsClient = new net.Socket();
gpsClient.connect(9998, 'localhost', function() {
gpsClient.write(JSON.stringify(packet));
})
}
}
}
I have tried to wrap it in a try-catch but it still crashes out the calling process with Error: connect ECONNREFUSED 127.0.0.1:9998
thanks for any help
You can listen to the socket's on error event handler.
gpsClient.on('error', function(){})
The above one is a much generic event handler. If you want to handle specific errors. You can go through the list available and use the appropriate one
https://socket.io/docs/client-api/#Event-%E2%80%98connect%E2%80%99
Related
I am working on a social app and build chat module using socket.io. App is very complicated now and very difficult to debug. sometimes event not received. How I can figure out issue is frontend side or backend side without acknowledgement method because it tells the emitter socket event is listen by client or server. it not handle the case when client can't listen it as user is connected with socket. I confirmed this using server logs.
client.on('webrtc_offer', function (event) {
console.log("========= webrtc_offer : ", event)
event.uid = event.endUserId
event.endUserId = client.uuid
if (event.uid in users) {
console.log(`emiting webrtc_offer event to peer ${event.uid}`)
client.broadcast.to(users[event.uid]).emit('webrtc_offer_send', event);
client.emit('ringing', { callee: event.uid, caller_id: event.endUserId })
}
})
How i can confirmed that "webrtc_offer_send" event is emitted from server or not.? I am not interested it is listen in frontend or not it not my concern.
I have node.js service and angular client using socket.io to transport some message during long time http request.
Service:
export const socketArray: SocketIO.Socket[] = [];
export let socketMapping: {[socketId: string]: number} = {};
const socketRegister: hapi.Plugin<any> = {
register: (server) => {
const io: SocketIO.Server = socket(server.listener);
// Whenever a session connected to socket, create a socket object and add it to socket array
io.on("connection", (socket) => {
console.log(`socket ${socket.id} connected`);
logger.info(`socket ${socket.id} connected`);
// Only put socket object into array if init message received
socket.on("init", msg => {
logger.info(`socket ${socket.id} initialized`);
socketArray.push(socket);
socketMapping[socket.id] = msg;
});
// Remove socket object from socket array when disconnected
socket.on("disconnect", (reason) => {
console.log(`socket ${socket.id} disconnected because: ${reason}`)
logger.info(`socket ${socket.id} disconnected because: ${reason}`);
for(let i = 0; i < socketArray.length; i ++) {
if(socketArray[i] === socket) {
socketArray.splice(i, 1);
return;
}
}
});
});
},
name: "socketRegister",
version: "1.0"
}
export const socketSender = async (socketId: string, channel: string, content: SocketMessage) => {
try {
// Add message to db here
// await storeMessage(socketMapping[socketId], content);
// Find corresponding socket and send message
logger.info(`trying sending message to ${socketId}`);
for (let i = 0; i < socketArray.length; i ++) {
if (socketArray[i].id === socketId) {
socketArray[i].emit(channel, JSON.stringify(content));
logger.info(`socket ${socketId} send message to ${channel}`);
if (content.isFinal == true) {
// TODO: delete all messages of the process if isFinal is true
await deleteProcess(content.processId);
}
return;
}
}
} catch (err) {
logger.error("Socket sender error: ", err.message);
}
};
Client:
connectSocket() {
if (!this.socket) {
try {
this.socket = io(socketUrl);
this.socket.emit('init', 'some-data');
} catch (err) {
console.log(err);
}
} else if (this.socket.disconnected) {
this.socket.connect();
this.socket.emit('init', 'some-data');
}
this.socket.on('some-channel', (data) => {
// Do something
});
this.socket.on('disconnect', (data) => {
console.log(data);
});
}
They usually work fine but produce disconnection error randomly. From my log file, we can see this:
2018-07-21T00:20:28.209Z[x]INFO: socket 8jBh7YC4A1btDTo_AAAN connected
2018-07-21T00:20:28.324Z[x]INFO: socket 8jBh7YC4A1btDTo_AAAN initialized
2018-07-21T00:21:48.314Z[x]INFO: socket 8jBh7YC4A1btDTo_AAAN disconnected because: ping timeout
2018-07-21T00:21:50.849Z[x]INFO: socket C6O7Vq38ygNiwGHcAAAO connected
2018-07-21T00:23:09.345Z[x]INFO: trying sending message to C6O7Vq38ygNiwGHcAAAO
And at the same time of disconnect message, front-end also noticed a disconnect event which saying transport close.
From the log, we can get the work flow is this:
Front-end started a socket connection and sent an init message to back-end. It also save the socket.
Back-end detected the connection and received init message
Back-end put the socket to the array so that it can be used anytime anywhere
The first socket was disconnected unexpectedly and another connection is published without front-end's awareness so front-end never send a message to initialize it.
Since front-end's saved socket is not changed, it used the old socket id when made http request. As a result, back-end sent message with the old socket which was already removed from socket array.
The situation doesn't happen frequently. Does anyone know what could cause the disconnect and unknown connect issue?
It really depends what "long time http request" is doing. node.js runs your Javascript as a single thread. That means it can literally only do one thing at a time. But, since many things that servers do are I/O related (read from a database, get data from a file, get data from another server, etc...) and node.js uses event-driven asynchronous I/O, it can often have many balls in the air at the same time so it appears to be working on lots of requests at once.
But, if your complex http request is CPU-intensive, using lots of CPU, then it's hogging the single Javascript thread and nothing else can get done while it is hogging the CPU. That means that all incoming HTTP or socket.io requests have to wait in a queue until the one node.js Javascript thread is free so it can grab the next event from the event queue and start to process that incoming request.
We could only really help you more specifically if we could see the code for this "very complex http request".
The usual way around CPU-hogging things in node.js is to offload CPU-intensive stuff to other processes. If it's mostly just this one piece of code that causes the problem, you can spin up several child processes (perhaps as many as the number of CPUs you have in your server) and then feed them the CPU-intensive work and leave your main node.js process free to handle incoming (non-CPU-intensive) requests with very low latency.
If you have multiple operations that might hog the CPU, then you either have to farm them all out to child processes (probably via some sort of work queue) or you can deploy clustering. The challenge with clustering is that a given socket.io connection will be to one particular server in your cluster and if it's that process that just happens to be executing a CPU-hogging operation, then all the socket.io connections assigned to that server would have bad latency. So, regular clustering is probably not so good for this type of issue. The work-queue and multiple specialized child processes to handle CPU-intensive work are probably better because those processes won't have any outside socket.io connections that they are responsible for.
Also, you should know that if you're using synchronous file I/O, that blocks the entire node.js Javascript thread. node.js can not run any other Javascript during a synchronous file I/O operation. node.js gets its scalability and its ability to have many operations in flight at the same from its asynchronous I/O model. If you use synchronous I/O, you completely break that and ruin scalability and responsiveness.
Synchronous file I/O belongs only in server startup code or in a single purpose script (not a server). It should never be used while processing a request in a server.
Two ways to make asynchronous file I/O a little more tolerable are by using streams or by using async/await with promisified fs methods.
I have a project where I have a iOS Objective-C app trying to talk to a Node.js server. I'm using socket.io (iOS) and socket.io on node.js.
The problem I am trying to solve is to get a message from the device to the server, and have the server return a response. To this end I'm attempting to do it via sending a message and expecting an acknowledgement containing the data the device is after.
The device code looks like this:
void (^serverAck)(uint64_t, void (^)(NSArray *)) = [_socket emitWithAck:#"ListProjects" withItems:#[]];
serverAck(0, ^(NSArray* data) {
if ([data count] == 0) {
NSError *error = [NSError errorWithDomain:#"CRXServer" code:1 userInfo:nil];
failureBlock(error);
} else {
successBlock(data);
}
});
And the node.js code looks like this:
var SocketIO = require('socket.io');
var io = SocketIO(8099);
io.on('connection', function(socket) {
socket.on('ListProjects', function(data, getProjectsCallback) {
database.allProjects(function getAllProjectsCallback(err, rows) {
getProjectsCallback(rows);
});
});
});
When I attempt to run this, getProjectsCallback crashes the server because it is not a function. From comments made on another thread, I understand that this will be a function if the call to the server is correct and expecting an ack.
Anyone know what I've done wrong?
P.S. Her's a dump from socket.o's log showing the request coming in:
engine:socket packet +0ms
socket.io-parser decoded 20["getProjects"] as {"type":2,"nsp":"/","id":0,"data":["getProjects"]} +14ms
socket.io:socket got packet {"type":2,"nsp":"/","id":0,"data":["getProjects"]} +15ms
socket.io:socket emitting event ["getProjects"] +0ms
socket.io:socket attaching ack callback to event +0ms
Getting all projects ...
Releasing connection
Got the project list
/Users/derekclarkson/projects/crux-Server/node_modules/mysql/lib/protocol/Parser.js:82
throw err;
^
TypeError: getProjectsCallback is not a function
at getAllProjectsCallback (/Users/derekclarkson/projects/crux-Server/Server.js:20:13)
at Query.executeCodeblockCallback [as _callback] (/Users/derekclarkson/projects/crux-Server/Database.js:321:17)
So it looks like socket.io is attaching an ack, but somehow it's not being passed to the callback.
Not sure if it's a bug or a protocol limitation, but it doesn't work when you pass an empty array to emitWithAck:withItems:. You'll see that server-side, data contains your callback function, rather than getProjectsCallback as you expect.
So, two options:
in that situation, recognise that the first argument to your listener handler will be the callback, rather than the second
or add any random data to the items array (e.g. #[#"x"])
I think I would go for the second option in case someone fixes this issue in the future.
In 0.9.16, I use socket.emit with callback so that the chat server return some data and I can handle the result as per the acknowledgement. But after the upgrade to 1.3.5 I've found a error in console like this
Uncaught TypeError: Cannot read property 'apply' of undefined.
I've done something like this,
From web
socket.emit('userToUser', { 'usename': 'John',
'message': 'hi'
}, function(callback){
//callback handled
});
Chat Server
socket.on('userToUser', function(content, callback){
//do something
if(callback) return callback({'result':'success', 'messageid':content.messageid, 'chatid':content.chatid});
});
When I removed the callback from client side, there is no error.
So I believe there will be some changes to be done in the callback.
I'm getting the acknowledgement and the chat is working properly, but my concern is about the console error which leads to socketio.js
Socket.prototype.onack = function(packet){
debug('calling ack %s with %j', packet.id, packet.data);
var fn = this.acks[packet.id];
fn.apply(this, packet.data);
delete this.acks[packet.id];
};
Guys, please help
Finally I've fixed the issue. It was a mistake in my code , I've done multiple callbacks in the chat server.
like this:
socket.on('userToUser', function(content, callback){
mysql.insertChat(content, function(err, data){
return callback({'result':'1'}) //first callback
})
sendToUser(content, function(errm successData){
return callback({'result':'success','chatid':content.chatid});
//second callback ->wrong
})
});
In the previous versions it was a warning, now its an error !! That's it. So please avoid multiple callbacks
Please have a look at this and might be useful for every nodejs developer:
http://www.toptal.com/nodejs/top-10-common-nodejs-developer-mistakes/#remote-developer-job
Thanks guys for upvoting !
EDIT: This is not how callbacks work with socket.io v1.3.5. In fact, the emit function does not accept any callbacks at all.
I'm guessing you want to send an acknowledgement to the sending node that its message has been received. To accomplish that, you need to make another socket.emit call from the server (on the server, socket variable represents the connection to a specific node in the network whereas on the client, the socket variable represents the connection to the server). Consequently, on the client, you need to handle the acknowledgement with a socket.on which is where you'll put your callback.
To broadcast the acknowledgement to ALL nodes in the network, you should use io.emit.
Code sample for a chat application using socket.io is available here.
I'm running socket.io on node.js and the socket.io client on an Apache website. If I don't start the node.js server and load the client page, the error event is triggered with an empty error message, which results in the following console output:
GET http://example.com:1337/socket.io/1/?t=1359731838906 socket.io.js:1659
Socket.handshake socket.io.js:1659
Socket.connect socket.io.js:1699
Socket socket.io.js:1551
io.connect socket.io.js:94
(anonymous function) general.js:7
(anonymous function)
What can I do to stop this error being written to the console?
You can not "catch" these errors in the sense that they are not appearing in the console but you can still act upon them by listening for 'connect_error', 'connect_failed' and 'disconnect' of the socket and then either handling them directly or redirecting them to a custom function like handleErrors():
const socket = io.connect(url, { reconnection: false })
socket.on('update', data => console.log(data))
socket.on('connect_error', err => handleErrors(err))
socket.on('connect_failed', err => handleErrors(err))
socket.on('disconnect', err => handleErrors(err))
The only way to hide that error is by never calling io.connect in the first place – which of course would mean your app's socket functions wouldn't work even if the server is up.
It's important to understand that the error message you're seeing is neither something placed there by socket.io itself (via console.error()) nor is it an uncaught JS Exception.
The error message is placed in your console by the browser's XHR object itself. It's telling you that a XHR request has failed (since your server isn't running). The stack trace is telling you what code initiated the XHR request; it isn't a trace of an actual Exception.
Since socket.io must make a request to the server, there's no way it (or you) could prevent that error message from appearing in the console if the server isn't responding.
try
socket.on('connect_failed', function(){
console.log('Connection Failed');
});
Not tested this. Found it here Node.js socket.io-client connect_failed / connect_error event
If you're using Socket.io v4 and need to capture middleware errors, try
// client-side
socket.on("connect_error", (err) => {
console.log(err.message); // prints the message associated with the error
});
source https://socket.io/docs/v4/middlewares/#handling-middleware-error