diff --git a/README.md b/README.md index 7e4c3c7..87abb0c 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ To use this library you need to ensure you are using the correct version of Reac | `react-native-tcp-socket` version | Required React Native Version | | ----------------------------------------- | --------------------------------------------------------------------------------- | -| `3.x.x` | `>= 0.61` | +| `4.X.X`, `3.X.X` | `>= 0.61` | | `1.2.2` | `>= 0.??` | ## Usage @@ -184,10 +184,13 @@ Here are listed all methods implemented in `react-native-tcp-socket`, their func ### TcpSocket * **Methods:** * **[`TcpSocket.createConnection(options[, callback])`](#createconnection)** - * [`write(data[, encoding][, callback])`](https://nodejs.org/api/net.html#net_socket_write_data_encoding_callback) + * [`address()`](https://nodejs.org/api/net.html#net_socket_address) * [`destroy([error])`](https://nodejs.org/api/net.html#net_socket_destroy_error) + * [`end([data][, encoding][, callback])`](https://nodejs.org/api/net.html#net_socket_end_data_encoding_callback) + * [`setKeepAlive([enable][, initialDelay])`](https://nodejs.org/api/net.html#net_socket_setkeepalive_enable_initialdelay) - _`initialDelay` is ignored_ * [`setNoDelay([noDelay])`](https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay) * [`setTimeout(timeout[, callback])`](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback) + * [`write(data[, encoding][, callback])`](https://nodejs.org/api/net.html#net_socket_write_data_encoding_callback) #### `createConnection()` `createConnection(options[, callback])` creates a TCP connection using the given [`options`](#createconnection-options). diff --git a/android/src/main/java/com/asterinet/react/tcpsocket/TcpSocketClient.java b/android/src/main/java/com/asterinet/react/tcpsocket/TcpSocketClient.java index 371e974..30926d8 100644 --- a/android/src/main/java/com/asterinet/react/tcpsocket/TcpSocketClient.java +++ b/android/src/main/java/com/asterinet/react/tcpsocket/TcpSocketClient.java @@ -141,4 +141,15 @@ public void setNoDelay(final boolean noDelay) throws IOException { } socket.setTcpNoDelay(noDelay); } + + /** + * @param enable `true` to enable keep-alive functionality + */ + public void setKeepAlive(final boolean enable, final int initialDelay) throws IOException { + if (socket == null) { + throw new IOException("Socket is not connected."); + } + // `initialDelay` is ignored + socket.setKeepAlive(enable); + } } diff --git a/android/src/main/java/com/asterinet/react/tcpsocket/TcpSocketModule.java b/android/src/main/java/com/asterinet/react/tcpsocket/TcpSocketModule.java index a8c437b..6da6153 100644 --- a/android/src/main/java/com/asterinet/react/tcpsocket/TcpSocketModule.java +++ b/android/src/main/java/com/asterinet/react/tcpsocket/TcpSocketModule.java @@ -164,6 +164,7 @@ protected void doInBackgroundGuarded(Void... params) { }.executeOnExecutor(executorService); } + @SuppressWarnings("unused") @ReactMethod public void setNoDelay(@NonNull final Integer cId, final boolean noDelay) { final TcpSocketClient client = socketClients.get(cId); @@ -178,6 +179,21 @@ public void setNoDelay(@NonNull final Integer cId, final boolean noDelay) { } } + @SuppressWarnings("unused") + @ReactMethod + public void setKeepAlive(@NonNull final Integer cId, final boolean enable, final int initialDelay) { + final TcpSocketClient client = socketClients.get(cId); + if (client == null) { + onError(cId, TAG + "socket not found."); + return; + } + try { + client.setKeepAlive(enable, initialDelay); + } catch (IOException e) { + onError(cId, e.getMessage()); + } + } + private void requestNetwork(final int transportType) throws InterruptedException { final NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder(); requestBuilder.addTransportType(transportType); diff --git a/examples/tcpsockets/ios/Podfile.lock b/examples/tcpsockets/ios/Podfile.lock index cab7a23..f3eac35 100644 --- a/examples/tcpsockets/ios/Podfile.lock +++ b/examples/tcpsockets/ios/Podfile.lock @@ -233,7 +233,7 @@ PODS: - React-cxxreact (= 0.62.1) - React-jsi (= 0.62.1) - React-jsinspector (0.62.1) - - react-native-tcp-socket (3.4.2): + - react-native-tcp-socket (4.1.0): - CocoaAsyncSocket - React - React-RCTActionSheet (0.62.1): @@ -430,7 +430,7 @@ SPEC CHECKSUMS: React-jsi: 600d8e42510c3254fd2abd702f4b9d3f598d8f52 React-jsiexecutor: e9698dee4fd43ceb44832baf15d5745f455b0157 React-jsinspector: f74a62727e5604119abd4a1eda52c0a12144bcd5 - react-native-tcp-socket: a100fffbe7c96f92f457fa7fad6a29ec24bb8187 + react-native-tcp-socket: 7ae44fe823a4c8e73f66e4562fceef392f94ce13 React-RCTActionSheet: af8f28dd82fec89b8fe29637b8c779829e016a88 React-RCTAnimation: 0d21fff7c20fb8ee41de5f2ebb63221127febd96 React-RCTBlob: 9496bd93130b22069bfbc5d35e98653dae7c35c6 diff --git a/examples/tcpsockets/package.json b/examples/tcpsockets/package.json index 69683bc..ccee04a 100644 --- a/examples/tcpsockets/package.json +++ b/examples/tcpsockets/package.json @@ -14,7 +14,7 @@ "dependencies": { "react": "16.11.0", "react-native": "0.62.1", - "react-native-tcp-socket": "^3.5.0" + "react-native-tcp-socket": "^4.1.0" }, "devDependencies": { "@babel/core": "^7.7.2", diff --git a/ios/TcpSocketClient.h b/ios/TcpSocketClient.h index 672ee89..2c1b3b8 100644 --- a/ios/TcpSocketClient.h +++ b/ios/TcpSocketClient.h @@ -94,4 +94,6 @@ typedef enum RCTTCPError RCTTCPError; - (void)setNoDelay:(BOOL)noDelay; +- (void)setKeepAlive:(BOOL)enable initialDelay:(int)initialDelay; + @end diff --git a/ios/TcpSocketClient.m b/ios/TcpSocketClient.m index a6105f2..bbcbb0d 100644 --- a/ios/TcpSocketClient.m +++ b/ios/TcpSocketClient.m @@ -132,7 +132,21 @@ - (void)setNoDelay:(BOOL)noDelay int on = noDelay ? 1 : 0; if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&on, sizeof(on)) == -1) { /* TODO: handle error */ - RCTLogWarn(@"setNoDelay caused an unexpected error"); + RCTLogWarn(@"react-native-tcp-socket: setNoDelay() caused an unexpected error"); + } + }]; +} + +- (void)setKeepAlive:(BOOL)enable initialDelay:(int)initialDelay +{ + [_tcpSocket performBlock:^{ + int fd = [self->_tcpSocket socketFD]; + int on = enable ? 1 : 0; + int enableKA = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); + // `initialDelay` is ignored + if (enableKA == -1) { + /* TODO: handle error */ + RCTLogWarn(@"react-native-tcp-socket: setKeepAlive() caused an unexpected error"); } }]; } diff --git a/ios/TcpSockets.m b/ios/TcpSockets.m index 166ea54..5666e30 100644 --- a/ios/TcpSockets.m +++ b/ios/TcpSockets.m @@ -123,6 +123,13 @@ - (TcpSocketClient *)createSocket:(nonnull NSNumber*)cId [client setNoDelay:noDelay]; } +RCT_EXPORT_METHOD(setKeepAlive:(nonnull NSNumber*)cId enable:(BOOL)enable initialDelay:(int)initialDelay) { + TcpSocketClient* client = [self findClient:cId]; + if (!client) return; + + [client setKeepAlive:enable initialDelay:initialDelay]; +} + - (void)onConnect:(TcpSocketClient*) client { [self sendEventWithName:@"connect" diff --git a/src/TcpSocket.js b/src/TcpSocket.js index fb70202..aa0a206 100644 --- a/src/TcpSocket.js +++ b/src/TcpSocket.js @@ -162,10 +162,39 @@ export default class TcpSocket extends EventEmitter { * * Passing `true` for `noDelay` or not passing an argument will disable Nagle's algorithm for the socket. Passing false for noDelay will enable Nagle's algorithm. * - * @param {boolean} noDelay + * @param {boolean} noDelay Default: `true` */ setNoDelay(noDelay = true) { + if (this._state != STATE.CONNECTED) { + this.once('connect', () => this.setNoDelay(noDelay)); + return this; + } Sockets.setNoDelay(this._id, noDelay); + return this; + } + + /** + * Enable/disable keep-alive functionality, and optionally set the initial delay before the first keepalive probe is sent on an idle socket. + * + * `initialDelay` is ignored. + * + * @param {boolean} enable Default: `false` + * @param {number} initialDelay ***IGNORED**. Default: `0` + */ + setKeepAlive(enable = false, initialDelay = 0) { + if (this._state != STATE.CONNECTED) { + this.once('connect', () => this.setKeepAlive(enable, initialDelay)); + return this; + } + + if (initialDelay !== 0) { + console.warn( + 'react-native-tcp-socket: initialDelay param in socket.setKeepAlive() is ignored' + ); + } + + Sockets.setKeepAlive(this._id, enable, Math.floor(initialDelay)); + return this; } address() {