-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #11 from 13x54n/auth
Multi Node from different connection enabled
- Loading branch information
Showing
2 changed files
with
119 additions
and
105 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,95 +1,110 @@ | ||
/** | ||
* @author: Lexy <not.so.lexy@gmail.com, 13x54n> | ||
*/ | ||
const cluster = require("cluster"); | ||
const net = require("net"); // tcp | ||
const numCPUs = require("os").cpus().length; | ||
|
||
const port = 7070; | ||
const host = "127.0.0.1"; | ||
|
||
/** | ||
* @dev Cluster Module(Improved Performance): Refer to https://nodejs.org/api/cluster.html#cluster | ||
*/ | ||
if (cluster.isMaster) { | ||
// Fork workers | ||
for (let i = 0; i < numCPUs; i++) { | ||
cluster.fork(); | ||
} | ||
// Code for TCP server | ||
const server = net.createServer(); | ||
|
||
cluster.on("exit", (worker, code, signal) => { | ||
console.log(`Worker ${worker.process.pid} died`); | ||
// Fork a new worker in case of worker death | ||
cluster.fork(); | ||
}); | ||
} else { | ||
// Code for TCP server inside each worker | ||
const server = net.createServer(); | ||
/** | ||
* Determining the ideal maximum number of connections (server.maxConnections) for a TCP server | ||
* depends on various factors including hardware resources, expected workload, and the nature of | ||
* the application. | ||
* Here are some considerations to help determine an appropriate server.maxConnections value: | ||
* | ||
* server.maxConnections=numCPUs×connections per core=4×50=200 | ||
*/ | ||
server.maxConnections = 100; | ||
|
||
server.listen(port, host, () => { | ||
// @dev need to benchmark whats the ideal connections | ||
server.maxConnections = 100; | ||
|
||
// Start listening on defined port and host | ||
server.listen(port, host, () => { | ||
console.log(`Worker ${process.pid}: TCP Server is running on port ${port}.`); | ||
}); | ||
|
||
let sockets = []; | ||
|
||
// Event handler for new connections | ||
server.on("connection", function (sock) { | ||
if (sockets.length >= server.maxConnections) { | ||
// Handle exceeding max connections -> reject new connections | ||
console.log( | ||
`Worker ${process.pid}: TCP Server is running on port ${port}.` | ||
`Worker ${process.pid}: Max connections reached. Rejecting new connection from ${sock.remoteAddress}` | ||
); | ||
}); | ||
sock.destroy(); | ||
return; | ||
} | ||
|
||
// Log new connection | ||
console.log( | ||
`Worker ${process.pid}: CONNECTED: ${sock.remoteAddress}:${sock.remotePort}` | ||
); | ||
|
||
// Add the socket to the sockets array | ||
sockets.push(sock); | ||
|
||
let sockets = []; | ||
// Event handler for incoming data | ||
sock.on("data", function (data) { | ||
try { | ||
// Parse incoming data assuming it's JSON | ||
const bufferData = Buffer.from(data); | ||
const bufferString = bufferData.toString("utf8"); | ||
const jsonData = JSON.parse(bufferString); | ||
|
||
server.on("connection", function (sock) { | ||
if (sockets.length >= server.maxConnections) { | ||
/** | ||
* Handle exceeding max connections -> reject new connections, but better algorithm can be implemented | ||
*/ | ||
console.log( | ||
`Worker ${process.pid}: Max connections reached. Rejecting new connection from ${sock.remoteAddress}` | ||
`Worker ${process.pid}: Received data from ${sock.remoteAddress}:${sock.remotePort}:`, | ||
jsonData | ||
); | ||
|
||
// Handle different message types | ||
if (jsonData.method_name === "broadcast") { | ||
// Broadcast message to all connected clients | ||
broadcastToAll("Some Calculated Output!"); | ||
} else if (jsonData.method_name === "private_message") { | ||
// Example of sending a private message to a specific client | ||
sendPrivateMessage(sock, "This is a private message!"); | ||
} | ||
} catch (error) { | ||
console.error( | ||
`Worker ${process.pid}: Error parsing JSON or handling message: ${error.message}` | ||
); | ||
sock.destroy(); | ||
return; | ||
} | ||
}); | ||
|
||
// Event handler for connection close | ||
sock.on("close", function () { | ||
console.log( | ||
`Worker ${process.pid}: CONNECTED: ${sock.remoteAddress}:${sock.remotePort}`// this should be user public address | ||
`Worker ${process.pid}: CLOSED: ${sock.remoteAddress} ${sock.remotePort}` | ||
); | ||
|
||
sock.on("data", function (data) { | ||
// @dev socket connection must only be listed if node is authorized | ||
sockets.push(sock); | ||
// console.log(`Worker ${process.pid}: DATA ${sock.remoteAddress}: ${data}`); | ||
|
||
sock.write(`${sock.remoteAddress}:${sock.remotePort} said ${data}, and Helllo, from Lexy!\n`); | ||
// Write the data back to all the connected clients | ||
// sockets.forEach(function (sock) { | ||
// }); | ||
|
||
// @dev receive socket information | ||
// switch(data){ | ||
// case '': | ||
// return; | ||
// default: | ||
// return; | ||
// } | ||
}); | ||
|
||
// Add a 'close' event handler to this instance of socket | ||
sock.on("close", function (data) { | ||
console.log( | ||
`Worker ${process.pid}: CLOSED: ${sock.remoteAddress} ${sock.remotePort}` | ||
); | ||
let index = sockets.findIndex(function (o) { | ||
return ( | ||
o.remoteAddress === sock.remoteAddress && | ||
o.remotePort === sock.remotePort | ||
); | ||
}); | ||
if (index !== -1) sockets.splice(index, 1); | ||
}); | ||
// Remove the closed socket from the sockets array | ||
sockets = sockets.filter((socket) => socket !== sock); | ||
}); | ||
}); | ||
|
||
// Function to broadcast message to all connected sockets | ||
function broadcastToAll(message) { | ||
sockets.forEach(function (socket) { | ||
if (!socket.destroyed) { | ||
socket.write(JSON.stringify(message)); | ||
} | ||
}); | ||
} | ||
|
||
// Function to get all connected peers | ||
function getAllPeers() { | ||
const peers = sockets.map((socket) => ({ | ||
remoteAddress: socket.remoteAddress, | ||
remotePort: socket.remotePort, | ||
// connectedAt: socket.connectTime, // Assuming connectTime is a custom property or you have a way to track connection time | ||
})); | ||
|
||
return peers; | ||
} | ||
|
||
// Function to send a private message to a specific client | ||
function sendPrivateMessage(target, message) { | ||
if (!target.destroyed) { | ||
const peers = getAllPeers(); | ||
target.write(`${JSON.stringify(peers)}\n`); | ||
} else { | ||
console.log( | ||
`Worker ${process.pid}: Unable to send private message: Client ${target.remoteAddress}:${target.remotePort} is disconnected.` | ||
); | ||
} | ||
} |