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 <TargetConditionals.h>
#import <Availability.h> #import <Availability.h>
NS_ASSUME_NONNULL_BEGIN
extern NSString *const GCDAsyncUdpSocketException; extern NSString *const GCDAsyncUdpSocketException;
extern NSString *const GCDAsyncUdpSocketErrorDomain; extern NSString *const GCDAsyncUdpSocketErrorDomain;
extern NSString *const GCDAsyncUdpSocketQueueName; extern NSString *const GCDAsyncUdpSocketQueueName;
extern NSString *const GCDAsyncUdpSocketThreadName; extern NSString *const GCDAsyncUdpSocketThreadName;
enum GCDAsyncUdpSocketError typedef NS_ENUM(NSInteger, GCDAsyncUdpSocketError) {
{
GCDAsyncUdpSocketNoError = 0, // Never used GCDAsyncUdpSocketNoError = 0, // Never used
GCDAsyncUdpSocketBadConfigError, // Invalid configuration GCDAsyncUdpSocketBadConfigError, // Invalid configuration
GCDAsyncUdpSocketBadParamError, // Invalid parameter was passed GCDAsyncUdpSocketBadParamError, // Invalid parameter was passed
@ -28,7 +28,59 @@ enum GCDAsyncUdpSocketError
GCDAsyncUdpSocketClosedError, // The socket was closed GCDAsyncUdpSocketClosedError, // The socket was closed
GCDAsyncUdpSocketOtherError, // Description provided in userInfo 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. * You may optionally set a receive filter for the socket.
@ -78,7 +130,7 @@ typedef enum GCDAsyncUdpSocketError GCDAsyncUdpSocketError;
* [udpSocket setReceiveFilter:filter withQueue:myParsingQueue]; * [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. * 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. * The delegate queue and socket queue can optionally be the same.
**/ **/
- (id)init; - (instancetype)init;
- (id)initWithSocketQueue:(dispatch_queue_t)sq; - (instancetype)initWithSocketQueue:(nullable dispatch_queue_t)sq;
- (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq; - (instancetype)initWithDelegate:(nullable id <GCDAsyncUdpSocketDelegate>)aDelegate delegateQueue:(nullable dispatch_queue_t)dq;
- (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq socketQueue:(dispatch_queue_t)sq; - (instancetype)initWithDelegate:(nullable id <GCDAsyncUdpSocketDelegate>)aDelegate delegateQueue:(nullable dispatch_queue_t)dq socketQueue:(nullable dispatch_queue_t)sq;
#pragma mark Configuration #pragma mark Configuration
- (id)delegate; - (nullable id <GCDAsyncUdpSocketDelegate>)delegate;
- (void)setDelegate:(id)delegate; - (void)setDelegate:(nullable id <GCDAsyncUdpSocketDelegate>)delegate;
- (void)synchronouslySetDelegate:(id)delegate; - (void)synchronouslySetDelegate:(nullable id <GCDAsyncUdpSocketDelegate>)delegate;
- (dispatch_queue_t)delegateQueue; - (nullable dispatch_queue_t)delegateQueue;
- (void)setDelegateQueue:(dispatch_queue_t)delegateQueue; - (void)setDelegateQueue:(nullable dispatch_queue_t)delegateQueue;
- (void)synchronouslySetDelegateQueue:(dispatch_queue_t)delegateQueue; - (void)synchronouslySetDelegateQueue:(nullable dispatch_queue_t)delegateQueue;
- (void)getDelegate:(id *)delegatePtr delegateQueue:(dispatch_queue_t *)delegateQueuePtr; - (void)getDelegate:(id <GCDAsyncUdpSocketDelegate> __nullable * __nullable)delegatePtr delegateQueue:(dispatch_queue_t __nullable * __nullable)delegateQueuePtr;
- (void)setDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue; - (void)setDelegate:(nullable id <GCDAsyncUdpSocketDelegate>)delegate delegateQueue:(nullable dispatch_queue_t)delegateQueue;
- (void)synchronouslySetDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue; - (void)synchronouslySetDelegate:(nullable id <GCDAsyncUdpSocketDelegate>)delegate delegateQueue:(nullable dispatch_queue_t)delegateQueue;
/** /**
* By default, both IPv4 and IPv6 are enabled. * 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. * 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 IPv4 UDP packet is UINT16_MAX = 65535.
* The theoretical maximum size of any IPv6 UDP packet is UINT32_MAX = 4294967295. * 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; - (uint32_t)maxReceiveIPv6BufferSize;
- (void)setMaxReceiveIPv6BufferSize:(uint32_t)max; - (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. * User data allows you to associate arbitrary information with the socket.
* This data is not used internally in any way. * This data is not used internally in any way.
**/ **/
- (id)userData; - (nullable id)userData;
- (void)setUserData:(id)arbitraryUserData; - (void)setUserData:(nullable id)arbitraryUserData;
#pragma mark Diagnostics #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 * Note: Address info may not be available until after the socket has been binded, connected
* or until after data has been sent. * or until after data has been sent.
**/ **/
- (NSData *)localAddress; - (nullable NSData *)localAddress;
- (NSString *)localHost; - (nullable NSString *)localHost;
- (uint16_t)localPort; - (uint16_t)localPort;
- (NSData *)localAddress_IPv4; - (nullable NSData *)localAddress_IPv4;
- (NSString *)localHost_IPv4; - (nullable NSString *)localHost_IPv4;
- (uint16_t)localPort_IPv4; - (uint16_t)localPort_IPv4;
- (NSData *)localAddress_IPv6; - (nullable NSData *)localAddress_IPv6;
- (NSString *)localHost_IPv6; - (nullable NSString *)localHost_IPv6;
- (uint16_t)localPort_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. * 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. * If the socket is not connected, these methods will return nil / 0.
**/ **/
- (NSData *)connectedAddress; - (nullable NSData *)connectedAddress;
- (NSString *)connectedHost; - (nullable NSString *)connectedHost;
- (uint16_t)connectedPort; - (uint16_t)connectedPort;
/** /**
@ -319,7 +386,7 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address,
* On success, returns YES. * On success, returns YES.
* Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass NULL for errPtr. * 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. * 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. * On success, returns YES.
* Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr. * 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 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 #pragma mark Broadcast
@ -597,7 +675,7 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address,
* Note: This method invokes setSendFilter:withQueue:isAsynchronous: (documented below), * Note: This method invokes setSendFilter:withQueue:isAsynchronous: (documented below),
* passing YES for the isAsynchronous parameter. * 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. * 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. * 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. * For example, you can't query properties on the socket.
**/ **/
- (void)setSendFilter:(GCDAsyncUdpSocketSendFilterBlock)filterBlock - (void)setSendFilter:(nullable GCDAsyncUdpSocketSendFilterBlock)filterBlock
withQueue:(dispatch_queue_t)filterQueue withQueue:(nullable dispatch_queue_t)filterQueue
isAsynchronous:(BOOL)isAsynchronous; isAsynchronous:(BOOL)isAsynchronous;
#pragma mark Receiving #pragma mark Receiving
@ -729,7 +807,7 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address,
* Note: This method invokes setReceiveFilter:withQueue:isAsynchronous: (documented below), * Note: This method invokes setReceiveFilter:withQueue:isAsynchronous: (documented below),
* passing YES for the isAsynchronous parameter. * 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. * 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. * 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. * For example, you can't query properties on the socket.
**/ **/
- (void)setReceiveFilter:(GCDAsyncUdpSocketReceiveFilterBlock)filterBlock - (void)setReceiveFilter:(nullable GCDAsyncUdpSocketReceiveFilterBlock)filterBlock
withQueue:(dispatch_queue_t)filterQueue withQueue:(nullable dispatch_queue_t)filterQueue
isAsynchronous:(BOOL)isAsynchronous; isAsynchronous:(BOOL)isAsynchronous;
#pragma mark Closing #pragma mark Closing
@ -897,8 +975,8 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address,
* However, if you need one for any reason, * However, if you need one for any reason,
* these methods are a convenient way to get access to a safe instance of one. * these methods are a convenient way to get access to a safe instance of one.
**/ **/
- (CFReadStreamRef)readStream; - (nullable CFReadStreamRef)readStream;
- (CFWriteStreamRef)writeStream; - (nullable CFWriteStreamRef)writeStream;
/** /**
* This method is only available from within the context of a performBlock: invocation. * 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. * Extracting host/port/family information from raw address data.
**/ **/
+ (NSString *)hostFromAddress:(NSData *)address; + (nullable NSString *)hostFromAddress:(NSData *)address;
+ (uint16_t)portFromAddress:(NSData *)address; + (uint16_t)portFromAddress:(NSData *)address;
+ (int)familyFromAddress:(NSData *)address; + (int)familyFromAddress:(NSData *)address;
+ (BOOL)isIPv4Address:(NSData *)address; + (BOOL)isIPv4Address:(NSData *)address;
+ (BOOL)isIPv6Address:(NSData *)address; + (BOOL)isIPv6Address:(NSData *)address;
+ (BOOL)getHost:(NSString **)hostPtr port:(uint16_t *)portPtr fromAddress:(NSData *)address; + (BOOL)getHost:(NSString * __nullable * __nullable)hostPtr port:(uint16_t * __nullable)portPtr fromAddress:(NSData *)address;
+ (BOOL)getHost:(NSString **)hostPtr port:(uint16_t *)portPtr family:(int *)afPtr fromAddress:(NSData *)address; + (BOOL)getHost:(NSString * __nullable * __nullable)hostPtr port:(uint16_t * __nullable)portPtr family:(int * __nullable)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;
@end @end
NS_ASSUME_NONNULL_END

View File

@ -167,6 +167,8 @@ enum GCDAsyncUdpSocketConfig
uint16_t max4ReceiveSize; uint16_t max4ReceiveSize;
uint32_t max6ReceiveSize; uint32_t max6ReceiveSize;
uint16_t maxSendSize;
int socket4FD; int socket4FD;
int socket6FD; int socket6FD;
@ -350,14 +352,14 @@ enum GCDAsyncUdpSocketConfig
return [self initWithDelegate:nil delegateQueue:NULL socketQueue:sq]; 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(); LogTrace();
return [self initWithDelegate:aDelegate delegateQueue:dq socketQueue:NULL]; 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(); LogTrace();
@ -373,8 +375,10 @@ enum GCDAsyncUdpSocketConfig
#endif #endif
} }
max4ReceiveSize = 9216; max4ReceiveSize = 65535;
max6ReceiveSize = 9216; max6ReceiveSize = 65535;
maxSendSize = 65535;
socket4FD = SOCKET_NULL; socket4FD = SOCKET_NULL;
socket6FD = 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 = ^{ dispatch_block_t block = ^{
delegate = newDelegate; delegate = newDelegate;
@ -505,12 +509,12 @@ enum GCDAsyncUdpSocketConfig
} }
} }
- (void)setDelegate:(id)newDelegate - (void)setDelegate:(id <GCDAsyncUdpSocketDelegate>)newDelegate
{ {
[self setDelegate:newDelegate synchronously:NO]; [self setDelegate:newDelegate synchronously:NO];
} }
- (void)synchronouslySetDelegate:(id)newDelegate - (void)synchronouslySetDelegate:(id <GCDAsyncUdpSocketDelegate>)newDelegate
{ {
[self setDelegate:newDelegate synchronously:YES]; [self setDelegate:newDelegate synchronously:YES];
} }
@ -566,7 +570,7 @@ enum GCDAsyncUdpSocketConfig
[self setDelegateQueue:newDelegateQueue synchronously:YES]; [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)) 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 = ^{ 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]; [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]; [self setDelegate:newDelegate delegateQueue:newDelegateQueue synchronously:YES];
} }
@ -864,6 +868,37 @@ enum GCDAsyncUdpSocketConfig
dispatch_async(socketQueue, block); 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 - (id)userData
{ {
@ -906,9 +941,9 @@ enum GCDAsyncUdpSocketConfig
{ {
LogTrace(); 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 NSData *address = [anAddress copy]; // In case param is NSMutableData
dispatch_async(delegateQueue, ^{ @autoreleasepool { dispatch_async(delegateQueue, ^{ @autoreleasepool {
@ -922,10 +957,9 @@ enum GCDAsyncUdpSocketConfig
{ {
LogTrace(); 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 { dispatch_async(delegateQueue, ^{ @autoreleasepool {
[theDelegate udpSocket:self didNotConnect:error]; [theDelegate udpSocket:self didNotConnect:error];
@ -937,10 +971,9 @@ enum GCDAsyncUdpSocketConfig
{ {
LogTrace(); 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 { dispatch_async(delegateQueue, ^{ @autoreleasepool {
[theDelegate udpSocket:self didSendDataWithTag:tag]; [theDelegate udpSocket:self didSendDataWithTag:tag];
@ -952,10 +985,9 @@ enum GCDAsyncUdpSocketConfig
{ {
LogTrace(); 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 { dispatch_async(delegateQueue, ^{ @autoreleasepool {
[theDelegate udpSocket:self didNotSendDataWithTag:tag dueToError:error]; [theDelegate udpSocket:self didNotSendDataWithTag:tag dueToError:error];
@ -969,10 +1001,9 @@ enum GCDAsyncUdpSocketConfig
SEL selector = @selector(udpSocket:didReceiveData:fromAddress:withFilterContext:); 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 { dispatch_async(delegateQueue, ^{ @autoreleasepool {
[theDelegate udpSocket:self didReceiveData:data fromAddress:address withFilterContext:context]; [theDelegate udpSocket:self didReceiveData:data fromAddress:address withFilterContext:context];
@ -984,10 +1015,9 @@ enum GCDAsyncUdpSocketConfig
{ {
LogTrace(); 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 { dispatch_async(delegateQueue, ^{ @autoreleasepool {
[theDelegate udpSocketDidClose:self withError:error]; [theDelegate udpSocketDidClose:self withError:error];
@ -1204,9 +1234,17 @@ enum GCDAsyncUdpSocketConfig
} }
else if (res->ai_family == AF_INET6) else if (res->ai_family == AF_INET6)
{ {
// 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 // Found IPv6 address
// Wrap the native address structure and add to list // Wrap the native address structure and add to list
[addresses addObject:[NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]]; [addresses addObject:[NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]];
} }
} }
@ -1983,6 +2021,39 @@ enum GCDAsyncUdpSocketConfig
return SOCKET_NULL; 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; return socketFD;
}; };
@ -3453,6 +3524,70 @@ enum GCDAsyncUdpSocketConfig
return result; 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 #pragma mark Broadcast
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -3526,6 +3661,8 @@ enum GCDAsyncUdpSocketConfig
return; return;
} }
GCDAsyncUdpSendPacket *packet = [[GCDAsyncUdpSendPacket alloc] initWithData:data timeout:timeout tag:tag]; GCDAsyncUdpSendPacket *packet = [[GCDAsyncUdpSendPacket alloc] initWithData:data timeout:timeout tag:tag];
dispatch_async(socketQueue, ^{ @autoreleasepool { dispatch_async(socketQueue, ^{ @autoreleasepool {
@ -4293,7 +4430,9 @@ enum GCDAsyncUdpSocketConfig
struct sockaddr_in sockaddr4; struct sockaddr_in sockaddr4;
socklen_t sockaddr4len = sizeof(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); void *buf = malloc(bufSize);
result = recvfrom(socket4FD, buf, bufSize, 0, (struct sockaddr *)&sockaddr4, &sockaddr4len); result = recvfrom(socket4FD, buf, bufSize, 0, (struct sockaddr *)&sockaddr4, &sockaddr4len);
@ -4328,7 +4467,9 @@ enum GCDAsyncUdpSocketConfig
struct sockaddr_in6 sockaddr6; struct sockaddr_in6 sockaddr6;
socklen_t sockaddr6len = sizeof(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); void *buf = malloc(bufSize);
result = recvfrom(socket6FD, buf, bufSize, 0, (struct sockaddr *)&sockaddr6, &sockaddr6len); 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 setMaxReceiveIPv4BufferSize:UINT16_MAX];
[_udpSocket setMaxReceiveIPv6BufferSize:UINT16_MAX]; [_udpSocket setMaxReceiveIPv6BufferSize:UINT16_MAX];
[_udpSocket enableReusePort:true error:error];
BOOL result; BOOL result;
if (address) { if (address) {