Skip to content

Commit

Permalink
fixed: setBroadcast hangs on Android
Browse files Browse the repository at this point in the history
It seems we must not call `setBroadcast()` after receiving thread was started.
At least on my setup (Android emulator or Samsung Galaxy S8 running on Android API 28)
this will result in deadlock.

Therefore I removed automatic start of receive on bind and introduced new method
`startReveiving()`.

It can be called like this:

```
let udpClient = dgram.createSocket('udp4')
this.udpClient.on('bound', () => {
  this.udpClient.setBroadcast(true)
  this.udpClient.startReceiving()
})
udpClient.bind()
```

See issue: tradle#82
  • Loading branch information
shynst committed Mar 10, 2019
1 parent 5216ece commit aef3f01
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 12 deletions.
20 changes: 18 additions & 2 deletions UdpSocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ UdpSocket.prototype.bind = function(...args) {

if (!port) port = 0

if (callback) this.once('listening', callback.bind(this))
if (callback) this.once('bound', callback.bind(this))

this._state = STATE.BINDING
this._debug('binding, address:', address, 'port:', port)
Expand All @@ -109,7 +109,23 @@ UdpSocket.prototype.bind = function(...args) {
self._address = addr.address
self._port = addr.port
self._state = STATE.BOUND
self.emit('listening')
self.emit('bound')
})
}

UdpSocket.prototype.startReceiving = function(callback=noop) {
if (this._state !== STATE.BOUND) {
throw new Error('you must bind before startReceiving()')
}

this.once('listening', callback)

Sockets.startReceiving(this._id, err => {
err = normalizeError(err)
if (err) return this.emit('error', err)

this._debug('listening')
this.emit('listening')
})
}

Expand Down
6 changes: 3 additions & 3 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ android {
dependencies {
def supportLibVersion = project.hasProperty('supportLibVersion') ? project.supportLibVersion : DEFAULT_SUPPORT_LIB_VERSION

compile fileTree(dir: 'libs', include: ['*.jar'])
compile "com.android.support:appcompat-v7:$supportLibVersion"
compile 'com.facebook.react:react-native:+'
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "com.android.support:appcompat-v7:$supportLibVersion"
compileOnly 'com.facebook.react:react-native:+'
}
11 changes: 11 additions & 0 deletions android/src/main/java/com/tradle/react/UdpSocketClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,17 @@ public void bind(Integer port, @Nullable String address) throws IOException {

mSocket.setReuseAddress(mReuseAddress);
mSocket.bind(socketAddress);
}

/**
* Start receiving thread.
*
* @throws IllegalStateException if socket is not bound.
*/
public void startReceiving() throws IllegalStateException {
if (null == mSocket || !mSocket.isBound() || mReceiverTask == null) {
throw new IllegalStateException("Socket is not bound.");
}

// begin listening for data in the background
mReceiverTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
Expand Down
24 changes: 24 additions & 0 deletions android/src/main/java/com/tradle/react/UdpSockets.java
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,30 @@ protected void doInBackgroundGuarded(Void... params) {
}.execute();
}

/**
* Start receiving thread.
*/
@ReactMethod
public void startReceiving(final Integer cId, final Callback callback) {
new GuardedAsyncTask<Void, Void>(getReactApplicationContext()) {
@Override
protected void doInBackgroundGuarded(Void... params) {
UdpSocketClient client = findClient(cId, callback);
if (client == null) {
return;
}

try {
client.startReceiving();
callback.invoke();
} catch (IllegalStateException ise) {
// Socket is not bound or a problem occurred during starting thread
callback.invoke(UdpErrorUtil.getError(null, ise.getMessage()));
}
}
}.execute();
}

/**
* Joins a multi-cast group
*/
Expand Down
5 changes: 5 additions & 0 deletions ios/UdpSocketClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ typedef enum RCTUDPError RCTUDPError;
*/
- (BOOL)bind:(u_int16_t) port address:(NSString*)address options:(NSDictionary *)options error:(NSError**)error;

/**
* Start receiving thread
*/
- (BOOL)startReceiving:(NSError **) error;

/**
* Join multicast groupt
*
Expand Down
11 changes: 8 additions & 3 deletions ios/UdpSocketClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,13 @@ - (BOOL)bind:(u_int16_t)port address:(NSString *)address options:(NSDictionary *
_address = address;

_udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:[self methodQueue]];

[_udpSocket setMaxReceiveIPv4BufferSize:UINT16_MAX];
[_udpSocket setMaxReceiveIPv6BufferSize:UINT16_MAX];

BOOL reusePort = options[@"reusePort"] ?: NO;
[_udpSocket enableReusePort:reusePort error:error];

BOOL result;
if (address) {
struct sockaddr_in ip;
Expand All @@ -114,7 +114,12 @@ - (BOOL)bind:(u_int16_t)port address:(NSString *)address options:(NSDictionary *
result = [_udpSocket bindToPort:_port error:error];
}

return result && [_udpSocket beginReceiving:error];
return result;
}

- (BOOL)startReceiving:(NSError **) error
{
return [_udpSocket beginReceiving:error];
}

- (BOOL)joinMulticastGroup:(NSString *)address error:(NSError **) error
Expand Down
24 changes: 20 additions & 4 deletions ios/UdpSockets.m
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,22 @@ - (void)dealloc
callback(@[[NSNull null], [client address]]);
}

RCT_EXPORT_METHOD(startReceiving:(nonnull NSNumber*)cId
callback:(RCTResponseSenderBlock)callback)
{
UdpSocketClient* client = [self findClient:cId callback:callback];
if (!client) return;

NSError *error = nil;
if (![client startReceiving:&error])
{
NSString *msg = error.localizedFailureReason ?: error.localizedDescription;
callback(@[msg ?: @"unknown error when startReceiving"]);
return;
}
callback(@[[NSNull null]]);
}

RCT_EXPORT_METHOD(send:(nonnull NSNumber*)cId
string:(NSString*)base64String
port:(int)port
Expand Down Expand Up @@ -108,19 +124,19 @@ - (void)dealloc
RCT_EXPORT_METHOD(addMembership:(nonnull NSNumber*)cId
multicastAddress:(NSString *)address) {
UdpSocketClient *client = _clients[cId];

if (!client) return;

NSError *error = nil;
[client joinMulticastGroup:address error:&error];
}

RCT_EXPORT_METHOD(dropMembership:(nonnull NSNumber*)cId
multicastAddress:(NSString *)address) {
UdpSocketClient *client = _clients[cId];

if (!client) return;

NSError *error = nil;
[client leaveMulticastGroup:address error:&error];
}
Expand Down

0 comments on commit aef3f01

Please sign in to comment.