Updated CocoaAsyncSocket. Added SO_REUSEPORT

This commit is contained in:
Gavin Conway 2017-08-12 22:18:39 +10:00 committed by Mark Vayngrib
parent cfcb081d61
commit 6f62be0d6e
3 changed files with 296 additions and 126 deletions

View File

@ -13,14 +13,14 @@
#import <TargetConditionals.h>
#import <Availability.h>
NS_ASSUME_NONNULL_BEGIN
extern NSString *const GCDAsyncUdpSocketException;
extern NSString *const GCDAsyncUdpSocketErrorDomain;
extern NSString *const GCDAsyncUdpSocketQueueName;
extern NSString *const GCDAsyncUdpSocketThreadName;
enum GCDAsyncUdpSocketError
{
typedef NS_ENUM(NSInteger, GCDAsyncUdpSocketError) {
GCDAsyncUdpSocketNoError = 0, // Never used
GCDAsyncUdpSocketBadConfigError, // Invalid configuration
GCDAsyncUdpSocketBadParamError, // Invalid parameter was passed
@ -28,7 +28,59 @@ enum GCDAsyncUdpSocketError
GCDAsyncUdpSocketClosedError, // The socket was closed
GCDAsyncUdpSocketOtherError, // Description provided in userInfo
};
typedef enum GCDAsyncUdpSocketError GCDAsyncUdpSocketError;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@class GCDAsyncUdpSocket;
@protocol GCDAsyncUdpSocketDelegate <NSObject>
@optional
/**
* By design, UDP is a connectionless protocol, and connecting is not needed.
* However, you may optionally choose to connect to a particular host for reasons
* outlined in the documentation for the various connect methods listed above.
*
* This method is called if one of the connect methods are invoked, and the connection is successful.
**/
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didConnectToAddress:(NSData *)address;
/**
* By design, UDP is a connectionless protocol, and connecting is not needed.
* However, you may optionally choose to connect to a particular host for reasons
* outlined in the documentation for the various connect methods listed above.
*
* This method is called if one of the connect methods are invoked, and the connection fails.
* This may happen, for example, if a domain name is given for the host and the domain name is unable to be resolved.
**/
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotConnect:(NSError * _Nullable)error;
/**
* Called when the datagram with the given tag has been sent.
**/
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag;
/**
* Called if an error occurs while trying to send a datagram.
* This could be due to a timeout, or something more serious such as the data being too large to fit in a sigle packet.
**/
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError * _Nullable)error;
/**
* Called when the socket has received the requested datagram.
**/
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data
fromAddress:(NSData *)address
withFilterContext:(nullable id)filterContext;
/**
* Called when the socket is closed.
**/
- (void)udpSocketDidClose:(GCDAsyncUdpSocket *)sock withError:(NSError * _Nullable)error;
@end
/**
* You may optionally set a receive filter for the socket.
@ -78,7 +130,7 @@ typedef enum GCDAsyncUdpSocketError GCDAsyncUdpSocketError;
* [udpSocket setReceiveFilter:filter withQueue:myParsingQueue];
*
**/
typedef BOOL (^GCDAsyncUdpSocketReceiveFilterBlock)(NSData *data, NSData *address, id *context);
typedef BOOL (^GCDAsyncUdpSocketReceiveFilterBlock)(NSData *data, NSData *address, id __nullable * __nonnull context);
/**
* You may optionally set a send filter for the socket.
@ -126,24 +178,24 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address,
*
* The delegate queue and socket queue can optionally be the same.
**/
- (id)init;
- (id)initWithSocketQueue:(dispatch_queue_t)sq;
- (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq;
- (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq socketQueue:(dispatch_queue_t)sq;
- (instancetype)init;
- (instancetype)initWithSocketQueue:(nullable dispatch_queue_t)sq;
- (instancetype)initWithDelegate:(nullable id <GCDAsyncUdpSocketDelegate>)aDelegate delegateQueue:(nullable dispatch_queue_t)dq;
- (instancetype)initWithDelegate:(nullable id <GCDAsyncUdpSocketDelegate>)aDelegate delegateQueue:(nullable dispatch_queue_t)dq socketQueue:(nullable dispatch_queue_t)sq;
#pragma mark Configuration
- (id)delegate;
- (void)setDelegate:(id)delegate;
- (void)synchronouslySetDelegate:(id)delegate;
- (nullable id <GCDAsyncUdpSocketDelegate>)delegate;
- (void)setDelegate:(nullable id <GCDAsyncUdpSocketDelegate>)delegate;
- (void)synchronouslySetDelegate:(nullable id <GCDAsyncUdpSocketDelegate>)delegate;
- (dispatch_queue_t)delegateQueue;
- (void)setDelegateQueue:(dispatch_queue_t)delegateQueue;
- (void)synchronouslySetDelegateQueue:(dispatch_queue_t)delegateQueue;
- (nullable dispatch_queue_t)delegateQueue;
- (void)setDelegateQueue:(nullable dispatch_queue_t)delegateQueue;
- (void)synchronouslySetDelegateQueue:(nullable dispatch_queue_t)delegateQueue;
- (void)getDelegate:(id *)delegatePtr delegateQueue:(dispatch_queue_t *)delegateQueuePtr;
- (void)setDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue;
- (void)synchronouslySetDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue;
- (void)getDelegate:(id <GCDAsyncUdpSocketDelegate> __nullable * __nullable)delegatePtr delegateQueue:(dispatch_queue_t __nullable * __nullable)delegateQueuePtr;
- (void)setDelegate:(nullable id <GCDAsyncUdpSocketDelegate>)delegate delegateQueue:(nullable dispatch_queue_t)delegateQueue;
- (void)synchronouslySetDelegate:(nullable id <GCDAsyncUdpSocketDelegate>)delegate delegateQueue:(nullable dispatch_queue_t)delegateQueue;
/**
* By default, both IPv4 and IPv6 are enabled.
@ -179,7 +231,7 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address,
/**
* Gets/Sets the maximum size of the buffer that will be allocated for receive operations.
* The default maximum size is 9216 bytes.
* The default maximum size is 65535 bytes.
*
* The theoretical maximum size of any IPv4 UDP packet is UINT16_MAX = 65535.
* The theoretical maximum size of any IPv6 UDP packet is UINT32_MAX = 4294967295.
@ -199,12 +251,27 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address,
- (uint32_t)maxReceiveIPv6BufferSize;
- (void)setMaxReceiveIPv6BufferSize:(uint32_t)max;
/**
* Gets/Sets the maximum size of the buffer that will be allocated for send operations.
* The default maximum size is 65535 bytes.
*
* Given that a typical link MTU is 1500 bytes, a large UDP datagram will have to be
* fragmented, and thats both expensive and risky (if one fragment goes missing, the
* entire datagram is lost). You are much better off sending a large number of smaller
* UDP datagrams, preferably using a path MTU algorithm to avoid fragmentation.
*
* You must set it before the sockt is created otherwise it won't work.
*
**/
- (uint16_t)maxSendBufferSize;
- (void)setMaxSendBufferSize:(uint16_t)max;
/**
* User data allows you to associate arbitrary information with the socket.
* This data is not used internally in any way.
**/
- (id)userData;
- (void)setUserData:(id)arbitraryUserData;
- (nullable id)userData;
- (void)setUserData:(nullable id)arbitraryUserData;
#pragma mark Diagnostics
@ -217,16 +284,16 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address,
* Note: Address info may not be available until after the socket has been binded, connected
* or until after data has been sent.
**/
- (NSData *)localAddress;
- (NSString *)localHost;
- (nullable NSData *)localAddress;
- (nullable NSString *)localHost;
- (uint16_t)localPort;
- (NSData *)localAddress_IPv4;
- (NSString *)localHost_IPv4;
- (nullable NSData *)localAddress_IPv4;
- (nullable NSString *)localHost_IPv4;
- (uint16_t)localPort_IPv4;
- (NSData *)localAddress_IPv6;
- (NSString *)localHost_IPv6;
- (nullable NSData *)localAddress_IPv6;
- (nullable NSString *)localHost_IPv6;
- (uint16_t)localPort_IPv6;
/**
@ -239,8 +306,8 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address,
* will not be available unless the socket is explicitly connected to a remote host/port.
* If the socket is not connected, these methods will return nil / 0.
**/
- (NSData *)connectedAddress;
- (NSString *)connectedHost;
- (nullable NSData *)connectedAddress;
- (nullable NSString *)connectedHost;
- (uint16_t)connectedPort;
/**
@ -319,7 +386,7 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address,
* On success, returns YES.
* Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass NULL for errPtr.
**/
- (BOOL)bindToPort:(uint16_t)port interface:(NSString *)interface error:(NSError **)errPtr;
- (BOOL)bindToPort:(uint16_t)port interface:(nullable NSString *)interface error:(NSError **)errPtr;
/**
* Binds the UDP socket to the given address, specified as a sockaddr structure wrapped in a NSData object.
@ -418,10 +485,21 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address,
* On success, returns YES.
* Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr.
**/
- (BOOL)joinMulticastGroup:(NSString *)group onInterface:(NSString *)interface error:(NSError **)errPtr;
- (BOOL)joinMulticastGroup:(NSString *)group onInterface:(nullable NSString *)interface error:(NSError **)errPtr;
- (BOOL)leaveMulticastGroup:(NSString *)group error:(NSError **)errPtr;
- (BOOL)leaveMulticastGroup:(NSString *)group onInterface:(NSString *)interface error:(NSError **)errPtr;
- (BOOL)leaveMulticastGroup:(NSString *)group onInterface:(nullable NSString *)interface error:(NSError **)errPtr;
#pragma mark Reuse Port
/**
* By default, only one socket can be bound to a given IP address + port at a time.
* To enable multiple processes to simultaneously bind to the same address+port,
* you need to enable this functionality in the socket. All processes that wish to
* use the address+port simultaneously must all enable reuse port on the socket
* bound to that port.
**/
- (BOOL)enableReusePort:(BOOL)flag error:(NSError **)errPtr;
#pragma mark Broadcast
@ -597,7 +675,7 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address,
* Note: This method invokes setSendFilter:withQueue:isAsynchronous: (documented below),
* passing YES for the isAsynchronous parameter.
**/
- (void)setSendFilter:(GCDAsyncUdpSocketSendFilterBlock)filterBlock withQueue:(dispatch_queue_t)filterQueue;
- (void)setSendFilter:(nullable GCDAsyncUdpSocketSendFilterBlock)filterBlock withQueue:(nullable dispatch_queue_t)filterQueue;
/**
* The receive filter can be run via dispatch_async or dispatch_sync.
@ -612,8 +690,8 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address,
* then you cannot perform any tasks which may invoke dispatch_sync on the socket queue.
* For example, you can't query properties on the socket.
**/
- (void)setSendFilter:(GCDAsyncUdpSocketSendFilterBlock)filterBlock
withQueue:(dispatch_queue_t)filterQueue
- (void)setSendFilter:(nullable GCDAsyncUdpSocketSendFilterBlock)filterBlock
withQueue:(nullable dispatch_queue_t)filterQueue
isAsynchronous:(BOOL)isAsynchronous;
#pragma mark Receiving
@ -729,7 +807,7 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address,
* Note: This method invokes setReceiveFilter:withQueue:isAsynchronous: (documented below),
* passing YES for the isAsynchronous parameter.
**/
- (void)setReceiveFilter:(GCDAsyncUdpSocketReceiveFilterBlock)filterBlock withQueue:(dispatch_queue_t)filterQueue;
- (void)setReceiveFilter:(nullable GCDAsyncUdpSocketReceiveFilterBlock)filterBlock withQueue:(nullable dispatch_queue_t)filterQueue;
/**
* The receive filter can be run via dispatch_async or dispatch_sync.
@ -744,8 +822,8 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address,
* then you cannot perform any tasks which may invoke dispatch_sync on the socket queue.
* For example, you can't query properties on the socket.
**/
- (void)setReceiveFilter:(GCDAsyncUdpSocketReceiveFilterBlock)filterBlock
withQueue:(dispatch_queue_t)filterQueue
- (void)setReceiveFilter:(nullable GCDAsyncUdpSocketReceiveFilterBlock)filterBlock
withQueue:(nullable dispatch_queue_t)filterQueue
isAsynchronous:(BOOL)isAsynchronous;
#pragma mark Closing
@ -897,8 +975,8 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address,
* However, if you need one for any reason,
* these methods are a convenient way to get access to a safe instance of one.
**/
- (CFReadStreamRef)readStream;
- (CFWriteStreamRef)writeStream;
- (nullable CFReadStreamRef)readStream;
- (nullable CFWriteStreamRef)writeStream;
/**
* This method is only available from within the context of a performBlock: invocation.
@ -931,66 +1009,16 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address,
* Extracting host/port/family information from raw address data.
**/
+ (NSString *)hostFromAddress:(NSData *)address;
+ (nullable NSString *)hostFromAddress:(NSData *)address;
+ (uint16_t)portFromAddress:(NSData *)address;
+ (int)familyFromAddress:(NSData *)address;
+ (BOOL)isIPv4Address:(NSData *)address;
+ (BOOL)isIPv6Address:(NSData *)address;
+ (BOOL)getHost:(NSString **)hostPtr port:(uint16_t *)portPtr fromAddress:(NSData *)address;
+ (BOOL)getHost:(NSString **)hostPtr port:(uint16_t *)portPtr family:(int *)afPtr fromAddress:(NSData *)address;
@end
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@protocol GCDAsyncUdpSocketDelegate
@optional
/**
* By design, UDP is a connectionless protocol, and connecting is not needed.
* However, you may optionally choose to connect to a particular host for reasons
* outlined in the documentation for the various connect methods listed above.
*
* This method is called if one of the connect methods are invoked, and the connection is successful.
**/
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didConnectToAddress:(NSData *)address;
/**
* By design, UDP is a connectionless protocol, and connecting is not needed.
* However, you may optionally choose to connect to a particular host for reasons
* outlined in the documentation for the various connect methods listed above.
*
* This method is called if one of the connect methods are invoked, and the connection fails.
* This may happen, for example, if a domain name is given for the host and the domain name is unable to be resolved.
**/
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotConnect:(NSError *)error;
/**
* Called when the datagram with the given tag has been sent.
**/
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag;
/**
* Called if an error occurs while trying to send a datagram.
* This could be due to a timeout, or something more serious such as the data being too large to fit in a sigle packet.
**/
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError *)error;
/**
* Called when the socket has received the requested datagram.
**/
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data
fromAddress:(NSData *)address
withFilterContext:(id)filterContext;
/**
* Called when the socket is closed.
**/
- (void)udpSocketDidClose:(GCDAsyncUdpSocket *)sock withError:(NSError *)error;
+ (BOOL)getHost:(NSString * __nullable * __nullable)hostPtr port:(uint16_t * __nullable)portPtr fromAddress:(NSData *)address;
+ (BOOL)getHost:(NSString * __nullable * __nullable)hostPtr port:(uint16_t * __nullable)portPtr family:(int * __nullable)afPtr fromAddress:(NSData *)address;
@end
NS_ASSUME_NONNULL_END

View File

@ -166,6 +166,8 @@ enum GCDAsyncUdpSocketConfig
uint16_t max4ReceiveSize;
uint32_t max6ReceiveSize;
uint16_t maxSendSize;
int socket4FD;
int socket6FD;
@ -350,14 +352,14 @@ enum GCDAsyncUdpSocketConfig
return [self initWithDelegate:nil delegateQueue:NULL socketQueue:sq];
}
- (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq
- (id)initWithDelegate:(id <GCDAsyncUdpSocketDelegate>)aDelegate delegateQueue:(dispatch_queue_t)dq
{
LogTrace();
return [self initWithDelegate:aDelegate delegateQueue:dq socketQueue:NULL];
}
- (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq socketQueue:(dispatch_queue_t)sq
- (id)initWithDelegate:(id <GCDAsyncUdpSocketDelegate>)aDelegate delegateQueue:(dispatch_queue_t)dq socketQueue:(dispatch_queue_t)sq
{
LogTrace();
@ -373,9 +375,11 @@ enum GCDAsyncUdpSocketConfig
#endif
}
max4ReceiveSize = 9216;
max6ReceiveSize = 9216;
max4ReceiveSize = 65535;
max6ReceiveSize = 65535;
maxSendSize = 65535;
socket4FD = SOCKET_NULL;
socket6FD = SOCKET_NULL;
@ -488,7 +492,7 @@ enum GCDAsyncUdpSocketConfig
}
}
- (void)setDelegate:(id)newDelegate synchronously:(BOOL)synchronously
- (void)setDelegate:(id <GCDAsyncUdpSocketDelegate>)newDelegate synchronously:(BOOL)synchronously
{
dispatch_block_t block = ^{
delegate = newDelegate;
@ -505,12 +509,12 @@ enum GCDAsyncUdpSocketConfig
}
}
- (void)setDelegate:(id)newDelegate
- (void)setDelegate:(id <GCDAsyncUdpSocketDelegate>)newDelegate
{
[self setDelegate:newDelegate synchronously:NO];
}
- (void)synchronouslySetDelegate:(id)newDelegate
- (void)synchronouslySetDelegate:(id <GCDAsyncUdpSocketDelegate>)newDelegate
{
[self setDelegate:newDelegate synchronously:YES];
}
@ -566,7 +570,7 @@ enum GCDAsyncUdpSocketConfig
[self setDelegateQueue:newDelegateQueue synchronously:YES];
}
- (void)getDelegate:(id *)delegatePtr delegateQueue:(dispatch_queue_t *)delegateQueuePtr
- (void)getDelegate:(id <GCDAsyncUdpSocketDelegate> *)delegatePtr delegateQueue:(dispatch_queue_t *)delegateQueuePtr
{
if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey))
{
@ -588,7 +592,7 @@ enum GCDAsyncUdpSocketConfig
}
}
- (void)setDelegate:(id)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue synchronously:(BOOL)synchronously
- (void)setDelegate:(id <GCDAsyncUdpSocketDelegate>)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue synchronously:(BOOL)synchronously
{
dispatch_block_t block = ^{
@ -613,12 +617,12 @@ enum GCDAsyncUdpSocketConfig
}
}
- (void)setDelegate:(id)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue
- (void)setDelegate:(id <GCDAsyncUdpSocketDelegate>)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue
{
[self setDelegate:newDelegate delegateQueue:newDelegateQueue synchronously:NO];
}
- (void)synchronouslySetDelegate:(id)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue
- (void)synchronouslySetDelegate:(id <GCDAsyncUdpSocketDelegate>)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue
{
[self setDelegate:newDelegate delegateQueue:newDelegateQueue synchronously:YES];
}
@ -864,6 +868,37 @@ enum GCDAsyncUdpSocketConfig
dispatch_async(socketQueue, block);
}
- (void)setMaxSendBufferSize:(uint16_t)max
{
dispatch_block_t block = ^{
LogVerbose(@"%@ %u", THIS_METHOD, (unsigned)max);
maxSendSize = max;
};
if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey))
block();
else
dispatch_async(socketQueue, block);
}
- (uint16_t)maxSendBufferSize
{
__block uint16_t result = 0;
dispatch_block_t block = ^{
result = maxSendSize;
};
if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey))
block();
else
dispatch_sync(socketQueue, block);
return result;
}
- (id)userData
{
@ -906,9 +941,9 @@ enum GCDAsyncUdpSocketConfig
{
LogTrace();
if (delegateQueue && [delegate respondsToSelector:@selector(udpSocket:didConnectToAddress:)])
__strong id theDelegate = delegate;
if (delegateQueue && [theDelegate respondsToSelector:@selector(udpSocket:didConnectToAddress:)])
{
id theDelegate = delegate;
NSData *address = [anAddress copy]; // In case param is NSMutableData
dispatch_async(delegateQueue, ^{ @autoreleasepool {
@ -922,10 +957,9 @@ enum GCDAsyncUdpSocketConfig
{
LogTrace();
if (delegateQueue && [delegate respondsToSelector:@selector(udpSocket:didNotConnect:)])
__strong id theDelegate = delegate;
if (delegateQueue && [theDelegate respondsToSelector:@selector(udpSocket:didNotConnect:)])
{
id theDelegate = delegate;
dispatch_async(delegateQueue, ^{ @autoreleasepool {
[theDelegate udpSocket:self didNotConnect:error];
@ -937,10 +971,9 @@ enum GCDAsyncUdpSocketConfig
{
LogTrace();
if (delegateQueue && [delegate respondsToSelector:@selector(udpSocket:didSendDataWithTag:)])
__strong id theDelegate = delegate;
if (delegateQueue && [theDelegate respondsToSelector:@selector(udpSocket:didSendDataWithTag:)])
{
id theDelegate = delegate;
dispatch_async(delegateQueue, ^{ @autoreleasepool {
[theDelegate udpSocket:self didSendDataWithTag:tag];
@ -952,10 +985,9 @@ enum GCDAsyncUdpSocketConfig
{
LogTrace();
if (delegateQueue && [delegate respondsToSelector:@selector(udpSocket:didNotSendDataWithTag:dueToError:)])
__strong id theDelegate = delegate;
if (delegateQueue && [theDelegate respondsToSelector:@selector(udpSocket:didNotSendDataWithTag:dueToError:)])
{
id theDelegate = delegate;
dispatch_async(delegateQueue, ^{ @autoreleasepool {
[theDelegate udpSocket:self didNotSendDataWithTag:tag dueToError:error];
@ -969,10 +1001,9 @@ enum GCDAsyncUdpSocketConfig
SEL selector = @selector(udpSocket:didReceiveData:fromAddress:withFilterContext:);
if (delegateQueue && [delegate respondsToSelector:selector])
__strong id theDelegate = delegate;
if (delegateQueue && [theDelegate respondsToSelector:selector])
{
id theDelegate = delegate;
dispatch_async(delegateQueue, ^{ @autoreleasepool {
[theDelegate udpSocket:self didReceiveData:data fromAddress:address withFilterContext:context];
@ -984,10 +1015,9 @@ enum GCDAsyncUdpSocketConfig
{
LogTrace();
if (delegateQueue && [delegate respondsToSelector:@selector(udpSocketDidClose:withError:)])
__strong id theDelegate = delegate;
if (delegateQueue && [theDelegate respondsToSelector:@selector(udpSocketDidClose:withError:)])
{
id theDelegate = delegate;
dispatch_async(delegateQueue, ^{ @autoreleasepool {
[theDelegate udpSocketDidClose:self withError:error];
@ -1204,9 +1234,17 @@ enum GCDAsyncUdpSocketConfig
}
else if (res->ai_family == AF_INET6)
{
// Found IPv6 address
// Wrap the native address structure and add to list
// Fixes connection issues with IPv6, it is the same solution for udp socket.
// https://github.com/robbiehanson/CocoaAsyncSocket/issues/429#issuecomment-222477158
struct sockaddr_in6 *sockaddr = (struct sockaddr_in6 *)res->ai_addr;
in_port_t *portPtr = &sockaddr->sin6_port;
if ((portPtr != NULL) && (*portPtr == 0)) {
*portPtr = htons(port);
}
// Found IPv6 address
// Wrap the native address structure and add to list
[addresses addObject:[NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]];
}
}
@ -1982,6 +2020,39 @@ enum GCDAsyncUdpSocketConfig
close(socketFD);
return SOCKET_NULL;
}
/**
* The theoretical maximum size of any IPv4 UDP packet is UINT16_MAX = 65535.
* The theoretical maximum size of any IPv6 UDP packet is UINT32_MAX = 4294967295.
*
* The default maximum size of the UDP buffer in iOS is 9216 bytes.
*
* This is the reason of #222(GCD does not necessarily return the size of an entire UDP packet) and
* #535(GCDAsyncUDPSocket can not send data when data is greater than 9K)
*
*
* Enlarge the maximum size of UDP packet.
* I can not ensure the protocol type now so that the max size is set to 65535 :)
**/
status = setsockopt(socketFD, SOL_SOCKET, SO_SNDBUF, (const char*)&maxSendSize, sizeof(int));
if (status == -1)
{
if (errPtr)
*errPtr = [self errnoErrorWithReason:@"Error setting send buffer size (setsockopt)"];
close(socketFD);
return SOCKET_NULL;
}
status = setsockopt(socketFD, SOL_SOCKET, SO_RCVBUF, (const char*)&maxSendSize, sizeof(int));
if (status == -1)
{
if (errPtr)
*errPtr = [self errnoErrorWithReason:@"Error setting receive buffer size (setsockopt)"];
close(socketFD);
return SOCKET_NULL;
}
return socketFD;
};
@ -3453,6 +3524,70 @@ enum GCDAsyncUdpSocketConfig
return result;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Reuse port
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (BOOL)enableReusePort:(BOOL)flag error:(NSError **)errPtr
{
__block BOOL result = NO;
__block NSError *err = nil;
dispatch_block_t block = ^{ @autoreleasepool {
if (![self preOp:&err])
{
return_from_block;
}
if ((flags & kDidCreateSockets) == 0)
{
if (![self createSockets:&err])
{
return_from_block;
}
}
int value = flag ? 1 : 0;
if (socket4FD != SOCKET_NULL)
{
int error = setsockopt(socket4FD, SOL_SOCKET, SO_REUSEPORT, (const void *)&value, sizeof(value));
if (error)
{
err = [self errnoErrorWithReason:@"Error in setsockopt() function"];
return_from_block;
}
result = YES;
}
if (socket6FD != SOCKET_NULL)
{
int error = setsockopt(socket6FD, SOL_SOCKET, SO_REUSEPORT, (const void *)&value, sizeof(value));
if (error)
{
err = [self errnoErrorWithReason:@"Error in setsockopt() function"];
return_from_block;
}
result = YES;
}
}};
if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey))
block();
else
dispatch_sync(socketQueue, block);
if (errPtr)
*errPtr = err;
return result;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Broadcast
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -3525,6 +3660,8 @@ enum GCDAsyncUdpSocketConfig
LogWarn(@"Ignoring attempt to send nil/empty data.");
return;
}
GCDAsyncUdpSendPacket *packet = [[GCDAsyncUdpSendPacket alloc] initWithData:data timeout:timeout tag:tag];
@ -4293,7 +4430,9 @@ enum GCDAsyncUdpSocketConfig
struct sockaddr_in sockaddr4;
socklen_t sockaddr4len = sizeof(sockaddr4);
size_t bufSize = MIN(max4ReceiveSize, socket4FDBytesAvailable);
// #222: GCD does not necessarily return the size of an entire UDP packet
// from dispatch_source_get_data(), so we must use the maximum packet size.
size_t bufSize = max4ReceiveSize;
void *buf = malloc(bufSize);
result = recvfrom(socket4FD, buf, bufSize, 0, (struct sockaddr *)&sockaddr4, &sockaddr4len);
@ -4328,7 +4467,9 @@ enum GCDAsyncUdpSocketConfig
struct sockaddr_in6 sockaddr6;
socklen_t sockaddr6len = sizeof(sockaddr6);
size_t bufSize = MIN(max6ReceiveSize, socket6FDBytesAvailable);
// #222: GCD does not necessarily return the size of an entire UDP packet
// from dispatch_source_get_data(), so we must use the maximum packet size.
size_t bufSize = max6ReceiveSize;
void *buf = malloc(bufSize);
result = recvfrom(socket6FD, buf, bufSize, 0, (struct sockaddr *)&sockaddr6, &sockaddr6len);

View File

@ -96,6 +96,7 @@ NSString *const RCTUDPErrorDomain = @"RCTUDPErrorDomain";
[_udpSocket setMaxReceiveIPv4BufferSize:UINT16_MAX];
[_udpSocket setMaxReceiveIPv6BufferSize:UINT16_MAX];
[_udpSocket enableReusePort:true error:error];
BOOL result;
if (address) {