intial server implementation
This commit is contained in:
parent
c67d90052b
commit
0ea4912a3a
102
TcpSocket.js
102
TcpSocket.js
|
@ -23,20 +23,36 @@ var Sockets = NativeModules.TcpSockets;
|
||||||
var base64 = require('base64-js');
|
var base64 = require('base64-js');
|
||||||
var Base64Str = require('./base64-str');
|
var Base64Str = require('./base64-str');
|
||||||
var noop = function () {};
|
var noop = function () {};
|
||||||
var instances = 0;
|
var usedIds = [];
|
||||||
var STATE = {
|
var STATE = {
|
||||||
DISCONNECTED: 0,
|
DISCONNECTED: 0,
|
||||||
CONNECTING: 1,
|
CONNECTING: 1,
|
||||||
CONNECTED: 2
|
CONNECTED: 2
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = TcpSocket;
|
exports.Socket = TcpSocket;
|
||||||
|
|
||||||
function TcpSocket(options) {
|
function TcpSocket(options) {
|
||||||
EventEmitter.call(this);
|
EventEmitter.call(this);
|
||||||
|
|
||||||
this._id = instances++;
|
options = options || {};
|
||||||
this._state = STATE.DISCONNECTED;
|
|
||||||
|
var nativeSocket = false;
|
||||||
|
if (!options._id) {
|
||||||
|
// javascript generated sockets range from 1-1000
|
||||||
|
this._id = Math.floor((Math.random() * 1000) + 1);
|
||||||
|
while (usedIds.indexOf(this._id) !== -1) {
|
||||||
|
this._id = Math.floor((Math.random() * 1000) + 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// native generated sockets range from 5000-6000
|
||||||
|
// e.g. incoming server connections
|
||||||
|
this._id = options._id;
|
||||||
|
nativeSocket = true;
|
||||||
|
}
|
||||||
|
usedIds.push(this._id);
|
||||||
|
|
||||||
|
this._state = nativeSocket ? STATE.CONNECTED : STATE.DISCONNECTED;
|
||||||
this._connecting = false;
|
this._connecting = false;
|
||||||
this._hadError = false;
|
this._hadError = false;
|
||||||
this._host = null;
|
this._host = null;
|
||||||
|
@ -63,7 +79,9 @@ function TcpSocket(options) {
|
||||||
this.on = this.addListener.bind(this);
|
this.on = this.addListener.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Sockets.createSocket(this._id); // later
|
if (nativeSocket === false) {
|
||||||
|
Sockets.createSocket(this._id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inherits(TcpSocket, EventEmitter);
|
inherits(TcpSocket, EventEmitter);
|
||||||
|
@ -82,7 +100,7 @@ TcpSocket.prototype.connect = function(options, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof callback === 'function') {
|
if (typeof callback === 'function') {
|
||||||
this.once('connected', callback);
|
this.once('connect', callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
var host = options.host || 'localhost';
|
var host = options.host || 'localhost';
|
||||||
|
@ -282,3 +300,75 @@ function normalizeError (err) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.Server = TcpServer;
|
||||||
|
|
||||||
|
function TcpServer(options, connectionListener) {
|
||||||
|
if (!(this instanceof TcpServer)) {
|
||||||
|
return new TcpServer(options, connectionListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
EventEmitter.call(this);
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this._socket = new exports.Socket(options);
|
||||||
|
this._socket.on('connect', function() {
|
||||||
|
self.emit('listening');
|
||||||
|
});
|
||||||
|
this._socket.on('error', function(error) {
|
||||||
|
self.emit('error', error);
|
||||||
|
self._socket.destroy();
|
||||||
|
});
|
||||||
|
this._socket.on('close', function() {
|
||||||
|
self.emit('close');
|
||||||
|
});
|
||||||
|
this._socket.on('connection', function(socketId) {
|
||||||
|
var socket = new exports.Socket({_id : socketId });
|
||||||
|
self.emit('connection', socket);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (typeof options === 'function') {
|
||||||
|
connectionListener = options;
|
||||||
|
options = {};
|
||||||
|
self.on('connection', connectionListener);
|
||||||
|
} else {
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
if (typeof connectionListener === 'function') {
|
||||||
|
self.on('connection', connectionListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this._connections = 0;
|
||||||
|
|
||||||
|
// this.allowHalfOpen = options.allowHalfOpen || false;
|
||||||
|
// this.pauseOnConnect = !!options.pauseOnConnect;
|
||||||
|
}
|
||||||
|
|
||||||
|
inherits(TcpServer, EventEmitter);
|
||||||
|
|
||||||
|
TcpServer.prototype.listen = function(options, callback) {
|
||||||
|
var port = Number(options.port);
|
||||||
|
var hostname = options.hostname || 'localhost';
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
this.on('listening', callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sockets.listen(this._socket._id, hostname, port);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
TcpServer.prototype.getConnections = function(callback) {
|
||||||
|
/* nop */
|
||||||
|
};
|
||||||
|
|
||||||
|
TcpServer.prototype.close = function(callback) {
|
||||||
|
if (callback) {
|
||||||
|
this.on('close', callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._socket.end();
|
||||||
|
};
|
||||||
|
|
|
@ -6,7 +6,13 @@
|
||||||
|
|
||||||
var ipRegex = require('ip-regex');
|
var ipRegex = require('ip-regex');
|
||||||
|
|
||||||
exports.Socket = require('./TcpSocket');
|
var Socket = require('./TcpSocket').Socket;
|
||||||
|
var Server = require('./TcpSocket').Server;
|
||||||
|
|
||||||
|
exports.createServer = function(options, connectionListener) {
|
||||||
|
return new Server(options, connectionListener);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// Target API:
|
// Target API:
|
||||||
//
|
//
|
||||||
|
@ -20,38 +26,38 @@ exports.Socket = require('./TcpSocket');
|
||||||
// connect(port, [host], [cb])
|
// connect(port, [host], [cb])
|
||||||
// connect(path, [cb]);
|
// connect(path, [cb]);
|
||||||
//
|
//
|
||||||
exports.connect = exports.createConnection = function() {
|
// exports.connect = exports.createConnection = function() {
|
||||||
var args = normalizeConnectArgs(arguments);
|
// var args = normalizeConnectArgs(arguments);
|
||||||
exports.Socket._debug('createConnection', args);
|
// Socket._debug('createConnection', args);
|
||||||
var s = new exports.Socket(args[0]);
|
// var s = new Socket(args[0]);
|
||||||
return exports.Socket.prototype.connect.apply(s, args);
|
// return Socket.prototype.connect.apply(s, args);
|
||||||
};
|
// };
|
||||||
|
//
|
||||||
|
// // Returns an array [options] or [options, cb]
|
||||||
|
// // It is the same as the argument of Socket.prototype.connect().
|
||||||
|
// function normalizeConnectArgs(args) {
|
||||||
|
// var options = {};
|
||||||
|
//
|
||||||
|
// if (args[0] !== null && typeof args[0] === 'object') {
|
||||||
|
// // connect(options, [cb])
|
||||||
|
// options = args[0];
|
||||||
|
// }/* else if (isPipeName(args[0])) {
|
||||||
|
// // connect(path, [cb]);
|
||||||
|
// options.path = args[0];
|
||||||
|
// }*/ else {
|
||||||
|
// // connect(port, [host], [cb])
|
||||||
|
// options.port = args[0];
|
||||||
|
// if (typeof args[1] === 'string') {
|
||||||
|
// options.host = args[1];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// var cb = args[args.length - 1];
|
||||||
|
// return typeof cb === 'function' ? [options, cb] : [options];
|
||||||
|
// }
|
||||||
|
|
||||||
// Returns an array [options] or [options, cb]
|
exports.connect = exports.createConnection = function(options: { port: number, host: ?string, localAddress: ?string, localPort: ?number, family: ?number }, callback : ?any) : Socket {
|
||||||
// It is the same as the argument of Socket.prototype.connect().
|
var tcpSocket = new Socket();
|
||||||
function normalizeConnectArgs(args) {
|
|
||||||
var options = {};
|
|
||||||
|
|
||||||
if (args[0] !== null && typeof args[0] === 'object') {
|
|
||||||
// connect(options, [cb])
|
|
||||||
options = args[0];
|
|
||||||
}/* else if (isPipeName(args[0])) {
|
|
||||||
// connect(path, [cb]);
|
|
||||||
options.path = args[0];
|
|
||||||
}*/ else {
|
|
||||||
// connect(port, [host], [cb])
|
|
||||||
options.port = args[0];
|
|
||||||
if (typeof args[1] === 'string') {
|
|
||||||
options.host = args[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var cb = args[args.length - 1];
|
|
||||||
return typeof cb === 'function' ? [options, cb] : [options];
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.createConnection = function(options: { port: number,host: ?string, localAddress: ?string, localPort: ?number, family: ?number }, callback : ?any) : exports.Socket {
|
|
||||||
var tcpSocket = new exports.Socket();
|
|
||||||
tcpSocket.connect(options, callback);
|
tcpSocket.connect(options, callback);
|
||||||
return tcpSocket;
|
return tcpSocket;
|
||||||
};
|
};
|
||||||
|
@ -73,3 +79,6 @@ exports.isIPv4 = function(input: string) : boolean {
|
||||||
exports.isIPv6 = function(input: string) : boolean {
|
exports.isIPv6 = function(input: string) : boolean {
|
||||||
return exports.isIP(input) === 6;
|
return exports.isIP(input) === 6;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.Socket = Socket;
|
||||||
|
exports.Server = Server;
|
||||||
|
|
|
@ -13,58 +13,54 @@ global.Buffer = global.Buffer || require('buffer').Buffer;
|
||||||
var net = require('net');
|
var net = require('net');
|
||||||
|
|
||||||
function randomPort() {
|
function randomPort() {
|
||||||
return Math.random() * 60536 | 0 + 5000 // 60536-65536
|
return Math.random() * 60536 | 0 + 5000; // 60536-65536
|
||||||
}
|
}
|
||||||
|
|
||||||
var a = net.createConnection({ port: randomPort() }, function(err) {
|
var aPort = randomPort();
|
||||||
if (err) throw err
|
|
||||||
|
|
||||||
console.log('connected');
|
var a = net.createServer({}, function(socket) {
|
||||||
|
console.log('server connected');
|
||||||
|
|
||||||
|
// socket.on('data', function (data) {
|
||||||
|
// var str = String.fromCharCode.apply(null, new Uint8Array(data));
|
||||||
|
// console.log('a received', str);
|
||||||
|
// a.close();
|
||||||
|
// b.end();
|
||||||
|
// });
|
||||||
|
|
||||||
|
socket.on('data', function (data) {
|
||||||
|
console.log('Server Received: ' + data);
|
||||||
|
socket.write('Echo server\r\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('error', function(error) {
|
||||||
|
console.log('error ' + error);
|
||||||
|
});
|
||||||
|
}).listen({ port: aPort });
|
||||||
|
|
||||||
|
// a.on('listening', function() {
|
||||||
|
// console.log('listening');
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// a.on('error', function(error) {
|
||||||
|
// console.log('error ' + error);
|
||||||
|
// });
|
||||||
|
|
||||||
|
var b = net.createConnection({ port: aPort }, function(err) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('client connected');
|
||||||
|
b.write('Hello, server! Love, Client.');
|
||||||
});
|
});
|
||||||
|
|
||||||
a.on('error', function(err) {
|
b.on('data', function(data) {
|
||||||
console.log(err);
|
console.log('Client Received: ' + data);
|
||||||
|
b.end(); // kill client after server's response
|
||||||
|
a.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
var b = net.createConnection({ port: randomPort() }, function(err) {
|
|
||||||
if (err) throw err
|
|
||||||
|
|
||||||
console.log('connected');
|
|
||||||
});
|
|
||||||
|
|
||||||
b.on('error', function(err) {
|
|
||||||
console.log(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// a.on('message', function(data, rinfo) {
|
|
||||||
// var str = String.fromCharCode.apply(null, new Uint8Array(data));
|
|
||||||
// console.log('a received', str, rinfo)
|
|
||||||
// a.close()
|
|
||||||
// b.close()
|
|
||||||
// })
|
|
||||||
//
|
|
||||||
// b.on('message', function(data, rinfo) {
|
|
||||||
// var str = String.fromCharCode.apply(null, new Uint8Array(data));
|
|
||||||
// console.log('b received', str, rinfo)
|
|
||||||
//
|
|
||||||
// // echo back
|
|
||||||
// b.send(data, 0, data.length, aPort, '127.0.0.1', function(err) {
|
|
||||||
// if (err) throw err
|
|
||||||
//
|
|
||||||
// console.log('sent')
|
|
||||||
// })
|
|
||||||
// })
|
|
||||||
//
|
|
||||||
// b.once('listening', function() {
|
|
||||||
// var msg = toByteArray('hello')
|
|
||||||
// a.send(msg, 0, msg.length, bPort, '127.0.0.1', function(err) {
|
|
||||||
// if (err) throw err
|
|
||||||
//
|
|
||||||
// console.log('sent')
|
|
||||||
// })
|
|
||||||
// })
|
|
||||||
|
|
||||||
var rctsockets = React.createClass({
|
var rctsockets = React.createClass({
|
||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
|
@ -96,14 +92,14 @@ var styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// // only works for 8-bit chars
|
// only works for 8-bit chars
|
||||||
// function toByteArray(obj) {
|
function toByteArray(obj) {
|
||||||
// var uint = new Uint8Array(obj.length);
|
var uint = new Uint8Array(obj.length);
|
||||||
// for (var i = 0, l = obj.length; i < l; i++){
|
for (var i = 0, l = obj.length; i < l; i++){
|
||||||
// uint[i] = obj.charCodeAt(i);
|
uint[i] = obj.charCodeAt(i);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// return new Uint8Array(uint);
|
return new Uint8Array(uint);
|
||||||
// }
|
}
|
||||||
|
|
||||||
AppRegistry.registerComponent('rctsockets', () => rctsockets);
|
AppRegistry.registerComponent('rctsockets', () => rctsockets);
|
||||||
|
|
|
@ -369,7 +369,7 @@
|
||||||
83CBB9F71A601CBA00E9B192 /* Project object */ = {
|
83CBB9F71A601CBA00E9B192 /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastUpgradeCheck = 0610;
|
LastUpgradeCheck = 0720;
|
||||||
ORGANIZATIONNAME = Facebook;
|
ORGANIZATIONNAME = Facebook;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
00E356ED1AD99517003FC87E = {
|
00E356ED1AD99517003FC87E = {
|
||||||
|
@ -615,6 +615,7 @@
|
||||||
INFOPLIST_FILE = rctsocketsTests/Info.plist;
|
INFOPLIST_FILE = rctsocketsTests/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.2;
|
IPHONEOS_DEPLOYMENT_TARGET = 8.2;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/rctsockets.app/rctsockets";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/rctsockets.app/rctsockets";
|
||||||
};
|
};
|
||||||
|
@ -632,6 +633,7 @@
|
||||||
INFOPLIST_FILE = rctsocketsTests/Info.plist;
|
INFOPLIST_FILE = rctsocketsTests/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.2;
|
IPHONEOS_DEPLOYMENT_TARGET = 8.2;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/rctsockets.app/rctsockets";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/rctsockets.app/rctsockets";
|
||||||
};
|
};
|
||||||
|
@ -650,6 +652,7 @@
|
||||||
INFOPLIST_FILE = rctsockets/Info.plist;
|
INFOPLIST_FILE = rctsockets/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
OTHER_LDFLAGS = "-ObjC";
|
OTHER_LDFLAGS = "-ObjC";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
||||||
PRODUCT_NAME = rctsockets;
|
PRODUCT_NAME = rctsockets;
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
|
@ -666,6 +669,7 @@
|
||||||
INFOPLIST_FILE = rctsockets/Info.plist;
|
INFOPLIST_FILE = rctsockets/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
OTHER_LDFLAGS = "-ObjC";
|
OTHER_LDFLAGS = "-ObjC";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
||||||
PRODUCT_NAME = rctsockets;
|
PRODUCT_NAME = rctsockets;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
|
@ -690,6 +694,7 @@
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
GCC_DYNAMIC_NO_PIC = NO;
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
GCC_OPTIMIZATION_LEVEL = 0;
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0620"
|
LastUpgradeVersion = "0720"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
@ -37,10 +37,10 @@
|
||||||
</BuildActionEntries>
|
</BuildActionEntries>
|
||||||
</BuildAction>
|
</BuildAction>
|
||||||
<TestAction
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
buildConfiguration = "Debug">
|
|
||||||
<Testables>
|
<Testables>
|
||||||
<TestableReference
|
<TestableReference
|
||||||
skipped = "NO">
|
skipped = "NO">
|
||||||
|
@ -62,15 +62,18 @@
|
||||||
ReferencedContainer = "container:rctsockets.xcodeproj">
|
ReferencedContainer = "container:rctsockets.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
buildConfiguration = "Debug"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
debugDocumentVersioning = "YES"
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
allowLocationSimulation = "YES">
|
allowLocationSimulation = "YES">
|
||||||
<BuildableProductRunnable
|
<BuildableProductRunnable
|
||||||
runnableDebuggingMode = "0">
|
runnableDebuggingMode = "0">
|
||||||
|
@ -86,10 +89,10 @@
|
||||||
</AdditionalOptions>
|
</AdditionalOptions>
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
<ProfileAction
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
savedToolIdentifier = ""
|
savedToolIdentifier = ""
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
buildConfiguration = "Release"
|
|
||||||
debugDocumentVersioning = "YES">
|
debugDocumentVersioning = "YES">
|
||||||
<BuildableProductRunnable
|
<BuildableProductRunnable
|
||||||
runnableDebuggingMode = "0">
|
runnableDebuggingMode = "0">
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
|
@ -22,6 +22,13 @@
|
||||||
<string>1</string>
|
<string>1</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>NSAppTransportSecurity</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>NSLocationWhenInUseUsageDescription</key>
|
||||||
|
<string></string>
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
<string>LaunchScreen</string>
|
<string>LaunchScreen</string>
|
||||||
<key>UIRequiredDeviceCapabilities</key>
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
|
@ -36,13 +43,5 @@
|
||||||
</array>
|
</array>
|
||||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>NSLocationWhenInUseUsageDescription</key>
|
|
||||||
<string></string>
|
|
||||||
<key>NSAppTransportSecurity</key>
|
|
||||||
<dict>
|
|
||||||
<!--See http://ste.vn/2015/06/10/configuring-app-transport-security-ios-9-osx-10-11/-->
|
|
||||||
<key>NSAllowsArbitraryLoads</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
BOOL foundElement = NO;
|
BOOL foundElement = NO;
|
||||||
|
|
||||||
__block NSString *redboxError = nil;
|
__block NSString *redboxError = nil;
|
||||||
RCTSetLogFunction(^(RCTLogLevel level, NSString *fileName, NSNumber *lineNumber, NSString *message) {
|
RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
|
||||||
if (level >= RCTLogLevelError) {
|
if (level >= RCTLogLevelError) {
|
||||||
redboxError = message;
|
redboxError = message;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,12 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"buffer": "^3.5.3",
|
"buffer": "^3.5.3",
|
||||||
"events": "^1.1.0",
|
"events": "^1.1.0",
|
||||||
"react-native": "^0.15.0",
|
"react-native": "^0.17.0",
|
||||||
"react-native-tcp": "../../",
|
"react-native-tcp": "../../",
|
||||||
"util": "^0.10.3"
|
"util": "^0.10.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"babel-eslint": "^4.1.6",
|
||||||
|
"eslint-plugin-react": "^3.11.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,292 +0,0 @@
|
||||||
// https://www.npmjs.com/package/another-telnet-client
|
|
||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var util = require('util');
|
|
||||||
var events = require('events');
|
|
||||||
var net = require('net');
|
|
||||||
|
|
||||||
|
|
||||||
// define a constructor (object) and inherit EventEmitter functions
|
|
||||||
function Telnet() {
|
|
||||||
events.EventEmitter.call(this);
|
|
||||||
if (false === (this instanceof Telnet)) {
|
|
||||||
return new Telnet();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
util.inherits(Telnet, events.EventEmitter);
|
|
||||||
|
|
||||||
Telnet.prototype.connect = function (opts) {
|
|
||||||
var self = this;
|
|
||||||
var host = (typeof opts.host !== 'undefined' ? opts.host : '127.0.0.1');
|
|
||||||
var port = (typeof opts.port !== 'undefined' ? opts.port : 23);
|
|
||||||
this.timeout = (typeof opts.timeout !== 'undefined' ? opts.timeout : 500);
|
|
||||||
this.shellPrompt = (typeof opts.shellPrompt !== 'undefined' ? opts.shellPrompt : /(?:\/ )?#\s/);
|
|
||||||
this.loginPrompt = (typeof opts.loginPrompt !== 'undefined' ? opts.loginPrompt : /login[: ]*$/i);
|
|
||||||
this.passwordPrompt = (typeof opts.passwordPrompt !== 'undefined' ? opts.passwordPrompt : /Password: /i);
|
|
||||||
this.username = (typeof opts.username !== 'undefined' ? opts.username : 'root');
|
|
||||||
this.password = (typeof opts.password !== 'undefined' ? opts.password : 'guest');
|
|
||||||
this.enable = (typeof opts.enable !== 'undefined' ? opts.enable : false);
|
|
||||||
this.enablePrompt = (typeof opts.enablePrompt !== 'undefined' ? opts.enablePrompt : /Password: /i);
|
|
||||||
this.enablePassword = (typeof opts.enablePassword !== 'undefined' ? opts.enablePassword : 'enablepass');
|
|
||||||
this.irs = (typeof opts.irs !== 'undefined' ? opts.irs : '\r\n');
|
|
||||||
this.ors = (typeof opts.ors !== 'undefined' ? opts.ors : '\n');
|
|
||||||
this.echoLines = (typeof opts.echoLines !== 'undefined' ? opts.echoLines : 1);
|
|
||||||
this.pageSeparator = (typeof opts.pageSeparator !== 'undefined' ? opts.pageSeparator : '---- More');
|
|
||||||
this.ignoreOutput = (typeof opts.ignoreOutput !== 'undefined' ? opts.ignoreOutput : false);
|
|
||||||
this.ignoreOutputTimeout = (typeof opts.ignoreOutputTimeout !== 'undefined' ? opts.ignoreOutputTimeout : 1000);
|
|
||||||
this.response = '';
|
|
||||||
this.telnetState = null;
|
|
||||||
|
|
||||||
this.telnetSocket = net.createConnection({
|
|
||||||
port: port,
|
|
||||||
host: host
|
|
||||||
}, function () {
|
|
||||||
self.telnetState = 'start';
|
|
||||||
self.stringData = '';
|
|
||||||
self.emit('connect');
|
|
||||||
});
|
|
||||||
|
|
||||||
this.telnetSocket.setTimeout(this.timeout, function () {
|
|
||||||
if (self.telnetSocket._connecting === true) {
|
|
||||||
// info: cannot connect; emit error and destroy
|
|
||||||
self.emit('error', 'Cannot connect');
|
|
||||||
|
|
||||||
self.telnetSocket.destroy();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
self.emit('timeout');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.telnetSocket.on('data', function (data) {
|
|
||||||
parseData(data, self);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.telnetSocket.on('error', function (error) {
|
|
||||||
self.emit('error', error);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.telnetSocket.on('end', function () {
|
|
||||||
self.emit('end');
|
|
||||||
});
|
|
||||||
|
|
||||||
this.telnetSocket.on('close', function () {
|
|
||||||
self.emit('close');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Telnet.prototype.exec = function (cmd, opts, callback) {
|
|
||||||
var self = this;
|
|
||||||
cmd += this.ors;
|
|
||||||
|
|
||||||
if (opts && opts instanceof Function) {
|
|
||||||
callback = opts;
|
|
||||||
}
|
|
||||||
else if (opts && opts instanceof Object) {
|
|
||||||
self.shellPrompt = opts.shellPrompt || self.shellPrompt;
|
|
||||||
self.loginPrompt = opts.loginPrompt || self.loginPrompt;
|
|
||||||
self.timeout = opts.timeout || self.timeout;
|
|
||||||
self.irs = opts.irs || self.irs;
|
|
||||||
self.ors = opts.ors || self.ors;
|
|
||||||
self.echoLines = opts.echoLines || self.echoLines;
|
|
||||||
self.ignoreOutput = opts.ignoreOutput || self.ignoreOutput;
|
|
||||||
self.ignoreOutputTimeout = opts.ignoreOutputTimeout || self.ignoreOutputTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.telnetSocket.writable) {
|
|
||||||
this.telnetSocket.write(cmd, function () {
|
|
||||||
self.telnetState = 'response';
|
|
||||||
self.emit('writedone');
|
|
||||||
|
|
||||||
if (self.ignoreOutput === true) {
|
|
||||||
setTimeout(function () {
|
|
||||||
self.ignoreOutput = false;
|
|
||||||
callback(null);
|
|
||||||
}, self.ignoreOutputTimeout);
|
|
||||||
} else {
|
|
||||||
self.once('responseready', function () {
|
|
||||||
if (callback && self.cmdOutput !== undefined) {
|
|
||||||
callback(self.cmdOutput.join('\n'));
|
|
||||||
}
|
|
||||||
else if (callback && self.cmdOutput === undefined) {
|
|
||||||
callback(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset stored response
|
|
||||||
self.stringData = '';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
callback(new Error("Socket not writable"));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Telnet.prototype.end = function () {
|
|
||||||
this.telnetSocket.end();
|
|
||||||
};
|
|
||||||
|
|
||||||
Telnet.prototype.destroy = function () {
|
|
||||||
this.telnetSocket.destroy();
|
|
||||||
};
|
|
||||||
|
|
||||||
function parseData(chunk, telnetObj) {
|
|
||||||
var promptIndex = '';
|
|
||||||
var tempStringData = '';
|
|
||||||
|
|
||||||
if (chunk[0] === 255 && chunk[1] !== 255) {
|
|
||||||
telnetObj.stringData = '';
|
|
||||||
var negReturn = negotiate(telnetObj, chunk);
|
|
||||||
|
|
||||||
if (negReturn === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
chunk = negReturn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (telnetObj.ignoreOutput === true) {
|
|
||||||
telnetObj.stringData = '';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (telnetObj.telnetState === 'start') {
|
|
||||||
telnetObj.telnetState = 'getprompt';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (telnetObj.telnetState === 'getprompt') {
|
|
||||||
tempStringData = chunk.toString();
|
|
||||||
promptIndex = tempStringData.search(telnetObj.shellPrompt);
|
|
||||||
|
|
||||||
if (promptIndex !== -1) {
|
|
||||||
telnetObj.shellPrompt = tempStringData.substring(promptIndex);
|
|
||||||
telnetObj.telnetState = 'sendcmd';
|
|
||||||
telnetObj.stringData = '';
|
|
||||||
telnetObj.emit('ready', telnetObj.shellPrompt);
|
|
||||||
}
|
|
||||||
else if (tempStringData.search(telnetObj.loginPrompt) !== -1) {
|
|
||||||
telnetObj.telnetState = 'login';
|
|
||||||
login(telnetObj, 'username');
|
|
||||||
}
|
|
||||||
else if (tempStringData.search(telnetObj.passwordPrompt) !== -1) {
|
|
||||||
telnetObj.telnetState = 'login';
|
|
||||||
login(telnetObj, 'password');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (telnetObj.telnetState === 'enable') {
|
|
||||||
tempStringData = chunk.toString();
|
|
||||||
|
|
||||||
if (tempStringData.search(telnetObj.enablePrompt) !== -1) {
|
|
||||||
telnetObj.telnetState = 'login';
|
|
||||||
login(telnetObj, 'enablePassword');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (telnetObj.telnetState === 'getenprompt') {
|
|
||||||
tempStringData = chunk.toString();
|
|
||||||
|
|
||||||
if (tempStringData.search(telnetObj.shellPrompt) !== -1) {
|
|
||||||
telnetObj.telnetState = 'login';
|
|
||||||
login(telnetObj, 'enable');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (telnetObj.telnetState === 'response') {
|
|
||||||
tempStringData = chunk.toString();
|
|
||||||
telnetObj.stringData += tempStringData;
|
|
||||||
promptIndex = tempStringData.search(telnetObj.shellPrompt);
|
|
||||||
|
|
||||||
if (promptIndex === -1 && tempStringData.length !== 0) {
|
|
||||||
if (tempStringData.search(telnetObj.pageSeparator) !== -1) {
|
|
||||||
telnetObj.telnetSocket.write(Buffer('20', 'hex'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
telnetObj.cmdOutput = telnetObj.stringData.split(telnetObj.irs);
|
|
||||||
|
|
||||||
for (var i = 0; i < telnetObj.cmdOutput.length; i++) {
|
|
||||||
if (telnetObj.cmdOutput[i].search(telnetObj.pageSeparator) !== -1) {
|
|
||||||
telnetObj.cmdOutput.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (telnetObj.echoLines === 1) {
|
|
||||||
telnetObj.cmdOutput.shift();
|
|
||||||
}
|
|
||||||
else if (telnetObj.echoLines > 1) {
|
|
||||||
telnetObj.cmdOutput.splice(0, telnetObj.echoLines);
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove prompt
|
|
||||||
telnetObj.cmdOutput.pop();
|
|
||||||
|
|
||||||
telnetObj.emit('responseready');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function login(telnetObj, handle) {
|
|
||||||
if (handle === 'username') {
|
|
||||||
if (telnetObj.telnetSocket.writable) {
|
|
||||||
telnetObj.telnetSocket.write(telnetObj.username + telnetObj.ors, function () {
|
|
||||||
telnetObj.telnetState = 'getprompt';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (handle === 'password') {
|
|
||||||
if (telnetObj.telnetSocket.writable) {
|
|
||||||
telnetObj.telnetSocket.write(telnetObj.password + telnetObj.ors, function () {
|
|
||||||
if (telnetObj.enable) {
|
|
||||||
telnetObj.telnetState = 'getenprompt';
|
|
||||||
} else {
|
|
||||||
telnetObj.telnetState = 'getprompt';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (handle === 'enable') {
|
|
||||||
if (telnetObj.telnetSocket.writable) {
|
|
||||||
telnetObj.telnetSocket.write("en" + telnetObj.ors, function () {
|
|
||||||
telnetObj.telnetState = 'enable';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (handle === 'enablePassword') {
|
|
||||||
if (telnetObj.telnetSocket.writable) {
|
|
||||||
telnetObj.telnetSocket.write(telnetObj.enablePassword + telnetObj.ors, function () {
|
|
||||||
telnetObj.telnetState = 'getprompt';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function negotiate(telnetObj, chunk) {
|
|
||||||
// info: http://tools.ietf.org/html/rfc1143#section-7
|
|
||||||
// refuse to start performing and ack the start of performance
|
|
||||||
// DO -> WONT; WILL -> DO
|
|
||||||
var packetLength = chunk.length, negData = chunk, cmdData, negResp;
|
|
||||||
|
|
||||||
for (var i = 0; i < packetLength; i += 3) {
|
|
||||||
if (chunk[i] !== 255) {
|
|
||||||
negData = chunk.slice(0, i);
|
|
||||||
cmdData = chunk.slice(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
negResp = negData.toString('hex').replace(/fd/g, 'fc').replace(/fb/g, 'fd');
|
|
||||||
|
|
||||||
if (telnetObj.telnetSocket.writable) {
|
|
||||||
telnetObj.telnetSocket.write(Buffer(negResp, 'hex'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cmdData !== undefined) {
|
|
||||||
return cmdData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Telnet;
|
|
||||||
}());
|
|
|
@ -29,16 +29,19 @@ typedef enum RCTTCPError RCTTCPError;
|
||||||
|
|
||||||
@protocol SocketClientDelegate <NSObject>
|
@protocol SocketClientDelegate <NSObject>
|
||||||
|
|
||||||
- (void)onConnect:(TcpSocketClient*) client;
|
- (void)onConnect:(TcpSocketClient*)client;
|
||||||
- (void)onData:(TcpSocketClient*) client data:(NSData *)data;
|
- (void)onConnection:(TcpSocketClient*)client toClient:(NSNumber *)clientID;
|
||||||
- (void)onClose:(TcpSocketClient*) client withError:(NSError *)err;
|
- (void)onData:(NSNumber *)clientID data:(NSData *)data;
|
||||||
- (void)onError:(TcpSocketClient*) client withError:(NSError *)err;
|
- (void)onClose:(TcpSocketClient*)client withError:(NSError *)err;
|
||||||
|
- (void)onError:(TcpSocketClient*)client withError:(NSError *)err;
|
||||||
|
- (NSNumber*)generateRandomId;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface TcpSocketClient : NSObject
|
@interface TcpSocketClient : NSObject
|
||||||
|
|
||||||
@property (nonatomic, retain) NSNumber * id;
|
@property (nonatomic, retain) NSNumber * id;
|
||||||
|
@property (nonatomic, weak) id<SocketClientDelegate> clientDelegate;
|
||||||
|
|
||||||
///---------------------------------------------------------------------------------------
|
///---------------------------------------------------------------------------------------
|
||||||
/// @name Class Methods
|
/// @name Class Methods
|
||||||
|
@ -65,6 +68,8 @@ typedef enum RCTTCPError RCTTCPError;
|
||||||
*/
|
*/
|
||||||
- (BOOL)connect:(NSString *)host port:(int)port withOptions:(NSDictionary *)options error:(NSError **)error;
|
- (BOOL)connect:(NSString *)host port:(int)port withOptions:(NSDictionary *)options error:(NSError **)error;
|
||||||
|
|
||||||
|
- (BOOL)listen:(NSString *)host port:(int)port error:(NSError **)error;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* write data
|
* write data
|
||||||
*
|
*
|
||||||
|
|
|
@ -18,13 +18,13 @@ NSString *const RCTTCPErrorDomain = @"RCTTCPErrorDomain";
|
||||||
{
|
{
|
||||||
@private
|
@private
|
||||||
GCDAsyncSocket *_tcpSocket;
|
GCDAsyncSocket *_tcpSocket;
|
||||||
id<SocketClientDelegate> _clientDelegate;
|
|
||||||
NSMutableDictionary<NSNumber *, RCTResponseSenderBlock> *_pendingSends;
|
NSMutableDictionary<NSNumber *, RCTResponseSenderBlock> *_pendingSends;
|
||||||
NSLock *_lock;
|
NSLock *_lock;
|
||||||
long _sendTag;
|
long _sendTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithClientId:(NSNumber *)clientID andConfig:(id<SocketClientDelegate>) aDelegate;
|
- (id)initWithClientId:(NSNumber *)clientID andConfig:(id<SocketClientDelegate>)aDelegate;
|
||||||
|
- (id)initWithClientId:(NSNumber *)clientID andConfig:(id<SocketClientDelegate>)aDelegate andSocket:(GCDAsyncSocket*)tcpSocket;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -32,10 +32,15 @@ NSString *const RCTTCPErrorDomain = @"RCTTCPErrorDomain";
|
||||||
|
|
||||||
+ (id)socketClientWithId:(nonnull NSNumber *)clientID andConfig:(id<SocketClientDelegate>)delegate
|
+ (id)socketClientWithId:(nonnull NSNumber *)clientID andConfig:(id<SocketClientDelegate>)delegate
|
||||||
{
|
{
|
||||||
return [[[self class] alloc] initWithClientId:clientID andConfig:delegate];
|
return [[[self class] alloc] initWithClientId:clientID andConfig:delegate andSocket:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithClientId:(NSNumber *)clientID andConfig:(id<SocketClientDelegate>) aDelegate
|
- (id)initWithClientId:(NSNumber *)clientID andConfig:(id<SocketClientDelegate>)aDelegate
|
||||||
|
{
|
||||||
|
return [self initWithClientId:clientID andConfig:aDelegate andSocket:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)initWithClientId:(NSNumber *)clientID andConfig:(id<SocketClientDelegate>)aDelegate andSocket:(GCDAsyncSocket*)tcpSocket;
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self) {
|
if (self) {
|
||||||
|
@ -43,6 +48,7 @@ NSString *const RCTTCPErrorDomain = @"RCTTCPErrorDomain";
|
||||||
_clientDelegate = aDelegate;
|
_clientDelegate = aDelegate;
|
||||||
_pendingSends = [NSMutableDictionary dictionary];
|
_pendingSends = [NSMutableDictionary dictionary];
|
||||||
_lock = [[NSLock alloc] init];
|
_lock = [[NSLock alloc] init];
|
||||||
|
_tcpSocket = tcpSocket;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
@ -82,6 +88,27 @@ NSString *const RCTTCPErrorDomain = @"RCTTCPErrorDomain";
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (BOOL)listen:(NSString *)host port:(int)port error:(NSError **)error
|
||||||
|
{
|
||||||
|
if (_tcpSocket) {
|
||||||
|
if (error) {
|
||||||
|
*error = [self badInvocationError:@"this client's socket is already connected"];
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_tcpSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:[self methodQueue]];
|
||||||
|
|
||||||
|
BOOL result = [_tcpSocket acceptOnInterface:host port:port error:error];
|
||||||
|
if (result == YES) {
|
||||||
|
[_clientDelegate onConnect: self];
|
||||||
|
[_tcpSocket readDataWithTimeout:-1 tag:_id.longValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)setPendingSend:(RCTResponseSenderBlock)callback forKey:(NSNumber *)key
|
- (void)setPendingSend:(RCTResponseSenderBlock)callback forKey:(NSNumber *)key
|
||||||
{
|
{
|
||||||
[_lock lock];
|
[_lock lock];
|
||||||
|
@ -130,12 +157,12 @@ NSString *const RCTTCPErrorDomain = @"RCTTCPErrorDomain";
|
||||||
{
|
{
|
||||||
[_tcpSocket writeData:data withTimeout:-1 tag:_sendTag];
|
[_tcpSocket writeData:data withTimeout:-1 tag:_sendTag];
|
||||||
if (callback) {
|
if (callback) {
|
||||||
[self setPendingSend:callback forKey:[NSNumber numberWithLong:_sendTag]];
|
[self setPendingSend:callback forKey:@(_sendTag)];
|
||||||
}
|
}
|
||||||
|
|
||||||
_sendTag++;
|
_sendTag++;
|
||||||
|
|
||||||
[_tcpSocket readDataWithTimeout:-1 tag:-1];
|
[_tcpSocket readDataWithTimeout:-1 tag:_id.longValue];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)end
|
- (void)end
|
||||||
|
@ -150,9 +177,19 @@ NSString *const RCTTCPErrorDomain = @"RCTTCPErrorDomain";
|
||||||
|
|
||||||
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
|
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
|
||||||
if (!_clientDelegate) return;
|
if (!_clientDelegate) return;
|
||||||
[_clientDelegate onData:self data:data];
|
[_clientDelegate onData:@(tag) data:data];
|
||||||
|
|
||||||
[sock readDataWithTimeout:-1 tag:-1];
|
[sock readDataWithTimeout:-1 tag:tag];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
|
||||||
|
{
|
||||||
|
TcpSocketClient *inComing = [[TcpSocketClient alloc] initWithClientId:[_clientDelegate generateRandomId]
|
||||||
|
andConfig:_clientDelegate
|
||||||
|
andSocket:newSocket];
|
||||||
|
[_clientDelegate onConnection: inComing
|
||||||
|
toClient: _id];
|
||||||
|
[newSocket readDataWithTimeout:-1 tag:inComing.id.longValue];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port
|
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port
|
||||||
|
@ -160,11 +197,13 @@ NSString *const RCTTCPErrorDomain = @"RCTTCPErrorDomain";
|
||||||
if (!_clientDelegate) return;
|
if (!_clientDelegate) return;
|
||||||
[_clientDelegate onConnect:self];
|
[_clientDelegate onConnect:self];
|
||||||
|
|
||||||
[sock readDataWithTimeout:-1 tag:-1];
|
[sock readDataWithTimeout:-1 tag:_id.longValue];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)socketDidCloseReadStream:(GCDAsyncSocket *)sock
|
- (void)socketDidCloseReadStream:(GCDAsyncSocket *)sock
|
||||||
{
|
{
|
||||||
|
// TODO : investigate for half-closed sockets
|
||||||
|
/* no-op */
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
|
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
|
||||||
|
|
|
@ -16,6 +16,4 @@
|
||||||
|
|
||||||
@interface TcpSockets : NSObject<SocketClientDelegate, RCTBridgeModule>
|
@interface TcpSockets : NSObject<SocketClientDelegate, RCTBridgeModule>
|
||||||
|
|
||||||
+(NSMutableDictionary<NSNumber *,TcpSocketClient *> *)clients;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
101
ios/TcpSockets.m
101
ios/TcpSockets.m
|
@ -15,47 +15,38 @@
|
||||||
#import "TcpSocketClient.h"
|
#import "TcpSocketClient.h"
|
||||||
|
|
||||||
@implementation TcpSockets
|
@implementation TcpSockets
|
||||||
|
{
|
||||||
|
NSMutableDictionary<NSNumber *,TcpSocketClient *> *_clients;
|
||||||
|
}
|
||||||
|
|
||||||
RCT_EXPORT_MODULE()
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
@synthesize bridge = _bridge;
|
@synthesize bridge = _bridge;
|
||||||
|
|
||||||
+ (void) initialize {
|
-(void)dealloc
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
||||||
selector:@selector(closeAllSockets)
|
|
||||||
name:RCTReloadNotification
|
|
||||||
object:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSMutableDictionary<NSNumber *,TcpSocketClient *> *) clients
|
|
||||||
{
|
{
|
||||||
static NSMutableDictionary* c = nil;
|
for (NSNumber *cId in _clients.allKeys) {
|
||||||
|
[self destroyClient:cId callback:nil];
|
||||||
static dispatch_once_t oncePredicate;
|
}
|
||||||
|
|
||||||
dispatch_once(&oncePredicate, ^{
|
|
||||||
c = [[NSMutableDictionary alloc] init];
|
|
||||||
});
|
|
||||||
|
|
||||||
return c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(createSocket:(nonnull NSNumber*)cId)
|
RCT_EXPORT_METHOD(createSocket:(nonnull NSNumber*)cId)
|
||||||
{
|
{
|
||||||
NSMutableDictionary<NSNumber *,TcpSocketClient *> *_clients = [TcpSockets clients];
|
|
||||||
if (!cId) {
|
if (!cId) {
|
||||||
RCTLogError(@"%@.createSocket called with nil id parameter.", [self class]);
|
RCTLogError(@"%@.createSocket called with nil id parameter.", [self class]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TcpSocketClient *client = [_clients objectForKey:cId];
|
if (!_clients) {
|
||||||
if (client) {
|
_clients = [NSMutableDictionary new];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_clients[cId]) {
|
||||||
RCTLogError(@"%@.createSocket called twice with the same id.", [self class]);
|
RCTLogError(@"%@.createSocket called twice with the same id.", [self class]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
client = [TcpSocketClient socketClientWithId:cId andConfig:self];
|
_clients[cId] = [TcpSocketClient socketClientWithId:cId andConfig:self];
|
||||||
[_clients setObject:client forKey:cId];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(connect:(nonnull NSNumber*)cId
|
RCT_EXPORT_METHOD(connect:(nonnull NSNumber*)cId
|
||||||
|
@ -63,7 +54,7 @@ RCT_EXPORT_METHOD(connect:(nonnull NSNumber*)cId
|
||||||
port:(int)port
|
port:(int)port
|
||||||
withOptions:(NSDictionary *)options)
|
withOptions:(NSDictionary *)options)
|
||||||
{
|
{
|
||||||
TcpSocketClient* client = [TcpSockets findClient:cId callback:nil];
|
TcpSocketClient* client = [self findClient:cId callback:nil];
|
||||||
if (!client) return;
|
if (!client) return;
|
||||||
|
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
|
@ -78,7 +69,7 @@ RCT_EXPORT_METHOD(write:(nonnull NSNumber*)cId
|
||||||
string:(NSString *)base64String
|
string:(NSString *)base64String
|
||||||
encoded:(BOOL)encoded
|
encoded:(BOOL)encoded
|
||||||
callback:(RCTResponseSenderBlock)callback) {
|
callback:(RCTResponseSenderBlock)callback) {
|
||||||
TcpSocketClient* client = [TcpSockets findClient:cId callback:callback];
|
TcpSocketClient* client = [self findClient:cId callback:callback];
|
||||||
if (!client) return;
|
if (!client) return;
|
||||||
|
|
||||||
// iOS7+
|
// iOS7+
|
||||||
|
@ -89,30 +80,43 @@ RCT_EXPORT_METHOD(write:(nonnull NSNumber*)cId
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(end:(nonnull NSNumber*)cId
|
RCT_EXPORT_METHOD(end:(nonnull NSNumber*)cId
|
||||||
callback:(RCTResponseSenderBlock)callback) {
|
callback:(RCTResponseSenderBlock)callback) {
|
||||||
[TcpSockets endClient:cId callback:callback];
|
[self endClient:cId callback:callback];
|
||||||
}
|
}
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(destroy:(nonnull NSNumber*)cId
|
RCT_EXPORT_METHOD(destroy:(nonnull NSNumber*)cId
|
||||||
callback:(RCTResponseSenderBlock)callback) {
|
callback:(RCTResponseSenderBlock)callback) {
|
||||||
[TcpSockets destroyClient:cId callback:callback];
|
[self destroyClient:cId callback:callback];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) onConnect:(TcpSocketClient*) client
|
RCT_EXPORT_METHOD(listen:(nonnull NSNumber*)cId
|
||||||
|
host:(NSString *)host
|
||||||
|
port:(int)port)
|
||||||
|
{
|
||||||
|
TcpSocketClient* client = [self findClient:cId callback:nil];
|
||||||
|
if (!client) return;
|
||||||
|
|
||||||
|
NSError *error = nil;
|
||||||
|
if (![client listen:host port:port error:&error])
|
||||||
|
{
|
||||||
|
[self onError:client withError:error];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)onConnect:(TcpSocketClient*) client
|
||||||
{
|
{
|
||||||
[self.bridge.eventDispatcher sendDeviceEventWithName:[NSString stringWithFormat:@"tcp-%@-event", client.id]
|
[self.bridge.eventDispatcher sendDeviceEventWithName:[NSString stringWithFormat:@"tcp-%@-event", client.id]
|
||||||
body:@{ @"event": @"connect" }];
|
body:@{ @"event": @"connect" }];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) onData:(TcpSocketClient*) client data:(NSData *)data
|
- (void)onData:(NSNumber *)clientID data:(NSData *)data
|
||||||
{
|
{
|
||||||
NSMutableDictionary<NSNumber *,TcpSocketClient *> *_clients = [TcpSockets clients];
|
|
||||||
NSNumber *clientID = [[_clients allKeysForObject:client] objectAtIndex:0];
|
|
||||||
NSString *base64String = [data base64EncodedStringWithOptions:0];
|
NSString *base64String = [data base64EncodedStringWithOptions:0];
|
||||||
[self.bridge.eventDispatcher sendDeviceEventWithName:[NSString stringWithFormat:@"tcp-%@-event", clientID]
|
[self.bridge.eventDispatcher sendDeviceEventWithName:[NSString stringWithFormat:@"tcp-%@-event", clientID]
|
||||||
body:@{ @"event": @"data", @"data": base64String }];
|
body:@{ @"event": @"data", @"data": base64String }];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) onClose:(TcpSocketClient*) client withError:(NSError *)err
|
- (void)onClose:(TcpSocketClient*) client withError:(NSError *)err
|
||||||
{
|
{
|
||||||
if (err) {
|
if (err) {
|
||||||
[self onError:client withError:err];
|
[self onError:client withError:err];
|
||||||
|
@ -121,7 +125,7 @@ RCT_EXPORT_METHOD(destroy:(nonnull NSNumber*)cId
|
||||||
[self.bridge.eventDispatcher sendDeviceEventWithName:[NSString stringWithFormat:@"tcp-%@-event", client.id]
|
[self.bridge.eventDispatcher sendDeviceEventWithName:[NSString stringWithFormat:@"tcp-%@-event", client.id]
|
||||||
body:@{ @"event": @"close", @"data": err == nil ? @NO : @YES }];
|
body:@{ @"event": @"close", @"data": err == nil ? @NO : @YES }];
|
||||||
|
|
||||||
NSMutableDictionary<NSNumber *,TcpSocketClient *> *_clients = [TcpSockets clients];
|
client.clientDelegate = nil;
|
||||||
[_clients removeObjectForKey:client.id];
|
[_clients removeObjectForKey:client.id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,10 +136,9 @@ RCT_EXPORT_METHOD(destroy:(nonnull NSNumber*)cId
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+(TcpSocketClient*)findClient:(nonnull NSNumber*)cId callback:(RCTResponseSenderBlock)callback
|
-(TcpSocketClient*)findClient:(nonnull NSNumber*)cId callback:(RCTResponseSenderBlock)callback
|
||||||
{
|
{
|
||||||
NSMutableDictionary<NSNumber *,TcpSocketClient *> *_clients = [TcpSockets clients];
|
TcpSocketClient *client = _clients[cId];
|
||||||
TcpSocketClient *client = [_clients objectForKey:cId];
|
|
||||||
if (!client) {
|
if (!client) {
|
||||||
if (!callback) {
|
if (!callback) {
|
||||||
RCTLogError(@"%@.missing callback parameter.", [self class]);
|
RCTLogError(@"%@.missing callback parameter.", [self class]);
|
||||||
|
@ -149,10 +152,10 @@ RCT_EXPORT_METHOD(destroy:(nonnull NSNumber*)cId
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
+(void) endClient:(nonnull NSNumber*)cId
|
-(void)endClient:(nonnull NSNumber*)cId
|
||||||
callback:(RCTResponseSenderBlock)callback
|
callback:(RCTResponseSenderBlock)callback
|
||||||
{
|
{
|
||||||
TcpSocketClient* client = [TcpSockets findClient:cId callback:callback];
|
TcpSocketClient* client = [self findClient:cId callback:callback];
|
||||||
if (!client) return;
|
if (!client) return;
|
||||||
|
|
||||||
[client end];
|
[client end];
|
||||||
|
@ -160,22 +163,30 @@ RCT_EXPORT_METHOD(destroy:(nonnull NSNumber*)cId
|
||||||
if (callback) callback(@[]);
|
if (callback) callback(@[]);
|
||||||
}
|
}
|
||||||
|
|
||||||
+(void) destroyClient:(nonnull NSNumber*)cId
|
-(void)destroyClient:(nonnull NSNumber*)cId
|
||||||
callback:(RCTResponseSenderBlock)callback
|
callback:(RCTResponseSenderBlock)callback
|
||||||
{
|
{
|
||||||
NSMutableDictionary<NSNumber *,TcpSocketClient *> *_clients = [TcpSockets clients];
|
TcpSocketClient* client = [self findClient:cId callback:nil];
|
||||||
TcpSocketClient* client = [TcpSockets findClient:cId callback:nil];
|
|
||||||
if (!client) return;
|
if (!client) return;
|
||||||
|
|
||||||
[client destroy];
|
[client destroy];
|
||||||
[_clients removeObjectForKey:cId];
|
[_clients removeObjectForKey:cId];
|
||||||
}
|
}
|
||||||
|
|
||||||
+(void) closeAllSockets {
|
-(void)onConnection:(TcpSocketClient *)client toClient:(NSNumber *)clientID {
|
||||||
NSMutableDictionary<NSNumber *,TcpSocketClient *> *_clients = [TcpSockets clients];
|
_clients[client.id] = client;
|
||||||
for (NSNumber* cId in _clients) {
|
|
||||||
[TcpSockets endClient:cId callback:nil];
|
[self.bridge.eventDispatcher sendDeviceEventWithName:[NSString stringWithFormat:@"tcp-%@-event", clientID]
|
||||||
}
|
body:@{ @"event": @"connection", @"data": client.id }];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSNumber*)generateRandomId {
|
||||||
|
int r = 0;
|
||||||
|
do {
|
||||||
|
r = (arc4random() % 1000) + 5001;
|
||||||
|
} while(_clients[@(r)]);
|
||||||
|
|
||||||
|
return @(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
Loading…
Reference in New Issue