Good day, everyone.
I wanted to know if this was possible in Node.js. Suppose I have two different node applications, and they are both going to compete to be a server on a certain port. Whichever reaches the port first becomes the server, and the other one automatically becomes a client of that server.
Here is a simple diagram of what I am trying to explain:
So in this example, process 1 becomes the server because it reached the port first. Process 2 automatically becomes Process 1's client. However, I also want the functionality that if anything happens to Process 1 and the connection fails, then Process 2 becomes the new server on that same port.
Here is a diagram on what I mean:
Here is the code I have so far:
var net = require('net');
var TIMEOUT_TIME = 3000; // in milliseconds
var PORT_NUMBER = 1337;
// Attempt to create server.
var application1 = net.createServer(function (socket) {
socket.write('Hello Server 1\r\n');
socket.end("hello");
console.log("Someone connected to Server 1. \n");
socket.pipe(socket);
});
application1.listen(PORT_NUMBER, function(){
console.log("\nServer 1 is now the official server. \n");
setTimeout(function() {
application1.close();
console.log("Server 1 has been closed.");
}, TIMEOUT_TIME);
})
.on('error', function(err) {
console.log('there was an error.');
if(portIsUsed(err)) {
console.log("Port Was In Use! (This is Server 1 trying to connect.)");
net.connect(PORT_NUMBER, function() {
console.log("Server 1 connected to port.\n");
});
}
})
.on('end', function() {
console.log("server 1 disconnected from port");
});
// Attempt to create server.
var application2 = net.createServer(function (socket) {
socket.write('Hello Server 2\r\n');
socket.end("hello");
console.log("someone went into application2.");
socket.pipe(socket);
});
application2.listen(PORT_NUMBER, function() {
console.log("\nServer 2 is now the official server. \n");
})
.on('error', function(err) {
if(portIsUsed(err)) {
console.log("Port Was In Use! (This is Server 2 trying to connect.)\n");
net.connect(PORT_NUMBER, function() {
console.log("Server 2 connected to port.\n");
})
.on("end", function() {
// code here for when the connection ends?
});
}
})
.on('end', function() {
console.log("server 2 disconnected from port");
});
function portIsUsed(err) {
return err.code === "EADDRINUSE";
}
Application2 successfully detects the EADDRINUSE error (because Application1 connects first, so the port is used), but how do I make become the new server in the case that Application1 drops? I tried using .on("end", ...) inside the .on("error",...) in application2, but its not working.
Is this even possible in Node.js, due to its single-threaded nature? In the Node API I saw that cluster was used to work on more than 1 thread if I had a multicore machine. Should I use that instead?
I'm new to Node.js, so any help pointing me the right way would be appreciated.
Thank you,
I saw You're trying to write same app twice to be able handle request and balance between them.
So thats solution:
write normal single-threaded app and then run it using pm2 (https://github.com/Unitech/pm2):
install
npm install pm2 -g
run
pm2 start app.js -i max
use keymetrics (https://keymetrics.io/):
pm2 interact public_key secret_key
doing app that trying to dominate on port is not good approach, even when You're switching roles from server to client.
it's better to know that one app runs on one port and another on second port and it's normal.
Related
I'm using socket.io-client to create a socket connection to my locally-running server. See my code below:
// Working example of connecting to a local server that is not SSL protected
var io = require('socket.io-client')
var socket = io.connect('http://localhost:3000', {reconnect: true});
socket.on('connect', function(){ console.log("inside 'connect'") } );
socket.on('connection', function(){ console.log("inside 'connection'") } );
socket.on('event', function(data){ console.log("inside 'event'") } );
socket.on('disconnect', function(){ console.log("inside 'disconnect'") } );
var payload = {email: 'fake#gmail.com', password: 'tester'};
var tokens = {browserId: 'b965e554-b4d2-5d53-fd69-b2ca5483537a'};
socket.emit("publish", {logic:"user", method:"signIn"}, payload, tokens, function(err, creds) {
console.log("inside the socket client emit callback. err: " + err);
console.log("creds: " + creds);
});
Now for my problem. As I stated in the comment at the top of that code, I can connect to my local nodejs server and get the response I expect when I turn off SSL encryption on my server. As soon as I turn SSL on, I stop getting any response at all from the code above. I don't see any message in my server logs or from the command line, where I'm running the code above with node.
My goal is to be able to run the code above, with SSL turned on in my server, and get the same response that I get when SSL is turned off. I've tried a bunch of variations on the code I included above, such as:
connecting to "https://localhost:3000"
connecting to "//localhost:3000"
connecting to "https://localhost:3443" (this is the port I have to connect to when I have the nodejs server running with SSL)
changing {reconnect:true} to {reconnect:true,secure:true}
I'm truly stumped, and I've been doing a bunch of research on the web and on my node server. It's my company's code and I didn't originally implement the SSL components, so I've spent a few hours looking at our code and trying to understand how adding SSL changes everything. I'm also a student and have about 2 years of experience behind me, so I'm good but I'm no expert. Have I said anything above that indicates if my task is impossible to achieve, or if maybe I have just overlooked something? Any leads on things to check out would be appreciated :)
I'm trying to make a front-end for my privately hosted Counter-Strike Global Offensive servers, on the front-end when I hit run server, everything works great and the server starts up and logs to console. But how can I view information like the server IP address, players in the server, and other things?
This is what I have so far for running the server:
router.post('/create', function(req, res) {
console.log(req.body);
var child = spawn('/home/steam/steamcmd/csgo/srcds_run -game csgo -console +game_type 0 +game_mode 0 +host_workshop_collection 249376192 -tickrate 128 +maxplayers 20')
child.stderr.on('data', function(err) {
console.log(err);
});
child.stdin.on('data', function(chunk) {
console.log(chunk);
});
child.stdout.on('data', function(chunk) {
});
});
Like for e.g. if I used a paid server host I would have a control panel where I could see server IP, restart / stop view players in game and other things. I hope this was clear enough and sorry if it was poorly written. I'm not sure how else to word this.
Does the server accept input once it has started? If so you can write to it using
child.stdin.write('my command\n');
Otherwise you're going to have to query it using something like gamedig
Why I can't close the server by requesting localhost:13777/close in browser (it continues to accept new requests), but it will gracefully close on timeout 15000? Node version is 0.10.18. I fell into this problem, trying to use code example from docs on exceptions handling by domains (it was giving me 'Not running' error every time I secondly tried to request error page) and finally came to this code.
var server
server = require("http").createServer(function(req,res){
if(req.url == "/close")
{
console.log("Closing server (no timeout)")
setTimeout(function(){
console.log("I'm the timeout")
}, 5000);
server.close(function(){
console.log("Server closed (no timeout)")
})
res.end('closed');
}
else
{
res.end('ok');
}
});
server.listen(13777,function(){console.log("Server listening")});
setTimeout(function(){
console.log("Closing server (timeout 15000)")
server.close(function(){console.log("Server closed (timeout 15000)")})
}, 15000);
The server is still waiting on requests from the client. The client is utilizing HTTP keep-alive.
I think you will find that while the existing client can make new requests (as the connection is already established), other clients won't be able to.
Nodejs doesn't implement a complex service layer on top of http.Server. By calling server.close() you are instructing the server to no longer accept any "new" connections. When a HTTP Connection:keep-alive is issued the server will keep the socket open until the client terminates or the timeout is reached. Additional clients will not be able to issue requests
The timeout can be changed using server.setTimeout() https://nodejs.org/api/http.html#http_server_settimeout_msecs_callback
Remember if a client has created a connection before the close event that connection can continually be used.
It seems that a lot of people do not like this current functionality but this issue has been open for quite a while:
https://github.com/nodejs/node/issues/2642
As the other answers point out, connections may persist indefinitely and the call to server.close() will not truly terminate the server if any such connections exist.
We can write a simple wrapper function which attaches a destroy method to a given server that terminates all connections, and closes the server (thereby ensuring that the server ends nearly immediately!)
Given code like this:
let server = http.createServer((req, res) => {
// ...
});
later(() => server.close()); // Fails to reliably close the server!
We can define destroyableServer and use the following:
let destroyableServer = server => {
// Track all connections so that we can end them if we want to destroy `server`
let sockets = new Set();
server.on('connection', socket => {
sockets.add(socket);
socket.once('close', () => sockets.delete(socket)); // Stop tracking closed sockets
});
server.destroy = () => {
for (let socket of sockets) socket.destroy();
sockets.clear();
return new Promise((rsv, rjc) => server.close(err => err ? rjc(err) : rsv()));
};
return server;
};
let server = destroyableServer(http.createServer((req, res) => {
// ...
}));
later(() => server.destroy()); // Reliably closes the server almost immediately!
Note the overhead of entering every unique socket object into a Set
Is it possible for a server to connect to another using Socket.IO and be treated like a client?
And have it join rooms, recieve io.sockets.in('lobby').emit(). And more?
The first server is also listening for connections/messages as well.
Hey Brad, here's my full .js app below for reference:
var io = require("socket.io").listen(8099);
io.set('log level', 1);
io.sockets.on("connection", function (socket) {
console.log('A Client has Connected to this Server');
//Let Everyone Know I just Joined
socket.broadcast.to('lobby').emit("message",'UC,' + socket.id); // Send to everyone in Room but NOT me
socket.on("message", function (data) {
//Missing code
socket2.send('message,' + data); //Forward Message to Second Server
});
socket.on("disconnect", function (data) {
//Send Notification to Second Server
//Need to figure out later
//Send Notification to Everyone
socket.broadcast.emit("message",'UD,' + socket.id ); //Send to Everyone but NOT me
//Remove user from Session ID
arSessionIDs.removeByValue(socket.id);
//Send Notification to Console
console.log("disconnecting " + arRoster[socket.id][1]);
});
});
var io_client = require( 'socket.io-client' );
var socket2 = io_client.connect('http://192.168.0.104:8090');
socket2.on('connect', function () {
socket2.emit('C3434M,Test');
});
Yes, absolutely. Just use the Socket.IO client in your server application directly.
https://github.com/LearnBoost/socket.io-client
You can install it with npm install socket.io-client. Then to use:
var socket = io.connect('http://example.com');
socket.on('connect', function () {
// socket connected
socket.emit('server custom event', { my: 'data' });
});
I realize this is an old post, but I was working on something similar and decided to come back and contribute something as it got me thinking.
Here's a basic Client -> Server 1 -> Server 2 setup
Server #1
// Server 1
var io = require("socket.io").listen(8099); // This is the Server for SERVER 1
var other_server = require("socket.io-client")('http://example.com:8100'); // This is a client connecting to the SERVER 2
other_server.on("connect",function(){
other_server.on('message',function(data){
// We received a message from Server 2
// We are going to forward/broadcast that message to the "Lobby" room
io.to('lobby').emit('message',data);
});
});
io.sockets.on("connection",function(socket){
// Display a connected message
console.log("User-Client Connected!");
// Lets force this connection into the lobby room.
socket.join('lobby');
// Some roster/user management logic to track them
// This would be upto you to add :)
// When we receive a message...
socket.on("message",function(data){
// We need to just forward this message to our other guy
// We are literally just forwarding the whole data packet
other_server.emit("message",data);
});
socket.on("disconnect",function(data){
// We need to notify Server 2 that the client has disconnected
other_server.emit("message","UD,"+socket.id);
// Other logic you may or may not want
// Your other disconnect code here
});
});
And here's Server #2
// Server 2
var io = require("socket.io").listen(8100);
io.sockets.on("connection",function(socket){
// Display a connected message
console.log("Server-Client Connected!");
// When we receive a message...
socket.on("message",function(data){
// We got a message. I don't know, what we should do with this
});
});
This is our Client, who sends the original message.
// Client
var socket = io('http://localhost');
socket.on('connect', function(){
socket.emit("message","This is my message");
socket.on('message',function(data){
console.log("We got a message: ",data);
});
});
I am making this post a Community Wiki so that someone can improve this if they feel like it.
The code has not been tested, use at your own risk.
I had the same problem, but instead to use socket.io-client I decided to use a more simple approach (at least for me) using redis pub/sub, the result is pretty simple.
You can take a look at my solution here: https://github.com/alissonperez/scalable-socket-io-server
With this solution you can have how much process/servers you want (using auto-scaling solution), you just use redis as a way to forward your messages between your servers.
Currently I have the following code, working with Node.js, socket.io and Redis:
var io = require('socket.io'), redis = require("redis"), client = redis.createClient();
io.sockets.on('connection', function (socket) {
var socket_id = socket.id;
socket.on('chat', function(data) {
client.set('user:' + socket_id, data['colour']);
// The user left the page. Remove them from Redis.
socket.on('disconnect', function () {
client.del('user:' + socket_id);
client.quit();
});
});
});
This works fine for normal socket connections and disconnections, but there seems to be a problem if Node goes down, or if I just restart Node as part of normal development.
The key is never deleted from Redis. So the number of entries stored in the Redis database gradually grows and grows. I'm also not sure whether the Redis client exists cleanly.
How can I clean up Redis entries and quit the Redis client when Node exits, as well as when the socket disconnects?
You could handle this when node exits, but e.g. in case the power goes down, there's no way you can clean it up at shutdown time. I'd wipe old stuff from the DB at startup time instead.
I've run in to this problem too. My solution was to just use a specific Redis database on the server for Socket.io data. A simple FLUSHDB to that database on start up will clean out stuck keys.
var socketIoDatabase = 4;
var client = redis.createClient();
client.select(socketIoDatabase);
client.flushdb();
Of course if you have multiple node processes using this same database clearing it will cause problems. In this case you can do it during a maintenance window or something while all node processes are terminated.
check this out:
http://nodejs.org/docs/v0.4.12/api/process.html#event_uncaughtException_
var io = require('socket.io'), redis = require("redis"), client = redis.createClient();
io.sockets.on('connection', function (socket) {
var socket_id = socket.id;
socket.on('chat', function(data) {
client.set('user:' + socket_id, data['colour']);
// The user left the page. Remove them from Redis.
socket.on('disconnect', function () {
client.del('user:' + socket_id);
client.quit();
});
});
});
// this will be activated on any error without try catch :)
process.on('uncaughtException', function (err) {
console.log('Caught exception: ' + err);
});