react-native-udp/ios/UdpSocketClient.m

213 lines
5.2 KiB
Mathematica
Raw Normal View History

2015-05-09 21:55:49 +00:00
//
// RCTUDPClient.m
// react-native-udp
//
// Created by Mark Vayngrib on 5/9/15.
// Copyright (c) 2015 Tradle, Inc. All rights reserved.
//
#import <netinet/in.h>
#import <arpa/inet.h>
2015-05-10 14:26:29 +00:00
#import "UdpSocketClient.h"
2015-05-09 21:55:49 +00:00
#import "RCTBridgeModule.h"
#import "GCDAsyncUdpSocket.h"
NSString *const RCTUDPErrorDomain = @"RCTUDPErrorDomain";
2015-05-10 14:26:29 +00:00
@interface UdpSocketClient()
2015-05-09 21:55:49 +00:00
{
@private
uint16_t _port;
NSString* _address;
GCDAsyncUdpSocket *_udpSocket;
2015-12-16 19:47:43 +00:00
NSMutableDictionary<NSNumber *, RCTResponseSenderBlock> *_pendingSends;
NSLock *_lock;
2015-05-09 21:55:49 +00:00
long tag;
}
- (id)initWithConfig:(id<SocketClientDelegate>) aDelegate;
@end
2015-05-10 14:26:29 +00:00
@implementation UdpSocketClient
2015-05-09 21:55:49 +00:00
+ (id)socketClientWithConfig:(id<SocketClientDelegate>)delegate
{
return [[[self class] alloc] initWithConfig:delegate];
}
- (id)initWithConfig:(id<SocketClientDelegate>) aDelegate
{
self = [super init];
if (self) {
_clientDelegate = aDelegate;
_pendingSends = [NSMutableDictionary dictionary];
_lock = [[NSLock alloc] init];
2015-05-09 21:55:49 +00:00
}
return self;
}
- (void)setPendingSend:(RCTResponseSenderBlock)callback forKey:(NSNumber *)key
{
[_lock lock];
@try {
[_pendingSends setObject:callback forKey:key];
} @finally {
[_lock unlock];
}
}
- (RCTResponseSenderBlock)getPendingSend:(NSNumber *)key
{
[_lock lock];
@try {
return [_pendingSends objectForKey:key];
} @finally {
[_lock unlock];
}
}
- (void)dropPendingSend:(NSNumber *)key
{
[_lock lock];
@try {
[_pendingSends removeObjectForKey:key];
} @finally {
[_lock unlock];
}
}
2015-05-09 21:55:49 +00:00
- (BOOL) bind:(u_int16_t)port address:(NSString *)address error:(NSError **) error
{
if (_port) {
if (error) {
*error = [self badInvocationError:@"this client's socket is already bound"];
}
return false;
}
_port = port;
_address = address;
_udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:[self methodQueue]];
BOOL result;
if (address) {
struct sockaddr_in ip;
ip.sin_family = AF_INET;
2015-05-10 14:26:29 +00:00
ip.sin_port = htons(_port);
2015-05-09 21:55:49 +00:00
inet_pton(AF_INET, [address cStringUsingEncoding:NSASCIIStringEncoding], &ip.sin_addr);
NSData * hostAndPort = [NSData dataWithBytes:&ip length:sizeof(ip)];
result = [_udpSocket bindToAddress:hostAndPort error:error];
}
else {
2015-05-10 14:26:29 +00:00
result = [_udpSocket bindToPort:_port error:error];
2015-05-09 21:55:49 +00:00
}
return result && [_udpSocket beginReceiving:error];
}
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)msgTag
{
NSNumber* tagNum = [NSNumber numberWithLong:msgTag];
RCTResponseSenderBlock callback = [self getPendingSend:tagNum];
2015-05-09 21:55:49 +00:00
if (callback) {
callback(@[]);
[self dropPendingSend:tagNum];
2015-05-09 21:55:49 +00:00
}
}
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotSendDataWithTag:(long)msgTag dueToError:(NSError *)error
{
// NSError* err = [self sendFailedError:[error description]];
NSNumber* tagNum = [NSNumber numberWithLong:msgTag];
RCTResponseSenderBlock callback = [self getPendingSend:tagNum];
2015-05-09 21:55:49 +00:00
if (callback) {
NSString* msg = [[error userInfo] valueForKey:@"NSLocalizedFailureReason"];
callback(@[msg]);
[self dropPendingSend:tagNum];
2015-05-09 21:55:49 +00:00
}
}
- (void) send:(NSData *)data
remotePort:(u_int16_t)port
remoteAddress:(NSString *)address
callback:(RCTResponseSenderBlock)callback
{
[_udpSocket sendData:data toHost:address port:port withTimeout:-1 tag:tag];
if (callback) {
[self setPendingSend:callback forKey:[NSNumber numberWithLong:tag]];
2015-05-09 21:55:49 +00:00
}
tag++;
}
- (NSDictionary* ) address
{
return @{
@"address": [_udpSocket localHost],
@"port": [NSNumber numberWithInt:[_udpSocket localPort]]
};
}
- (void) close
{
[_udpSocket close];
}
2015-07-29 16:16:01 +00:00
- (BOOL) setBroadcast:(BOOL)flag
error:(NSError **)error
{
return [_udpSocket enableBroadcast:flag error:error];
}
2015-05-09 21:55:49 +00:00
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data
fromAddress:(NSData *)address
withFilterContext:(id)filterContext
{
if (!_clientDelegate) return;
NSString *host = nil;
uint16_t port = 0;
[GCDAsyncUdpSocket getHost:&host port:&port fromAddress:address];
[_clientDelegate onData:self data:data host:host port:port];
}
- (NSError *)badParamError:(NSString *)errMsg
{
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
return [NSError errorWithDomain:RCTUDPErrorDomain
code:RCTUDPBadParamError
userInfo:userInfo];
}
- (NSError *)badInvocationError:(NSString *)errMsg
{
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
return [NSError errorWithDomain:RCTUDPErrorDomain
code:RCTUDPInvalidInvocationError
userInfo:userInfo];
}
- (NSError *)sendFailedError:(NSString *)errMsg
{
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
return [NSError errorWithDomain:RCTUDPErrorDomain
code:RCTUDPSendFailedError
userInfo:userInfo];
}
- (dispatch_queue_t)methodQueue
{
// return dispatch_queue_create("com.facebook.React.UDPSocketsQueue", DISPATCH_QUEUE_SERIAL);
return dispatch_get_main_queue();
}
@end