set version to 0.0.1

end now closes the socket after writes are done
This commit is contained in:
Andy Prock 2015-12-17 08:10:00 -08:00
parent 87e2093be5
commit 3eb82c2fde
7 changed files with 152 additions and 48 deletions

View File

@ -21,6 +21,7 @@ var {
} = require('react-native'); } = require('react-native');
var Sockets = NativeModules.TcpSockets; var Sockets = NativeModules.TcpSockets;
var base64 = require('base64-js'); var base64 = require('base64-js');
var Base64Str = require('./base64-str');
var noop = function () {}; var noop = function () {};
var instances = 0; var instances = 0;
var STATE = { var STATE = {
@ -124,6 +125,8 @@ function isLegalPort(port) {
} }
TcpSocket.prototype.setTimeout = function(msecs, callback) { TcpSocket.prototype.setTimeout = function(msecs, callback) {
var self = this;
if (this._timeout) { if (this._timeout) {
clearTimeout(this._timeout); clearTimeout(this._timeout);
this._timeout = null; this._timeout = null;
@ -137,7 +140,8 @@ TcpSocket.prototype.setTimeout = function(msecs, callback) {
var self = this; var self = this;
this._timeout = setTimeout(msecs, function() { this._timeout = setTimeout(msecs, function() {
self.emit('timeout'); self.emit('timeout');
this._timeout = null; self._timeout = null;
self.destroy();
}); });
} }
}; };
@ -191,11 +195,13 @@ TcpSocket.prototype.end = function(data, encoding) {
}; };
TcpSocket.prototype.destroy = function() { TcpSocket.prototype.destroy = function() {
if (!this._destroyed) {
this._destroyed = true; this._destroyed = true;
this._debug('destroying'); this._debug('destroying');
this._subscription.remove(); this._subscription.remove();
Sockets.destroy(this._id); Sockets.destroy(this._id, this._debug.bind(this, 'closed'));
}
}; };
TcpSocket.prototype._onEvent = function(info) { TcpSocket.prototype._onEvent = function(info) {
@ -239,8 +245,8 @@ TcpSocket.prototype.write = function(buffer, encoding, callback) {
callback = callback || noop; callback = callback || noop;
var str; var str;
if (typeof buffer === 'string') { if (typeof buffer === 'string') {
console.warn('socket.WRITE(): interpreting as UTF8'); console.warn('socket.WRITE(): encoding as base64');
str = buffer; str = Base64Str.encode(buffer);
} else if (typeof Buffer !== 'undefined' && global.Buffer.isBuffer(buffer)) { } else if (typeof Buffer !== 'undefined' && global.Buffer.isBuffer(buffer)) {
encoded = true; encoded = true;
str = buffer.toString('base64'); str = buffer.toString('base64');
@ -252,9 +258,14 @@ TcpSocket.prototype.write = function(buffer, encoding, callback) {
} }
Sockets.write(this._id, str, encoded, function(err) { Sockets.write(this._id, str, encoded, function(err) {
if (self._timeout) {
clearTimeout(self._timeout);
self._timeout = null;
}
err = normalizeError(err); err = normalizeError(err);
if (err) { if (err) {
self._debug('send failed', err); self._debug('write failed', err);
return callback(err); return callback(err);
} }

100
base64-str.js Normal file
View File

@ -0,0 +1,100 @@
/**
* Source: https://gist.github.com/ncerminara/11257943
*/
'use strict';
(function () {
var Base64Str = {
_keyStr: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
encode: function(e: string) {
var t = '';
var n, r, i, s, o, u, a;
var f = 0;
e = Base64Str._utf8_encode(e);
while (f < e.length) {
n = e.charCodeAt(f++);
r = e.charCodeAt(f++);
i = e.charCodeAt(f++);
s = n >> 2;
o = (n & 3) << 4 | r >> 4;
u = (r & 15) << 2 | i >> 6;
a = i & 63;
if (isNaN(r)) {
u = a = 64;
} else if (isNaN(i)) {
a = 64;
}
t = t + this._keyStr.charAt(s) + this._keyStr.charAt(o) + this._keyStr
.charAt(u) + this._keyStr.charAt(a);
}
return t;
},
decode: function(e: string) {
var t = '';
var n, r, i;
var s, o, u, a;
var f = 0;
e = e.replace(/[^A-Za-z0-9\+\/\=]/g, '');
while (f < e.length) {
s = this._keyStr.indexOf(e.charAt(f++));
o = this._keyStr.indexOf(e.charAt(f++));
u = this._keyStr.indexOf(e.charAt(f++));
a = this._keyStr.indexOf(e.charAt(f++));
n = s << 2 | o >> 4;
r = (o & 15) << 4 | u >> 2;
i = (u & 3) << 6 | a;
t = t + String.fromCharCode(n);
if (u !== 64) {
t = t + String.fromCharCode(r);
}
if (a !== 64) {
t = t + String.fromCharCode(i);
}
}
t = Base64Str._utf8_decode(t);
return t;
},
_utf8_encode: function(e) {
e = e.replace(/\r\n/g, '\n');
var t = '';
for (var n = 0; n < e.length; n++) {
var r = e.charCodeAt(n);
if (r < 128) {
t += String.fromCharCode(r);
} else if (r > 127 && r < 2048) {
t += String.fromCharCode(r >> 6 | 192);
t += String.fromCharCode(r & 63 | 128);
} else {
t += String.fromCharCode(r >> 12 | 224);
t += String.fromCharCode(r >> 6 & 63 | 128);
t += String.fromCharCode(r & 63 | 128);
}
}
return t;
},
_utf8_decode: function(e) {
var t = '';
var n = 0;
var r = 0, /*c1 = 0, */c2 = 0;
while (n < e.length) {
r = e.charCodeAt(n);
if (r < 128) {
t += String.fromCharCode(r);
n++;
} else if (r > 191 && r < 224) {
c2 = e.charCodeAt(n + 1);
t += String.fromCharCode((r & 31) << 6 | c2 & 63);
n += 2;
} else {
c2 = e.charCodeAt(n + 1);
var c3 = e.charCodeAt(n + 2);
t += String.fromCharCode((r & 15) << 12 | (c2 & 63) << 6 | c3 & 63);
n += 3;
}
}
return t;
}
};
module.exports = Base64Str;
}());

View File

@ -38,9 +38,7 @@ typedef enum RCTTCPError RCTTCPError;
@interface TcpSocketClient : NSObject @interface TcpSocketClient : NSObject
@property (nonatomic, retain) NSString * id; @property (nonatomic, retain) NSNumber * id;
@property (nonatomic, retain) NSString * host;
@property (nonatomic) u_int16_t port;
///--------------------------------------------------------------------------------------- ///---------------------------------------------------------------------------------------
/// @name Class Methods /// @name Class Methods
@ -53,7 +51,7 @@ typedef enum RCTTCPError RCTTCPError;
* @return New RCTTCPClient * @return New RCTTCPClient
*/ */
+ (id)socketClientWithConfig:(id<SocketClientDelegate>) delegate; + (id)socketClientWithId:(NSNumber *)clientID andConfig:(id<SocketClientDelegate>) delegate;
///--------------------------------------------------------------------------------------- ///---------------------------------------------------------------------------------------
/// @name Instance Methods /// @name Instance Methods

View File

@ -23,21 +23,22 @@ NSString *const RCTTCPErrorDomain = @"RCTTCPErrorDomain";
long _sendTag; long _sendTag;
} }
- (id)initWithConfig:(id<SocketClientDelegate>) aDelegate; - (id)initWithClientId:(NSNumber *)clientID andConfig:(id<SocketClientDelegate>) aDelegate;
@end @end
@implementation TcpSocketClient @implementation TcpSocketClient
+ (id)socketClientWithConfig:(id<SocketClientDelegate>)delegate + (id)socketClientWithId:(nonnull NSNumber *)clientID andConfig:(id<SocketClientDelegate>)delegate
{ {
return [[[self class] alloc] initWithConfig:delegate]; return [[[self class] alloc] initWithClientId:clientID andConfig:delegate];
} }
- (id)initWithConfig:(id<SocketClientDelegate>) aDelegate - (id)initWithClientId:(NSNumber *)clientID andConfig:(id<SocketClientDelegate>) aDelegate
{ {
self = [super init]; self = [super init];
if (self) { if (self) {
_id = clientID;
_clientDelegate = aDelegate; _clientDelegate = aDelegate;
_pendingSends = [NSMutableDictionary dictionary]; _pendingSends = [NSMutableDictionary dictionary];
} }
@ -104,7 +105,7 @@ NSString *const RCTTCPErrorDomain = @"RCTTCPErrorDomain";
- (void)end - (void)end
{ {
[_tcpSocket disconnectAfterReadingAndWriting]; [_tcpSocket disconnectAfterWriting];
} }
- (void)destroy - (void)destroy
@ -121,6 +122,9 @@ NSString *const RCTTCPErrorDomain = @"RCTTCPErrorDomain";
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port - (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port
{ {
if (!_clientDelegate) return;
[_clientDelegate onConnect:self];
[sock readDataWithTimeout:-1 tag:-1]; [sock readDataWithTimeout:-1 tag:-1];
} }

View File

@ -54,7 +54,7 @@ RCT_EXPORT_METHOD(createSocket:(nonnull NSNumber*)cId)
return; return;
} }
client = [TcpSocketClient socketClientWithConfig:self]; client = [TcpSocketClient socketClientWithId:cId andConfig:self];
[_clients setObject:client forKey:cId]; [_clients setObject:client forKey:cId];
} }
@ -75,21 +75,15 @@ RCT_EXPORT_METHOD(connect:(nonnull NSNumber*)cId
} }
RCT_EXPORT_METHOD(write:(nonnull NSNumber*)cId RCT_EXPORT_METHOD(write:(nonnull NSNumber*)cId
string:(NSString *)string string:(NSString *)base64String
encoded:(BOOL)encoded encoded:(BOOL)encoded
callback:(RCTResponseSenderBlock)callback) { callback:(RCTResponseSenderBlock)callback) {
TcpSocketClient* client = [TcpSockets findClient:cId callback:callback]; TcpSocketClient* client = [TcpSockets findClient:cId callback:callback];
if (!client) return; if (!client) return;
NSData *data;
if (encoded) {
// iOS7+ // iOS7+
// TODO: use https://github.com/nicklockwood/Base64 for compatibility with earlier iOS versions // TODO: use https://github.com/nicklockwood/Base64 for compatibility with earlier iOS versions
data = [[NSData alloc] initWithBase64EncodedString:string options:0]; NSData *data = [[NSData alloc] initWithBase64EncodedString:base64String options:0];
} else {
data = [string dataUsingEncoding:[NSString defaultCStringEncoding]];
}
[client writeData:data callback:callback]; [client writeData:data callback:callback];
} }
@ -98,15 +92,14 @@ RCT_EXPORT_METHOD(end:(nonnull NSNumber*)cId
[TcpSockets endClient:cId callback:callback]; [TcpSockets endClient:cId callback:callback];
} }
RCT_EXPORT_METHOD(destroy:(nonnull NSNumber*)cId) { RCT_EXPORT_METHOD(destroy:(nonnull NSNumber*)cId
[TcpSockets destroyClient:cId]; callback:(RCTResponseSenderBlock)callback) {
[TcpSockets destroyClient:cId callback:callback];
} }
- (void) onConnect:(TcpSocketClient*) client - (void) onConnect:(TcpSocketClient*) client
{ {
NSMutableDictionary<NSNumber *,TcpSocketClient *> *_clients = [TcpSockets clients]; [self.bridge.eventDispatcher sendDeviceEventWithName:[NSString stringWithFormat:@"tcp-%@-event", client.id]
NSNumber *clientID = [[_clients allKeysForObject:client] objectAtIndex:0];
[self.bridge.eventDispatcher sendDeviceEventWithName:[NSString stringWithFormat:@"tcp-%@-event", clientID]
body:@{ @"event": @"connect" }]; body:@{ @"event": @"connect" }];
} }
@ -121,21 +114,20 @@ RCT_EXPORT_METHOD(destroy:(nonnull NSNumber*)cId) {
- (void) onClose:(TcpSocketClient*) client withError:(NSError *)err - (void) onClose:(TcpSocketClient*) client withError:(NSError *)err
{ {
if (err) {
[self onError:client withError:err]; [self onError:client withError:err];
}
[self.bridge.eventDispatcher sendDeviceEventWithName:[NSString stringWithFormat:@"tcp-%@-event", client.id]
body:@{ @"event": @"close", @"data": err == nil ? @NO : @YES }];
NSMutableDictionary<NSNumber *,TcpSocketClient *> *_clients = [TcpSockets clients]; NSMutableDictionary<NSNumber *,TcpSocketClient *> *_clients = [TcpSockets clients];
NSNumber *clientID = [[_clients allKeysForObject:client] objectAtIndex:0]; [_clients removeObjectForKey:client.id];
[self.bridge.eventDispatcher sendDeviceEventWithName:[NSString stringWithFormat:@"tcp-%@-event", clientID]
body:@{ @"event": @"close", @"data": err == nil ? @NO : @YES }];
} }
- (void)onError:(TcpSocketClient*) client withError:(NSError *)err { - (void)onError:(TcpSocketClient*) client withError:(NSError *)err {
NSMutableDictionary<NSNumber *,TcpSocketClient *> *_clients = [TcpSockets clients];
NSNumber *clientID = [[_clients allKeysForObject:client] objectAtIndex:0];
NSString* msg = [[err userInfo] valueForKey:@"NSLocalizedFailureReason"]; NSString* msg = [[err userInfo] valueForKey:@"NSLocalizedFailureReason"];
[self.bridge.eventDispatcher sendDeviceEventWithName:[NSString stringWithFormat:@"tcp-%@-event", clientID] [self.bridge.eventDispatcher sendDeviceEventWithName:[NSString stringWithFormat:@"tcp-%@-event", client.id]
body:@{ @"event": @"error", @"data": @[msg] }]; body:@{ @"event": @"error", @"data": @[msg] }];
} }
@ -147,8 +139,7 @@ RCT_EXPORT_METHOD(destroy:(nonnull NSNumber*)cId) {
if (!client) { if (!client) {
if (!callback) { if (!callback) {
RCTLogError(@"%@.missing callback parameter.", [self class]); RCTLogError(@"%@.missing callback parameter.", [self class]);
} } else {
else {
callback(@[[NSString stringWithFormat:@"no client found with id %@", cId]]); callback(@[[NSString stringWithFormat:@"no client found with id %@", cId]]);
} }
@ -161,17 +152,16 @@ RCT_EXPORT_METHOD(destroy:(nonnull NSNumber*)cId) {
+(void) endClient:(nonnull NSNumber*)cId +(void) endClient:(nonnull NSNumber*)cId
callback:(RCTResponseSenderBlock)callback callback:(RCTResponseSenderBlock)callback
{ {
NSMutableDictionary<NSNumber *,TcpSocketClient *> *_clients = [TcpSockets clients];
TcpSocketClient* client = [TcpSockets findClient:cId callback:callback]; TcpSocketClient* client = [TcpSockets findClient:cId callback:callback];
if (!client) return; if (!client) return;
[client end]; [client end];
[_clients removeObjectForKey:cId];
if (callback) callback(@[]); if (callback) callback(@[]);
} }
+(void) destroyClient:(nonnull NSNumber*)cId +(void) destroyClient:(nonnull NSNumber*)cId
callback:(RCTResponseSenderBlock)callback
{ {
NSMutableDictionary<NSNumber *,TcpSocketClient *> *_clients = [TcpSockets clients]; NSMutableDictionary<NSNumber *,TcpSocketClient *> *_clients = [TcpSockets clients];
TcpSocketClient* client = [TcpSockets findClient:cId callback:nil]; TcpSocketClient* client = [TcpSockets findClient:cId callback:nil];

View File

@ -105,7 +105,7 @@
58B511D31A9E6C8500147676 /* Project object */ = { 58B511D31A9E6C8500147676 /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastUpgradeCheck = 0610; LastUpgradeCheck = 0720;
ORGANIZATIONNAME = "Tradle, Inc."; ORGANIZATIONNAME = "Tradle, Inc.";
TargetAttributes = { TargetAttributes = {
58B511DA1A9E6C8500147676 = { 58B511DA1A9E6C8500147676 = {
@ -179,6 +179,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
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;

View File

@ -1,6 +1,6 @@
{ {
"name": "react-native-tcp", "name": "react-native-tcp",
"version": "1.1.1", "version": "0.0.1",
"description": "node's dgram API for react-native", "description": "node's dgram API for react-native",
"main": "TcpSockets.js", "main": "TcpSockets.js",
"scripts": { "scripts": {