Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 28 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ client.on('close', function(){
});

// Write on the socket
client.write("Hello server!");
client.write('Hello server!');

// Close socket
client.destroy();
Expand Down Expand Up @@ -155,17 +155,20 @@ server.on('close', () => {
* [`destroy()`](#destroy)

#### `createConnection()`
`createConnection(options[, callback])` creates a TCP connection using the given [`options`](#options).
##### `options`
**Required**. Available options for creating a socket. It is an `object` with the following properties:

| Property | Type | Description |
| --------------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------- |
| **`host`** | `<string>` | **Required**. A valid server IP address in IPv4 format or `"localhost"`. |
| **`port`** | `<number>` | **Required**. A valid server port. |
| `[localAddress]` | `<string>` | A valid local IP address to bind the socket. If not specified, the OS will decide. It is **highly recommended** to specify a `localAddress` to prevent overload errors and improve performance. |
| `[localPort]` | `<number>` | A valid local port to bind the socket. If not specified, the OS will decide. |
| `[interface]`| `<string>` | The interface to bind the socket. If not specified, it will use the current active connection. The options are: `"wifi"`. |
`createConnection(options[, callback])` creates a TCP connection using the given [`options`](#createconnection-options).
##### `createConnection: options`
**Required**. Available options for creating a socket. It must be an `object` with the following properties:

| Property | Type | iOS | Android |Description |
| --------------------- | ------ | :--: | :-----: |-------------------------------------------------------------------------------------------------- |
| **`port`** | `<number>` | ✅ | ✅ | **Required**. Port the socket should connect to. |
| `host` | `<string>` | ✅ | ✅ | Host the socket should connect to. IP address in IPv4 format or `'localhost'`. **Default**: `'localhost'`. |
| `localAddress` | `<string>` | ✅ | ✅ | Local address the socket should connect from. If not specified, the OS will decide. It is **highly recommended** to specify a `localAddress` to prevent overload errors and improve performance. |
| `localPort` | `<number>` | ✅ | ✅ | Local port the socket should connect from. If not specified, the OS will decide. |
| `interface`| `<string>` | ❌ | ✅ | Interface the socket should connect from. If not specified, it will use the current active connection. The options are: `'wifi'`. |
| `reuseAddress`| `<boolean>` | ❌ | ✅ | Enable/disable the reuseAddress socket option. **Default**: `true`. |

**Note**: The platforms marked as ❌ use the default value.

#### `write()`
* `data`: `<string> | <Buffer> | <Uint8Array>`
Expand All @@ -177,11 +180,22 @@ server.on('close', () => {
### Server
* **Methods:**
* [`createServer(callback)`](#createserver)
* [`listen(port[, host])`](#listen)
* [`listen(options[, callback])`](#listen)
* [`close()`](#close)

#### `listen()`
`listen(port[, host])` creates a TCP server socket listening on the given port. If the host is not explicity selected, the socket will be bound to `'0.0.0.0'`.
`listen(options[, callback])` creates a TCP server socket using the given [`options`](#listen-options).

##### `listen: options`
**Required**. Available options for creating a server socket. It must be an `object` with the following properties:

| Property | Type | iOS | Android |Description |
| --------------------- | ------ | :--: | :-----: |-------------------------------------------------------------------------------------------------- |
| **`port`** | `<number>` | ✅ | ✅ | **Required**. Port the socket should listen to. |
| `host` | `<string>` | ✅ | ✅ | Host the socket should listen to. IP address in IPv4 format or `'localhost'`. **Default**: `'0.0.0.0'`. |
| `reuseAddress`| `<boolean>` | ❌ | ✅ | Enable/disable the reuseAddress socket option. **Default**: `true`. |

**Note**: The platforms marked as ❌ use the default value.

## Maintainers
Looking for maintainers!
Expand Down
3 changes: 2 additions & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.1'
classpath 'com.android.tools.build:gradle:3.5.3'
}
}
}
Expand Down Expand Up @@ -66,6 +66,7 @@ dependencies {
}

def configureReactNativePom(def pom) {
//noinspection UnnecessaryQualifiedReference
def packageJson = new groovy.json.JsonSlurper().parseText(file('../package.json').text)

pom.project {
Expand Down
4 changes: 2 additions & 2 deletions android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Sat Oct 05 12:20:33 CEST 2019
#Sat Jan 11 13:40:56 CET 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ public class TcpReceiverTask extends AsyncTask<Pair<TcpSocketClient, TcpReceiver
/**
* An infinite loop to block and read data from the socket.
*/
@SafeVarargs
@Override
protected Void doInBackground(Pair<TcpSocketClient, TcpReceiverTask.OnDataReceivedListener>... params) {
protected final Void doInBackground(Pair<TcpSocketClient, TcpReceiverTask.OnDataReceivedListener>... params) {
if (params.length > 1) {
throw new IllegalArgumentException("This task is only for a single socket/listener pair.");
}
Expand Down Expand Up @@ -52,6 +53,7 @@ protected Void doInBackground(Pair<TcpSocketClient, TcpReceiverTask.OnDataReceiv
/**
* Listener interface for receive events.
*/
@SuppressWarnings("WeakerAccess")
public interface OnDataReceivedListener {
void onConnection(Integer serverId, Integer clientId, InetSocketAddress socketAddress);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,68 @@
import android.os.AsyncTask;
import android.util.Pair;

import com.facebook.react.bridge.ReadableMap;

import java.io.OutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;

public class TcpSocketClient {
class TcpSocketClient {
private TcpReceiverTask receiverTask;
private Socket socket;
private TcpReceiverTask.OnDataReceivedListener mReceiverListener;

protected Integer id;

public TcpSocketClient() {
private final int id;

TcpSocketClient(final int id) {
this.id = id;
}

/**
* TcpSocketClient constructor
*
* @param address server address
* @param port server port
* @param localAddress local address to bound to
* @param localPort local port to bound to
* @param address server address
* @param port server port
* @param options extra options
*/
public TcpSocketClient(final TcpReceiverTask.OnDataReceivedListener receiverListener, final Integer id,
final String address, final Integer port, final String localAddress, final int localPort, final Network network)
final String address, final Integer port, final ReadableMap options, final Network network)
throws IOException {
this.id = id;
this(id);
// Get the addresses
String localAddress = options.getString("localAddress");
InetAddress localInetAddress = InetAddress.getByName(localAddress);
InetAddress remoteInetAddress = InetAddress.getByName(address);
// Create the socket
socket = new Socket();
if (network != null)
network.bindSocket(socket);
socket.setReuseAddress(true);
// setReuseAddress
try {
boolean reuseAddress = options.getBoolean("reuseAddress");
socket.setReuseAddress(reuseAddress);
} catch (Exception e) {
// Default to true
socket.setReuseAddress(true);
}
// bind
int localPort = options.getInt("localPort");
socket.bind(new InetSocketAddress(localInetAddress, localPort));
socket.connect(new InetSocketAddress(remoteInetAddress, port));
receiverTask = new TcpReceiverTask();
mReceiverListener = receiverListener;
//noinspection unchecked
receiverTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, new Pair<>(this, receiverListener));
}

public TcpSocketClient(final TcpReceiverTask.OnDataReceivedListener receiverListener, final Integer id, final Socket socket) {
this.id = id;
TcpSocketClient(final TcpReceiverTask.OnDataReceivedListener receiverListener, final Integer id, final Socket socket) {
this(id);
this.socket = socket;
receiverTask = new TcpReceiverTask();
mReceiverListener = receiverListener;
//noinspection unchecked
receiverTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, new Pair<>(this, receiverListener));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.asterinet.react.tcpsocket;


import android.annotation.SuppressLint;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
Expand Down Expand Up @@ -35,7 +36,7 @@ public class TcpSocketModule extends ReactContextBaseJavaModule implements TcpRe
private final SparseArray<Network> mNetworkMap = new SparseArray<>();
private Network mSelectedNetwork;

public static final String TAG = "TcpSockets";
private static final String TAG = "TcpSockets";

public TcpSocketModule(ReactApplicationContext reactContext) {
super(reactContext);
Expand Down Expand Up @@ -108,6 +109,8 @@ public void onUnavailable() {
* @param port socket port to be bound
* @param options extra options
*/
@SuppressLint("StaticFieldLeak")
@SuppressWarnings("unused")
@ReactMethod
public void connect(final Integer cId, final String host, final Integer port, final ReadableMap options) {
new GuardedAsyncTask<Void, Void>(mReactContext.getExceptionHandler()) {
Expand All @@ -123,13 +126,12 @@ protected void doInBackgroundGuarded(Void... params) {
onError(cId, TAG + "createSocket called twice with the same id.");
return;
}
String localAddress = options.getString("localAddress");
String iface = options.getString("interface");
int localPort = options.getInt("localPort");
try {
// Get the network interface
String localAddress = options.getString("localAddress");
String iface = options.getString("interface");
selectNetwork(iface, localAddress);
client = new TcpSocketClient(TcpSocketModule.this, cId, host, port, localAddress, localPort, mSelectedNetwork);
client = new TcpSocketClient(TcpSocketModule.this, cId, host, port, options, mSelectedNetwork);
socketClients.put(cId, client);
onConnect(cId, host, port);
} catch (Exception e) {
Expand All @@ -139,6 +141,8 @@ protected void doInBackgroundGuarded(Void... params) {
}.execute();
}

@SuppressLint("StaticFieldLeak")
@SuppressWarnings("unused")
@ReactMethod
public void write(final Integer cId, final String base64String, final Callback callback) {
new GuardedAsyncTask<Void, Void>(mReactContext.getExceptionHandler()) {
Expand All @@ -163,6 +167,8 @@ protected void doInBackgroundGuarded(Void... params) {
}.execute();
}

@SuppressLint("StaticFieldLeak")
@SuppressWarnings("unused")
@ReactMethod
public void end(final Integer cId) {
new GuardedAsyncTask<Void, Void>(mReactContext.getExceptionHandler()) {
Expand All @@ -178,19 +184,24 @@ protected void doInBackgroundGuarded(Void... params) {
}.execute();
}

@SuppressWarnings("unused")
@ReactMethod
public void destroy(final Integer cId) {
end(cId);
}

@SuppressLint("StaticFieldLeak")
@SuppressWarnings("unused")
@ReactMethod
public void listen(final Integer cId, final String host, final Integer port) {
public void listen(final Integer cId, final ReadableMap options) {
new GuardedAsyncTask<Void, Void>(mReactContext.getExceptionHandler()) {
@Override
protected void doInBackgroundGuarded(Void... params) {
try {
TcpSocketServer server = new TcpSocketServer(socketClients, TcpSocketModule.this, cId, host, port);
TcpSocketServer server = new TcpSocketServer(socketClients, TcpSocketModule.this, cId, options);
socketClients.put(cId, server);
int port = options.getInt("port");
String host = options.getString("host");
onConnect(cId, host, port);
} catch (Exception uhe) {
onError(cId, uhe.getMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@

import androidx.annotation.NonNull;

@SuppressWarnings("unused")
public class TcpSocketPackage implements ReactPackage {
@Override
public @NonNull List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
//noinspection ArraysAsListWithZeroOrOneArgument
return Arrays.<NativeModule>asList(new TcpSocketModule(reactContext));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.asterinet.react.tcpsocket;

import android.annotation.SuppressLint;
import android.os.AsyncTask;
import android.util.SparseArray;

import com.facebook.react.bridge.ReadableMap;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
Expand All @@ -13,9 +16,10 @@ public class TcpSocketServer extends TcpSocketClient {
private ServerSocket serverSocket;
private TcpReceiverTask.OnDataReceivedListener mReceiverListener;
private int clientSocketIds;
private SparseArray<TcpSocketClient> socketClients;
private final SparseArray<TcpSocketClient> socketClients;
private final SparseArray<TcpSocketClient> serverSocketClients = new SparseArray<>();

@SuppressLint("StaticFieldLeak")
private final AsyncTask listening = new AsyncTask() {
@Override
protected Void doInBackground(Object[] objects) {
Expand All @@ -39,14 +43,25 @@ protected Void doInBackground(Object[] objects) {


public TcpSocketServer(final SparseArray<TcpSocketClient> socketClients, final TcpReceiverTask.OnDataReceivedListener receiverListener, final Integer id,
final String address, final Integer port) throws IOException {
this.id = id;
final ReadableMap options) throws IOException {
super(id);
// Get data from options
int port = options.getInt("port");
String address = options.getString("host");
this.socketClients = socketClients;
clientSocketIds = (1 + this.id) * 1000;
clientSocketIds = (1 + getId()) * 1000;
// Get the addresses
InetAddress localInetAddress = InetAddress.getByName(address);
// Create the socket
serverSocket = new ServerSocket(port, 50, localInetAddress);
// setReuseAddress
try {
boolean reuseAddress = options.getBoolean("reuseAddress");
serverSocket.setReuseAddress(reuseAddress);
} catch (Exception e) {
// Default to true
serverSocket.setReuseAddress(true);
}
mReceiverListener = receiverListener;
listen();
}
Expand All @@ -61,6 +76,7 @@ private int getClientId() {
}

private void listen() {
//noinspection unchecked
listening.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}

Expand Down
3 changes: 2 additions & 1 deletion examples/tcpsockets/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class App extends React.Component {
socket.on('close', (error) => {
this.updateChatter('server client closed ' + (error ? error : ''));
});
}).listen(serverPort, serverHost, (address) => {
}).listen({port: serverPort, host: serverHost, reuseAddress: true}, (address) => {
this.updateChatter('opened server on ' + JSON.stringify(address));
});

Expand All @@ -64,6 +64,7 @@ class App extends React.Component {
port: serverPort,
host: serverHost,
localAddress: "127.0.0.1",
reuseAddress: true,
// localPort: 20000,
// interface: "wifi"
}, (address) => {
Expand Down
5 changes: 2 additions & 3 deletions ios/TcpSocketClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,10 @@ typedef enum RCTTCPError RCTTCPError;
/**
* Starts listening on a local host and port
*
* @param local ip address
* @param local port
* @param options NSDictionary which must have a @"port" and @"host" to specify where to listen
* @return true if connected, false if there was an error
*/
- (BOOL)listen:(NSString *)host port:(int)port error:(NSError **)error;
- (BOOL)listen:(NSDictionary *)options error:(NSError **)error;

/**
* Returns the address information
Expand Down
Loading