Initial commit

This commit is contained in:
Mark Vayngrib 2015-05-09 17:55:49 -04:00
commit df9879dd5f
22 changed files with 8582 additions and 0 deletions

33
.flowconfig Normal file
View 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
View 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/

View 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

File diff suppressed because it is too large Load Diff

22
LICENSE Normal file
View 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.

File diff suppressed because it is too large Load Diff

255
RCTUDPSocket.js Normal file
View 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
View 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
View 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
View 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
View 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

View 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>

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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"
}
}