Last active
January 3, 2016 04:58
-
-
Save dawsontoth/8412167 to your computer and use it in GitHub Desktop.
Titanium client side simple JSON based socket communications.
The NodeJS server side component to this is available here: https://gist.github.com/dawsontoth/8412318
This file contains hidden or 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 lox = require('lib/lox'); | |
lox.start(); | |
lox.exampleOfSendWithoutCallback(); | |
lox.exampleOfSendWithCallback(function(result) { | |
console.log('Got data back! ' + result); | |
}); |
This file contains hidden or 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.create = create; | |
/* | |
Implementation. | |
*/ | |
function create(args) { | |
var socket, | |
reconnectTO = 1000, | |
decayedTO = 1, | |
listenSocketTimeout = 0, | |
failCount = 0, | |
retryPaused = false; | |
function listenSocket() { | |
listenSocketTimeout = 0; | |
socket = Ti.Network && Ti.Network.Socket && Ti.Network.Socket.createTCP({ | |
host: args.host, | |
port: args.ports && args.ports[failCount % args.ports.length] || args.port, | |
connected: socketConnected, | |
error: disconnectAndRetry | |
}); | |
socket && socket.connect(); | |
} | |
function socketConnected(e) { | |
failCount = 0; | |
Ti.Stream.pump(e.socket, pumpSocket, 1024, true); | |
args.connected && args.connected(); | |
} | |
function disconnect() { | |
if (listenSocketTimeout) { | |
clearTimeout(listenSocketTimeout); | |
listenSocketTimeout = 0; | |
} | |
if (socket) { | |
try { | |
socket.close(); | |
} | |
catch (e) { | |
// Oh, well... | |
} | |
socket = null; | |
} | |
decayedTO = decayedTO * 2; | |
} | |
function retry() { | |
listenSocketTimeout = setTimeout(listenSocket, decayedTO); | |
} | |
function resume() { | |
decayedTO = reconnectTO; | |
retry(); | |
} | |
function disconnectAndRetry() { | |
failCount += 1; | |
disconnect(); | |
retry(); | |
} | |
function isConnected() { | |
return socket && socket.state == Ti.Network.Socket.CONNECTED | |
} | |
function write(str) { | |
if (!isConnected()) { | |
return false; | |
} | |
else { | |
//noinspection JSValidateTypes | |
socket.write(Ti.createBuffer({ value: str })); | |
return true; | |
} | |
} | |
function pauseRetry() { | |
retryPaused = listenSocketTimeout > 0; | |
} | |
function resumeRetry() { | |
if (retryPaused) { | |
retryPaused = false; | |
retry(); | |
} | |
} | |
function pumpSocket(e) { | |
// Has the remote socket closed its end? | |
if (e.bytesProcessed < 0) { | |
disconnectAndRetry(); | |
return; | |
} | |
try { | |
e.buffer && args.read && args.read(e.buffer.toString()); | |
} catch (ex) { | |
Ti.API.error('[LOX] Socket Error: ' + ex); | |
} | |
} | |
function clean() { | |
disconnect(); | |
args = socket = decayedTO = retryPaused = listenSocketTimeout = failCount = null; | |
} | |
return { | |
start: retry, | |
isConnected: isConnected, | |
write: write, | |
pauseRetry: pauseRetry, | |
resumeRetry: resumeRetry, | |
resume: resume, | |
disconnect: disconnect, | |
clean: clean | |
}; | |
} |
This file contains hidden or 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 loxProtocol = require('lox.protocol'), | |
gracefulSocket = require('graceful.socket'); | |
/* | |
State. | |
*/ | |
var dataDir = Ti.Filesystem.applicationDataDirectory, | |
socket = gracefulSocket.create({ | |
ports: [ 3001 ], // Multiple ports can be used; after a failure to connect, we'll try a different port. | |
host: '127.0.0.1', | |
connected: updateConfig, | |
read: handleSocketRead | |
}), | |
isPaused = false, | |
isResumed = false, | |
downloadClient = null, | |
downloadID = 0, | |
callbacks = {}, | |
udp = {}; | |
/* | |
Public API. | |
*/ | |
exports.start = start; | |
exports.exampleOfSendWithoutCallback = exampleOfSendWithoutCallback; | |
exports.exampleOfSendWithCallback = exampleOfSendWithCallback; | |
/* | |
Implementation. | |
*/ | |
function start() { | |
socket.start(); | |
} | |
/* | |
Server interactions. | |
*/ | |
function handleSocketRead(obj) { | |
var data = loxProtocol.fromPacket(obj); | |
if (!data) { | |
// We haven't received a full command yet; return until later. | |
return; | |
} | |
console.log('Received ' + data.command); | |
// For You TODO: Do something with data.command and data.result. | |
switch (data.command) { | |
case 'bar': | |
callbacks.bar && callbacks.bar(data.result); | |
break; | |
} | |
// Handle coalesced packets. | |
handleSocketRead(); | |
} | |
function exampleOfSendWithoutCallback() { | |
writeSocket('foo', 'bar'); | |
} | |
function exampleOfSendWithCallback(cb) { | |
callbacks.bar = cb; | |
writeSocket('bar', 'baz'); | |
} | |
/* | |
Lifecycle methods. | |
*/ | |
Ti.App.addEventListener('pause', pause); | |
Ti.App.addEventListener('paused', pause); | |
function pause() { | |
if (isPaused) { return; } | |
isResumed = false; | |
isPaused = true; | |
socket.disconnect(); | |
} | |
Ti.App.addEventListener('resume', resume); | |
Ti.App.addEventListener('resumed', resume); | |
function resume() { | |
if (isResumed) { return; } | |
isPaused = false; | |
isResumed = true; | |
socket.resume(); | |
} | |
function writeSocket(cmd, obj) { | |
console.log('Sending ' + cmd); | |
return socket.write(loxProtocol.toPacket(cmd, obj)); | |
} |
This file contains hidden or 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 a simple JSON based socket protocol. | |
* | |
* 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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment