mirror of
https://github.com/status-im/react-native-udp.git
synced 2025-02-26 23:40:30 +00:00
Initial commit
This commit is contained in:
commit
df9879dd5f
33
.flowconfig
Normal file
33
.flowconfig
Normal file
@ -0,0 +1,33 @@
|
||||
[ignore]
|
||||
|
||||
# We fork some components by platform.
|
||||
.*/*.web.js
|
||||
.*/*.android.js
|
||||
|
||||
# Some modules have their own node_modules with overlap
|
||||
.*/node_modules/node-haste/.*
|
||||
|
||||
# Ignore react-tools where there are overlaps, but don't ignore anything that
|
||||
# react-native relies on
|
||||
.*/node_modules/react-tools/src/vendor/core/ExecutionEnvironment.js
|
||||
.*/node_modules/react-tools/src/browser/eventPlugins/ResponderEventPlugin.js
|
||||
.*/node_modules/react-tools/src/browser/ui/React.js
|
||||
.*/node_modules/react-tools/src/core/ReactInstanceHandles.js
|
||||
.*/node_modules/react-tools/src/event/EventPropagators.js
|
||||
|
||||
# Ignore commoner tests
|
||||
.*/node_modules/react-tools/node_modules/commoner/test/.*
|
||||
|
||||
# See https://github.com/facebook/flow/issues/442
|
||||
.*/react-tools/node_modules/commoner/lib/reader.js
|
||||
|
||||
# Ignore jest
|
||||
.*/react-native/node_modules/jest-cli/.*
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
node_modules/react-native/Libraries/react-native/react-native-interface.js
|
||||
|
||||
[options]
|
||||
module.system=haste
|
30
.gitignore
vendored
Normal file
30
.gitignore
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directory
|
||||
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
|
||||
node_modules
|
||||
|
||||
*.xcodeproj/project.xcworkspace/
|
||||
*.xcodeproj/xcuserdata/
|
996
GCDAsyncSocket/GCDAsyncUdpSocket.h
Normal file
996
GCDAsyncSocket/GCDAsyncUdpSocket.h
Normal file
@ -0,0 +1,996 @@
|
||||
//
|
||||
// GCDAsyncUdpSocket
|
||||
//
|
||||
// This class is in the public domain.
|
||||
// Originally created by Robbie Hanson of Deusty LLC.
|
||||
// Updated and maintained by Deusty LLC and the Apple development community.
|
||||
//
|
||||
// https://github.com/robbiehanson/CocoaAsyncSocket
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <dispatch/dispatch.h>
|
||||
#import <TargetConditionals.h>
|
||||
#import <Availability.h>
|
||||
|
||||
extern NSString *const GCDAsyncUdpSocketException;
|
||||
extern NSString *const GCDAsyncUdpSocketErrorDomain;
|
||||
|
||||
extern NSString *const GCDAsyncUdpSocketQueueName;
|
||||
extern NSString *const GCDAsyncUdpSocketThreadName;
|
||||
|
||||
enum GCDAsyncUdpSocketError
|
||||
{
|
||||
GCDAsyncUdpSocketNoError = 0, // Never used
|
||||
GCDAsyncUdpSocketBadConfigError, // Invalid configuration
|
||||
GCDAsyncUdpSocketBadParamError, // Invalid parameter was passed
|
||||
GCDAsyncUdpSocketSendTimeoutError, // A send operation timed out
|
||||
GCDAsyncUdpSocketClosedError, // The socket was closed
|
||||
GCDAsyncUdpSocketOtherError, // Description provided in userInfo
|
||||
};
|
||||
typedef enum GCDAsyncUdpSocketError GCDAsyncUdpSocketError;
|
||||
|
||||
/**
|
||||
* You may optionally set a receive filter for the socket.
|
||||
* A filter can provide several useful features:
|
||||
*
|
||||
* 1. Many times udp packets need to be parsed.
|
||||
* Since the filter can run in its own independent queue, you can parallelize this parsing quite easily.
|
||||
* The end result is a parallel socket io, datagram parsing, and packet processing.
|
||||
*
|
||||
* 2. Many times udp packets are discarded because they are duplicate/unneeded/unsolicited.
|
||||
* The filter can prevent such packets from arriving at the delegate.
|
||||
* And because the filter can run in its own independent queue, this doesn't slow down the delegate.
|
||||
*
|
||||
* - Since the udp protocol does not guarantee delivery, udp packets may be lost.
|
||||
* Many protocols built atop udp thus provide various resend/re-request algorithms.
|
||||
* This sometimes results in duplicate packets arriving.
|
||||
* A filter may allow you to architect the duplicate detection code to run in parallel to normal processing.
|
||||
*
|
||||
* - Since the udp socket may be connectionless, its possible for unsolicited packets to arrive.
|
||||
* Such packets need to be ignored.
|
||||
*
|
||||
* 3. Sometimes traffic shapers are needed to simulate real world environments.
|
||||
* A filter allows you to write custom code to simulate such environments.
|
||||
* The ability to code this yourself is especially helpful when your simulated environment
|
||||
* is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router),
|
||||
* or the system tools to handle this aren't available (e.g. on a mobile device).
|
||||
*
|
||||
* @param data - The packet that was received.
|
||||
* @param address - The address the data was received from.
|
||||
* See utilities section for methods to extract info from address.
|
||||
* @param context - Out parameter you may optionally set, which will then be passed to the delegate method.
|
||||
* For example, filter block can parse the data and then,
|
||||
* pass the parsed data to the delegate.
|
||||
*
|
||||
* @returns - YES if the received packet should be passed onto the delegate.
|
||||
* NO if the received packet should be discarded, and not reported to the delegete.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* GCDAsyncUdpSocketReceiveFilterBlock filter = ^BOOL (NSData *data, NSData *address, id *context) {
|
||||
*
|
||||
* MyProtocolMessage *msg = [MyProtocol parseMessage:data];
|
||||
*
|
||||
* *context = response;
|
||||
* return (response != nil);
|
||||
* };
|
||||
* [udpSocket setReceiveFilter:filter withQueue:myParsingQueue];
|
||||
*
|
||||
**/
|
||||
typedef BOOL (^GCDAsyncUdpSocketReceiveFilterBlock)(NSData *data, NSData *address, id *context);
|
||||
|
||||
/**
|
||||
* You may optionally set a send filter for the socket.
|
||||
* A filter can provide several interesting possibilities:
|
||||
*
|
||||
* 1. Optional caching of resolved addresses for domain names.
|
||||
* The cache could later be consulted, resulting in fewer system calls to getaddrinfo.
|
||||
*
|
||||
* 2. Reusable modules of code for bandwidth monitoring.
|
||||
*
|
||||
* 3. Sometimes traffic shapers are needed to simulate real world environments.
|
||||
* A filter allows you to write custom code to simulate such environments.
|
||||
* The ability to code this yourself is especially helpful when your simulated environment
|
||||
* is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router),
|
||||
* or the system tools to handle this aren't available (e.g. on a mobile device).
|
||||
*
|
||||
* @param data - The packet that was received.
|
||||
* @param address - The address the data was received from.
|
||||
* See utilities section for methods to extract info from address.
|
||||
* @param tag - The tag that was passed in the send method.
|
||||
*
|
||||
* @returns - YES if the packet should actually be sent over the socket.
|
||||
* NO if the packet should be silently dropped (not sent over the socket).
|
||||
*
|
||||
* Regardless of the return value, the delegate will be informed that the packet was successfully sent.
|
||||
*
|
||||
**/
|
||||
typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, long tag);
|
||||
|
||||
|
||||
@interface GCDAsyncUdpSocket : NSObject
|
||||
|
||||
/**
|
||||
* GCDAsyncUdpSocket uses the standard delegate paradigm,
|
||||
* but executes all delegate callbacks on a given delegate dispatch queue.
|
||||
* This allows for maximum concurrency, while at the same time providing easy thread safety.
|
||||
*
|
||||
* You MUST set a delegate AND delegate dispatch queue before attempting to
|
||||
* use the socket, or you will get an error.
|
||||
*
|
||||
* The socket queue is optional.
|
||||
* If you pass NULL, GCDAsyncSocket will automatically create its own socket queue.
|
||||
* If you choose to provide a socket queue, the socket queue must not be a concurrent queue,
|
||||
* then please see the discussion for the method markSocketQueueTargetQueue.
|
||||
*
|
||||
* 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;
|
||||
|
||||
#pragma mark Configuration
|
||||
|
||||
- (id)delegate;
|
||||
- (void)setDelegate:(id)delegate;
|
||||
- (void)synchronouslySetDelegate:(id)delegate;
|
||||
|
||||
- (dispatch_queue_t)delegateQueue;
|
||||
- (void)setDelegateQueue:(dispatch_queue_t)delegateQueue;
|
||||
- (void)synchronouslySetDelegateQueue:(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;
|
||||
|
||||
/**
|
||||
* By default, both IPv4 and IPv6 are enabled.
|
||||
*
|
||||
* This means GCDAsyncUdpSocket automatically supports both protocols,
|
||||
* and can send to IPv4 or IPv6 addresses,
|
||||
* as well as receive over IPv4 and IPv6.
|
||||
*
|
||||
* For operations that require DNS resolution, GCDAsyncUdpSocket supports both IPv4 and IPv6.
|
||||
* If a DNS lookup returns only IPv4 results, GCDAsyncUdpSocket will automatically use IPv4.
|
||||
* If a DNS lookup returns only IPv6 results, GCDAsyncUdpSocket will automatically use IPv6.
|
||||
* If a DNS lookup returns both IPv4 and IPv6 results, then the protocol used depends on the configured preference.
|
||||
* If IPv4 is preferred, then IPv4 is used.
|
||||
* If IPv6 is preferred, then IPv6 is used.
|
||||
* If neutral, then the first IP version in the resolved array will be used.
|
||||
*
|
||||
* Starting with Mac OS X 10.7 Lion and iOS 5, the default IP preference is neutral.
|
||||
* On prior systems the default IP preference is IPv4.
|
||||
**/
|
||||
- (BOOL)isIPv4Enabled;
|
||||
- (void)setIPv4Enabled:(BOOL)flag;
|
||||
|
||||
- (BOOL)isIPv6Enabled;
|
||||
- (void)setIPv6Enabled:(BOOL)flag;
|
||||
|
||||
- (BOOL)isIPv4Preferred;
|
||||
- (BOOL)isIPv6Preferred;
|
||||
- (BOOL)isIPVersionNeutral;
|
||||
|
||||
- (void)setPreferIPv4;
|
||||
- (void)setPreferIPv6;
|
||||
- (void)setIPVersionNeutral;
|
||||
|
||||
/**
|
||||
* Gets/Sets the maximum size of the buffer that will be allocated for receive operations.
|
||||
* The default maximum size is 9216 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.
|
||||
*
|
||||
* Since the OS/GCD notifies us of the size of each received UDP packet,
|
||||
* the actual allocated buffer size for each packet is exact.
|
||||
* And in practice the size of UDP packets is generally much smaller than the max.
|
||||
* Indeed most protocols will send and receive packets of only a few bytes,
|
||||
* or will set a limit on the size of packets to prevent fragmentation in the IP layer.
|
||||
*
|
||||
* If you set the buffer size too small, the sockets API in the OS will silently discard
|
||||
* any extra data, and you will not be notified of the error.
|
||||
**/
|
||||
- (uint16_t)maxReceiveIPv4BufferSize;
|
||||
- (void)setMaxReceiveIPv4BufferSize:(uint16_t)max;
|
||||
|
||||
- (uint32_t)maxReceiveIPv6BufferSize;
|
||||
- (void)setMaxReceiveIPv6BufferSize:(uint32_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;
|
||||
|
||||
#pragma mark Diagnostics
|
||||
|
||||
/**
|
||||
* Returns the local address info for the socket.
|
||||
*
|
||||
* The localAddress method returns a sockaddr structure wrapped in a NSData object.
|
||||
* The localHost method returns the human readable IP address as a string.
|
||||
*
|
||||
* 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;
|
||||
- (uint16_t)localPort;
|
||||
|
||||
- (NSData *)localAddress_IPv4;
|
||||
- (NSString *)localHost_IPv4;
|
||||
- (uint16_t)localPort_IPv4;
|
||||
|
||||
- (NSData *)localAddress_IPv6;
|
||||
- (NSString *)localHost_IPv6;
|
||||
- (uint16_t)localPort_IPv6;
|
||||
|
||||
/**
|
||||
* Returns the remote address info for the socket.
|
||||
*
|
||||
* The connectedAddress method returns a sockaddr structure wrapped in a NSData object.
|
||||
* The connectedHost method returns the human readable IP address as a string.
|
||||
*
|
||||
* Note: Since UDP is connectionless by design, connected address info
|
||||
* 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;
|
||||
- (uint16_t)connectedPort;
|
||||
|
||||
/**
|
||||
* Returns whether or not this socket has been connected to a single host.
|
||||
* By design, UDP is a connectionless protocol, and connecting is not needed.
|
||||
* If connected, the socket will only be able to send/receive data to/from the connected host.
|
||||
**/
|
||||
- (BOOL)isConnected;
|
||||
|
||||
/**
|
||||
* Returns whether or not this socket has been closed.
|
||||
* The only way a socket can be closed is if you explicitly call one of the close methods.
|
||||
**/
|
||||
- (BOOL)isClosed;
|
||||
|
||||
/**
|
||||
* Returns whether or not this socket is IPv4.
|
||||
*
|
||||
* By default this will be true, unless:
|
||||
* - IPv4 is disabled (via setIPv4Enabled:)
|
||||
* - The socket is explicitly bound to an IPv6 address
|
||||
* - The socket is connected to an IPv6 address
|
||||
**/
|
||||
- (BOOL)isIPv4;
|
||||
|
||||
/**
|
||||
* Returns whether or not this socket is IPv6.
|
||||
*
|
||||
* By default this will be true, unless:
|
||||
* - IPv6 is disabled (via setIPv6Enabled:)
|
||||
* - The socket is explicitly bound to an IPv4 address
|
||||
* _ The socket is connected to an IPv4 address
|
||||
*
|
||||
* This method will also return false on platforms that do not support IPv6.
|
||||
* Note: The iPhone does not currently support IPv6.
|
||||
**/
|
||||
- (BOOL)isIPv6;
|
||||
|
||||
#pragma mark Binding
|
||||
|
||||
/**
|
||||
* Binds the UDP socket to the given port.
|
||||
* Binding should be done for server sockets that receive data prior to sending it.
|
||||
* Client sockets can skip binding,
|
||||
* as the OS will automatically assign the socket an available port when it starts sending data.
|
||||
*
|
||||
* You may optionally pass a port number of zero to immediately bind the socket,
|
||||
* yet still allow the OS to automatically assign an available port.
|
||||
*
|
||||
* You cannot bind a socket after its been connected.
|
||||
* You can only bind a socket once.
|
||||
* You can still connect a socket (if desired) after binding.
|
||||
*
|
||||
* 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 error:(NSError **)errPtr;
|
||||
|
||||
/**
|
||||
* Binds the UDP socket to the given port and optional interface.
|
||||
* Binding should be done for server sockets that receive data prior to sending it.
|
||||
* Client sockets can skip binding,
|
||||
* as the OS will automatically assign the socket an available port when it starts sending data.
|
||||
*
|
||||
* You may optionally pass a port number of zero to immediately bind the socket,
|
||||
* yet still allow the OS to automatically assign an available port.
|
||||
*
|
||||
* The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35").
|
||||
* You may also use the special strings "localhost" or "loopback" to specify that
|
||||
* the socket only accept packets from the local machine.
|
||||
*
|
||||
* You cannot bind a socket after its been connected.
|
||||
* You can only bind a socket once.
|
||||
* You can still connect a socket (if desired) after binding.
|
||||
*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Binds the UDP socket to the given address, specified as a sockaddr structure wrapped in a NSData object.
|
||||
*
|
||||
* If you have an existing struct sockaddr you can convert it to a NSData object like so:
|
||||
* struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len];
|
||||
* struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len];
|
||||
*
|
||||
* Binding should be done for server sockets that receive data prior to sending it.
|
||||
* Client sockets can skip binding,
|
||||
* as the OS will automatically assign the socket an available port when it starts sending data.
|
||||
*
|
||||
* You cannot bind a socket after its been connected.
|
||||
* You can only bind a socket once.
|
||||
* You can still connect a socket (if desired) after binding.
|
||||
*
|
||||
* 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)bindToAddress:(NSData *)localAddr error:(NSError **)errPtr;
|
||||
|
||||
#pragma mark Connecting
|
||||
|
||||
/**
|
||||
* Connects the UDP socket to the given host and port.
|
||||
* By design, UDP is a connectionless protocol, and connecting is not needed.
|
||||
*
|
||||
* Choosing to connect to a specific host/port has the following effect:
|
||||
* - You will only be able to send data to the connected host/port.
|
||||
* - You will only be able to receive data from the connected host/port.
|
||||
* - You will receive ICMP messages that come from the connected host/port, such as "connection refused".
|
||||
*
|
||||
* The actual process of connecting a UDP socket does not result in any communication on the socket.
|
||||
* It simply changes the internal state of the socket.
|
||||
*
|
||||
* You cannot bind a socket after it has been connected.
|
||||
* You can only connect a socket once.
|
||||
*
|
||||
* The host may be a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2").
|
||||
*
|
||||
* This method is asynchronous as it requires a DNS lookup to resolve the given host name.
|
||||
* If an obvious error is detected, this method immediately returns NO and sets errPtr.
|
||||
* If you don't care about the error, you can pass nil for errPtr.
|
||||
* Otherwise, this method returns YES and begins the asynchronous connection process.
|
||||
* The result of the asynchronous connection process will be reported via the delegate methods.
|
||||
**/
|
||||
- (BOOL)connectToHost:(NSString *)host onPort:(uint16_t)port error:(NSError **)errPtr;
|
||||
|
||||
/**
|
||||
* Connects the UDP socket to the given address, specified as a sockaddr structure wrapped in a NSData object.
|
||||
*
|
||||
* If you have an existing struct sockaddr you can convert it to a NSData object like so:
|
||||
* struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len];
|
||||
* struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len];
|
||||
*
|
||||
* By design, UDP is a connectionless protocol, and connecting is not needed.
|
||||
*
|
||||
* Choosing to connect to a specific address has the following effect:
|
||||
* - You will only be able to send data to the connected address.
|
||||
* - You will only be able to receive data from the connected address.
|
||||
* - You will receive ICMP messages that come from the connected address, such as "connection refused".
|
||||
*
|
||||
* Connecting a UDP socket does not result in any communication on the socket.
|
||||
* It simply changes the internal state of the socket.
|
||||
*
|
||||
* You cannot bind a socket after its been connected.
|
||||
* You can only connect a socket once.
|
||||
*
|
||||
* On success, returns YES.
|
||||
* Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr.
|
||||
*
|
||||
* Note: Unlike the connectToHost:onPort:error: method, this method does not require a DNS lookup.
|
||||
* Thus when this method returns, the connection has either failed or fully completed.
|
||||
* In other words, this method is synchronous, unlike the asynchronous connectToHost::: method.
|
||||
* However, for compatibility and simplification of delegate code, if this method returns YES
|
||||
* then the corresponding delegate method (udpSocket:didConnectToHost:port:) is still invoked.
|
||||
**/
|
||||
- (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr;
|
||||
|
||||
#pragma mark Multicast
|
||||
|
||||
/**
|
||||
* Join multicast group.
|
||||
* Group should be an IP address (eg @"225.228.0.1").
|
||||
*
|
||||
* 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 error:(NSError **)errPtr;
|
||||
|
||||
/**
|
||||
* Join multicast group.
|
||||
* Group should be an IP address (eg @"225.228.0.1").
|
||||
* The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35").
|
||||
*
|
||||
* 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)leaveMulticastGroup:(NSString *)group error:(NSError **)errPtr;
|
||||
- (BOOL)leaveMulticastGroup:(NSString *)group onInterface:(NSString *)interface error:(NSError **)errPtr;
|
||||
|
||||
#pragma mark Broadcast
|
||||
|
||||
/**
|
||||
* By default, the underlying socket in the OS will not allow you to send broadcast messages.
|
||||
* In order to send broadcast messages, you need to enable this functionality in the socket.
|
||||
*
|
||||
* A broadcast is a UDP message to addresses like "192.168.255.255" or "255.255.255.255" that is
|
||||
* delivered to every host on the network.
|
||||
* The reason this is generally disabled by default (by the OS) is to prevent
|
||||
* accidental broadcast messages from flooding the network.
|
||||
**/
|
||||
- (BOOL)enableBroadcast:(BOOL)flag error:(NSError **)errPtr;
|
||||
|
||||
#pragma mark Sending
|
||||
|
||||
/**
|
||||
* Asynchronously sends the given data, with the given timeout and tag.
|
||||
*
|
||||
* This method may only be used with a connected socket.
|
||||
* Recall that connecting is optional for a UDP socket.
|
||||
* For connected sockets, data can only be sent to the connected address.
|
||||
* For non-connected sockets, the remote destination is specified for each packet.
|
||||
* For more information about optionally connecting udp sockets, see the documentation for the connect methods above.
|
||||
*
|
||||
* @param data
|
||||
* The data to send.
|
||||
* If data is nil or zero-length, this method does nothing.
|
||||
* If passing NSMutableData, please read the thread-safety notice below.
|
||||
*
|
||||
* @param timeout
|
||||
* The timeout for the send opeartion.
|
||||
* If the timeout value is negative, the send operation will not use a timeout.
|
||||
*
|
||||
* @param tag
|
||||
* The tag is for your convenience.
|
||||
* It is not sent or received over the socket in any manner what-so-ever.
|
||||
* It is reported back as a parameter in the udpSocket:didSendDataWithTag:
|
||||
* or udpSocket:didNotSendDataWithTag:dueToError: methods.
|
||||
* You can use it as an array index, state id, type constant, etc.
|
||||
*
|
||||
*
|
||||
* Thread-Safety Note:
|
||||
* If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while
|
||||
* the socket is sending it. In other words, it's not safe to alter the data until after the delegate method
|
||||
* udpSocket:didSendDataWithTag: or udpSocket:didNotSendDataWithTag:dueToError: is invoked signifying
|
||||
* that this particular send operation has completed.
|
||||
* This is due to the fact that GCDAsyncUdpSocket does NOT copy the data.
|
||||
* It simply retains it for performance reasons.
|
||||
* Often times, if NSMutableData is passed, it is because a request/response was built up in memory.
|
||||
* Copying this data adds an unwanted/unneeded overhead.
|
||||
* If you need to write data from an immutable buffer, and you need to alter the buffer before the socket
|
||||
* completes sending the bytes (which is NOT immediately after this method returns, but rather at a later time
|
||||
* when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method.
|
||||
**/
|
||||
- (void)sendData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
|
||||
|
||||
/**
|
||||
* Asynchronously sends the given data, with the given timeout and tag, to the given host and port.
|
||||
*
|
||||
* This method cannot be used with a connected socket.
|
||||
* Recall that connecting is optional for a UDP socket.
|
||||
* For connected sockets, data can only be sent to the connected address.
|
||||
* For non-connected sockets, the remote destination is specified for each packet.
|
||||
* For more information about optionally connecting udp sockets, see the documentation for the connect methods above.
|
||||
*
|
||||
* @param data
|
||||
* The data to send.
|
||||
* If data is nil or zero-length, this method does nothing.
|
||||
* If passing NSMutableData, please read the thread-safety notice below.
|
||||
*
|
||||
* @param host
|
||||
* The destination to send the udp packet to.
|
||||
* May be specified as a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2").
|
||||
* You may also use the convenience strings of "loopback" or "localhost".
|
||||
*
|
||||
* @param port
|
||||
* The port of the host to send to.
|
||||
*
|
||||
* @param timeout
|
||||
* The timeout for the send opeartion.
|
||||
* If the timeout value is negative, the send operation will not use a timeout.
|
||||
*
|
||||
* @param tag
|
||||
* The tag is for your convenience.
|
||||
* It is not sent or received over the socket in any manner what-so-ever.
|
||||
* It is reported back as a parameter in the udpSocket:didSendDataWithTag:
|
||||
* or udpSocket:didNotSendDataWithTag:dueToError: methods.
|
||||
* You can use it as an array index, state id, type constant, etc.
|
||||
*
|
||||
*
|
||||
* Thread-Safety Note:
|
||||
* If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while
|
||||
* the socket is sending it. In other words, it's not safe to alter the data until after the delegate method
|
||||
* udpSocket:didSendDataWithTag: or udpSocket:didNotSendDataWithTag:dueToError: is invoked signifying
|
||||
* that this particular send operation has completed.
|
||||
* This is due to the fact that GCDAsyncUdpSocket does NOT copy the data.
|
||||
* It simply retains it for performance reasons.
|
||||
* Often times, if NSMutableData is passed, it is because a request/response was built up in memory.
|
||||
* Copying this data adds an unwanted/unneeded overhead.
|
||||
* If you need to write data from an immutable buffer, and you need to alter the buffer before the socket
|
||||
* completes sending the bytes (which is NOT immediately after this method returns, but rather at a later time
|
||||
* when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method.
|
||||
**/
|
||||
- (void)sendData:(NSData *)data
|
||||
toHost:(NSString *)host
|
||||
port:(uint16_t)port
|
||||
withTimeout:(NSTimeInterval)timeout
|
||||
tag:(long)tag;
|
||||
|
||||
/**
|
||||
* Asynchronously sends the given data, with the given timeout and tag, to the given address.
|
||||
*
|
||||
* This method cannot be used with a connected socket.
|
||||
* Recall that connecting is optional for a UDP socket.
|
||||
* For connected sockets, data can only be sent to the connected address.
|
||||
* For non-connected sockets, the remote destination is specified for each packet.
|
||||
* For more information about optionally connecting udp sockets, see the documentation for the connect methods above.
|
||||
*
|
||||
* @param data
|
||||
* The data to send.
|
||||
* If data is nil or zero-length, this method does nothing.
|
||||
* If passing NSMutableData, please read the thread-safety notice below.
|
||||
*
|
||||
* @param remoteAddr
|
||||
* The address to send the data to (specified as a sockaddr structure wrapped in a NSData object).
|
||||
*
|
||||
* @param timeout
|
||||
* The timeout for the send opeartion.
|
||||
* If the timeout value is negative, the send operation will not use a timeout.
|
||||
*
|
||||
* @param tag
|
||||
* The tag is for your convenience.
|
||||
* It is not sent or received over the socket in any manner what-so-ever.
|
||||
* It is reported back as a parameter in the udpSocket:didSendDataWithTag:
|
||||
* or udpSocket:didNotSendDataWithTag:dueToError: methods.
|
||||
* You can use it as an array index, state id, type constant, etc.
|
||||
*
|
||||
*
|
||||
* Thread-Safety Note:
|
||||
* If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while
|
||||
* the socket is sending it. In other words, it's not safe to alter the data until after the delegate method
|
||||
* udpSocket:didSendDataWithTag: or udpSocket:didNotSendDataWithTag:dueToError: is invoked signifying
|
||||
* that this particular send operation has completed.
|
||||
* This is due to the fact that GCDAsyncUdpSocket does NOT copy the data.
|
||||
* It simply retains it for performance reasons.
|
||||
* Often times, if NSMutableData is passed, it is because a request/response was built up in memory.
|
||||
* Copying this data adds an unwanted/unneeded overhead.
|
||||
* If you need to write data from an immutable buffer, and you need to alter the buffer before the socket
|
||||
* completes sending the bytes (which is NOT immediately after this method returns, but rather at a later time
|
||||
* when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method.
|
||||
**/
|
||||
- (void)sendData:(NSData *)data toAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout tag:(long)tag;
|
||||
|
||||
/**
|
||||
* You may optionally set a send filter for the socket.
|
||||
* A filter can provide several interesting possibilities:
|
||||
*
|
||||
* 1. Optional caching of resolved addresses for domain names.
|
||||
* The cache could later be consulted, resulting in fewer system calls to getaddrinfo.
|
||||
*
|
||||
* 2. Reusable modules of code for bandwidth monitoring.
|
||||
*
|
||||
* 3. Sometimes traffic shapers are needed to simulate real world environments.
|
||||
* A filter allows you to write custom code to simulate such environments.
|
||||
* The ability to code this yourself is especially helpful when your simulated environment
|
||||
* is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router),
|
||||
* or the system tools to handle this aren't available (e.g. on a mobile device).
|
||||
*
|
||||
* For more information about GCDAsyncUdpSocketSendFilterBlock, see the documentation for its typedef.
|
||||
* To remove a previously set filter, invoke this method and pass a nil filterBlock and NULL filterQueue.
|
||||
*
|
||||
* Note: This method invokes setSendFilter:withQueue:isAsynchronous: (documented below),
|
||||
* passing YES for the isAsynchronous parameter.
|
||||
**/
|
||||
- (void)setSendFilter:(GCDAsyncUdpSocketSendFilterBlock)filterBlock withQueue:(dispatch_queue_t)filterQueue;
|
||||
|
||||
/**
|
||||
* The receive filter can be run via dispatch_async or dispatch_sync.
|
||||
* Most typical situations call for asynchronous operation.
|
||||
*
|
||||
* However, there are a few situations in which synchronous operation is preferred.
|
||||
* Such is the case when the filter is extremely minimal and fast.
|
||||
* This is because dispatch_sync is faster than dispatch_async.
|
||||
*
|
||||
* If you choose synchronous operation, be aware of possible deadlock conditions.
|
||||
* Since the socket queue is executing your block via dispatch_sync,
|
||||
* 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
|
||||
isAsynchronous:(BOOL)isAsynchronous;
|
||||
|
||||
#pragma mark Receiving
|
||||
|
||||
/**
|
||||
* There are two modes of operation for receiving packets: one-at-a-time & continuous.
|
||||
*
|
||||
* In one-at-a-time mode, you call receiveOnce everytime your delegate is ready to process an incoming udp packet.
|
||||
* Receiving packets one-at-a-time may be better suited for implementing certain state machine code,
|
||||
* where your state machine may not always be ready to process incoming packets.
|
||||
*
|
||||
* In continuous mode, the delegate is invoked immediately everytime incoming udp packets are received.
|
||||
* Receiving packets continuously is better suited to real-time streaming applications.
|
||||
*
|
||||
* You may switch back and forth between one-at-a-time mode and continuous mode.
|
||||
* If the socket is currently in continuous mode, calling this method will switch it to one-at-a-time mode.
|
||||
*
|
||||
* When a packet is received (and not filtered by the optional receive filter),
|
||||
* the delegate method (udpSocket:didReceiveData:fromAddress:withFilterContext:) is invoked.
|
||||
*
|
||||
* If the socket is able to begin receiving packets, this method returns YES.
|
||||
* Otherwise it returns NO, and sets the errPtr with appropriate error information.
|
||||
*
|
||||
* An example error:
|
||||
* You created a udp socket to act as a server, and immediately called receive.
|
||||
* You forgot to first bind the socket to a port number, and received a error with a message like:
|
||||
* "Must bind socket before you can receive data."
|
||||
**/
|
||||
- (BOOL)receiveOnce:(NSError **)errPtr;
|
||||
|
||||
/**
|
||||
* There are two modes of operation for receiving packets: one-at-a-time & continuous.
|
||||
*
|
||||
* In one-at-a-time mode, you call receiveOnce everytime your delegate is ready to process an incoming udp packet.
|
||||
* Receiving packets one-at-a-time may be better suited for implementing certain state machine code,
|
||||
* where your state machine may not always be ready to process incoming packets.
|
||||
*
|
||||
* In continuous mode, the delegate is invoked immediately everytime incoming udp packets are received.
|
||||
* Receiving packets continuously is better suited to real-time streaming applications.
|
||||
*
|
||||
* You may switch back and forth between one-at-a-time mode and continuous mode.
|
||||
* If the socket is currently in one-at-a-time mode, calling this method will switch it to continuous mode.
|
||||
*
|
||||
* For every received packet (not filtered by the optional receive filter),
|
||||
* the delegate method (udpSocket:didReceiveData:fromAddress:withFilterContext:) is invoked.
|
||||
*
|
||||
* If the socket is able to begin receiving packets, this method returns YES.
|
||||
* Otherwise it returns NO, and sets the errPtr with appropriate error information.
|
||||
*
|
||||
* An example error:
|
||||
* You created a udp socket to act as a server, and immediately called receive.
|
||||
* You forgot to first bind the socket to a port number, and received a error with a message like:
|
||||
* "Must bind socket before you can receive data."
|
||||
**/
|
||||
- (BOOL)beginReceiving:(NSError **)errPtr;
|
||||
|
||||
/**
|
||||
* If the socket is currently receiving (beginReceiving has been called), this method pauses the receiving.
|
||||
* That is, it won't read any more packets from the underlying OS socket until beginReceiving is called again.
|
||||
*
|
||||
* Important Note:
|
||||
* GCDAsyncUdpSocket may be running in parallel with your code.
|
||||
* That is, your delegate is likely running on a separate thread/dispatch_queue.
|
||||
* When you invoke this method, GCDAsyncUdpSocket may have already dispatched delegate methods to be invoked.
|
||||
* Thus, if those delegate methods have already been dispatch_async'd,
|
||||
* your didReceive delegate method may still be invoked after this method has been called.
|
||||
* You should be aware of this, and program defensively.
|
||||
**/
|
||||
- (void)pauseReceiving;
|
||||
|
||||
/**
|
||||
* You may optionally set a receive filter for the socket.
|
||||
* This receive filter may be set to run in its own queue (independent of delegate queue).
|
||||
*
|
||||
* A filter can provide several useful features.
|
||||
*
|
||||
* 1. Many times udp packets need to be parsed.
|
||||
* Since the filter can run in its own independent queue, you can parallelize this parsing quite easily.
|
||||
* The end result is a parallel socket io, datagram parsing, and packet processing.
|
||||
*
|
||||
* 2. Many times udp packets are discarded because they are duplicate/unneeded/unsolicited.
|
||||
* The filter can prevent such packets from arriving at the delegate.
|
||||
* And because the filter can run in its own independent queue, this doesn't slow down the delegate.
|
||||
*
|
||||
* - Since the udp protocol does not guarantee delivery, udp packets may be lost.
|
||||
* Many protocols built atop udp thus provide various resend/re-request algorithms.
|
||||
* This sometimes results in duplicate packets arriving.
|
||||
* A filter may allow you to architect the duplicate detection code to run in parallel to normal processing.
|
||||
*
|
||||
* - Since the udp socket may be connectionless, its possible for unsolicited packets to arrive.
|
||||
* Such packets need to be ignored.
|
||||
*
|
||||
* 3. Sometimes traffic shapers are needed to simulate real world environments.
|
||||
* A filter allows you to write custom code to simulate such environments.
|
||||
* The ability to code this yourself is especially helpful when your simulated environment
|
||||
* is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router),
|
||||
* or the system tools to handle this aren't available (e.g. on a mobile device).
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* GCDAsyncUdpSocketReceiveFilterBlock filter = ^BOOL (NSData *data, NSData *address, id *context) {
|
||||
*
|
||||
* MyProtocolMessage *msg = [MyProtocol parseMessage:data];
|
||||
*
|
||||
* *context = response;
|
||||
* return (response != nil);
|
||||
* };
|
||||
* [udpSocket setReceiveFilter:filter withQueue:myParsingQueue];
|
||||
*
|
||||
* For more information about GCDAsyncUdpSocketReceiveFilterBlock, see the documentation for its typedef.
|
||||
* To remove a previously set filter, invoke this method and pass a nil filterBlock and NULL filterQueue.
|
||||
*
|
||||
* Note: This method invokes setReceiveFilter:withQueue:isAsynchronous: (documented below),
|
||||
* passing YES for the isAsynchronous parameter.
|
||||
**/
|
||||
- (void)setReceiveFilter:(GCDAsyncUdpSocketReceiveFilterBlock)filterBlock withQueue:(dispatch_queue_t)filterQueue;
|
||||
|
||||
/**
|
||||
* The receive filter can be run via dispatch_async or dispatch_sync.
|
||||
* Most typical situations call for asynchronous operation.
|
||||
*
|
||||
* However, there are a few situations in which synchronous operation is preferred.
|
||||
* Such is the case when the filter is extremely minimal and fast.
|
||||
* This is because dispatch_sync is faster than dispatch_async.
|
||||
*
|
||||
* If you choose synchronous operation, be aware of possible deadlock conditions.
|
||||
* Since the socket queue is executing your block via dispatch_sync,
|
||||
* 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
|
||||
isAsynchronous:(BOOL)isAsynchronous;
|
||||
|
||||
#pragma mark Closing
|
||||
|
||||
/**
|
||||
* Immediately closes the underlying socket.
|
||||
* Any pending send operations are discarded.
|
||||
*
|
||||
* The GCDAsyncUdpSocket instance may optionally be used again.
|
||||
* (it will setup/configure/use another unnderlying BSD socket).
|
||||
**/
|
||||
- (void)close;
|
||||
|
||||
/**
|
||||
* Closes the underlying socket after all pending send operations have been sent.
|
||||
*
|
||||
* The GCDAsyncUdpSocket instance may optionally be used again.
|
||||
* (it will setup/configure/use another unnderlying BSD socket).
|
||||
**/
|
||||
- (void)closeAfterSending;
|
||||
|
||||
#pragma mark Advanced
|
||||
/**
|
||||
* GCDAsyncSocket maintains thread safety by using an internal serial dispatch_queue.
|
||||
* In most cases, the instance creates this queue itself.
|
||||
* However, to allow for maximum flexibility, the internal queue may be passed in the init method.
|
||||
* This allows for some advanced options such as controlling socket priority via target queues.
|
||||
* However, when one begins to use target queues like this, they open the door to some specific deadlock issues.
|
||||
*
|
||||
* For example, imagine there are 2 queues:
|
||||
* dispatch_queue_t socketQueue;
|
||||
* dispatch_queue_t socketTargetQueue;
|
||||
*
|
||||
* If you do this (pseudo-code):
|
||||
* socketQueue.targetQueue = socketTargetQueue;
|
||||
*
|
||||
* Then all socketQueue operations will actually get run on the given socketTargetQueue.
|
||||
* This is fine and works great in most situations.
|
||||
* But if you run code directly from within the socketTargetQueue that accesses the socket,
|
||||
* you could potentially get deadlock. Imagine the following code:
|
||||
*
|
||||
* - (BOOL)socketHasSomething
|
||||
* {
|
||||
* __block BOOL result = NO;
|
||||
* dispatch_block_t block = ^{
|
||||
* result = [self someInternalMethodToBeRunOnlyOnSocketQueue];
|
||||
* }
|
||||
* if (is_executing_on_queue(socketQueue))
|
||||
* block();
|
||||
* else
|
||||
* dispatch_sync(socketQueue, block);
|
||||
*
|
||||
* return result;
|
||||
* }
|
||||
*
|
||||
* What happens if you call this method from the socketTargetQueue? The result is deadlock.
|
||||
* This is because the GCD API offers no mechanism to discover a queue's targetQueue.
|
||||
* Thus we have no idea if our socketQueue is configured with a targetQueue.
|
||||
* If we had this information, we could easily avoid deadlock.
|
||||
* But, since these API's are missing or unfeasible, you'll have to explicitly set it.
|
||||
*
|
||||
* IF you pass a socketQueue via the init method,
|
||||
* AND you've configured the passed socketQueue with a targetQueue,
|
||||
* THEN you should pass the end queue in the target hierarchy.
|
||||
*
|
||||
* For example, consider the following queue hierarchy:
|
||||
* socketQueue -> ipQueue -> moduleQueue
|
||||
*
|
||||
* This example demonstrates priority shaping within some server.
|
||||
* All incoming client connections from the same IP address are executed on the same target queue.
|
||||
* And all connections for a particular module are executed on the same target queue.
|
||||
* Thus, the priority of all networking for the entire module can be changed on the fly.
|
||||
* Additionally, networking traffic from a single IP cannot monopolize the module.
|
||||
*
|
||||
* Here's how you would accomplish something like that:
|
||||
* - (dispatch_queue_t)newSocketQueueForConnectionFromAddress:(NSData *)address onSocket:(GCDAsyncSocket *)sock
|
||||
* {
|
||||
* dispatch_queue_t socketQueue = dispatch_queue_create("", NULL);
|
||||
* dispatch_queue_t ipQueue = [self ipQueueForAddress:address];
|
||||
*
|
||||
* dispatch_set_target_queue(socketQueue, ipQueue);
|
||||
* dispatch_set_target_queue(iqQueue, moduleQueue);
|
||||
*
|
||||
* return socketQueue;
|
||||
* }
|
||||
* - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
|
||||
* {
|
||||
* [clientConnections addObject:newSocket];
|
||||
* [newSocket markSocketQueueTargetQueue:moduleQueue];
|
||||
* }
|
||||
*
|
||||
* Note: This workaround is ONLY needed if you intend to execute code directly on the ipQueue or moduleQueue.
|
||||
* This is often NOT the case, as such queues are used solely for execution shaping.
|
||||
**/
|
||||
- (void)markSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreConfiguredTargetQueue;
|
||||
- (void)unmarkSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreviouslyConfiguredTargetQueue;
|
||||
|
||||
/**
|
||||
* It's not thread-safe to access certain variables from outside the socket's internal queue.
|
||||
*
|
||||
* For example, the socket file descriptor.
|
||||
* File descriptors are simply integers which reference an index in the per-process file table.
|
||||
* However, when one requests a new file descriptor (by opening a file or socket),
|
||||
* the file descriptor returned is guaranteed to be the lowest numbered unused descriptor.
|
||||
* So if we're not careful, the following could be possible:
|
||||
*
|
||||
* - Thread A invokes a method which returns the socket's file descriptor.
|
||||
* - The socket is closed via the socket's internal queue on thread B.
|
||||
* - Thread C opens a file, and subsequently receives the file descriptor that was previously the socket's FD.
|
||||
* - Thread A is now accessing/altering the file instead of the socket.
|
||||
*
|
||||
* In addition to this, other variables are not actually objects,
|
||||
* and thus cannot be retained/released or even autoreleased.
|
||||
* An example is the sslContext, of type SSLContextRef, which is actually a malloc'd struct.
|
||||
*
|
||||
* Although there are internal variables that make it difficult to maintain thread-safety,
|
||||
* it is important to provide access to these variables
|
||||
* to ensure this class can be used in a wide array of environments.
|
||||
* This method helps to accomplish this by invoking the current block on the socket's internal queue.
|
||||
* The methods below can be invoked from within the block to access
|
||||
* those generally thread-unsafe internal variables in a thread-safe manner.
|
||||
* The given block will be invoked synchronously on the socket's internal queue.
|
||||
*
|
||||
* If you save references to any protected variables and use them outside the block, you do so at your own peril.
|
||||
**/
|
||||
- (void)performBlock:(dispatch_block_t)block;
|
||||
|
||||
/**
|
||||
* These methods are only available from within the context of a performBlock: invocation.
|
||||
* See the documentation for the performBlock: method above.
|
||||
*
|
||||
* Provides access to the socket's file descriptor(s).
|
||||
* If the socket isn't connected, or explicity bound to a particular interface,
|
||||
* it might actually have multiple internal socket file descriptors - one for IPv4 and one for IPv6.
|
||||
**/
|
||||
- (int)socketFD;
|
||||
- (int)socket4FD;
|
||||
- (int)socket6FD;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
/**
|
||||
* These methods are only available from within the context of a performBlock: invocation.
|
||||
* See the documentation for the performBlock: method above.
|
||||
*
|
||||
* Returns (creating if necessary) a CFReadStream/CFWriteStream for the internal socket.
|
||||
*
|
||||
* Generally GCDAsyncUdpSocket doesn't use CFStream. (It uses the faster GCD API's.)
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* This method is only available from within the context of a performBlock: invocation.
|
||||
* See the documentation for the performBlock: method above.
|
||||
*
|
||||
* Configures the socket to allow it to operate when the iOS application has been backgrounded.
|
||||
* In other words, this method creates a read & write stream, and invokes:
|
||||
*
|
||||
* CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
|
||||
* CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
|
||||
*
|
||||
* Returns YES if successful, NO otherwise.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* [asyncUdpSocket performBlock:^{
|
||||
* [asyncUdpSocket enableBackgroundingOnSocket];
|
||||
* }];
|
||||
*
|
||||
*
|
||||
* NOTE : Apple doesn't currently support backgrounding UDP sockets. (Only TCP for now).
|
||||
**/
|
||||
//- (BOOL)enableBackgroundingOnSockets;
|
||||
|
||||
#endif
|
||||
|
||||
#pragma mark Utilities
|
||||
|
||||
/**
|
||||
* Extracting host/port/family information from raw address data.
|
||||
**/
|
||||
|
||||
+ (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;
|
||||
|
||||
@end
|
||||
|
5367
GCDAsyncSocket/GCDAsyncUdpSocket.m
Normal file
5367
GCDAsyncSocket/GCDAsyncUdpSocket.m
Normal file
File diff suppressed because it is too large
Load Diff
22
LICENSE
Normal file
22
LICENSE
Normal file
@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Tradle
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
1017
RCTUDP.xcodeproj/project.pbxproj
Normal file
1017
RCTUDP.xcodeproj/project.pbxproj
Normal file
File diff suppressed because it is too large
Load Diff
255
RCTUDPSocket.js
Normal file
255
RCTUDPSocket.js
Normal file
@ -0,0 +1,255 @@
|
||||
//
|
||||
// react-native-udp
|
||||
//
|
||||
// Created by Mark Vayngrib on 05/10/15.
|
||||
// Copyright (c) 2015 Tradle, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
/**
|
||||
* @providesModule RCTUDPSocket
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react-native')
|
||||
var {
|
||||
Component
|
||||
} = React
|
||||
|
||||
var mixInEventEmitter = require('mixInEventEmitter')
|
||||
var DeviceEventEmitter = require('RCTDeviceEventEmitter')
|
||||
var NativeModules = require('NativeModules')
|
||||
var sockets = NativeModules.UDP // just UDP for now
|
||||
var noop = function () {}
|
||||
var instances = 0
|
||||
var STATE = {
|
||||
UNBOUND: 0,
|
||||
BINDING: 1,
|
||||
BOUND: 2
|
||||
}
|
||||
|
||||
class RCTSocket extends Component {
|
||||
id: String;
|
||||
|
||||
_state: STATE.UNBOUND;
|
||||
|
||||
_address: undefined;
|
||||
|
||||
_port: undefined;
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.id = instances++
|
||||
this.subscriptiom = DeviceEventEmitter.addListener(
|
||||
'udp-' + this.id + '-data', this._onReceive.bind(this)
|
||||
);
|
||||
|
||||
// ensure compatibility with node's EventEmitter
|
||||
if (!this.on) this.on = this.addListener.bind(this)
|
||||
|
||||
sockets.createSocket(this.id, {
|
||||
type: props.type || 'udp4'
|
||||
}) // later
|
||||
}
|
||||
|
||||
_debug() {
|
||||
var args = [].slice.call(arguments)
|
||||
args.unshift(this.id)
|
||||
console.log.apply(console, args)
|
||||
}
|
||||
|
||||
bind(port, address, callback) {
|
||||
var self = this
|
||||
if (typeof address === 'function') {
|
||||
callback = address
|
||||
address = undefined
|
||||
}
|
||||
|
||||
// address = address || '0.0.0.0' //'127.0.0.1'
|
||||
if (callback) this.once('listening', callback)
|
||||
|
||||
this._state = STATE.BINDING
|
||||
this._debug('binding, address:', address, 'port:', port)
|
||||
sockets.bind(this.id, port, address, function(err, addr) {
|
||||
if (err) {
|
||||
// questionable: may want to self-destruct and
|
||||
// force user to create a new socket
|
||||
self._state = STATE.UNBOUND
|
||||
self._debug('failed to bind', err)
|
||||
return self.emit('error', err)
|
||||
}
|
||||
|
||||
self._address = addr.address
|
||||
self._port = addr.port
|
||||
self._state = STATE.BOUND
|
||||
self.emit('listening')
|
||||
})
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.subscription.remove();
|
||||
}
|
||||
|
||||
_onReceive(info) {
|
||||
this._debug('received', info)
|
||||
|
||||
var buf = toByteArray(info.data)
|
||||
var rinfo = {
|
||||
address: info.address,
|
||||
port: info.port,
|
||||
family: 'IPv4', // not necessarily
|
||||
size: buf.length
|
||||
}
|
||||
|
||||
if (typeof Buffer !== 'undefined') buf = new Buffer(buf)
|
||||
|
||||
this.emit('message', buf, rinfo)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* socket.send(buf, offset, length, port, address, [callback])
|
||||
*
|
||||
* For UDP sockets, the destination port and IP address must be
|
||||
* specified. A string may be supplied for the address parameter, and it will
|
||||
* be resolved with DNS. An optional callback may be specified to detect any
|
||||
* DNS errors and when buf may be re-used. Note that DNS lookups will delay
|
||||
* the time that a send takes place, at least until the next tick. The only
|
||||
* way to know for sure that a send has taken place is to use the callback.
|
||||
*
|
||||
* If the socket has not been previously bound with a call to bind, it's
|
||||
* assigned a random port number and bound to the "all interfaces" address
|
||||
* (0.0.0.0 for udp4 sockets, ::0 for udp6 sockets).
|
||||
*
|
||||
* @param {Array|string} message to be sent
|
||||
* @param {number} offset Offset in the buffer where the message starts.
|
||||
* @param {number} length Number of bytes in the message.
|
||||
* @param {number} port destination port
|
||||
* @param {string} address destination IP
|
||||
* @param {function} callback Callback when message is done being delivered.
|
||||
* Optional.
|
||||
*/
|
||||
// Socket.prototype.send = function (buf, host, port, cb) {
|
||||
send(buffer, offset, length, port, address, callback) {
|
||||
var self = this
|
||||
|
||||
if (offset !== 0) throw new Error('Non-zero offset not supported yet')
|
||||
|
||||
if (this._state === STATE.UNBOUND) {
|
||||
throw new Error('bind before sending, seriously dude')
|
||||
}
|
||||
else if (this._state === STATE.BINDING) {
|
||||
// we're ok, GCDAsync(Udp)Socket handles queueing internally
|
||||
}
|
||||
|
||||
callback = callback || noop
|
||||
if (typeof buffer === 'string') {
|
||||
buffer = toByteArray(buffer)
|
||||
}
|
||||
else if (typeof Buffer !== 'undefined' && Buffer.isBuffer(buffer)) {
|
||||
buffer = buffer.toJSON().data
|
||||
}
|
||||
|
||||
self._debug('sending', buffer)
|
||||
sockets.send(this.id, buffer, +port, address, function(err) {
|
||||
if (err) {
|
||||
self._debug('send failed', err)
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
self._debug('sent')
|
||||
callback()
|
||||
})
|
||||
}
|
||||
|
||||
address() {
|
||||
if (this._state !== STATE.BOUND) {
|
||||
throw new Error('socket is not bound yet')
|
||||
}
|
||||
|
||||
return {
|
||||
address: this._address,
|
||||
port: this._port,
|
||||
family: 'IPv4'
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
var self = this
|
||||
if (this._destroyed) return
|
||||
|
||||
this._destroyed = true
|
||||
this._debug('closing')
|
||||
sockets.close(this.id, function() {
|
||||
self._debug('closed')
|
||||
})
|
||||
|
||||
this.emit('close')
|
||||
}
|
||||
|
||||
setBroadcast(flag) {
|
||||
// nothing yet
|
||||
}
|
||||
|
||||
setTTL(ttl) {
|
||||
// nothing yet
|
||||
}
|
||||
|
||||
setMulticastTTL(ttl, callback) {
|
||||
// nothing yet
|
||||
}
|
||||
|
||||
setMulticastLoopback(flag, callback) {
|
||||
// nothing yet
|
||||
}
|
||||
|
||||
addMembership(multicastAddress, multicastInterface, callback) {
|
||||
// nothing yet
|
||||
}
|
||||
|
||||
dropMembership(multicastAddress, multicastInterface, callback) {
|
||||
// nothing yet
|
||||
}
|
||||
|
||||
ref() {
|
||||
// anything?
|
||||
}
|
||||
|
||||
unref() {
|
||||
// anything?
|
||||
}
|
||||
}
|
||||
|
||||
mixInEventEmitter(RCTSocket, {
|
||||
'listening': true,
|
||||
'message': true,
|
||||
'close': true,
|
||||
'error': true
|
||||
})
|
||||
|
||||
function toByteArray(obj) {
|
||||
if (typeof obj === 'object') {
|
||||
var i = 0
|
||||
var arr = []
|
||||
while (true) {
|
||||
if (!(i in obj)) break
|
||||
|
||||
arr.push(+obj[i])
|
||||
i++
|
||||
}
|
||||
|
||||
return new Uint8Array(arr)
|
||||
}
|
||||
else if (typeof obj !== 'string') {
|
||||
throw new Error('unsupported format')
|
||||
}
|
||||
|
||||
var uint = new Uint8Array(obj.length);
|
||||
for (var i = 0, l = obj.length; i < l; i++){
|
||||
uint[i] = obj.charCodeAt(i);
|
||||
}
|
||||
|
||||
return new Uint8Array(uint);
|
||||
}
|
||||
module.exports = RCTSocket
|
68
README.md
Normal file
68
README.md
Normal file
@ -0,0 +1,68 @@
|
||||
# UDP in React Native
|
||||
|
||||
node's [dgram](https://nodejs.org/api/dgram.html) API in React Native
|
||||
|
||||
## This module is used by [Tradle](https://github.com/tradle)
|
||||
|
||||
PR's welcome!
|
||||
|
||||
## Install
|
||||
|
||||
* Create a new react-native project. [Check react-native getting started](http://facebook.github.io/react-native/docs/getting-started.html#content)
|
||||
|
||||
* in PROJECT_DIR/node_modules/react-native, execute:
|
||||
```
|
||||
npm install --save react-native-udp
|
||||
```
|
||||
|
||||
* Drag RCTUDP.xcodeproj from node_modules/react-native-udp into your XCode project. Click on the project in XCode, go to Build Phases, then Link Binary With Libraries and add libReactUDP.a
|
||||
|
||||
Buckle up, Dorothy
|
||||
|
||||
## Usage
|
||||
|
||||
### package.json
|
||||
|
||||
_only if you want to write require('dgram') in your javascript_
|
||||
|
||||
```json
|
||||
{
|
||||
"browser": {
|
||||
"dgram": "react-native-udp"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### JS
|
||||
|
||||
_see/run index.ios.js for a complete example, but basically it's just like dgram_
|
||||
|
||||
var dgram = require('dgram')
|
||||
// OR, if not shimming via package.json "browser" field:
|
||||
// var dgram = require('RCTUDP')
|
||||
var socket = dgram.createSocket('udp4')
|
||||
socket.bind(12345)
|
||||
socket.once('listening', function() {
|
||||
var buf = toByteArray('excellent!')
|
||||
socket.send(buf, 0, buf.length, remotePort, remoteHost, function(err) {
|
||||
if (err) throw err
|
||||
|
||||
console.log('message was sent')
|
||||
})
|
||||
})
|
||||
|
||||
socket.on('message', function(msg, rinfo) {
|
||||
console.log('message was received', msg)
|
||||
})
|
||||
|
||||
### Note
|
||||
|
||||
If you want to send and receive node Buffer objects, you'll have to "npm install buffer" and set it as a global for RCTUDP to pick it up:
|
||||
|
||||
```js
|
||||
global.Buffer = global.Buffer || require('buffer').Buffer
|
||||
```
|
||||
|
||||
## Contributors
|
||||
|
||||
[Tradle, Inc.](https://github.com/tradle/about/wiki)
|
15
dgram.js
Normal file
15
dgram.js
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
/**
|
||||
* @providesModule RCTUDP
|
||||
* @flow
|
||||
*/
|
||||
|
||||
var RCTSocket = require('RCTUDPSocket')
|
||||
|
||||
module.exports = {
|
||||
createSocket: function(type) {
|
||||
return new RCTSocket({
|
||||
type: type
|
||||
})
|
||||
}
|
||||
}
|
16
iOS/AppDelegate.h
Normal file
16
iOS/AppDelegate.h
Normal file
@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Tradle, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||
|
||||
@property (nonatomic, strong) UIWindow *window;
|
||||
|
||||
@end
|
60
iOS/AppDelegate.m
Normal file
60
iOS/AppDelegate.m
Normal file
@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Tradle, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
#import "RCTRootView.h"
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
NSURL *jsCodeLocation;
|
||||
|
||||
/**
|
||||
* Loading JavaScript code - uncomment the one you want.
|
||||
*
|
||||
* OPTION 1
|
||||
* Load from development server. Start the server from the repository root:
|
||||
*
|
||||
* $ npm start
|
||||
*
|
||||
* To run on device, change `localhost` to the IP address of your computer
|
||||
* (you can get this by typing `ifconfig` into the terminal and selecting the
|
||||
* `inet` value under `en0:`) and make sure your computer and iOS device are
|
||||
* on the same Wi-Fi network.
|
||||
*/
|
||||
|
||||
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle"];
|
||||
|
||||
/**
|
||||
* OPTION 2
|
||||
* Load from pre-bundled file on disk. To re-generate the static bundle
|
||||
* from the root of your project directory, run
|
||||
*
|
||||
* $ react-native bundle --minify
|
||||
*
|
||||
* see http://facebook.github.io/react-native/docs/runningondevice.html
|
||||
*/
|
||||
|
||||
// jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||
|
||||
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
|
||||
moduleName:@"react-native-udp"
|
||||
launchOptions:launchOptions];
|
||||
|
||||
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
UIViewController *rootViewController = [[UIViewController alloc] init];
|
||||
rootViewController.view = rootView;
|
||||
self.window.rootViewController = rootViewController;
|
||||
[self.window makeKeyAndVisible];
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
42
iOS/Base.lproj/LaunchScreen.xib
Normal file
42
iOS/Base.lproj/LaunchScreen.xib
Normal file
@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7702" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7701"/>
|
||||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Powered by React Native" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
|
||||
<rect key="frame" x="20" y="439" width="441" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="RCTUDP" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
|
||||
<rect key="frame" x="20" y="140" width="441" height="43"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
|
||||
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
|
||||
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
|
||||
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
|
||||
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
|
||||
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
|
||||
</constraints>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<point key="canvasLocation" x="548" y="455"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
38
iOS/Images.xcassets/AppIcon.appiconset/Contents.json
Normal file
38
iOS/Images.xcassets/AppIcon.appiconset/Contents.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
42
iOS/Info.plist
Normal file
42
iOS/Info.plist
Normal file
@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
19
iOS/RCTUDP.h
Normal file
19
iOS/RCTUDP.h
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// RCTUDP.h
|
||||
// react-native-udp
|
||||
//
|
||||
// Created by Mark Vayngrib on 5/8/15.
|
||||
// Copyright (c) 2015 Tradle, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "RCTUDPClient.h"
|
||||
#import "RCTBridgeModule.h"
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
|
||||
@interface RCTUDP : NSObject<SocketClientDelegate, RCTBridgeModule>
|
||||
|
||||
@property(retain, nonatomic)NSMutableDictionary *clients;
|
||||
|
||||
@end
|
118
iOS/RCTUDP.m
Normal file
118
iOS/RCTUDP.m
Normal file
@ -0,0 +1,118 @@
|
||||
//
|
||||
// RCTUDP.m
|
||||
// react-native-udp
|
||||
//
|
||||
// Created by Mark Vayngrib on 5/8/15.
|
||||
// Copyright (c) 2015 Tradle, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTUDP.h"
|
||||
#import "RCTUDPClient.h"
|
||||
|
||||
@implementation RCTUDP
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
RCT_EXPORT_METHOD(createSocket:(NSString*)cId withOptions:(NSDictionary*)options)
|
||||
{
|
||||
if (!_clients) _clients = [[NSMutableDictionary alloc] init];
|
||||
|
||||
if (!cId) {
|
||||
RCTLogError(@"%@.createSocket called with nil id parameter.", [self class]);
|
||||
return;
|
||||
}
|
||||
|
||||
RCTUDPClient *client = [_clients objectForKey:cId];
|
||||
if (client) {
|
||||
RCTLogError(@"%@.createSocket called twice with the same id.", [self class]);
|
||||
return;
|
||||
}
|
||||
|
||||
client = [RCTUDPClient socketClientWithConfig:self];
|
||||
[_clients setObject:client forKey:cId];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(bind:(NSString*)cId
|
||||
port:(int)port
|
||||
address:(NSString *)address
|
||||
callback:(RCTResponseSenderBlock)callback)
|
||||
{
|
||||
RCTUDPClient* client = [self findClient:cId callback:callback];
|
||||
if (!client) return;
|
||||
|
||||
NSError *error = nil;
|
||||
if (![client bind:port address:address error:&error])
|
||||
{
|
||||
callback(@[error]);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(@[[NSNull null], [client address]]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(send:(NSString*)cId
|
||||
data:(NSData*)data
|
||||
port:(int)port
|
||||
address:(NSString*)address
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
RCTUDPClient* client = [self findClient:cId callback:callback];
|
||||
if (!client) return;
|
||||
|
||||
[client send:data remotePort:port remoteAddress:address callback:callback];
|
||||
if (callback) callback(@[]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(close:(NSString*)cId
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
RCTUDPClient* client = [self findClient:cId callback:callback];
|
||||
if (!client) return;
|
||||
|
||||
[client close];
|
||||
[_clients removeObjectForKey:cId];
|
||||
|
||||
if (callback) callback(@[]);
|
||||
}
|
||||
|
||||
- (void) onData:(RCTUDPClient*) client data:(NSData *)data host:(NSString *)host port:(uint16_t)port
|
||||
{
|
||||
NSString *clientID = [[_clients allKeysForObject:client] objectAtIndex:0];
|
||||
NSPropertyListFormat format;
|
||||
NSArray* arr = [NSPropertyListSerialization propertyListFromData:data
|
||||
mutabilityOption:NSPropertyListMutableContainers
|
||||
format:&format
|
||||
errorDescription:NULL];
|
||||
|
||||
[self.bridge.eventDispatcher sendDeviceEventWithName:[NSString stringWithFormat:@"udp-%@-data", clientID]
|
||||
body:@{
|
||||
@"data": arr,
|
||||
@"address": host,
|
||||
@"port": [NSNumber numberWithInt:port]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
-(RCTUDPClient*)findClient:(NSString*)cId callback:(RCTResponseSenderBlock)callback
|
||||
{
|
||||
RCTUDPClient *client = [_clients objectForKey:cId];
|
||||
if (!client) {
|
||||
if (!callback) {
|
||||
RCTLogError(@"%@.missing callback parameter.", [self class]);
|
||||
}
|
||||
else {
|
||||
callback(@[[NSString stringWithFormat:@"no client found with id %@", cId]]);
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
@end
|
85
iOS/RCTUDPClient.h
Normal file
85
iOS/RCTUDPClient.h
Normal file
@ -0,0 +1,85 @@
|
||||
//
|
||||
// RCTUDPClient.h
|
||||
// react-native-udp
|
||||
//
|
||||
// Created by Mark Vayngrib on 5/9/15.
|
||||
// Copyright (c) 2015 Tradle, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "RCTBridgeModule.h"
|
||||
|
||||
extern NSString *const RCTUDPErrorDomain;
|
||||
|
||||
enum RCTUDPError
|
||||
{
|
||||
RCTUDPNoError = 0, // Never used
|
||||
RCTUDPInvalidInvocationError,// Invalid method invocation
|
||||
RCTUDPBadConfigError, // Invalid configuration
|
||||
RCTUDPBadParamError, // Invalid parameter was passed
|
||||
RCTUDPSendTimeoutError, // A send operation timed out
|
||||
RCTUDPSendFailedError, // A send operation failed
|
||||
RCTUDPClosedError, // The socket was closed
|
||||
RCTUDPOtherError, // Description provided in userInfo
|
||||
};
|
||||
|
||||
typedef enum RCTUDPError RCTUDPError;
|
||||
|
||||
@class RCTUDPClient;
|
||||
|
||||
@protocol SocketClientDelegate <NSObject>
|
||||
|
||||
- (void)onData:(RCTUDPClient*) client data:(NSData *)data host:(NSString*) host port:(uint16_t) port;
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTUDPClient : NSObject
|
||||
|
||||
@property (nonatomic, retain) NSString* id;
|
||||
@property (nonatomic, retain) NSString* host;
|
||||
@property (nonatomic) u_int16_t port;
|
||||
|
||||
///---------------------------------------------------------------------------------------
|
||||
/// @name Class Methods
|
||||
///---------------------------------------------------------------------------------------
|
||||
/**
|
||||
* Initializes a new RCTUDPClient
|
||||
*
|
||||
* @param delegate The object holding the callbacks, usually 'self'.
|
||||
*
|
||||
* @return New RCTUDPClient
|
||||
*/
|
||||
|
||||
+ (id)socketClientWithConfig:(id<SocketClientDelegate>) delegate;
|
||||
|
||||
///---------------------------------------------------------------------------------------
|
||||
/// @name Instance Methods
|
||||
///---------------------------------------------------------------------------------------
|
||||
/**
|
||||
* Binds to a host and port
|
||||
*
|
||||
* @param port
|
||||
* @param host ip address
|
||||
* @return true if bound, false if there was an error
|
||||
*/
|
||||
- (BOOL)bind:(u_int16_t) port address:(NSString*) address error:(NSError**)error;
|
||||
|
||||
/**
|
||||
* send data to another host and port
|
||||
*
|
||||
* @param port
|
||||
* @param host ip address
|
||||
*/
|
||||
- (void)send:(NSData*) data remotePort:(u_int16_t) port remoteAddress:(NSString*) address callback:(RCTResponseSenderBlock) callback;
|
||||
|
||||
/**
|
||||
* @return { address: ip, port: port }
|
||||
*/
|
||||
- (NSDictionary *)address;
|
||||
|
||||
/**
|
||||
* close client
|
||||
*/
|
||||
- (void)close;
|
||||
|
||||
@end
|
174
iOS/RCTUDPClient.m
Normal file
174
iOS/RCTUDPClient.m
Normal file
@ -0,0 +1,174 @@
|
||||
//
|
||||
// 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>
|
||||
#import "RCTUDPClient.h"
|
||||
#import "RCTBridgeModule.h"
|
||||
#import "GCDAsyncUdpSocket.h"
|
||||
|
||||
NSString *const RCTUDPErrorDomain = @"RCTUDPErrorDomain";
|
||||
|
||||
@interface RCTUDPClient()
|
||||
{
|
||||
@private
|
||||
uint16_t _port;
|
||||
NSString* _address;
|
||||
GCDAsyncUdpSocket *_udpSocket;
|
||||
id<SocketClientDelegate> _clientDelegate;
|
||||
NSMutableDictionary* _pendingSends;
|
||||
long tag;
|
||||
}
|
||||
|
||||
- (id)initWithConfig:(id<SocketClientDelegate>) aDelegate;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTUDPClient
|
||||
|
||||
+ (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];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (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;
|
||||
ip.sin_port = htons(6003);
|
||||
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 {
|
||||
result = [_udpSocket bindToPort:port error:error];
|
||||
}
|
||||
|
||||
return result && [_udpSocket beginReceiving:error];
|
||||
}
|
||||
|
||||
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)msgTag
|
||||
{
|
||||
NSNumber* tagNum = [NSNumber numberWithLong:msgTag];
|
||||
RCTResponseSenderBlock callback = [_pendingSends objectForKey:tagNum];
|
||||
if (callback) {
|
||||
callback(@[]);
|
||||
[_pendingSends removeObjectForKey:tagNum];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotSendDataWithTag:(long)msgTag dueToError:(NSError *)error
|
||||
{
|
||||
// NSError* err = [self sendFailedError:[error description]];
|
||||
NSNumber* tagNum = [NSNumber numberWithLong:msgTag];
|
||||
RCTResponseSenderBlock callback = [_pendingSends objectForKey:tagNum];
|
||||
if (callback) {
|
||||
callback(@[error]);
|
||||
[_pendingSends removeObjectForKey:tagNum];
|
||||
}
|
||||
}
|
||||
|
||||
- (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) {
|
||||
[_pendingSends setObject:callback forKey:[NSNumber numberWithLong:tag]];
|
||||
}
|
||||
|
||||
tag++;
|
||||
}
|
||||
|
||||
- (NSDictionary* ) address
|
||||
{
|
||||
return @{
|
||||
@"address": [_udpSocket localHost],
|
||||
@"port": [NSNumber numberWithInt:[_udpSocket localPort]]
|
||||
};
|
||||
}
|
||||
|
||||
- (void) close
|
||||
{
|
||||
[_udpSocket close];
|
||||
}
|
||||
|
||||
- (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
|
8
iOS/main.jsbundle
Normal file
8
iOS/main.jsbundle
Normal file
@ -0,0 +1,8 @@
|
||||
// Offline JS
|
||||
// To re-generate the offline bundle, run this from the root of your project:
|
||||
//
|
||||
// $ react-native bundle --minify
|
||||
//
|
||||
// See http://facebook.github.io/react-native/docs/runningondevice.html for more details.
|
||||
|
||||
throw new Error('Offline JS file is empty. See iOS/main.jsbundle for instructions');
|
18
iOS/main.m
Normal file
18
iOS/main.m
Normal file
@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Tradle, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
int main(int argc, char * argv[]) {
|
||||
@autoreleasepool {
|
||||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
||||
}
|
||||
}
|
128
index.ios.js
Normal file
128
index.ios.js
Normal file
@ -0,0 +1,128 @@
|
||||
/**
|
||||
* Sample React Native App
|
||||
* https://github.com/facebook/react-native
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react-native');
|
||||
var {
|
||||
AppRegistry,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} = React;
|
||||
|
||||
function randomPort() {
|
||||
return Math.random() * 65536 | 0
|
||||
}
|
||||
|
||||
var dgram = require('RCTUDP')
|
||||
var a = dgram.createSocket('udp4')
|
||||
var aPort = randomPort()
|
||||
a.bind(bPort, function(err) {
|
||||
if (err) throw err
|
||||
|
||||
console.log('address', a.address())
|
||||
})
|
||||
|
||||
var b = dgram.createSocket('udp4')
|
||||
var bPort = randomPort()
|
||||
b.bind(bPort, function(err) {
|
||||
if (err) throw err
|
||||
|
||||
console.log('address', b.address())
|
||||
})
|
||||
|
||||
a.on('message', function(data, rinfo) {
|
||||
var str = String.fromCharCode.apply(null, new Uint8Array(data));
|
||||
console.log('a received', str, rinfo)
|
||||
a.close()
|
||||
b.close()
|
||||
})
|
||||
|
||||
b.on('message', function(data, rinfo) {
|
||||
var str = String.fromCharCode.apply(null, new Uint8Array(data));
|
||||
console.log('b received', str, rinfo)
|
||||
|
||||
// echo back
|
||||
b.send(data, 0, data.length, aPort, '127.0.0.1', function(err) {
|
||||
if (err) throw err
|
||||
|
||||
console.log('sent')
|
||||
})
|
||||
})
|
||||
|
||||
b.once('listening', function() {
|
||||
var msg = toByteArray('hello')
|
||||
a.send(msg, 0, msg.length, bPort, '127.0.0.1', function(err) {
|
||||
if (err) throw err
|
||||
|
||||
console.log('sent')
|
||||
})
|
||||
})
|
||||
|
||||
var rctsockets = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.welcome}>
|
||||
Welcome to React Native!
|
||||
</Text>
|
||||
<Text style={styles.instructions}>
|
||||
To get started, edit index.ios.js
|
||||
</Text>
|
||||
<Text style={styles.instructions}>
|
||||
Press Cmd+R to reload,{'\n'}
|
||||
Cmd+D or shake for dev menu
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#F5FCFF',
|
||||
},
|
||||
welcome: {
|
||||
fontSize: 20,
|
||||
textAlign: 'center',
|
||||
margin: 10,
|
||||
},
|
||||
instructions: {
|
||||
textAlign: 'center',
|
||||
color: '#333333',
|
||||
marginBottom: 5,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
function toByteArray(obj) {
|
||||
if (typeof obj === 'object') {
|
||||
var i = 0
|
||||
var arr = []
|
||||
while (true) {
|
||||
if (!(i in obj)) break
|
||||
|
||||
arr.push(+obj[i])
|
||||
i++
|
||||
}
|
||||
|
||||
return new Uint8Array(arr)
|
||||
}
|
||||
else if (typeof obj !== 'string') {
|
||||
throw new Error('unsupported format')
|
||||
}
|
||||
|
||||
var uint = new Uint8Array(obj.length);
|
||||
for (var i = 0, l = obj.length; i < l; i++){
|
||||
uint[i] = obj.charCodeAt(i);
|
||||
}
|
||||
|
||||
return new Uint8Array(uint);
|
||||
}
|
||||
|
||||
AppRegistry.registerComponent('react-native-udp', () => rctsockets);
|
31
package.json
Normal file
31
package.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "react-native-udp",
|
||||
"version": "0.0.1",
|
||||
"description": "node's dgram API for react-native",
|
||||
"main": "./dgram.js",
|
||||
"scripts": {
|
||||
"start": "exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/tradle/react-native-udp.git"
|
||||
},
|
||||
"keywords": [
|
||||
"react-component",
|
||||
"reactnative",
|
||||
"react-native",
|
||||
"dgram",
|
||||
"udp",
|
||||
"sockets",
|
||||
"ios"
|
||||
],
|
||||
"author": "Mark Vayngrib <mark.vayngrib@lablz.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/tradle/react-native-udp/issues"
|
||||
},
|
||||
"homepage": "https://github.com/tradle/react-native-udp",
|
||||
"dependencies": {
|
||||
"react-native": "^0.4.2"
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user