From 3572684dbaae9a5caa971b30aaa26f497622ab79 Mon Sep 17 00:00:00 2001 From: Kevin Mershon Date: Thu, 23 Apr 2015 19:38:23 -0700 Subject: [PATCH 01/12] Read 512 bytes at a chunk directly instead of reading strings --- src/android/Connection.java | 12 ++++++------ src/android/SocketPlugin.java | 28 +++++++++++++++------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/android/Connection.java b/src/android/Connection.java index e7457e9..b9bf683 100644 --- a/src/android/Connection.java +++ b/src/android/Connection.java @@ -118,7 +118,7 @@ public void writeBinary(byte[] data) throws IOException { * @see java.lang.Thread#run() */ public void run() { - String chunk = null; + byte[] chunk = new byte[512]; // creating connection try { @@ -133,12 +133,12 @@ public void run() { try { if (this.isConnected()) { - chunk = reader.readLine(); + int bytesRead = callbackSocket.getInputStream().read(chunk); + byte[] line = new byte[bytesRead]; + System.arraycopy(chunk, 0, line, 0, bytesRead); - if (chunk != null) { - chunk = chunk.replaceAll("\"\"", "null"); - System.out.print("## RECEIVED DATA: " + chunk); - hook.sendMessage(this.host, this.port, chunk); + if (bytesRead > 0) { + hook.sendMessage(this.host, this.port, line); } } } catch (Exception e) { diff --git a/src/android/SocketPlugin.java b/src/android/SocketPlugin.java index e9b9caa..94551f8 100644 --- a/src/android/SocketPlugin.java +++ b/src/android/SocketPlugin.java @@ -2,6 +2,8 @@ import android.annotation.SuppressLint; +import android.util.Base64; + import java.io.IOException; import java.util.HashMap; @@ -293,55 +295,55 @@ private void disconnect (JSONArray args, CallbackContext callbackContext) { } catch (JSONException e) { callbackContext.error("Invalid parameters for 'connect' action:" + e.getMessage()); } - } + } } /** * Closes all existing connections - * + * * @param callbackContext */ private void disconnectAll (CallbackContext callbackContext) { // building iterator Iterator> it = this.pool.entrySet().iterator(); - + while( it.hasNext() ) { - + // retrieving object Map.Entry pairs = (Entry) it.next(); Connection socket = pairs.getValue(); - + // checking connection if (socket.isConnected()) { socket.close(); } - + // removing from pool this.pool.remove(pairs.getKey()); } - + callbackContext.success("All connections were closed."); } /** * Callback for Connection object data receive. Relay information to javascript object method: window.tlantic.plugins.socket.receive(); - * + * * @param host * @param port * @param chunk */ - public synchronized void sendMessage(String host, int port, String chunk) { - final String receiveHook = "window.tlantic.plugins.socket.receive(\"" + host + "\"," + port + ",\"" + this.buildKey(host, port) + "\",\"" + chunk.replace("\"", "\\\"") + "\");"; - + public synchronized void sendMessage(String host, int port, byte[] chunk) { + final String receiveHook = "window.tlantic.plugins.socket.receive(\"" + host + "\"," + port + ",\"" + this.buildKey(host, port) + "\",\"" + Base64.encodeToString(chunk, Base64.URL_SAFE) + "\");"; + cordova.getActivity().runOnUiThread(new Runnable() { @Override public void run() { webView.loadUrl("javascript:" + receiveHook); } - + }); } - + } From 763544ed68e9e08bb9850b189e1e4ade958bbe09 Mon Sep 17 00:00:00 2001 From: Kevin Mershon Date: Sun, 26 Apr 2015 23:49:48 -0700 Subject: [PATCH 02/12] Use default flags for base64 encoding --- src/android/SocketPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/android/SocketPlugin.java b/src/android/SocketPlugin.java index 94551f8..8a25a25 100644 --- a/src/android/SocketPlugin.java +++ b/src/android/SocketPlugin.java @@ -334,7 +334,7 @@ private void disconnectAll (CallbackContext callbackContext) { * @param chunk */ public synchronized void sendMessage(String host, int port, byte[] chunk) { - final String receiveHook = "window.tlantic.plugins.socket.receive(\"" + host + "\"," + port + ",\"" + this.buildKey(host, port) + "\",\"" + Base64.encodeToString(chunk, Base64.URL_SAFE) + "\");"; + final String receiveHook = "window.tlantic.plugins.socket.receive(\"" + host + "\"," + port + ",\"" + this.buildKey(host, port) + "\",\"" + Base64.encodeToString(chunk, Base64.DEFAULT) + "\");"; cordova.getActivity().runOnUiThread(new Runnable() { From beee81519b89287fb3d73e19eb9688ecf5e4aff4 Mon Sep 17 00:00:00 2001 From: Kevin Mershon Date: Tue, 12 May 2015 22:52:31 -0700 Subject: [PATCH 03/12] Use atob to decode base64 string --- src/android/Connection.java | 275 ++++++++------- src/android/SocketPlugin.java | 638 +++++++++++++++++----------------- www/socket.js | 4 +- 3 files changed, 458 insertions(+), 459 deletions(-) diff --git a/src/android/Connection.java b/src/android/Connection.java index b9bf683..d8015da 100644 --- a/src/android/Connection.java +++ b/src/android/Connection.java @@ -12,148 +12,147 @@ /** * @author viniciusl * - * This class represents a socket connection, behaving like a thread to listen + * This class represents a socket connection, behaving like a thread to listen * a TCP port and receive data */ public class Connection extends Thread { - private SocketPlugin hook; - - private Socket callbackSocket; - private PrintWriter writer; - private OutputStream outputStream; - private BufferedReader reader; - - private Boolean mustClose; - private String host; - private int port; - - - /** - * Creates a TCP socket connection object. - * - * @param pool Object containing "sendMessage" method to be called as a callback for data receive. - * @param host Target host for socket connection. - * @param port Target port for socket connection - */ - public Connection(SocketPlugin pool, String host, int port) { - super(); - setDaemon(true); - - this.mustClose = false; - this.host = host; - this.port = port; - this.hook = pool; - } - - - /** - * Returns socket connection state. - * - * @return true if socket connection is established or false case else. - */ - public boolean isConnected() { - - boolean result = ( - this.callbackSocket == null ? false : - this.callbackSocket.isConnected() && - this.callbackSocket.isBound() && - !this.callbackSocket.isClosed() && - !this.callbackSocket.isInputShutdown() && - !this.callbackSocket.isOutputShutdown()); - - // if everything apparently is fine, time to test the streams - if (result) { - try { - this.callbackSocket.getInputStream().available(); - } catch (IOException e) { - // connection lost - result = false; - } - } - - return result; - } - - /** - * Closes socket connection. - */ - public void close() { - // closing connection - try { - //this.writer.close(); - //this.reader.close(); - callbackSocket.shutdownInput(); - callbackSocket.shutdownOutput(); - callbackSocket.close(); - this.mustClose = true; - } catch (IOException e) { - e.printStackTrace(); - } - } - - - /** - * Writes on socket output stream to send data to target host. - * - * @param data information to be sent - */ - public void write(String data) { - this.writer.println(data); - } - - - - /** - * Outputs to socket output stream to send binary data to target host. - * - * @param data information to be sent - */ - public void writeBinary(byte[] data) throws IOException { - this.outputStream.write(data); - } - - - - /* (non-Javadoc) - * @see java.lang.Thread#run() - */ - public void run() { + private SocketPlugin hook; + + private Socket callbackSocket; + private PrintWriter writer; + private OutputStream outputStream; + private BufferedReader reader; + + private Boolean mustClose; + private String host; + private int port; + + + /** + * Creates a TCP socket connection object. + * + * @param pool Object containing "sendMessage" method to be called as a callback for data receive. + * @param host Target host for socket connection. + * @param port Target port for socket connection + */ + public Connection(SocketPlugin pool, String host, int port) { + super(); + setDaemon(true); + + this.mustClose = false; + this.host = host; + this.port = port; + this.hook = pool; + } + + + /** + * Returns socket connection state. + * + * @return true if socket connection is established or false case else. + */ + public boolean isConnected() { + + boolean result = ( + this.callbackSocket == null ? false : + this.callbackSocket.isConnected() && + this.callbackSocket.isBound() && + !this.callbackSocket.isClosed() && + !this.callbackSocket.isInputShutdown() && + !this.callbackSocket.isOutputShutdown()); + + // if everything apparently is fine, time to test the streams + if (result) { + try { + this.callbackSocket.getInputStream().available(); + } catch (IOException e) { + // connection lost + result = false; + } + } + + return result; + } + + /** + * Closes socket connection. + */ + public void close() { + // closing connection + try { + //this.writer.close(); + //this.reader.close(); + callbackSocket.shutdownInput(); + callbackSocket.shutdownOutput(); + callbackSocket.close(); + this.mustClose = true; + } catch (IOException e) { + e.printStackTrace(); + } + } + + + /** + * Writes on socket output stream to send data to target host. + * + * @param data information to be sent + */ + public void write(String data) { + this.writer.println(data); + } + + + + /** + * Outputs to socket output stream to send binary data to target host. + * + * @param data information to be sent + */ + public void writeBinary(byte[] data) throws IOException { + this.outputStream.write(data); + } + + + /* (non-Javadoc) + * @see java.lang.Thread#run() + */ + public void run() { byte[] chunk = new byte[512]; - // creating connection - try { - this.callbackSocket = new Socket(this.host, this.port); - this.writer = new PrintWriter(this.callbackSocket.getOutputStream(), true); - this.outputStream = this.callbackSocket.getOutputStream(); - this.reader = new BufferedReader(new InputStreamReader(callbackSocket.getInputStream())); - - // receiving data chunk - while(!this.mustClose){ - - try { - - if (this.isConnected()) { - int bytesRead = callbackSocket.getInputStream().read(chunk); - byte[] line = new byte[bytesRead]; - System.arraycopy(chunk, 0, line, 0, bytesRead); - - if (bytesRead > 0) { - hook.sendMessage(this.host, this.port, line); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - } catch (UnknownHostException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - } catch (IOException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - } - - } + // creating connection + try { + this.callbackSocket = new Socket(this.host, this.port); + this.writer = new PrintWriter(this.callbackSocket.getOutputStream(), true); + this.outputStream = this.callbackSocket.getOutputStream(); + this.reader = new BufferedReader(new InputStreamReader(callbackSocket.getInputStream())); + + // receiving data chunk + while(!this.mustClose){ + + try { + + if (this.isConnected()) { + int bytesRead = callbackSocket.getInputStream().read(chunk); + byte[] line = new byte[bytesRead]; + System.arraycopy(chunk, 0, line, 0, bytesRead); + + if (bytesRead > 0) { + hook.sendMessage(this.host, this.port, line); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + } catch (UnknownHostException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } catch (IOException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + + } } diff --git a/src/android/SocketPlugin.java b/src/android/SocketPlugin.java index 8a25a25..f88ad7c 100644 --- a/src/android/SocketPlugin.java +++ b/src/android/SocketPlugin.java @@ -19,7 +19,7 @@ /** * @author viniciusl * - * Plugin to handle TCP socket connections. + * Plugin to handle TCP socket connections. */ /** * @author viniciusl @@ -27,323 +27,323 @@ */ public class SocketPlugin extends CordovaPlugin { - private Map pool = new HashMap(); // pool of "active" connections - - /* (non-Javadoc) - * @see org.apache.cordova.CordovaPlugin#execute(java.lang.String, org.json.JSONArray, org.apache.cordova.CallbackContext) - */ - @Override - public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { - - if (action.equals("connect")) { - this.connect(args, callbackContext); - return true; - - }else if(action.equals("isConnected")) { - this.isConnected(args, callbackContext); - return true; - - }else if(action.equals("send")) { - this.send(args, callbackContext); - return true; - - }else if(action.equals("sendBinary")) { - this.sendBinary(args, callbackContext); - return true; - - } else if (action.equals("disconnect")) { - this.disconnect(args, callbackContext); - return true; - - } else if (action.equals("disconnectAll")) { - this.disconnectAll(callbackContext); - return true; - - } else { - return false; - } - } - - /** - * Build a key to identify a socket connection based on host and port information. - * - * @param host Target host - * @param port Target port - * @return connection key - */ - @SuppressLint("DefaultLocale") - private String buildKey(String host, int port) { - return (host.toLowerCase() + ":" + port); - } - - /** - * Opens a socket connection. - * - * @param args - * @param callbackContext - */ - private void connect (JSONArray args, CallbackContext callbackContext) { - String key; - String host; - int port; - Connection socket; - - // validating parameters - if (args.length() < 2) { - callbackContext.error("Missing arguments when calling 'connect' action."); - } else { - - // opening connection and adding into pool - try { - - // preparing parameters - host = args.getString(0); - port = args.getInt(1); - key = this.buildKey(host, port); - - // creating connection - if (this.pool.get(key) == null) { - socket = new Connection(this, host, port); - socket.start(); - this.pool.put(key, socket); - } - - // adding to pool - callbackContext.success(key); - - } catch (JSONException e) { - callbackContext.error("Invalid parameters for 'connect' action: " + e.getMessage()); - } - } - } - - /** - * Returns connection information - * - * @param args - * @param callbackContext - */ - private void isConnected(JSONArray args, CallbackContext callbackContext) { - Connection socket; - - // validating parameters - if (args.length() < 1) { - callbackContext.error("Missing arguments when calling 'isConnected' action."); - } else { - try { - // retrieving parameters - String key = args.getString(0); - - // getting socket - socket = this.pool.get(key); - - // checking if socket was not found and his connectivity - if (socket == null) { - callbackContext.error("No connection found with host " + key); - - } else { - - // ending send process - callbackContext.success( (socket.isConnected() ? 1 : 0) ); - } - - } catch (JSONException e) { - callbackContext.error("Unexpected error sending information: " + e.getMessage()); - } - } - } - - - /** - * Send information to target host - * - * @param args - * @param callbackContext - */ - private void send(JSONArray args, CallbackContext callbackContext) { - Connection socket; - - // validating parameters - if (args.length() < 2) { - callbackContext.error("Missing arguments when calling 'send' action."); - } else { - try { - // retrieving parameters - String key = args.getString(0); - String data = args.getString(1); - - // getting socket - socket = this.pool.get(key); - - // checking if socket was not found and his connectivity - if (socket == null) { - callbackContext.error("No connection found with host " + key); - - } else if (!socket.isConnected()) { - callbackContext.error("Invalid connection with host " + key); - - } else if (data.length() == 0) { - callbackContext.error("Cannot send empty data to " + key); - - } else { - - // write on output stream - socket.write(data); - - // ending send process - callbackContext.success(); - } - - } catch (JSONException e) { - callbackContext.error("Unexpected error sending information: " + e.getMessage()); - } - } - } - - - /** - * Send binary information to target host - * - * @param args - * @param callbackContext - */ - private void sendBinary(JSONArray args, CallbackContext callbackContext) { - Connection socket; - - // validating parameters - if (args.length() < 2) { - callbackContext.error("Missing arguments when calling 'sendBinary' action."); - } else { - try { - // retrieving parameters - String key = args.getString(0); - JSONArray jsData = args.getJSONArray(1); - byte[] data = new byte[jsData.length()]; - for (int i=0; i> it = this.pool.entrySet().iterator(); - - while( it.hasNext() ) { - - // retrieving object - Map.Entry pairs = (Entry) it.next(); - Connection socket = pairs.getValue(); - - // checking connection - if (socket.isConnected()) { - socket.close(); - } - - // removing from pool - this.pool.remove(pairs.getKey()); - } - - callbackContext.success("All connections were closed."); - } - - - /** - * Callback for Connection object data receive. Relay information to javascript object method: window.tlantic.plugins.socket.receive(); - * - * @param host - * @param port - * @param chunk - */ - public synchronized void sendMessage(String host, int port, byte[] chunk) { - final String receiveHook = "window.tlantic.plugins.socket.receive(\"" + host + "\"," + port + ",\"" + this.buildKey(host, port) + "\",\"" + Base64.encodeToString(chunk, Base64.DEFAULT) + "\");"; - - cordova.getActivity().runOnUiThread(new Runnable() { - - @Override - public void run() { - webView.loadUrl("javascript:" + receiveHook); - } - - }); - } + private Map pool = new HashMap(); // pool of "active" connections + + /* (non-Javadoc) + * @see org.apache.cordova.CordovaPlugin#execute(java.lang.String, org.json.JSONArray, org.apache.cordova.CallbackContext) + */ + @Override + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { + + if (action.equals("connect")) { + this.connect(args, callbackContext); + return true; + + }else if(action.equals("isConnected")) { + this.isConnected(args, callbackContext); + return true; + + }else if(action.equals("send")) { + this.send(args, callbackContext); + return true; + + }else if(action.equals("sendBinary")) { + this.sendBinary(args, callbackContext); + return true; + + } else if (action.equals("disconnect")) { + this.disconnect(args, callbackContext); + return true; + + } else if (action.equals("disconnectAll")) { + this.disconnectAll(callbackContext); + return true; + + } else { + return false; + } + } + + /** + * Build a key to identify a socket connection based on host and port information. + * + * @param host Target host + * @param port Target port + * @return connection key + */ + @SuppressLint("DefaultLocale") + private String buildKey(String host, int port) { + return (host.toLowerCase() + ":" + port); + } + + /** + * Opens a socket connection. + * + * @param args + * @param callbackContext + */ + private void connect (JSONArray args, CallbackContext callbackContext) { + String key; + String host; + int port; + Connection socket; + + // validating parameters + if (args.length() < 2) { + callbackContext.error("Missing arguments when calling 'connect' action."); + } else { + + // opening connection and adding into pool + try { + + // preparing parameters + host = args.getString(0); + port = args.getInt(1); + key = this.buildKey(host, port); + + // creating connection + if (this.pool.get(key) == null) { + socket = new Connection(this, host, port); + socket.start(); + this.pool.put(key, socket); + } + + // adding to pool + callbackContext.success(key); + + } catch (JSONException e) { + callbackContext.error("Invalid parameters for 'connect' action: " + e.getMessage()); + } + } + } + + /** + * Returns connection information + * + * @param args + * @param callbackContext + */ + private void isConnected(JSONArray args, CallbackContext callbackContext) { + Connection socket; + + // validating parameters + if (args.length() < 1) { + callbackContext.error("Missing arguments when calling 'isConnected' action."); + } else { + try { + // retrieving parameters + String key = args.getString(0); + + // getting socket + socket = this.pool.get(key); + + // checking if socket was not found and his connectivity + if (socket == null) { + callbackContext.error("No connection found with host " + key); + + } else { + + // ending send process + callbackContext.success( (socket.isConnected() ? 1 : 0) ); + } + + } catch (JSONException e) { + callbackContext.error("Unexpected error sending information: " + e.getMessage()); + } + } + } + + + /** + * Send information to target host + * + * @param args + * @param callbackContext + */ + private void send(JSONArray args, CallbackContext callbackContext) { + Connection socket; + + // validating parameters + if (args.length() < 2) { + callbackContext.error("Missing arguments when calling 'send' action."); + } else { + try { + // retrieving parameters + String key = args.getString(0); + String data = args.getString(1); + + // getting socket + socket = this.pool.get(key); + + // checking if socket was not found and his connectivity + if (socket == null) { + callbackContext.error("No connection found with host " + key); + + } else if (!socket.isConnected()) { + callbackContext.error("Invalid connection with host " + key); + + } else if (data.length() == 0) { + callbackContext.error("Cannot send empty data to " + key); + + } else { + + // write on output stream + socket.write(data); + + // ending send process + callbackContext.success(); + } + + } catch (JSONException e) { + callbackContext.error("Unexpected error sending information: " + e.getMessage()); + } + } + } + + + /** + * Send binary information to target host + * + * @param args + * @param callbackContext + */ + private void sendBinary(JSONArray args, CallbackContext callbackContext) { + Connection socket; + + // validating parameters + if (args.length() < 2) { + callbackContext.error("Missing arguments when calling 'sendBinary' action."); + } else { + try { + // retrieving parameters + String key = args.getString(0); + JSONArray jsData = args.getJSONArray(1); + byte[] data = new byte[jsData.length()]; + for (int i=0; i> it = this.pool.entrySet().iterator(); + + while( it.hasNext() ) { + + // retrieving object + Map.Entry pairs = (Entry) it.next(); + Connection socket = pairs.getValue(); + + // checking connection + if (socket.isConnected()) { + socket.close(); + } + + // removing from pool + this.pool.remove(pairs.getKey()); + } + + callbackContext.success("All connections were closed."); + } + + + /** + * Callback for Connection object data receive. Relay information to javascript object method: window.tlantic.plugins.socket.receive(); + * + * @param host + * @param port + * @param chunk + */ + public synchronized void sendMessage(String host, int port, byte[] chunk) { + final String receiveHook = "window.tlantic.plugins.socket.receive(\"" + host + "\"," + port + ",\"" + this.buildKey(host, port) + "\",window.atob(\"" + Base64.encodeToString(chunk, Base64.DEFAULT) + ")\");"; + + cordova.getActivity().runOnUiThread(new Runnable() { + + @Override + public void run() { + webView.loadUrl("javascript:" + receiveHook); + } + + }); + } } diff --git a/www/socket.js b/www/socket.js index f2340de..ceeadc5 100644 --- a/www/socket.js +++ b/www/socket.js @@ -26,7 +26,7 @@ Socket.prototype.disconnectAll = function (successCallback, errorCallback) { 'use strict'; exec(successCallback, errorCallback, this.pluginRef, 'disconnectAll', []); }; - + // Socket.prototype.isConnected = function (connectionId, successCallback, errorCallback) { 'use strict'; @@ -50,7 +50,7 @@ Socket.prototype.receive = function (host, port, connectionId, chunk) { 'use strict'; var evReceive = document.createEvent('Events'); - + evReceive.initEvent(this.receiveHookName, true, true); evReceive.metadata = { connection: { From 74c69a77dcafad3973ba4797f4a4340975bf51b9 Mon Sep 17 00:00:00 2001 From: Kevin Mershon Date: Tue, 12 May 2015 22:59:14 -0700 Subject: [PATCH 04/12] Fix typo --- src/android/SocketPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/android/SocketPlugin.java b/src/android/SocketPlugin.java index f88ad7c..e50e52e 100644 --- a/src/android/SocketPlugin.java +++ b/src/android/SocketPlugin.java @@ -334,7 +334,7 @@ private void disconnectAll (CallbackContext callbackContext) { * @param chunk */ public synchronized void sendMessage(String host, int port, byte[] chunk) { - final String receiveHook = "window.tlantic.plugins.socket.receive(\"" + host + "\"," + port + ",\"" + this.buildKey(host, port) + "\",window.atob(\"" + Base64.encodeToString(chunk, Base64.DEFAULT) + ")\");"; + final String receiveHook = "window.tlantic.plugins.socket.receive(\"" + host + "\"," + port + ",\"" + this.buildKey(host, port) + "\",window.atob(\"" + Base64.encodeToString(chunk, Base64.DEFAULT) + "\"));"; cordova.getActivity().runOnUiThread(new Runnable() { From cc9df4dbfa7ef608d24d398dbd1e707ed2e2aa3b Mon Sep 17 00:00:00 2001 From: Kevin Mershon Date: Tue, 12 May 2015 23:53:20 -0700 Subject: [PATCH 05/12] Implement writing/reading binary --- src/ios/CDVSocketPlugin.m | 116 ++++++++++++++++++-------------------- src/ios/Connection.h | 1 + src/ios/Connection.m | 50 ++++++++-------- 3 files changed, 82 insertions(+), 85 deletions(-) diff --git a/src/ios/CDVSocketPlugin.m b/src/ios/CDVSocketPlugin.m index 1dd6e72..8339cec 100644 --- a/src/ios/CDVSocketPlugin.m +++ b/src/ios/CDVSocketPlugin.m @@ -7,19 +7,19 @@ @implementation CDVSocketPlugin : CDVPlugin - (NSString*) buildKey : (NSString*) host : (int) port { NSString* tempHost = [host lowercaseString]; NSString* tempPort = [NSString stringWithFormat : @"%d", port]; - + return [[tempHost stringByAppendingString : @":"] stringByAppendingString:tempPort]; } - (void) connect : (CDVInvokedUrlCommand*) command { // Validating parameters if ([command.arguments count] < 2) { - + // Triggering parameter error CDVPluginResult* result = [CDVPluginResult resultWithStatus : CDVCommandStatus_ERROR messageAsString : @"Missing arguments when calling 'connect' action."]; - + [self.commandDelegate sendPluginResult : result callbackId : command.callbackId @@ -29,23 +29,23 @@ - (void) connect : (CDVInvokedUrlCommand*) command { if (!pool) { self->pool = [[NSMutableDictionary alloc] init]; } - + // Running in background to avoid thread locks [self.commandDelegate runInBackground:^{ - + CDVPluginResult* result = nil; Connection* socket = nil; NSString* key = nil; NSString* host = nil; int port = 0; - + // Opening connection and adding into pool @try { // Preparing parameters host = [command.arguments objectAtIndex : 0]; port = [[command.arguments objectAtIndex : 1] integerValue]; key = [self buildKey : host : port]; - + // Checking existing connections if ([pool objectForKey : key]) { NSLog(@"Recovered connection with %@", key); @@ -59,10 +59,10 @@ - (void) connect : (CDVInvokedUrlCommand*) command { socket = [[Connection alloc] initWithNetworkAddress:host :port]; [socket setDelegate:self]; [socket open]; - + // Adding to pool [self->pool setObject:socket forKey:key]; - + // Formatting success response result = [CDVPluginResult resultWithStatus : @@ -75,7 +75,7 @@ - (void) connect : (CDVInvokedUrlCommand*) command { resultWithStatus : CDVCommandStatus_ERROR messageAsString : @"Unexpected exception when executing 'connect' action."]; } - + // Returns the Callback Resolution [self.commandDelegate sendPluginResult : result callbackId : command.callbackId]; @@ -86,33 +86,33 @@ - (void) connect : (CDVInvokedUrlCommand*) command { - (void) isConnected : (CDVInvokedUrlCommand *) command { // Validating parameters if ([command.arguments count] < 1) { - + // Triggering parameter error CDVPluginResult* result = [CDVPluginResult resultWithStatus : CDVCommandStatus_ERROR messageAsString : @"Missing arguments when calling 'isConnected' action."]; - + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId ]; - + } else { - + // running in background to avoid thread locks [self.commandDelegate runInBackground : ^{ - + CDVPluginResult* result= nil; Connection* socket = nil; NSString* key = nil; - + @try { // Preparing parameters key = [command.arguments objectAtIndex:0]; - + // Getting connection from pool socket = [pool objectForKey:key]; - + // Checking if socket was not found and his conenctivity if (socket == nil) { NSLog(@"Connection not found"); @@ -121,7 +121,7 @@ - (void) isConnected : (CDVInvokedUrlCommand *) command { messageAsString : @"No connection found with host."]; } else { NSLog(@"Checking data connection..."); - + // Formatting success response result = [CDVPluginResult resultWithStatus : CDVCommandStatus_OK @@ -134,7 +134,7 @@ - (void) isConnected : (CDVInvokedUrlCommand *) command { resultWithStatus : CDVCommandStatus_ERROR messageAsString : @"Unexpected exception when executon 'isConnected' action."]; } - + // Returning callback resolution [self.commandDelegate sendPluginResult : result @@ -147,24 +147,24 @@ - (void) isConnected : (CDVInvokedUrlCommand *) command { - (BOOL) disposeConnection : (NSString *) key { Connection* socket = nil; BOOL result = NO; - + @try { // Getting connection from pool socket = [pool objectForKey : key]; - + // Closing connection if (socket) { [pool removeObjectForKey : key]; - + if ([socket isConnected]) [socket close]; - + socket = nil; - + NSLog(@"Closed connection with %@", key); } else NSLog(@"Connection %@ already closed!", key); - + // Setting success result = YES; } @@ -184,15 +184,15 @@ - (void) disconnect : (CDVInvokedUrlCommand*) command { CDVPluginResult* result = [CDVPluginResult resultWithStatus : CDVCommandStatus_ERROR messageAsString : @"Missing arguments when calling 'disconnect' action."]; - + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; } else { // Running in background to avoid thread locks [self.commandDelegate runInBackground : ^{ - + CDVPluginResult* result= nil; NSString *key = nil; - + @try { // Preparing parameters key = [command.arguments objectAtIndex : 0]; @@ -211,7 +211,7 @@ - (void) disconnect : (CDVInvokedUrlCommand*) command { resultWithStatus : CDVCommandStatus_ERROR messageAsString : @"Unexpected exception when executing 'disconnect' action."]; } - + // Returns the Callback Resolution [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; }]; @@ -221,24 +221,24 @@ - (void) disconnect : (CDVInvokedUrlCommand*) command { - (void) disconnectAll: (CDVInvokedUrlCommand *) command { // Running in background to avoid thread locks [self.commandDelegate runInBackground:^{ - + CDVPluginResult* result = nil; Connection * socket = nil; BOOL partial = NO; - + @try { - + // Iterating connection pool for (id key in pool) { socket = [pool objectForKey : key]; - + // Try to close it if (![self disposeConnection : key]) { // If no success, need to set as partial disconnection partial = YES; } } - + // Formatting result if (partial) result = [CDVPluginResult @@ -263,43 +263,43 @@ - (void) disconnectAll: (CDVInvokedUrlCommand *) command { } - (void) send: (CDVInvokedUrlCommand *) command { - + // Validating parameters if ([command.arguments count] < 2) { // Triggering parameter error CDVPluginResult* result = [CDVPluginResult resultWithStatus : CDVCommandStatus_ERROR messageAsString : @"Missing arguments when calling 'send' action."]; - + [self.commandDelegate sendPluginResult : result callbackId:command.callbackId ]; - + } else { - + // Running in background to avoid thread locks [self.commandDelegate runInBackground : ^{ - + CDVPluginResult* result= nil; Connection* socket = nil; NSString* data = nil; NSString* key = nil; - + @try { // Preparing parameters key = [command.arguments objectAtIndex : 0]; - + // Getting connection from pool socket = [pool objectForKey : key]; - + // Checking if socket was not found and his conenctivity if (socket == nil) { NSLog(@"Connection not found"); result = [CDVPluginResult resultWithStatus : CDVCommandStatus_ERROR messageAsString : @"No connection found with host."]; - + } else if (![socket isConnected]) { NSLog(@"Socket is not connected."); result = [CDVPluginResult @@ -308,11 +308,11 @@ - (void) send: (CDVInvokedUrlCommand *) command { } else { // Writting on output stream data = [command.arguments objectAtIndex : 1]; - + NSLog(@"Sending data to %@ - %@", key, data); - + [socket write:data]; - + // Formatting success response result = [CDVPluginResult resultWithStatus : CDVCommandStatus_OK @@ -325,27 +325,21 @@ - (void) send: (CDVInvokedUrlCommand *) command { resultWithStatus : CDVCommandStatus_ERROR messageAsString : @"Unexpected exception when executon 'send' action."]; } - + // Returning callback resolution [self.commandDelegate sendPluginResult : result callbackId : command.callbackId]; }]; } } -- (void) sendMessage :(NSString *)host :(int)port :(NSString *)chunk { - - // Handling escape chars - NSMutableString *data = [NSMutableString stringWithString : chunk]; - [data replaceOccurrencesOfString : @"\n" - withString : @"\\n" - options : NSCaseInsensitiveSearch - range : NSMakeRange(0, [data length])]; - +- (void) sendMessage :(NSString *)host :(int)port :(NSData *)chunk { + NSString *base64Encoded = [nsdata base64EncodedStringWithOptions:0]; + // Relay to webview - NSString *receiveHook = [NSString stringWithFormat : @"window.tlantic.plugins.socket.receive('%@', %d, '%@', '%@' );", - host, port, [self buildKey : host : port], [NSString stringWithString : data]]; - + NSString *receiveHook = [NSString stringWithFormat : @"window.tlantic.plugins.socket.receive('%@', %d, '%@', window.atob('%@') );", + host, port, [self buildKey : host : port], base64Encoded]; + [self writeJavascript:receiveHook]; } -@end \ No newline at end of file +@end diff --git a/src/ios/Connection.h b/src/ios/Connection.h index 651a316..c37611e 100644 --- a/src/ios/Connection.h +++ b/src/ios/Connection.h @@ -21,6 +21,7 @@ - (void) open; - (void) close; - (void) write : (NSString*) data; +- (void) writeBinary : (NSData*) chunk; - (void) stream : (NSStream *) theStream handleEvent : (NSStreamEvent) streamEvent; diff --git a/src/ios/Connection.m b/src/ios/Connection.m index 958847c..a27e909 100644 --- a/src/ios/Connection.m +++ b/src/ios/Connection.m @@ -24,16 +24,16 @@ - (void) open { // Init network communication settings CFReadStreamRef readStream; CFWriteStreamRef writeStream; - + // Opening connection CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)_host, _port, &readStream, &writeStream); - + // Configuring input stream reader = objc_retainedObject(readStream); [reader setDelegate:self]; [reader scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; [reader open]; - + // Configuring output stream writer = objc_retainedObject(writeStream); [writer setDelegate:self]; @@ -47,7 +47,7 @@ - (void) close { [writer removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [writer setDelegate:nil]; writer = nil; - + // Closing input stream [reader close]; [reader removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; @@ -60,50 +60,52 @@ - (void) write : (NSString *) data { [writer write : [chunk bytes] maxLength : [chunk length]]; } +- (void) writeBinary : (NSData *) chunk { + [writer write : [chunk bytes] maxLength : [chunk length]]; +} + - (void) stream : (NSStream *) theStream handleEvent : (NSStreamEvent) streamEvent { - switch (streamEvent) { - case NSStreamEventOpenCompleted: - NSLog(@"Stream opened!"); + switch (streamEvent) { + case NSStreamEventOpenCompleted: + NSLog(@"Stream opened!"); connected = YES; - break; - + break; + // Data receiving - case NSStreamEventHasBytesAvailable: + case NSStreamEventHasBytesAvailable: if (theStream == reader) { - uint8_t buffer[10240]; + uint8_t buffer[512]; NSInteger len; - + while ([reader hasBytesAvailable]) { len = [reader read : buffer maxLength : sizeof(buffer)]; - + + NSData *line = [buffer subdataWithRange:NSMakeRange(0, len)]; + if (len > 0) { - - NSString *chunk = [[NSString alloc] initWithBytes : buffer - length : len - encoding : NSASCIIStringEncoding]; - + if (nil != chunk) { - NSLog(@"Received data: %@", chunk); - [_hook sendMessage : _host : _port : chunk]; + NSLog(@"Received data: %@", line); + [_hook sendMessage : _host : _port : line]; } } } } break; - + case NSStreamEventErrorOccurred: NSLog(@"Cannot connect to the host!"); connected = NO; break; - + case NSStreamEventEndEncountered: NSLog(@"Stream closed!"); connected = NO; break; - + default: NSLog(@"Unknown event!"); } } -@end \ No newline at end of file +@end From 0afcb5c270e69551c9c03acfb95aeff44cea6efa Mon Sep 17 00:00:00 2001 From: Kevin Mershon Date: Wed, 13 May 2015 00:31:19 -0700 Subject: [PATCH 06/12] Fixes --- src/ios/CDVSocketPlugin.h | 3 ++- src/ios/CDVSocketPlugin.m | 2 +- src/ios/Connection.h | 4 ++-- src/ios/Connection.m | 8 +++++--- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/ios/CDVSocketPlugin.h b/src/ios/CDVSocketPlugin.h index a9d445b..ff9003a 100644 --- a/src/ios/CDVSocketPlugin.h +++ b/src/ios/CDVSocketPlugin.h @@ -10,7 +10,8 @@ -(void) disconnectAll: (CDVInvokedUrlCommand *) command; -(void) isConnected: (CDVInvokedUrlCommand *) command; -(void) send: (CDVInvokedUrlCommand *) command; +-(void) sendBinary: (CDVInvokedUrlCommand *) command; -(BOOL) disposeConnection :(NSString *)key; -@end \ No newline at end of file +@end diff --git a/src/ios/CDVSocketPlugin.m b/src/ios/CDVSocketPlugin.m index 8339cec..70c0482 100644 --- a/src/ios/CDVSocketPlugin.m +++ b/src/ios/CDVSocketPlugin.m @@ -333,7 +333,7 @@ - (void) send: (CDVInvokedUrlCommand *) command { } - (void) sendMessage :(NSString *)host :(int)port :(NSData *)chunk { - NSString *base64Encoded = [nsdata base64EncodedStringWithOptions:0]; + NSString *base64Encoded = [chunk base64EncodedStringWithOptions:0]; // Relay to webview NSString *receiveHook = [NSString stringWithFormat : @"window.tlantic.plugins.socket.receive('%@', %d, '%@', window.atob('%@') );", diff --git a/src/ios/Connection.h b/src/ios/Connection.h index c37611e..9b5ed6f 100644 --- a/src/ios/Connection.h +++ b/src/ios/Connection.h @@ -1,6 +1,6 @@ @protocol ConnectionDelegate -- (void) sendMessage : (NSString *) host : (int)port : (NSString *) chunk; +- (void) sendMessage : (NSString *) host : (int)port : (NSData *) line; @end @@ -25,4 +25,4 @@ - (void) stream : (NSStream *) theStream handleEvent : (NSStreamEvent) streamEvent; -@end \ No newline at end of file +@end diff --git a/src/ios/Connection.m b/src/ios/Connection.m index a27e909..439ee10 100644 --- a/src/ios/Connection.m +++ b/src/ios/Connection.m @@ -74,17 +74,19 @@ - (void) stream : (NSStream *) theStream handleEvent : (NSStreamEvent) streamEve // Data receiving case NSStreamEventHasBytesAvailable: if (theStream == reader) { - uint8_t buffer[512]; + /* uint8_t buffer[512]; */ + void* buffer = malloc(512); + NSData *sharedData = [[NSData alloc] initWithBytesNoCopy:buffer length:512 freeWhenDone:YES]; NSInteger len; while ([reader hasBytesAvailable]) { len = [reader read : buffer maxLength : sizeof(buffer)]; - NSData *line = [buffer subdataWithRange:NSMakeRange(0, len)]; + NSData *line = [sharedData subdataWithRange:NSMakeRange(0, len)]; if (len > 0) { - if (nil != chunk) { + if (nil != line) { NSLog(@"Received data: %@", line); [_hook sendMessage : _host : _port : line]; } From f1e64bbad0cd71b9369735eba126b8544d07697b Mon Sep 17 00:00:00 2001 From: Kevin Mershon Date: Wed, 13 May 2015 00:43:21 -0700 Subject: [PATCH 07/12] Start implementing sendBinary --- src/ios/CDVSocketPlugin.m | 75 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/src/ios/CDVSocketPlugin.m b/src/ios/CDVSocketPlugin.m index 70c0482..8f0e0b7 100644 --- a/src/ios/CDVSocketPlugin.m +++ b/src/ios/CDVSocketPlugin.m @@ -332,6 +332,81 @@ - (void) send: (CDVInvokedUrlCommand *) command { } } +- (void) sendBinary: (CDVInvokedUrlCommand *) command { + + // Validating parameters + if ([command.arguments count] < 2) { + // Triggering parameter error + CDVPluginResult* result = [CDVPluginResult + resultWithStatus : CDVCommandStatus_ERROR + messageAsString : @"Missing arguments when calling 'sendBinary' action."]; + + [self.commandDelegate + sendPluginResult : result + callbackId:command.callbackId + ]; + + } else { + + // Running in background to avoid thread locks + [self.commandDelegate runInBackground : ^{ + + CDVPluginResult* result= nil; + Connection* socket = nil; + NSData* data = nil; + NSString* key = nil; + + @try { + // Preparing parameters + key = [command.arguments objectAtIndex : 0]; + + // Getting connection from pool + socket = [pool objectForKey : key]; + + // Checking if socket was not found and his conenctivity + if (socket == nil) { + NSLog(@"Connection not found"); + result = [CDVPluginResult + resultWithStatus : CDVCommandStatus_ERROR + messageAsString : @"No connection found with host."]; + + } else if (![socket isConnected]) { + NSLog(@"Socket is not connected."); + result = [CDVPluginResult + resultWithStatus : CDVCommandStatus_ERROR + messageAsString : @"Invalid connection with host."]; + } else { + // Writting on output stream + data = [command.arguments objectAtIndex : 1]; + + /* void* buffer = malloc(512); */ + /* nsdata *shareddata = [[nsdata alloc] initwithbytesnocopy:buffer length:512 freewhendone:yes]; */ + + + + //NSLog(@"Sending data to %@ - %@", key, data); + + [socket writeBinary:data]; + + // Formatting success response + result = [CDVPluginResult + resultWithStatus : CDVCommandStatus_OK + messageAsString : key]; + } + } + @catch (NSException *exception) { + NSLog(@"Exception: %@", exception); + result = [CDVPluginResult + resultWithStatus : CDVCommandStatus_ERROR + messageAsString : @"Unexpected exception when executon 'sendBinary' action."]; + } + + // Returning callback resolution + [self.commandDelegate sendPluginResult : result callbackId : command.callbackId]; + }]; + } +} + - (void) sendMessage :(NSString *)host :(int)port :(NSData *)chunk { NSString *base64Encoded = [chunk base64EncodedStringWithOptions:0]; From bf1cc0bb9dcc76d50b0bcc374813d21d97c76fe4 Mon Sep 17 00:00:00 2001 From: Kevin Mershon Date: Wed, 13 May 2015 01:10:51 -0700 Subject: [PATCH 08/12] Get binary transfer to compile --- src/ios/CDVSocketPlugin.m | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/ios/CDVSocketPlugin.m b/src/ios/CDVSocketPlugin.m index 8f0e0b7..7550bec 100644 --- a/src/ios/CDVSocketPlugin.m +++ b/src/ios/CDVSocketPlugin.m @@ -353,7 +353,7 @@ - (void) sendBinary: (CDVInvokedUrlCommand *) command { CDVPluginResult* result= nil; Connection* socket = nil; - NSData* data = nil; + NSArray* data = nil; NSString* key = nil; @try { @@ -363,7 +363,7 @@ - (void) sendBinary: (CDVInvokedUrlCommand *) command { // Getting connection from pool socket = [pool objectForKey : key]; - // Checking if socket was not found and his conenctivity + // Checking if socket was not found and his connectivity if (socket == nil) { NSLog(@"Connection not found"); result = [CDVPluginResult @@ -379,14 +379,15 @@ - (void) sendBinary: (CDVInvokedUrlCommand *) command { // Writting on output stream data = [command.arguments objectAtIndex : 1]; - /* void* buffer = malloc(512); */ - /* nsdata *shareddata = [[nsdata alloc] initwithbytesnocopy:buffer length:512 freewhendone:yes]; */ - - + NSMutableData *buf = [[NSMutableData alloc] init]; - //NSLog(@"Sending data to %@ - %@", key, data); + for (int i = 0; i < [data count]; i++) + { + int byte = [data[i] intValue]; + [buf appendBytes : &byte length:1]; + } - [socket writeBinary:data]; + [socket writeBinary:buf]; // Formatting success response result = [CDVPluginResult From 9d928cffc5c7223ee486adb0a01b3433023d69ac Mon Sep 17 00:00:00 2001 From: Kevin Mershon Date: Wed, 13 May 2015 01:31:03 -0700 Subject: [PATCH 09/12] Use newer API to eval javascript --- src/ios/CDVSocketPlugin.m | 2 +- src/ios/Connection.m | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ios/CDVSocketPlugin.m b/src/ios/CDVSocketPlugin.m index 7550bec..6693a64 100644 --- a/src/ios/CDVSocketPlugin.m +++ b/src/ios/CDVSocketPlugin.m @@ -415,7 +415,7 @@ - (void) sendMessage :(NSString *)host :(int)port :(NSData *)chunk { NSString *receiveHook = [NSString stringWithFormat : @"window.tlantic.plugins.socket.receive('%@', %d, '%@', window.atob('%@') );", host, port, [self buildKey : host : port], base64Encoded]; - [self writeJavascript:receiveHook]; + [self.commandDelegate evalJs : receiveHook]; } @end diff --git a/src/ios/Connection.m b/src/ios/Connection.m index 439ee10..fca6818 100644 --- a/src/ios/Connection.m +++ b/src/ios/Connection.m @@ -87,7 +87,6 @@ - (void) stream : (NSStream *) theStream handleEvent : (NSStreamEvent) streamEve if (len > 0) { if (nil != line) { - NSLog(@"Received data: %@", line); [_hook sendMessage : _host : _port : line]; } } From 9d5b2497947394b9c89e165f5d40834e1f96dce9 Mon Sep 17 00:00:00 2001 From: Kevin Mershon Date: Wed, 13 May 2015 07:38:28 -0700 Subject: [PATCH 10/12] Fix preexisting typo, delete dead code, add a comment --- src/ios/CDVSocketPlugin.m | 2 +- src/ios/Connection.m | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ios/CDVSocketPlugin.m b/src/ios/CDVSocketPlugin.m index 6693a64..fe32409 100644 --- a/src/ios/CDVSocketPlugin.m +++ b/src/ios/CDVSocketPlugin.m @@ -376,7 +376,7 @@ - (void) sendBinary: (CDVInvokedUrlCommand *) command { resultWithStatus : CDVCommandStatus_ERROR messageAsString : @"Invalid connection with host."]; } else { - // Writting on output stream + // Writing on output stream data = [command.arguments objectAtIndex : 1]; NSMutableData *buf = [[NSMutableData alloc] init]; diff --git a/src/ios/Connection.m b/src/ios/Connection.m index fca6818..5553ef7 100644 --- a/src/ios/Connection.m +++ b/src/ios/Connection.m @@ -74,12 +74,13 @@ - (void) stream : (NSStream *) theStream handleEvent : (NSStreamEvent) streamEve // Data receiving case NSStreamEventHasBytesAvailable: if (theStream == reader) { - /* uint8_t buffer[512]; */ void* buffer = malloc(512); NSData *sharedData = [[NSData alloc] initWithBytesNoCopy:buffer length:512 freeWhenDone:YES]; NSInteger len; while ([reader hasBytesAvailable]) { + // This has a tendency to not fully read the packet, + // requiring subsequent combination of values len = [reader read : buffer maxLength : sizeof(buffer)]; NSData *line = [sharedData subdataWithRange:NSMakeRange(0, len)]; From 4f0254eda34346090a1a8b50977dbe0d16e1acf9 Mon Sep 17 00:00:00 2001 From: Kevin Mershon Date: Wed, 13 May 2015 12:03:06 -0700 Subject: [PATCH 11/12] Finish implementing packet completion --- src/ios/Connection.m | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/ios/Connection.m b/src/ios/Connection.m index 5553ef7..ff655fb 100644 --- a/src/ios/Connection.m +++ b/src/ios/Connection.m @@ -75,21 +75,27 @@ - (void) stream : (NSStream *) theStream handleEvent : (NSStreamEvent) streamEve case NSStreamEventHasBytesAvailable: if (theStream == reader) { void* buffer = malloc(512); - NSData *sharedData = [[NSData alloc] initWithBytesNoCopy:buffer length:512 freeWhenDone:YES]; - NSInteger len; + NSInteger len = 0; + NSMutableData *packet = [[NSMutableData alloc] init]; + NSData *line; + NSInteger totalLength = 0; while ([reader hasBytesAvailable]) { - // This has a tendency to not fully read the packet, + // NSInputStream is notorious for not fully reading a whole TCP packet, // requiring subsequent combination of values len = [reader read : buffer maxLength : sizeof(buffer)]; - NSData *line = [sharedData subdataWithRange:NSMakeRange(0, len)]; + // copy the bytes to the mutable buffer and update the total length + [packet appendBytes : buffer length:len]; + totalLength = totalLength + len; - if (len > 0) { + line = [packet subdataWithRange:NSMakeRange(0, totalLength)]; + } - if (nil != line) { - [_hook sendMessage : _host : _port : line]; - } + // now that no more bytes are available, send the packet + if (len >= 0) { + if (nil != line) { + [_hook sendMessage : _host : _port : line]; } } } From 1706d5919caab4cbb77e212ac5f0fe93e11b50ce Mon Sep 17 00:00:00 2001 From: Kevin Mershon Date: Fri, 3 Jul 2015 14:16:58 -0700 Subject: [PATCH 12/12] Call close on the socket, any time we dispose the connection --- src/ios/CDVSocketPlugin.m | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ios/CDVSocketPlugin.m b/src/ios/CDVSocketPlugin.m index fe32409..f79601b 100644 --- a/src/ios/CDVSocketPlugin.m +++ b/src/ios/CDVSocketPlugin.m @@ -156,8 +156,10 @@ - (BOOL) disposeConnection : (NSString *) key { if (socket) { [pool removeObjectForKey : key]; - if ([socket isConnected]) - [socket close]; + // Call close on the socket whether it's connected or not, to clean + // up the read/write streams and event handlers so we don't leak + // memory + [socket close]; socket = nil;