Created
January 14, 2014 03:02
-
-
Save dawsontoth/8412318 to your computer and use it in GitHub Desktop.
NodeJS server side simple JSON based socket communications.
The Titanium client side counterpart to this is here: https://gist.github.com/dawsontoth/8412167
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
// We'll use cluster to split work across all of our CPUs. | |
var cluster = require('cluster'); | |
// Are we the master process ... | |
if (cluster.isMaster) { | |
// Monitor the workers for failures. | |
cluster.on('exit', function(worker) { | |
console.log('Worker ' + worker.process.pid + ' died. Forking another...'); | |
cluster.fork(); | |
}); | |
// Fork one worker for each CPU. | |
var numCPUs = require('os').cpus().length; | |
for (var i = 0; i < numCPUs; i++) { | |
cluster.fork(); | |
} | |
console.log('Forked ' + numCPUs + ' workers.'); | |
} | |
// ... or are we the child process? | |
else { | |
require('./server').boot([ 3001 ]); | |
} |
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
/* | |
Public API. | |
*/ | |
exports.foo = foo; | |
exports.bar = bar; | |
exports.server; | |
/* | |
Implementation | |
*/ | |
function foo(socket, data) { | |
console.log('foo: Got ' + data + ' from the client.'); | |
} | |
function bar(socket, data) { | |
console.log('bar: Got ' + data + ' from the client.'); | |
exports.server.respond(socket, 'bar', { | |
success: true, | |
description: 'The server received ' + data + ' from the client!' | |
}); | |
} |
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 is the protocol that Lox uses to communicate. | |
* | |
* Payloads follow the following format: | |
* command{new line}payloadLength{new line}JSON payload | |
* For example: | |
* echo\n30\n{ "message": "Hello, world!" } | |
* | |
* Version: 1.0.0 | |
*/ | |
var socketData = {}; | |
/* | |
Public API. | |
*/ | |
exports.toPacket = toPacket; | |
exports.fromPacket = fromPacket; | |
exports.safeParse = safeParse; | |
/* | |
Implementation. | |
*/ | |
function safeParse(txt) { | |
try { | |
return JSON.parse(txt); | |
} | |
catch (err) { | |
console.error('[LOX] Parse failed: ' + err); | |
console.error(txt); | |
return false; | |
} | |
} | |
function toPacket(command, data) { | |
var str = '' + JSON.stringify(data); | |
return command + '\n' + str.length + '\n' + str; | |
} | |
function fromPacket(str, localData) { | |
if (!localData) { | |
localData = socketData; | |
} | |
if (localData.buffer === undefined) { | |
localData.remaining = 0; | |
localData.buffer = ''; | |
localData.result = []; | |
} | |
str !== undefined && (localData.buffer += str); | |
var nextNewLine; | |
if (!localData.command) { | |
if (!localData.buffer.length) { | |
return false; | |
} | |
nextNewLine = localData.buffer.indexOf('\n'); | |
if (nextNewLine <= 0) { | |
return false; | |
} | |
localData.command = localData.buffer.substr(0, nextNewLine); | |
localData.buffer = localData.buffer.substr(nextNewLine + 1); | |
} | |
if (!localData.remaining) { | |
nextNewLine = localData.buffer.indexOf('\n'); | |
if (nextNewLine <= 0) { | |
return false; | |
} | |
localData.remaining = +localData.buffer.substr(0, nextNewLine); | |
localData.buffer = localData.buffer.substr(nextNewLine + 1); | |
} | |
var took = Math.min(localData.remaining, localData.buffer.length); | |
localData.result.push(localData.buffer.substr(0, took)); | |
localData.buffer = localData.buffer.substr(took); | |
localData.remaining -= took; | |
if (localData.remaining > 0) { | |
return false; | |
} | |
if (localData.remaining < 0) { | |
throw 'Assertion failure: Negative buffered data.'; | |
} | |
var retVal = { | |
command: localData.command, | |
result: safeParse(localData.result.join('')) | |
}; | |
localData.command = false; | |
localData.result = []; | |
return retVal; | |
} |
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
var net = require('net'), | |
commands = require('./commands'), | |
loxProtocol = require('./lox.protocol.js'); | |
/* | |
Public API. | |
*/ | |
exports.boot = boot; | |
exports.respond = respond; | |
/* | |
Tell "commands" who we are, to avoid a cyclic require. | |
*/ | |
commands.server = exports; | |
/** | |
* Boot starts us listening for connections. | |
*/ | |
function boot(ports) { | |
ports.forEach(function(port) { | |
var devServer = net.createServer(); | |
devServer.on('connection', handleConnection); | |
devServer.listen(port); | |
console.log('[SOCKET] Listening on ' + port + '.'); | |
}); | |
} | |
/** | |
* Handles an incoming connection. | |
*/ | |
function handleConnection(socket) { | |
socket.on('data', handleData); | |
socket.on('error', handleError); | |
socket.on('close', handleClose); | |
} | |
/** | |
* Handles data coming in from a client. | |
*/ | |
function handleData(obj) { | |
if (!this.socketState) { | |
this.socketState = {}; | |
} | |
var data = loxProtocol.fromPacket(obj, this.socketState); | |
if (!data) { | |
return | |
} | |
var method = commands[data.command]; | |
if (!method) { | |
return; | |
} | |
console.log('[SERVER] Read ' + data.command + '.'); | |
method(this, data.result); | |
// Handle coalesced packets. | |
handleData.apply(this); | |
} | |
/** | |
* Sends a response to one socket. | |
*/ | |
function respond(socket, command, data) { | |
if (!socket.writable) { | |
return false; | |
} | |
try { | |
socket.write(loxProtocol.toPacket(command, data)); | |
return true; | |
} | |
catch (err) { | |
console.error(err); | |
return false; | |
} | |
} | |
/** | |
* Handles an error from the socket. | |
*/ | |
function handleError(err) { | |
console.error(err); | |
} | |
/** | |
* Handles a connection closing. | |
*/ | |
function handleClose() { | |
// For You TODO: Clean up whatever you want. | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment