Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | pmbaty | 1 | // |
2 | // GCDAsyncUdpSocket |
||
3 | // |
||
4 | // This class is in the public domain. |
||
5 | // Originally created by Robbie Hanson of Deusty LLC. |
||
6 | // Updated and maintained by Deusty LLC and the Apple development community. |
||
7 | // |
||
8 | // https://github.com/robbiehanson/CocoaAsyncSocket |
||
9 | // |
||
10 | |||
11 | #import <Foundation/Foundation.h> |
||
12 | #import <dispatch/dispatch.h> |
||
13 | #import <TargetConditionals.h> |
||
14 | #import <Availability.h> |
||
15 | |||
16 | NS_ASSUME_NONNULL_BEGIN |
||
17 | extern NSString *const GCDAsyncUdpSocketException; |
||
18 | extern NSString *const GCDAsyncUdpSocketErrorDomain; |
||
19 | |||
20 | extern NSString *const GCDAsyncUdpSocketQueueName; |
||
21 | extern NSString *const GCDAsyncUdpSocketThreadName; |
||
22 | |||
23 | typedef NS_ERROR_ENUM(GCDAsyncUdpSocketErrorDomain, GCDAsyncUdpSocketError) { |
||
24 | GCDAsyncUdpSocketNoError = 0, // Never used |
||
25 | GCDAsyncUdpSocketBadConfigError, // Invalid configuration |
||
26 | GCDAsyncUdpSocketBadParamError, // Invalid parameter was passed |
||
27 | GCDAsyncUdpSocketSendTimeoutError, // A send operation timed out |
||
28 | GCDAsyncUdpSocketClosedError, // The socket was closed |
||
29 | GCDAsyncUdpSocketOtherError, // Description provided in userInfo |
||
30 | }; |
||
31 | |||
32 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
33 | #pragma mark - |
||
34 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
35 | |||
36 | @class GCDAsyncUdpSocket; |
||
37 | |||
38 | @protocol GCDAsyncUdpSocketDelegate <NSObject> |
||
39 | @optional |
||
40 | |||
41 | /** |
||
42 | * By design, UDP is a connectionless protocol, and connecting is not needed. |
||
43 | * However, you may optionally choose to connect to a particular host for reasons |
||
44 | * outlined in the documentation for the various connect methods listed above. |
||
45 | * |
||
46 | * This method is called if one of the connect methods are invoked, and the connection is successful. |
||
47 | **/ |
||
48 | - (void)udpSocket:(GCDAsyncUdpSocket *)sock didConnectToAddress:(NSData *)address; |
||
49 | |||
50 | /** |
||
51 | * By design, UDP is a connectionless protocol, and connecting is not needed. |
||
52 | * However, you may optionally choose to connect to a particular host for reasons |
||
53 | * outlined in the documentation for the various connect methods listed above. |
||
54 | * |
||
55 | * This method is called if one of the connect methods are invoked, and the connection fails. |
||
56 | * This may happen, for example, if a domain name is given for the host and the domain name is unable to be resolved. |
||
57 | **/ |
||
58 | - (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotConnect:(NSError * _Nullable)error; |
||
59 | |||
60 | /** |
||
61 | * Called when the datagram with the given tag has been sent. |
||
62 | **/ |
||
63 | - (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag; |
||
64 | |||
65 | /** |
||
66 | * Called if an error occurs while trying to send a datagram. |
||
67 | * This could be due to a timeout, or something more serious such as the data being too large to fit in a sigle packet. |
||
68 | **/ |
||
69 | - (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError * _Nullable)error; |
||
70 | |||
71 | /** |
||
72 | * Called when the socket has received the requested datagram. |
||
73 | **/ |
||
74 | - (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data |
||
75 | fromAddress:(NSData *)address |
||
76 | withFilterContext:(nullable id)filterContext; |
||
77 | |||
78 | /** |
||
79 | * Called when the socket is closed. |
||
80 | **/ |
||
81 | - (void)udpSocketDidClose:(GCDAsyncUdpSocket *)sock withError:(NSError * _Nullable)error; |
||
82 | |||
83 | @end |
||
84 | |||
85 | /** |
||
86 | * You may optionally set a receive filter for the socket. |
||
87 | * A filter can provide several useful features: |
||
88 | * |
||
89 | * 1. Many times udp packets need to be parsed. |
||
90 | * Since the filter can run in its own independent queue, you can parallelize this parsing quite easily. |
||
91 | * The end result is a parallel socket io, datagram parsing, and packet processing. |
||
92 | * |
||
93 | * 2. Many times udp packets are discarded because they are duplicate/unneeded/unsolicited. |
||
94 | * The filter can prevent such packets from arriving at the delegate. |
||
95 | * And because the filter can run in its own independent queue, this doesn't slow down the delegate. |
||
96 | * |
||
97 | * - Since the udp protocol does not guarantee delivery, udp packets may be lost. |
||
98 | * Many protocols built atop udp thus provide various resend/re-request algorithms. |
||
99 | * This sometimes results in duplicate packets arriving. |
||
100 | * A filter may allow you to architect the duplicate detection code to run in parallel to normal processing. |
||
101 | * |
||
102 | * - Since the udp socket may be connectionless, its possible for unsolicited packets to arrive. |
||
103 | * Such packets need to be ignored. |
||
104 | * |
||
105 | * 3. Sometimes traffic shapers are needed to simulate real world environments. |
||
106 | * A filter allows you to write custom code to simulate such environments. |
||
107 | * The ability to code this yourself is especially helpful when your simulated environment |
||
108 | * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), |
||
109 | * or the system tools to handle this aren't available (e.g. on a mobile device). |
||
110 | * |
||
111 | * @param data - The packet that was received. |
||
112 | * @param address - The address the data was received from. |
||
113 | * See utilities section for methods to extract info from address. |
||
114 | * @param context - Out parameter you may optionally set, which will then be passed to the delegate method. |
||
115 | * For example, filter block can parse the data and then, |
||
116 | * pass the parsed data to the delegate. |
||
117 | * |
||
118 | * @returns - YES if the received packet should be passed onto the delegate. |
||
119 | * NO if the received packet should be discarded, and not reported to the delegete. |
||
120 | * |
||
121 | * Example: |
||
122 | * |
||
123 | * GCDAsyncUdpSocketReceiveFilterBlock filter = ^BOOL (NSData *data, NSData *address, id *context) { |
||
124 | * |
||
125 | * MyProtocolMessage *msg = [MyProtocol parseMessage:data]; |
||
126 | * |
||
127 | * *context = response; |
||
128 | * return (response != nil); |
||
129 | * }; |
||
130 | * [udpSocket setReceiveFilter:filter withQueue:myParsingQueue]; |
||
131 | * |
||
132 | **/ |
||
133 | typedef BOOL (^GCDAsyncUdpSocketReceiveFilterBlock)(NSData *data, NSData *address, id __nullable * __nonnull context); |
||
134 | |||
135 | /** |
||
136 | * You may optionally set a send filter for the socket. |
||
137 | * A filter can provide several interesting possibilities: |
||
138 | * |
||
139 | * 1. Optional caching of resolved addresses for domain names. |
||
140 | * The cache could later be consulted, resulting in fewer system calls to getaddrinfo. |
||
141 | * |
||
142 | * 2. Reusable modules of code for bandwidth monitoring. |
||
143 | * |
||
144 | * 3. Sometimes traffic shapers are needed to simulate real world environments. |
||
145 | * A filter allows you to write custom code to simulate such environments. |
||
146 | * The ability to code this yourself is especially helpful when your simulated environment |
||
147 | * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), |
||
148 | * or the system tools to handle this aren't available (e.g. on a mobile device). |
||
149 | * |
||
150 | * @param data - The packet that was received. |
||
151 | * @param address - The address the data was received from. |
||
152 | * See utilities section for methods to extract info from address. |
||
153 | * @param tag - The tag that was passed in the send method. |
||
154 | * |
||
155 | * @returns - YES if the packet should actually be sent over the socket. |
||
156 | * NO if the packet should be silently dropped (not sent over the socket). |
||
157 | * |
||
158 | * Regardless of the return value, the delegate will be informed that the packet was successfully sent. |
||
159 | * |
||
160 | **/ |
||
161 | typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, long tag); |
||
162 | |||
163 | |||
164 | @interface GCDAsyncUdpSocket : NSObject |
||
165 | |||
166 | /** |
||
167 | * GCDAsyncUdpSocket uses the standard delegate paradigm, |
||
168 | * but executes all delegate callbacks on a given delegate dispatch queue. |
||
169 | * This allows for maximum concurrency, while at the same time providing easy thread safety. |
||
170 | * |
||
171 | * You MUST set a delegate AND delegate dispatch queue before attempting to |
||
172 | * use the socket, or you will get an error. |
||
173 | * |
||
174 | * The socket queue is optional. |
||
175 | * If you pass NULL, GCDAsyncSocket will automatically create its own socket queue. |
||
176 | * If you choose to provide a socket queue, the socket queue must not be a concurrent queue, |
||
177 | * then please see the discussion for the method markSocketQueueTargetQueue. |
||
178 | * |
||
179 | * The delegate queue and socket queue can optionally be the same. |
||
180 | **/ |
||
181 | - (instancetype)init; |
||
182 | - (instancetype)initWithSocketQueue:(nullable dispatch_queue_t)sq; |
||
183 | - (instancetype)initWithDelegate:(nullable id<GCDAsyncUdpSocketDelegate>)aDelegate delegateQueue:(nullable dispatch_queue_t)dq; |
||
184 | - (instancetype)initWithDelegate:(nullable id<GCDAsyncUdpSocketDelegate>)aDelegate delegateQueue:(nullable dispatch_queue_t)dq socketQueue:(nullable dispatch_queue_t)sq NS_DESIGNATED_INITIALIZER; |
||
185 | |||
186 | #pragma mark Configuration |
||
187 | |||
188 | - (nullable id<GCDAsyncUdpSocketDelegate>)delegate; |
||
189 | - (void)setDelegate:(nullable id<GCDAsyncUdpSocketDelegate>)delegate; |
||
190 | - (void)synchronouslySetDelegate:(nullable id<GCDAsyncUdpSocketDelegate>)delegate; |
||
191 | |||
192 | - (nullable dispatch_queue_t)delegateQueue; |
||
193 | - (void)setDelegateQueue:(nullable dispatch_queue_t)delegateQueue; |
||
194 | - (void)synchronouslySetDelegateQueue:(nullable dispatch_queue_t)delegateQueue; |
||
195 | |||
196 | - (void)getDelegate:(id<GCDAsyncUdpSocketDelegate> __nullable * __nullable)delegatePtr delegateQueue:(dispatch_queue_t __nullable * __nullable)delegateQueuePtr; |
||
197 | - (void)setDelegate:(nullable id<GCDAsyncUdpSocketDelegate>)delegate delegateQueue:(nullable dispatch_queue_t)delegateQueue; |
||
198 | - (void)synchronouslySetDelegate:(nullable id<GCDAsyncUdpSocketDelegate>)delegate delegateQueue:(nullable dispatch_queue_t)delegateQueue; |
||
199 | |||
200 | /** |
||
201 | * By default, both IPv4 and IPv6 are enabled. |
||
202 | * |
||
203 | * This means GCDAsyncUdpSocket automatically supports both protocols, |
||
204 | * and can send to IPv4 or IPv6 addresses, |
||
205 | * as well as receive over IPv4 and IPv6. |
||
206 | * |
||
207 | * For operations that require DNS resolution, GCDAsyncUdpSocket supports both IPv4 and IPv6. |
||
208 | * If a DNS lookup returns only IPv4 results, GCDAsyncUdpSocket will automatically use IPv4. |
||
209 | * If a DNS lookup returns only IPv6 results, GCDAsyncUdpSocket will automatically use IPv6. |
||
210 | * If a DNS lookup returns both IPv4 and IPv6 results, then the protocol used depends on the configured preference. |
||
211 | * If IPv4 is preferred, then IPv4 is used. |
||
212 | * If IPv6 is preferred, then IPv6 is used. |
||
213 | * If neutral, then the first IP version in the resolved array will be used. |
||
214 | * |
||
215 | * Starting with Mac OS X 10.7 Lion and iOS 5, the default IP preference is neutral. |
||
216 | * On prior systems the default IP preference is IPv4. |
||
217 | **/ |
||
218 | - (BOOL)isIPv4Enabled; |
||
219 | - (void)setIPv4Enabled:(BOOL)flag; |
||
220 | |||
221 | - (BOOL)isIPv6Enabled; |
||
222 | - (void)setIPv6Enabled:(BOOL)flag; |
||
223 | |||
224 | - (BOOL)isIPv4Preferred; |
||
225 | - (BOOL)isIPv6Preferred; |
||
226 | - (BOOL)isIPVersionNeutral; |
||
227 | |||
228 | - (void)setPreferIPv4; |
||
229 | - (void)setPreferIPv6; |
||
230 | - (void)setIPVersionNeutral; |
||
231 | |||
232 | /** |
||
233 | * Gets/Sets the maximum size of the buffer that will be allocated for receive operations. |
||
234 | * The default maximum size is 65535 bytes. |
||
235 | * |
||
236 | * The theoretical maximum size of any IPv4 UDP packet is UINT16_MAX = 65535. |
||
237 | * The theoretical maximum size of any IPv6 UDP packet is UINT32_MAX = 4294967295. |
||
238 | * |
||
239 | * Since the OS/GCD notifies us of the size of each received UDP packet, |
||
240 | * the actual allocated buffer size for each packet is exact. |
||
241 | * And in practice the size of UDP packets is generally much smaller than the max. |
||
242 | * Indeed most protocols will send and receive packets of only a few bytes, |
||
243 | * or will set a limit on the size of packets to prevent fragmentation in the IP layer. |
||
244 | * |
||
245 | * If you set the buffer size too small, the sockets API in the OS will silently discard |
||
246 | * any extra data, and you will not be notified of the error. |
||
247 | **/ |
||
248 | - (uint16_t)maxReceiveIPv4BufferSize; |
||
249 | - (void)setMaxReceiveIPv4BufferSize:(uint16_t)max; |
||
250 | |||
251 | - (uint32_t)maxReceiveIPv6BufferSize; |
||
252 | - (void)setMaxReceiveIPv6BufferSize:(uint32_t)max; |
||
253 | |||
254 | /** |
||
255 | * Gets/Sets the maximum size of the buffer that will be allocated for send operations. |
||
256 | * The default maximum size is 65535 bytes. |
||
257 | * |
||
258 | * Given that a typical link MTU is 1500 bytes, a large UDP datagram will have to be |
||
259 | * fragmented, and that’s both expensive and risky (if one fragment goes missing, the |
||
260 | * entire datagram is lost). You are much better off sending a large number of smaller |
||
261 | * UDP datagrams, preferably using a path MTU algorithm to avoid fragmentation. |
||
262 | * |
||
263 | * You must set it before the sockt is created otherwise it won't work. |
||
264 | * |
||
265 | **/ |
||
266 | - (uint16_t)maxSendBufferSize; |
||
267 | - (void)setMaxSendBufferSize:(uint16_t)max; |
||
268 | |||
269 | /** |
||
270 | * User data allows you to associate arbitrary information with the socket. |
||
271 | * This data is not used internally in any way. |
||
272 | **/ |
||
273 | - (nullable id)userData; |
||
274 | - (void)setUserData:(nullable id)arbitraryUserData; |
||
275 | |||
276 | #pragma mark Diagnostics |
||
277 | |||
278 | /** |
||
279 | * Returns the local address info for the socket. |
||
280 | * |
||
281 | * The localAddress method returns a sockaddr structure wrapped in a NSData object. |
||
282 | * The localHost method returns the human readable IP address as a string. |
||
283 | * |
||
284 | * Note: Address info may not be available until after the socket has been binded, connected |
||
285 | * or until after data has been sent. |
||
286 | **/ |
||
287 | - (nullable NSData *)localAddress; |
||
288 | - (nullable NSString *)localHost; |
||
289 | - (uint16_t)localPort; |
||
290 | |||
291 | - (nullable NSData *)localAddress_IPv4; |
||
292 | - (nullable NSString *)localHost_IPv4; |
||
293 | - (uint16_t)localPort_IPv4; |
||
294 | |||
295 | - (nullable NSData *)localAddress_IPv6; |
||
296 | - (nullable NSString *)localHost_IPv6; |
||
297 | - (uint16_t)localPort_IPv6; |
||
298 | |||
299 | /** |
||
300 | * Returns the remote address info for the socket. |
||
301 | * |
||
302 | * The connectedAddress method returns a sockaddr structure wrapped in a NSData object. |
||
303 | * The connectedHost method returns the human readable IP address as a string. |
||
304 | * |
||
305 | * Note: Since UDP is connectionless by design, connected address info |
||
306 | * will not be available unless the socket is explicitly connected to a remote host/port. |
||
307 | * If the socket is not connected, these methods will return nil / 0. |
||
308 | **/ |
||
309 | - (nullable NSData *)connectedAddress; |
||
310 | - (nullable NSString *)connectedHost; |
||
311 | - (uint16_t)connectedPort; |
||
312 | |||
313 | /** |
||
314 | * Returns whether or not this socket has been connected to a single host. |
||
315 | * By design, UDP is a connectionless protocol, and connecting is not needed. |
||
316 | * If connected, the socket will only be able to send/receive data to/from the connected host. |
||
317 | **/ |
||
318 | - (BOOL)isConnected; |
||
319 | |||
320 | /** |
||
321 | * Returns whether or not this socket has been closed. |
||
322 | * The only way a socket can be closed is if you explicitly call one of the close methods. |
||
323 | **/ |
||
324 | - (BOOL)isClosed; |
||
325 | |||
326 | /** |
||
327 | * Returns whether or not this socket is IPv4. |
||
328 | * |
||
329 | * By default this will be true, unless: |
||
330 | * - IPv4 is disabled (via setIPv4Enabled:) |
||
331 | * - The socket is explicitly bound to an IPv6 address |
||
332 | * - The socket is connected to an IPv6 address |
||
333 | **/ |
||
334 | - (BOOL)isIPv4; |
||
335 | |||
336 | /** |
||
337 | * Returns whether or not this socket is IPv6. |
||
338 | * |
||
339 | * By default this will be true, unless: |
||
340 | * - IPv6 is disabled (via setIPv6Enabled:) |
||
341 | * - The socket is explicitly bound to an IPv4 address |
||
342 | * _ The socket is connected to an IPv4 address |
||
343 | * |
||
344 | * This method will also return false on platforms that do not support IPv6. |
||
345 | * Note: The iPhone does not currently support IPv6. |
||
346 | **/ |
||
347 | - (BOOL)isIPv6; |
||
348 | |||
349 | #pragma mark Binding |
||
350 | |||
351 | /** |
||
352 | * Binds the UDP socket to the given port. |
||
353 | * Binding should be done for server sockets that receive data prior to sending it. |
||
354 | * Client sockets can skip binding, |
||
355 | * as the OS will automatically assign the socket an available port when it starts sending data. |
||
356 | * |
||
357 | * You may optionally pass a port number of zero to immediately bind the socket, |
||
358 | * yet still allow the OS to automatically assign an available port. |
||
359 | * |
||
360 | * You cannot bind a socket after its been connected. |
||
361 | * You can only bind a socket once. |
||
362 | * You can still connect a socket (if desired) after binding. |
||
363 | * |
||
364 | * On success, returns YES. |
||
365 | * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass NULL for errPtr. |
||
366 | **/ |
||
367 | - (BOOL)bindToPort:(uint16_t)port error:(NSError **)errPtr; |
||
368 | |||
369 | /** |
||
370 | * Binds the UDP socket to the given port and optional interface. |
||
371 | * Binding should be done for server sockets that receive data prior to sending it. |
||
372 | * Client sockets can skip binding, |
||
373 | * as the OS will automatically assign the socket an available port when it starts sending data. |
||
374 | * |
||
375 | * You may optionally pass a port number of zero to immediately bind the socket, |
||
376 | * yet still allow the OS to automatically assign an available port. |
||
377 | * |
||
378 | * The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35"). |
||
379 | * You may also use the special strings "localhost" or "loopback" to specify that |
||
380 | * the socket only accept packets from the local machine. |
||
381 | * |
||
382 | * You cannot bind a socket after its been connected. |
||
383 | * You can only bind a socket once. |
||
384 | * You can still connect a socket (if desired) after binding. |
||
385 | * |
||
386 | * On success, returns YES. |
||
387 | * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass NULL for errPtr. |
||
388 | **/ |
||
389 | - (BOOL)bindToPort:(uint16_t)port interface:(nullable NSString *)interface error:(NSError **)errPtr; |
||
390 | |||
391 | /** |
||
392 | * Binds the UDP socket to the given address, specified as a sockaddr structure wrapped in a NSData object. |
||
393 | * |
||
394 | * If you have an existing struct sockaddr you can convert it to a NSData object like so: |
||
395 | * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len]; |
||
396 | * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len]; |
||
397 | * |
||
398 | * Binding should be done for server sockets that receive data prior to sending it. |
||
399 | * Client sockets can skip binding, |
||
400 | * as the OS will automatically assign the socket an available port when it starts sending data. |
||
401 | * |
||
402 | * You cannot bind a socket after its been connected. |
||
403 | * You can only bind a socket once. |
||
404 | * You can still connect a socket (if desired) after binding. |
||
405 | * |
||
406 | * On success, returns YES. |
||
407 | * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass NULL for errPtr. |
||
408 | **/ |
||
409 | - (BOOL)bindToAddress:(NSData *)localAddr error:(NSError **)errPtr; |
||
410 | |||
411 | #pragma mark Connecting |
||
412 | |||
413 | /** |
||
414 | * Connects the UDP socket to the given host and port. |
||
415 | * By design, UDP is a connectionless protocol, and connecting is not needed. |
||
416 | * |
||
417 | * Choosing to connect to a specific host/port has the following effect: |
||
418 | * - You will only be able to send data to the connected host/port. |
||
419 | * - You will only be able to receive data from the connected host/port. |
||
420 | * - You will receive ICMP messages that come from the connected host/port, such as "connection refused". |
||
421 | * |
||
422 | * The actual process of connecting a UDP socket does not result in any communication on the socket. |
||
423 | * It simply changes the internal state of the socket. |
||
424 | * |
||
425 | * You cannot bind a socket after it has been connected. |
||
426 | * You can only connect a socket once. |
||
427 | * |
||
428 | * The host may be a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2"). |
||
429 | * |
||
430 | * This method is asynchronous as it requires a DNS lookup to resolve the given host name. |
||
431 | * If an obvious error is detected, this method immediately returns NO and sets errPtr. |
||
432 | * If you don't care about the error, you can pass nil for errPtr. |
||
433 | * Otherwise, this method returns YES and begins the asynchronous connection process. |
||
434 | * The result of the asynchronous connection process will be reported via the delegate methods. |
||
435 | **/ |
||
436 | - (BOOL)connectToHost:(NSString *)host onPort:(uint16_t)port error:(NSError **)errPtr; |
||
437 | |||
438 | /** |
||
439 | * Connects the UDP socket to the given address, specified as a sockaddr structure wrapped in a NSData object. |
||
440 | * |
||
441 | * If you have an existing struct sockaddr you can convert it to a NSData object like so: |
||
442 | * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len]; |
||
443 | * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len]; |
||
444 | * |
||
445 | * By design, UDP is a connectionless protocol, and connecting is not needed. |
||
446 | * |
||
447 | * Choosing to connect to a specific address has the following effect: |
||
448 | * - You will only be able to send data to the connected address. |
||
449 | * - You will only be able to receive data from the connected address. |
||
450 | * - You will receive ICMP messages that come from the connected address, such as "connection refused". |
||
451 | * |
||
452 | * Connecting a UDP socket does not result in any communication on the socket. |
||
453 | * It simply changes the internal state of the socket. |
||
454 | * |
||
455 | * You cannot bind a socket after its been connected. |
||
456 | * You can only connect a socket once. |
||
457 | * |
||
458 | * On success, returns YES. |
||
459 | * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr. |
||
460 | * |
||
461 | * Note: Unlike the connectToHost:onPort:error: method, this method does not require a DNS lookup. |
||
462 | * Thus when this method returns, the connection has either failed or fully completed. |
||
463 | * In other words, this method is synchronous, unlike the asynchronous connectToHost::: method. |
||
464 | * However, for compatibility and simplification of delegate code, if this method returns YES |
||
465 | * then the corresponding delegate method (udpSocket:didConnectToHost:port:) is still invoked. |
||
466 | **/ |
||
467 | - (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr; |
||
468 | |||
469 | #pragma mark Multicast |
||
470 | |||
471 | /** |
||
472 | * Join multicast group. |
||
473 | * Group should be an IP address (eg @"225.228.0.1"). |
||
474 | * |
||
475 | * On success, returns YES. |
||
476 | * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr. |
||
477 | **/ |
||
478 | - (BOOL)joinMulticastGroup:(NSString *)group error:(NSError **)errPtr; |
||
479 | |||
480 | /** |
||
481 | * Join multicast group. |
||
482 | * Group should be an IP address (eg @"225.228.0.1"). |
||
483 | * The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35"). |
||
484 | * |
||
485 | * On success, returns YES. |
||
486 | * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr. |
||
487 | **/ |
||
488 | - (BOOL)joinMulticastGroup:(NSString *)group onInterface:(nullable NSString *)interface error:(NSError **)errPtr; |
||
489 | |||
490 | - (BOOL)leaveMulticastGroup:(NSString *)group error:(NSError **)errPtr; |
||
491 | - (BOOL)leaveMulticastGroup:(NSString *)group onInterface:(nullable NSString *)interface error:(NSError **)errPtr; |
||
492 | |||
493 | /** |
||
494 | * Send multicast on a specified interface. |
||
495 | * For IPv4, interface should be the the IP address of the interface (eg @"192.168.10.1"). |
||
496 | * For IPv6, interface should be the a network interface name (eg @"en0"). |
||
497 | * |
||
498 | * On success, returns YES. |
||
499 | * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr. |
||
500 | **/ |
||
501 | |||
502 | - (BOOL)sendIPv4MulticastOnInterface:(NSString*)interface error:(NSError **)errPtr; |
||
503 | - (BOOL)sendIPv6MulticastOnInterface:(NSString*)interface error:(NSError **)errPtr; |
||
504 | |||
505 | #pragma mark Reuse Port |
||
506 | |||
507 | /** |
||
508 | * By default, only one socket can be bound to a given IP address + port at a time. |
||
509 | * To enable multiple processes to simultaneously bind to the same address+port, |
||
510 | * you need to enable this functionality in the socket. All processes that wish to |
||
511 | * use the address+port simultaneously must all enable reuse port on the socket |
||
512 | * bound to that port. |
||
513 | **/ |
||
514 | - (BOOL)enableReusePort:(BOOL)flag error:(NSError **)errPtr; |
||
515 | |||
516 | #pragma mark Broadcast |
||
517 | |||
518 | /** |
||
519 | * By default, the underlying socket in the OS will not allow you to send broadcast messages. |
||
520 | * In order to send broadcast messages, you need to enable this functionality in the socket. |
||
521 | * |
||
522 | * A broadcast is a UDP message to addresses like "192.168.255.255" or "255.255.255.255" that is |
||
523 | * delivered to every host on the network. |
||
524 | * The reason this is generally disabled by default (by the OS) is to prevent |
||
525 | * accidental broadcast messages from flooding the network. |
||
526 | **/ |
||
527 | - (BOOL)enableBroadcast:(BOOL)flag error:(NSError **)errPtr; |
||
528 | |||
529 | #pragma mark Sending |
||
530 | |||
531 | /** |
||
532 | * Asynchronously sends the given data, with the given timeout and tag. |
||
533 | * |
||
534 | * This method may only be used with a connected socket. |
||
535 | * Recall that connecting is optional for a UDP socket. |
||
536 | * For connected sockets, data can only be sent to the connected address. |
||
537 | * For non-connected sockets, the remote destination is specified for each packet. |
||
538 | * For more information about optionally connecting udp sockets, see the documentation for the connect methods above. |
||
539 | * |
||
540 | * @param data |
||
541 | * The data to send. |
||
542 | * If data is nil or zero-length, this method does nothing. |
||
543 | * If passing NSMutableData, please read the thread-safety notice below. |
||
544 | * |
||
545 | * @param timeout |
||
546 | * The timeout for the send opeartion. |
||
547 | * If the timeout value is negative, the send operation will not use a timeout. |
||
548 | * |
||
549 | * @param tag |
||
550 | * The tag is for your convenience. |
||
551 | * It is not sent or received over the socket in any manner what-so-ever. |
||
552 | * It is reported back as a parameter in the udpSocket:didSendDataWithTag: |
||
553 | * or udpSocket:didNotSendDataWithTag:dueToError: methods. |
||
554 | * You can use it as an array index, state id, type constant, etc. |
||
555 | * |
||
556 | * |
||
557 | * Thread-Safety Note: |
||
558 | * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while |
||
559 | * the socket is sending it. In other words, it's not safe to alter the data until after the delegate method |
||
560 | * udpSocket:didSendDataWithTag: or udpSocket:didNotSendDataWithTag:dueToError: is invoked signifying |
||
561 | * that this particular send operation has completed. |
||
562 | * This is due to the fact that GCDAsyncUdpSocket does NOT copy the data. |
||
563 | * It simply retains it for performance reasons. |
||
564 | * Often times, if NSMutableData is passed, it is because a request/response was built up in memory. |
||
565 | * Copying this data adds an unwanted/unneeded overhead. |
||
566 | * If you need to write data from an immutable buffer, and you need to alter the buffer before the socket |
||
567 | * completes sending the bytes (which is NOT immediately after this method returns, but rather at a later time |
||
568 | * when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method. |
||
569 | **/ |
||
570 | - (void)sendData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; |
||
571 | |||
572 | /** |
||
573 | * Asynchronously sends the given data, with the given timeout and tag, to the given host and port. |
||
574 | * |
||
575 | * This method cannot be used with a connected socket. |
||
576 | * Recall that connecting is optional for a UDP socket. |
||
577 | * For connected sockets, data can only be sent to the connected address. |
||
578 | * For non-connected sockets, the remote destination is specified for each packet. |
||
579 | * For more information about optionally connecting udp sockets, see the documentation for the connect methods above. |
||
580 | * |
||
581 | * @param data |
||
582 | * The data to send. |
||
583 | * If data is nil or zero-length, this method does nothing. |
||
584 | * If passing NSMutableData, please read the thread-safety notice below. |
||
585 | * |
||
586 | * @param host |
||
587 | * The destination to send the udp packet to. |
||
588 | * May be specified as a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2"). |
||
589 | * You may also use the convenience strings of "loopback" or "localhost". |
||
590 | * |
||
591 | * @param port |
||
592 | * The port of the host to send to. |
||
593 | * |
||
594 | * @param timeout |
||
595 | * The timeout for the send opeartion. |
||
596 | * If the timeout value is negative, the send operation will not use a timeout. |
||
597 | * |
||
598 | * @param tag |
||
599 | * The tag is for your convenience. |
||
600 | * It is not sent or received over the socket in any manner what-so-ever. |
||
601 | * It is reported back as a parameter in the udpSocket:didSendDataWithTag: |
||
602 | * or udpSocket:didNotSendDataWithTag:dueToError: methods. |
||
603 | * You can use it as an array index, state id, type constant, etc. |
||
604 | * |
||
605 | * |
||
606 | * Thread-Safety Note: |
||
607 | * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while |
||
608 | * the socket is sending it. In other words, it's not safe to alter the data until after the delegate method |
||
609 | * udpSocket:didSendDataWithTag: or udpSocket:didNotSendDataWithTag:dueToError: is invoked signifying |
||
610 | * that this particular send operation has completed. |
||
611 | * This is due to the fact that GCDAsyncUdpSocket does NOT copy the data. |
||
612 | * It simply retains it for performance reasons. |
||
613 | * Often times, if NSMutableData is passed, it is because a request/response was built up in memory. |
||
614 | * Copying this data adds an unwanted/unneeded overhead. |
||
615 | * If you need to write data from an immutable buffer, and you need to alter the buffer before the socket |
||
616 | * completes sending the bytes (which is NOT immediately after this method returns, but rather at a later time |
||
617 | * when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method. |
||
618 | **/ |
||
619 | - (void)sendData:(NSData *)data |
||
620 | toHost:(NSString *)host |
||
621 | port:(uint16_t)port |
||
622 | withTimeout:(NSTimeInterval)timeout |
||
623 | tag:(long)tag; |
||
624 | |||
625 | /** |
||
626 | * Asynchronously sends the given data, with the given timeout and tag, to the given address. |
||
627 | * |
||
628 | * This method cannot be used with a connected socket. |
||
629 | * Recall that connecting is optional for a UDP socket. |
||
630 | * For connected sockets, data can only be sent to the connected address. |
||
631 | * For non-connected sockets, the remote destination is specified for each packet. |
||
632 | * For more information about optionally connecting udp sockets, see the documentation for the connect methods above. |
||
633 | * |
||
634 | * @param data |
||
635 | * The data to send. |
||
636 | * If data is nil or zero-length, this method does nothing. |
||
637 | * If passing NSMutableData, please read the thread-safety notice below. |
||
638 | * |
||
639 | * @param remoteAddr |
||
640 | * The address to send the data to (specified as a sockaddr structure wrapped in a NSData object). |
||
641 | * |
||
642 | * @param timeout |
||
643 | * The timeout for the send opeartion. |
||
644 | * If the timeout value is negative, the send operation will not use a timeout. |
||
645 | * |
||
646 | * @param tag |
||
647 | * The tag is for your convenience. |
||
648 | * It is not sent or received over the socket in any manner what-so-ever. |
||
649 | * It is reported back as a parameter in the udpSocket:didSendDataWithTag: |
||
650 | * or udpSocket:didNotSendDataWithTag:dueToError: methods. |
||
651 | * You can use it as an array index, state id, type constant, etc. |
||
652 | * |
||
653 | * |
||
654 | * Thread-Safety Note: |
||
655 | * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while |
||
656 | * the socket is sending it. In other words, it's not safe to alter the data until after the delegate method |
||
657 | * udpSocket:didSendDataWithTag: or udpSocket:didNotSendDataWithTag:dueToError: is invoked signifying |
||
658 | * that this particular send operation has completed. |
||
659 | * This is due to the fact that GCDAsyncUdpSocket does NOT copy the data. |
||
660 | * It simply retains it for performance reasons. |
||
661 | * Often times, if NSMutableData is passed, it is because a request/response was built up in memory. |
||
662 | * Copying this data adds an unwanted/unneeded overhead. |
||
663 | * If you need to write data from an immutable buffer, and you need to alter the buffer before the socket |
||
664 | * completes sending the bytes (which is NOT immediately after this method returns, but rather at a later time |
||
665 | * when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method. |
||
666 | **/ |
||
667 | - (void)sendData:(NSData *)data toAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout tag:(long)tag; |
||
668 | |||
669 | /** |
||
670 | * You may optionally set a send filter for the socket. |
||
671 | * A filter can provide several interesting possibilities: |
||
672 | * |
||
673 | * 1. Optional caching of resolved addresses for domain names. |
||
674 | * The cache could later be consulted, resulting in fewer system calls to getaddrinfo. |
||
675 | * |
||
676 | * 2. Reusable modules of code for bandwidth monitoring. |
||
677 | * |
||
678 | * 3. Sometimes traffic shapers are needed to simulate real world environments. |
||
679 | * A filter allows you to write custom code to simulate such environments. |
||
680 | * The ability to code this yourself is especially helpful when your simulated environment |
||
681 | * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), |
||
682 | * or the system tools to handle this aren't available (e.g. on a mobile device). |
||
683 | * |
||
684 | * For more information about GCDAsyncUdpSocketSendFilterBlock, see the documentation for its typedef. |
||
685 | * To remove a previously set filter, invoke this method and pass a nil filterBlock and NULL filterQueue. |
||
686 | * |
||
687 | * Note: This method invokes setSendFilter:withQueue:isAsynchronous: (documented below), |
||
688 | * passing YES for the isAsynchronous parameter. |
||
689 | **/ |
||
690 | - (void)setSendFilter:(nullable GCDAsyncUdpSocketSendFilterBlock)filterBlock withQueue:(nullable dispatch_queue_t)filterQueue; |
||
691 | |||
692 | /** |
||
693 | * The receive filter can be run via dispatch_async or dispatch_sync. |
||
694 | * Most typical situations call for asynchronous operation. |
||
695 | * |
||
696 | * However, there are a few situations in which synchronous operation is preferred. |
||
697 | * Such is the case when the filter is extremely minimal and fast. |
||
698 | * This is because dispatch_sync is faster than dispatch_async. |
||
699 | * |
||
700 | * If you choose synchronous operation, be aware of possible deadlock conditions. |
||
701 | * Since the socket queue is executing your block via dispatch_sync, |
||
702 | * then you cannot perform any tasks which may invoke dispatch_sync on the socket queue. |
||
703 | * For example, you can't query properties on the socket. |
||
704 | **/ |
||
705 | - (void)setSendFilter:(nullable GCDAsyncUdpSocketSendFilterBlock)filterBlock |
||
706 | withQueue:(nullable dispatch_queue_t)filterQueue |
||
707 | isAsynchronous:(BOOL)isAsynchronous; |
||
708 | |||
709 | #pragma mark Receiving |
||
710 | |||
711 | /** |
||
712 | * There are two modes of operation for receiving packets: one-at-a-time & continuous. |
||
713 | * |
||
714 | * In one-at-a-time mode, you call receiveOnce everytime your delegate is ready to process an incoming udp packet. |
||
715 | * Receiving packets one-at-a-time may be better suited for implementing certain state machine code, |
||
716 | * where your state machine may not always be ready to process incoming packets. |
||
717 | * |
||
718 | * In continuous mode, the delegate is invoked immediately everytime incoming udp packets are received. |
||
719 | * Receiving packets continuously is better suited to real-time streaming applications. |
||
720 | * |
||
721 | * You may switch back and forth between one-at-a-time mode and continuous mode. |
||
722 | * If the socket is currently in continuous mode, calling this method will switch it to one-at-a-time mode. |
||
723 | * |
||
724 | * When a packet is received (and not filtered by the optional receive filter), |
||
725 | * the delegate method (udpSocket:didReceiveData:fromAddress:withFilterContext:) is invoked. |
||
726 | * |
||
727 | * If the socket is able to begin receiving packets, this method returns YES. |
||
728 | * Otherwise it returns NO, and sets the errPtr with appropriate error information. |
||
729 | * |
||
730 | * An example error: |
||
731 | * You created a udp socket to act as a server, and immediately called receive. |
||
732 | * You forgot to first bind the socket to a port number, and received a error with a message like: |
||
733 | * "Must bind socket before you can receive data." |
||
734 | **/ |
||
735 | - (BOOL)receiveOnce:(NSError **)errPtr; |
||
736 | |||
737 | /** |
||
738 | * There are two modes of operation for receiving packets: one-at-a-time & continuous. |
||
739 | * |
||
740 | * In one-at-a-time mode, you call receiveOnce everytime your delegate is ready to process an incoming udp packet. |
||
741 | * Receiving packets one-at-a-time may be better suited for implementing certain state machine code, |
||
742 | * where your state machine may not always be ready to process incoming packets. |
||
743 | * |
||
744 | * In continuous mode, the delegate is invoked immediately everytime incoming udp packets are received. |
||
745 | * Receiving packets continuously is better suited to real-time streaming applications. |
||
746 | * |
||
747 | * You may switch back and forth between one-at-a-time mode and continuous mode. |
||
748 | * If the socket is currently in one-at-a-time mode, calling this method will switch it to continuous mode. |
||
749 | * |
||
750 | * For every received packet (not filtered by the optional receive filter), |
||
751 | * the delegate method (udpSocket:didReceiveData:fromAddress:withFilterContext:) is invoked. |
||
752 | * |
||
753 | * If the socket is able to begin receiving packets, this method returns YES. |
||
754 | * Otherwise it returns NO, and sets the errPtr with appropriate error information. |
||
755 | * |
||
756 | * An example error: |
||
757 | * You created a udp socket to act as a server, and immediately called receive. |
||
758 | * You forgot to first bind the socket to a port number, and received a error with a message like: |
||
759 | * "Must bind socket before you can receive data." |
||
760 | **/ |
||
761 | - (BOOL)beginReceiving:(NSError **)errPtr; |
||
762 | |||
763 | /** |
||
764 | * If the socket is currently receiving (beginReceiving has been called), this method pauses the receiving. |
||
765 | * That is, it won't read any more packets from the underlying OS socket until beginReceiving is called again. |
||
766 | * |
||
767 | * Important Note: |
||
768 | * GCDAsyncUdpSocket may be running in parallel with your code. |
||
769 | * That is, your delegate is likely running on a separate thread/dispatch_queue. |
||
770 | * When you invoke this method, GCDAsyncUdpSocket may have already dispatched delegate methods to be invoked. |
||
771 | * Thus, if those delegate methods have already been dispatch_async'd, |
||
772 | * your didReceive delegate method may still be invoked after this method has been called. |
||
773 | * You should be aware of this, and program defensively. |
||
774 | **/ |
||
775 | - (void)pauseReceiving; |
||
776 | |||
777 | /** |
||
778 | * You may optionally set a receive filter for the socket. |
||
779 | * This receive filter may be set to run in its own queue (independent of delegate queue). |
||
780 | * |
||
781 | * A filter can provide several useful features. |
||
782 | * |
||
783 | * 1. Many times udp packets need to be parsed. |
||
784 | * Since the filter can run in its own independent queue, you can parallelize this parsing quite easily. |
||
785 | * The end result is a parallel socket io, datagram parsing, and packet processing. |
||
786 | * |
||
787 | * 2. Many times udp packets are discarded because they are duplicate/unneeded/unsolicited. |
||
788 | * The filter can prevent such packets from arriving at the delegate. |
||
789 | * And because the filter can run in its own independent queue, this doesn't slow down the delegate. |
||
790 | * |
||
791 | * - Since the udp protocol does not guarantee delivery, udp packets may be lost. |
||
792 | * Many protocols built atop udp thus provide various resend/re-request algorithms. |
||
793 | * This sometimes results in duplicate packets arriving. |
||
794 | * A filter may allow you to architect the duplicate detection code to run in parallel to normal processing. |
||
795 | * |
||
796 | * - Since the udp socket may be connectionless, its possible for unsolicited packets to arrive. |
||
797 | * Such packets need to be ignored. |
||
798 | * |
||
799 | * 3. Sometimes traffic shapers are needed to simulate real world environments. |
||
800 | * A filter allows you to write custom code to simulate such environments. |
||
801 | * The ability to code this yourself is especially helpful when your simulated environment |
||
802 | * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), |
||
803 | * or the system tools to handle this aren't available (e.g. on a mobile device). |
||
804 | * |
||
805 | * Example: |
||
806 | * |
||
807 | * GCDAsyncUdpSocketReceiveFilterBlock filter = ^BOOL (NSData *data, NSData *address, id *context) { |
||
808 | * |
||
809 | * MyProtocolMessage *msg = [MyProtocol parseMessage:data]; |
||
810 | * |
||
811 | * *context = response; |
||
812 | * return (response != nil); |
||
813 | * }; |
||
814 | * [udpSocket setReceiveFilter:filter withQueue:myParsingQueue]; |
||
815 | * |
||
816 | * For more information about GCDAsyncUdpSocketReceiveFilterBlock, see the documentation for its typedef. |
||
817 | * To remove a previously set filter, invoke this method and pass a nil filterBlock and NULL filterQueue. |
||
818 | * |
||
819 | * Note: This method invokes setReceiveFilter:withQueue:isAsynchronous: (documented below), |
||
820 | * passing YES for the isAsynchronous parameter. |
||
821 | **/ |
||
822 | - (void)setReceiveFilter:(nullable GCDAsyncUdpSocketReceiveFilterBlock)filterBlock withQueue:(nullable dispatch_queue_t)filterQueue; |
||
823 | |||
824 | /** |
||
825 | * The receive filter can be run via dispatch_async or dispatch_sync. |
||
826 | * Most typical situations call for asynchronous operation. |
||
827 | * |
||
828 | * However, there are a few situations in which synchronous operation is preferred. |
||
829 | * Such is the case when the filter is extremely minimal and fast. |
||
830 | * This is because dispatch_sync is faster than dispatch_async. |
||
831 | * |
||
832 | * If you choose synchronous operation, be aware of possible deadlock conditions. |
||
833 | * Since the socket queue is executing your block via dispatch_sync, |
||
834 | * then you cannot perform any tasks which may invoke dispatch_sync on the socket queue. |
||
835 | * For example, you can't query properties on the socket. |
||
836 | **/ |
||
837 | - (void)setReceiveFilter:(nullable GCDAsyncUdpSocketReceiveFilterBlock)filterBlock |
||
838 | withQueue:(nullable dispatch_queue_t)filterQueue |
||
839 | isAsynchronous:(BOOL)isAsynchronous; |
||
840 | |||
841 | #pragma mark Closing |
||
842 | |||
843 | /** |
||
844 | * Immediately closes the underlying socket. |
||
845 | * Any pending send operations are discarded. |
||
846 | * |
||
847 | * The GCDAsyncUdpSocket instance may optionally be used again. |
||
848 | * (it will setup/configure/use another unnderlying BSD socket). |
||
849 | **/ |
||
850 | - (void)close; |
||
851 | |||
852 | /** |
||
853 | * Closes the underlying socket after all pending send operations have been sent. |
||
854 | * |
||
855 | * The GCDAsyncUdpSocket instance may optionally be used again. |
||
856 | * (it will setup/configure/use another unnderlying BSD socket). |
||
857 | **/ |
||
858 | - (void)closeAfterSending; |
||
859 | |||
860 | #pragma mark Advanced |
||
861 | /** |
||
862 | * GCDAsyncSocket maintains thread safety by using an internal serial dispatch_queue. |
||
863 | * In most cases, the instance creates this queue itself. |
||
864 | * However, to allow for maximum flexibility, the internal queue may be passed in the init method. |
||
865 | * This allows for some advanced options such as controlling socket priority via target queues. |
||
866 | * However, when one begins to use target queues like this, they open the door to some specific deadlock issues. |
||
867 | * |
||
868 | * For example, imagine there are 2 queues: |
||
869 | * dispatch_queue_t socketQueue; |
||
870 | * dispatch_queue_t socketTargetQueue; |
||
871 | * |
||
872 | * If you do this (pseudo-code): |
||
873 | * socketQueue.targetQueue = socketTargetQueue; |
||
874 | * |
||
875 | * Then all socketQueue operations will actually get run on the given socketTargetQueue. |
||
876 | * This is fine and works great in most situations. |
||
877 | * But if you run code directly from within the socketTargetQueue that accesses the socket, |
||
878 | * you could potentially get deadlock. Imagine the following code: |
||
879 | * |
||
880 | * - (BOOL)socketHasSomething |
||
881 | * { |
||
882 | * __block BOOL result = NO; |
||
883 | * dispatch_block_t block = ^{ |
||
884 | * result = [self someInternalMethodToBeRunOnlyOnSocketQueue]; |
||
885 | * } |
||
886 | * if (is_executing_on_queue(socketQueue)) |
||
887 | * block(); |
||
888 | * else |
||
889 | * dispatch_sync(socketQueue, block); |
||
890 | * |
||
891 | * return result; |
||
892 | * } |
||
893 | * |
||
894 | * What happens if you call this method from the socketTargetQueue? The result is deadlock. |
||
895 | * This is because the GCD API offers no mechanism to discover a queue's targetQueue. |
||
896 | * Thus we have no idea if our socketQueue is configured with a targetQueue. |
||
897 | * If we had this information, we could easily avoid deadlock. |
||
898 | * But, since these API's are missing or unfeasible, you'll have to explicitly set it. |
||
899 | * |
||
900 | * IF you pass a socketQueue via the init method, |
||
901 | * AND you've configured the passed socketQueue with a targetQueue, |
||
902 | * THEN you should pass the end queue in the target hierarchy. |
||
903 | * |
||
904 | * For example, consider the following queue hierarchy: |
||
905 | * socketQueue -> ipQueue -> moduleQueue |
||
906 | * |
||
907 | * This example demonstrates priority shaping within some server. |
||
908 | * All incoming client connections from the same IP address are executed on the same target queue. |
||
909 | * And all connections for a particular module are executed on the same target queue. |
||
910 | * Thus, the priority of all networking for the entire module can be changed on the fly. |
||
911 | * Additionally, networking traffic from a single IP cannot monopolize the module. |
||
912 | * |
||
913 | * Here's how you would accomplish something like that: |
||
914 | * - (dispatch_queue_t)newSocketQueueForConnectionFromAddress:(NSData *)address onSocket:(GCDAsyncSocket *)sock |
||
915 | * { |
||
916 | * dispatch_queue_t socketQueue = dispatch_queue_create("", NULL); |
||
917 | * dispatch_queue_t ipQueue = [self ipQueueForAddress:address]; |
||
918 | * |
||
919 | * dispatch_set_target_queue(socketQueue, ipQueue); |
||
920 | * dispatch_set_target_queue(iqQueue, moduleQueue); |
||
921 | * |
||
922 | * return socketQueue; |
||
923 | * } |
||
924 | * - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket |
||
925 | * { |
||
926 | * [clientConnections addObject:newSocket]; |
||
927 | * [newSocket markSocketQueueTargetQueue:moduleQueue]; |
||
928 | * } |
||
929 | * |
||
930 | * Note: This workaround is ONLY needed if you intend to execute code directly on the ipQueue or moduleQueue. |
||
931 | * This is often NOT the case, as such queues are used solely for execution shaping. |
||
932 | **/ |
||
933 | - (void)markSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreConfiguredTargetQueue; |
||
934 | - (void)unmarkSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreviouslyConfiguredTargetQueue; |
||
935 | |||
936 | /** |
||
937 | * It's not thread-safe to access certain variables from outside the socket's internal queue. |
||
938 | * |
||
939 | * For example, the socket file descriptor. |
||
940 | * File descriptors are simply integers which reference an index in the per-process file table. |
||
941 | * However, when one requests a new file descriptor (by opening a file or socket), |
||
942 | * the file descriptor returned is guaranteed to be the lowest numbered unused descriptor. |
||
943 | * So if we're not careful, the following could be possible: |
||
944 | * |
||
945 | * - Thread A invokes a method which returns the socket's file descriptor. |
||
946 | * - The socket is closed via the socket's internal queue on thread B. |
||
947 | * - Thread C opens a file, and subsequently receives the file descriptor that was previously the socket's FD. |
||
948 | * - Thread A is now accessing/altering the file instead of the socket. |
||
949 | * |
||
950 | * In addition to this, other variables are not actually objects, |
||
951 | * and thus cannot be retained/released or even autoreleased. |
||
952 | * An example is the sslContext, of type SSLContextRef, which is actually a malloc'd struct. |
||
953 | * |
||
954 | * Although there are internal variables that make it difficult to maintain thread-safety, |
||
955 | * it is important to provide access to these variables |
||
956 | * to ensure this class can be used in a wide array of environments. |
||
957 | * This method helps to accomplish this by invoking the current block on the socket's internal queue. |
||
958 | * The methods below can be invoked from within the block to access |
||
959 | * those generally thread-unsafe internal variables in a thread-safe manner. |
||
960 | * The given block will be invoked synchronously on the socket's internal queue. |
||
961 | * |
||
962 | * If you save references to any protected variables and use them outside the block, you do so at your own peril. |
||
963 | **/ |
||
964 | - (void)performBlock:(dispatch_block_t)block; |
||
965 | |||
966 | /** |
||
967 | * These methods are only available from within the context of a performBlock: invocation. |
||
968 | * See the documentation for the performBlock: method above. |
||
969 | * |
||
970 | * Provides access to the socket's file descriptor(s). |
||
971 | * If the socket isn't connected, or explicity bound to a particular interface, |
||
972 | * it might actually have multiple internal socket file descriptors - one for IPv4 and one for IPv6. |
||
973 | **/ |
||
974 | - (int)socketFD; |
||
975 | - (int)socket4FD; |
||
976 | - (int)socket6FD; |
||
977 | |||
978 | #if TARGET_OS_IPHONE |
||
979 | |||
980 | /** |
||
981 | * These methods are only available from within the context of a performBlock: invocation. |
||
982 | * See the documentation for the performBlock: method above. |
||
983 | * |
||
984 | * Returns (creating if necessary) a CFReadStream/CFWriteStream for the internal socket. |
||
985 | * |
||
986 | * Generally GCDAsyncUdpSocket doesn't use CFStream. (It uses the faster GCD API's.) |
||
987 | * However, if you need one for any reason, |
||
988 | * these methods are a convenient way to get access to a safe instance of one. |
||
989 | **/ |
||
990 | - (nullable CFReadStreamRef)readStream; |
||
991 | - (nullable CFWriteStreamRef)writeStream; |
||
992 | |||
993 | /** |
||
994 | * This method is only available from within the context of a performBlock: invocation. |
||
995 | * See the documentation for the performBlock: method above. |
||
996 | * |
||
997 | * Configures the socket to allow it to operate when the iOS application has been backgrounded. |
||
998 | * In other words, this method creates a read & write stream, and invokes: |
||
999 | * |
||
1000 | * CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); |
||
1001 | * CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); |
||
1002 | * |
||
1003 | * Returns YES if successful, NO otherwise. |
||
1004 | * |
||
1005 | * Example usage: |
||
1006 | * |
||
1007 | * [asyncUdpSocket performBlock:^{ |
||
1008 | * [asyncUdpSocket enableBackgroundingOnSocket]; |
||
1009 | * }]; |
||
1010 | * |
||
1011 | * |
||
1012 | * NOTE : Apple doesn't currently support backgrounding UDP sockets. (Only TCP for now). |
||
1013 | **/ |
||
1014 | //- (BOOL)enableBackgroundingOnSockets; |
||
1015 | |||
1016 | #endif |
||
1017 | |||
1018 | #pragma mark Utilities |
||
1019 | |||
1020 | /** |
||
1021 | * Extracting host/port/family information from raw address data. |
||
1022 | **/ |
||
1023 | |||
1024 | + (nullable NSString *)hostFromAddress:(NSData *)address; |
||
1025 | + (uint16_t)portFromAddress:(NSData *)address; |
||
1026 | + (int)familyFromAddress:(NSData *)address; |
||
1027 | |||
1028 | + (BOOL)isIPv4Address:(NSData *)address; |
||
1029 | + (BOOL)isIPv6Address:(NSData *)address; |
||
1030 | |||
1031 | + (BOOL)getHost:(NSString * __nullable * __nullable)hostPtr port:(uint16_t * __nullable)portPtr fromAddress:(NSData *)address; |
||
1032 | + (BOOL)getHost:(NSString * __nullable * __nullable)hostPtr port:(uint16_t * __nullable)portPtr family:(int * __nullable)afPtr fromAddress:(NSData *)address; |
||
1033 | |||
1034 | @end |
||
1035 | |||
1036 | NS_ASSUME_NONNULL_END |