*)options
87 | {
88 | NSString *urlStr = [url absoluteString];
89 | urlStr = [urlStr stringByRemovingPercentEncoding];
90 | if ([urlStr containsString:@"info="]) {
91 | NSString *plistString = [[urlStr componentsSeparatedByString:@"info="] lastObject];
92 |
93 | UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"获取设备信息成功" message:plistString delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
94 | [alert show];
95 |
96 | }
97 | return YES;
98 | }
99 | @end
100 |
--------------------------------------------------------------------------------
/iOS_GetUDID_Demo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/iOS_GetUDID_Demo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/iOS_GetUDID_Demo/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/iOS_GetUDID_Demo/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/iOS_GetUDID_Demo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleURLTypes
20 |
21 |
22 | CFBundleTypeRole
23 | Editor
24 | CFBundleURLName
25 | get_udid_demo
26 | CFBundleURLSchemes
27 |
28 | iOS-UDID-Demo
29 |
30 |
31 |
32 | CFBundleVersion
33 | 1
34 | LSRequiresIPhoneOS
35 |
36 | NSAppTransportSecurity
37 |
38 | NSAllowsArbitraryLoads
39 |
40 |
41 | UILaunchStoryboardName
42 | LaunchScreen
43 | UIMainStoryboardFile
44 | Main
45 | UIRequiredDeviceCapabilities
46 |
47 | armv7
48 |
49 | UISupportedInterfaceOrientations
50 |
51 | UIInterfaceOrientationPortrait
52 | UIInterfaceOrientationLandscapeLeft
53 | UIInterfaceOrientationLandscapeRight
54 |
55 | UISupportedInterfaceOrientations~ipad
56 |
57 | UIInterfaceOrientationPortrait
58 | UIInterfaceOrientationPortraitUpsideDown
59 | UIInterfaceOrientationLandscapeLeft
60 | UIInterfaceOrientationLandscapeRight
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/iOS_GetUDID_Demo/Server/CocoaAsyncSocket/Documentation.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Welcome to the CocoaAsyncSocket project!
5 |
6 |
7 | A wealth of documentation can be found on the Google Code homepage:
8 | https://github.com/robbiehanson/CocoaAsyncSocket
9 |
10 |
11 |
12 | If you are new to networking, it is recommended you start by reading the Intro page:
13 | https://github.com/robbiehanson/CocoaAsyncSocket/wiki/Intro
14 |
15 |
16 |
17 | If you are a seasoned networking professional, with 10+ years of experience writing low-level socket code,
18 | and detailed knowledge of the underlying BSD networking stack, then you can skip the CommonPitfalls page.
19 | Otherwise, it should be considered mandatory reading:
20 | https://github.com/robbiehanson/CocoaAsyncSocket/wiki/CommonPitfalls
21 |
22 |
23 |
24 | A little bit of investment in your knowledge and understanding of networking fundamentals can go a long way.
25 | And it can save you a LOT of time and frustration in the long run.
26 |
27 |
28 |
29 | Your first goto for reference should ALWAYS be the header files. They are extremely well documented. Please read them.
30 |
31 |
32 |
33 | Did I mention you should read the headers? They're docemented very nicely, in plain english.
34 |
35 |
36 |
37 | If you have any questions you are welcome to post to the CocoaAsyncSocket mailing list:
38 | http://groups.google.com/group/cocoaasyncsocket
39 |
40 | The list is archived, and available for browsing online.
41 | You may be able to instantly find the answer you're looking for with a quick search.
42 |
43 |
44 | We hope the CocoaAsyncSocket project can provide you with powerful and easy to use networking libraries.
45 |
46 |
47 |
--------------------------------------------------------------------------------
/iOS_GetUDID_Demo/Server/CocoaAsyncSocket/GCDAsyncSocket.h:
--------------------------------------------------------------------------------
1 | //
2 | // GCDAsyncSocket.h
3 | //
4 | // This class is in the public domain.
5 | // Originally created by Robbie Hanson in Q3 2010.
6 | // Updated and maintained by Deusty LLC and the Apple development community.
7 | //
8 | // https://github.com/robbiehanson/CocoaAsyncSocket
9 | //
10 |
11 | #import
12 | #import
13 | #import
14 | #import
15 | #import
16 |
17 | #include // AF_INET, AF_INET6
18 |
19 | @class GCDAsyncReadPacket;
20 | @class GCDAsyncWritePacket;
21 | @class GCDAsyncSocketPreBuffer;
22 | @protocol GCDAsyncSocketDelegate;
23 |
24 | NS_ASSUME_NONNULL_BEGIN
25 |
26 | extern NSString *const GCDAsyncSocketException;
27 | extern NSString *const GCDAsyncSocketErrorDomain;
28 |
29 | extern NSString *const GCDAsyncSocketQueueName;
30 | extern NSString *const GCDAsyncSocketThreadName;
31 |
32 | extern NSString *const GCDAsyncSocketManuallyEvaluateTrust;
33 | #if TARGET_OS_IPHONE
34 | extern NSString *const GCDAsyncSocketUseCFStreamForTLS;
35 | #endif
36 | #define GCDAsyncSocketSSLPeerName (NSString *)kCFStreamSSLPeerName
37 | #define GCDAsyncSocketSSLCertificates (NSString *)kCFStreamSSLCertificates
38 | #define GCDAsyncSocketSSLIsServer (NSString *)kCFStreamSSLIsServer
39 | extern NSString *const GCDAsyncSocketSSLPeerID;
40 | extern NSString *const GCDAsyncSocketSSLProtocolVersionMin;
41 | extern NSString *const GCDAsyncSocketSSLProtocolVersionMax;
42 | extern NSString *const GCDAsyncSocketSSLSessionOptionFalseStart;
43 | extern NSString *const GCDAsyncSocketSSLSessionOptionSendOneByteRecord;
44 | extern NSString *const GCDAsyncSocketSSLCipherSuites;
45 | #if !TARGET_OS_IPHONE
46 | extern NSString *const GCDAsyncSocketSSLDiffieHellmanParameters;
47 | #endif
48 |
49 | #define GCDAsyncSocketLoggingContext 65535
50 |
51 |
52 | typedef NS_ENUM(NSInteger, GCDAsyncSocketError) {
53 | GCDAsyncSocketNoError = 0, // Never used
54 | GCDAsyncSocketBadConfigError, // Invalid configuration
55 | GCDAsyncSocketBadParamError, // Invalid parameter was passed
56 | GCDAsyncSocketConnectTimeoutError, // A connect operation timed out
57 | GCDAsyncSocketReadTimeoutError, // A read operation timed out
58 | GCDAsyncSocketWriteTimeoutError, // A write operation timed out
59 | GCDAsyncSocketReadMaxedOutError, // Reached set maxLength without completing
60 | GCDAsyncSocketClosedError, // The remote peer closed the connection
61 | GCDAsyncSocketOtherError, // Description provided in userInfo
62 | };
63 |
64 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
65 | #pragma mark -
66 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
67 |
68 |
69 | @interface GCDAsyncSocket : NSObject
70 |
71 | /**
72 | * GCDAsyncSocket uses the standard delegate paradigm,
73 | * but executes all delegate callbacks on a given delegate dispatch queue.
74 | * This allows for maximum concurrency, while at the same time providing easy thread safety.
75 | *
76 | * You MUST set a delegate AND delegate dispatch queue before attempting to
77 | * use the socket, or you will get an error.
78 | *
79 | * The socket queue is optional.
80 | * If you pass NULL, GCDAsyncSocket will automatically create it's own socket queue.
81 | * If you choose to provide a socket queue, the socket queue must not be a concurrent queue.
82 | * If you choose to provide a socket queue, and the socket queue has a configured target queue,
83 | * then please see the discussion for the method markSocketQueueTargetQueue.
84 | *
85 | * The delegate queue and socket queue can optionally be the same.
86 | **/
87 | - (instancetype)init;
88 | - (instancetype)initWithSocketQueue:(nullable dispatch_queue_t)sq;
89 | - (instancetype)initWithDelegate:(nullable id)aDelegate delegateQueue:(nullable dispatch_queue_t)dq;
90 | - (instancetype)initWithDelegate:(nullable id)aDelegate delegateQueue:(nullable dispatch_queue_t)dq socketQueue:(nullable dispatch_queue_t)sq;
91 |
92 | /**
93 | * Create GCDAsyncSocket from already connect BSD socket file descriptor
94 | **/
95 | + (nullable instancetype)socketFromConnectedSocketFD:(int)socketFD socketQueue:(nullable dispatch_queue_t)sq error:(NSError**)error;
96 |
97 | + (nullable instancetype)socketFromConnectedSocketFD:(int)socketFD delegate:(nullable id)aDelegate delegateQueue:(nullable dispatch_queue_t)dq error:(NSError**)error;
98 |
99 | + (nullable instancetype)socketFromConnectedSocketFD:(int)socketFD delegate:(nullable id)aDelegate delegateQueue:(nullable dispatch_queue_t)dq socketQueue:(nullable dispatch_queue_t)sq error:(NSError **)error;
100 |
101 | #pragma mark Configuration
102 |
103 | @property (atomic, weak, readwrite, nullable) id delegate;
104 | #if OS_OBJECT_USE_OBJC
105 | @property (atomic, strong, readwrite, nullable) dispatch_queue_t delegateQueue;
106 | #else
107 | @property (atomic, assign, readwrite, nullable) dispatch_queue_t delegateQueue;
108 | #endif
109 |
110 | - (void)getDelegate:(id __nullable * __nullable)delegatePtr delegateQueue:(dispatch_queue_t __nullable * __nullable)delegateQueuePtr;
111 | - (void)setDelegate:(nullable id)delegate delegateQueue:(nullable dispatch_queue_t)delegateQueue;
112 |
113 | /**
114 | * If you are setting the delegate to nil within the delegate's dealloc method,
115 | * you may need to use the synchronous versions below.
116 | **/
117 | - (void)synchronouslySetDelegate:(nullable id)delegate;
118 | - (void)synchronouslySetDelegateQueue:(nullable dispatch_queue_t)delegateQueue;
119 | - (void)synchronouslySetDelegate:(nullable id)delegate delegateQueue:(nullable dispatch_queue_t)delegateQueue;
120 |
121 | /**
122 | * By default, both IPv4 and IPv6 are enabled.
123 | *
124 | * For accepting incoming connections, this means GCDAsyncSocket automatically supports both protocols,
125 | * and can simulataneously accept incoming connections on either protocol.
126 | *
127 | * For outgoing connections, this means GCDAsyncSocket can connect to remote hosts running either protocol.
128 | * If a DNS lookup returns only IPv4 results, GCDAsyncSocket will automatically use IPv4.
129 | * If a DNS lookup returns only IPv6 results, GCDAsyncSocket will automatically use IPv6.
130 | * If a DNS lookup returns both IPv4 and IPv6 results, the preferred protocol will be chosen.
131 | * By default, the preferred protocol is IPv4, but may be configured as desired.
132 | **/
133 |
134 | @property (atomic, assign, readwrite, getter=isIPv4Enabled) BOOL IPv4Enabled;
135 | @property (atomic, assign, readwrite, getter=isIPv6Enabled) BOOL IPv6Enabled;
136 |
137 | @property (atomic, assign, readwrite, getter=isIPv4PreferredOverIPv6) BOOL IPv4PreferredOverIPv6;
138 |
139 | /**
140 | * When connecting to both IPv4 and IPv6 using Happy Eyeballs (RFC 6555) https://tools.ietf.org/html/rfc6555
141 | * this is the delay between connecting to the preferred protocol and the fallback protocol.
142 | *
143 | * Defaults to 300ms.
144 | **/
145 | @property (atomic, assign, readwrite) NSTimeInterval alternateAddressDelay;
146 |
147 | /**
148 | * User data allows you to associate arbitrary information with the socket.
149 | * This data is not used internally by socket in any way.
150 | **/
151 | @property (atomic, strong, readwrite, nullable) id userData;
152 |
153 | #pragma mark Accepting
154 |
155 | /**
156 | * Tells the socket to begin listening and accepting connections on the given port.
157 | * When a connection is accepted, a new instance of GCDAsyncSocket will be spawned to handle it,
158 | * and the socket:didAcceptNewSocket: delegate method will be invoked.
159 | *
160 | * The socket will listen on all available interfaces (e.g. wifi, ethernet, etc)
161 | **/
162 | - (BOOL)acceptOnPort:(uint16_t)port error:(NSError **)errPtr;
163 |
164 | /**
165 | * This method is the same as acceptOnPort:error: with the
166 | * additional option of specifying which interface to listen on.
167 | *
168 | * For example, you could specify that the socket should only accept connections over ethernet,
169 | * and not other interfaces such as wifi.
170 | *
171 | * The interface may be specified by name (e.g. "en1" or "lo0") or by IP address (e.g. "192.168.4.34").
172 | * You may also use the special strings "localhost" or "loopback" to specify that
173 | * the socket only accept connections from the local machine.
174 | *
175 | * You can see the list of interfaces via the command line utility "ifconfig",
176 | * or programmatically via the getifaddrs() function.
177 | *
178 | * To accept connections on any interface pass nil, or simply use the acceptOnPort:error: method.
179 | **/
180 | - (BOOL)acceptOnInterface:(nullable NSString *)interface port:(uint16_t)port error:(NSError **)errPtr;
181 |
182 | /**
183 | * Tells the socket to begin listening and accepting connections on the unix domain at the given url.
184 | * When a connection is accepted, a new instance of GCDAsyncSocket will be spawned to handle it,
185 | * and the socket:didAcceptNewSocket: delegate method will be invoked.
186 | *
187 | * The socket will listen on all available interfaces (e.g. wifi, ethernet, etc)
188 | **/
189 | - (BOOL)acceptOnUrl:(NSURL *)url error:(NSError **)errPtr;
190 |
191 | #pragma mark Connecting
192 |
193 | /**
194 | * Connects to the given host and port.
195 | *
196 | * This method invokes connectToHost:onPort:viaInterface:withTimeout:error:
197 | * and uses the default interface, and no timeout.
198 | **/
199 | - (BOOL)connectToHost:(NSString *)host onPort:(uint16_t)port error:(NSError **)errPtr;
200 |
201 | /**
202 | * Connects to the given host and port with an optional timeout.
203 | *
204 | * This method invokes connectToHost:onPort:viaInterface:withTimeout:error: and uses the default interface.
205 | **/
206 | - (BOOL)connectToHost:(NSString *)host
207 | onPort:(uint16_t)port
208 | withTimeout:(NSTimeInterval)timeout
209 | error:(NSError **)errPtr;
210 |
211 | /**
212 | * Connects to the given host & port, via the optional interface, with an optional timeout.
213 | *
214 | * The host may be a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2").
215 | * The host may also be the special strings "localhost" or "loopback" to specify connecting
216 | * to a service on the local machine.
217 | *
218 | * The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35").
219 | * The interface may also be used to specify the local port (see below).
220 | *
221 | * To not time out use a negative time interval.
222 | *
223 | * This method will return NO if an error is detected, and set the error pointer (if one was given).
224 | * Possible errors would be a nil host, invalid interface, or socket is already connected.
225 | *
226 | * If no errors are detected, this method will start a background connect operation and immediately return YES.
227 | * The delegate callbacks are used to notify you when the socket connects, or if the host was unreachable.
228 | *
229 | * Since this class supports queued reads and writes, you can immediately start reading and/or writing.
230 | * All read/write operations will be queued, and upon socket connection,
231 | * the operations will be dequeued and processed in order.
232 | *
233 | * The interface may optionally contain a port number at the end of the string, separated by a colon.
234 | * This allows you to specify the local port that should be used for the outgoing connection. (read paragraph to end)
235 | * To specify both interface and local port: "en1:8082" or "192.168.4.35:2424".
236 | * To specify only local port: ":8082".
237 | * Please note this is an advanced feature, and is somewhat hidden on purpose.
238 | * You should understand that 99.999% of the time you should NOT specify the local port for an outgoing connection.
239 | * If you think you need to, there is a very good chance you have a fundamental misunderstanding somewhere.
240 | * Local ports do NOT need to match remote ports. In fact, they almost never do.
241 | * This feature is here for networking professionals using very advanced techniques.
242 | **/
243 | - (BOOL)connectToHost:(NSString *)host
244 | onPort:(uint16_t)port
245 | viaInterface:(nullable NSString *)interface
246 | withTimeout:(NSTimeInterval)timeout
247 | error:(NSError **)errPtr;
248 |
249 | /**
250 | * Connects to the given address, specified as a sockaddr structure wrapped in a NSData object.
251 | * For example, a NSData object returned from NSNetService's addresses method.
252 | *
253 | * If you have an existing struct sockaddr you can convert it to a NSData object like so:
254 | * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len];
255 | * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len];
256 | *
257 | * This method invokes connectToAdd
258 | **/
259 | - (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr;
260 |
261 | /**
262 | * This method is the same as connectToAddress:error: with an additional timeout option.
263 | * To not time out use a negative time interval, or simply use the connectToAddress:error: method.
264 | **/
265 | - (BOOL)connectToAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr;
266 |
267 | /**
268 | * Connects to the given address, using the specified interface and timeout.
269 | *
270 | * The address is specified as a sockaddr structure wrapped in a NSData object.
271 | * For example, a NSData object returned from NSNetService's addresses method.
272 | *
273 | * If you have an existing struct sockaddr you can convert it to a NSData object like so:
274 | * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len];
275 | * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len];
276 | *
277 | * The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35").
278 | * The interface may also be used to specify the local port (see below).
279 | *
280 | * The timeout is optional. To not time out use a negative time interval.
281 | *
282 | * This method will return NO if an error is detected, and set the error pointer (if one was given).
283 | * Possible errors would be a nil host, invalid interface, or socket is already connected.
284 | *
285 | * If no errors are detected, this method will start a background connect operation and immediately return YES.
286 | * The delegate callbacks are used to notify you when the socket connects, or if the host was unreachable.
287 | *
288 | * Since this class supports queued reads and writes, you can immediately start reading and/or writing.
289 | * All read/write operations will be queued, and upon socket connection,
290 | * the operations will be dequeued and processed in order.
291 | *
292 | * The interface may optionally contain a port number at the end of the string, separated by a colon.
293 | * This allows you to specify the local port that should be used for the outgoing connection. (read paragraph to end)
294 | * To specify both interface and local port: "en1:8082" or "192.168.4.35:2424".
295 | * To specify only local port: ":8082".
296 | * Please note this is an advanced feature, and is somewhat hidden on purpose.
297 | * You should understand that 99.999% of the time you should NOT specify the local port for an outgoing connection.
298 | * If you think you need to, there is a very good chance you have a fundamental misunderstanding somewhere.
299 | * Local ports do NOT need to match remote ports. In fact, they almost never do.
300 | * This feature is here for networking professionals using very advanced techniques.
301 | **/
302 | - (BOOL)connectToAddress:(NSData *)remoteAddr
303 | viaInterface:(nullable NSString *)interface
304 | withTimeout:(NSTimeInterval)timeout
305 | error:(NSError **)errPtr;
306 | /**
307 | * Connects to the unix domain socket at the given url, using the specified timeout.
308 | */
309 | - (BOOL)connectToUrl:(NSURL *)url withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr;
310 |
311 | #pragma mark Disconnecting
312 |
313 | /**
314 | * Disconnects immediately (synchronously). Any pending reads or writes are dropped.
315 | *
316 | * If the socket is not already disconnected, an invocation to the socketDidDisconnect:withError: delegate method
317 | * will be queued onto the delegateQueue asynchronously (behind any previously queued delegate methods).
318 | * In other words, the disconnected delegate method will be invoked sometime shortly after this method returns.
319 | *
320 | * Please note the recommended way of releasing a GCDAsyncSocket instance (e.g. in a dealloc method)
321 | * [asyncSocket setDelegate:nil];
322 | * [asyncSocket disconnect];
323 | * [asyncSocket release];
324 | *
325 | * If you plan on disconnecting the socket, and then immediately asking it to connect again,
326 | * you'll likely want to do so like this:
327 | * [asyncSocket setDelegate:nil];
328 | * [asyncSocket disconnect];
329 | * [asyncSocket setDelegate:self];
330 | * [asyncSocket connect...];
331 | **/
332 | - (void)disconnect;
333 |
334 | /**
335 | * Disconnects after all pending reads have completed.
336 | * After calling this, the read and write methods will do nothing.
337 | * The socket will disconnect even if there are still pending writes.
338 | **/
339 | - (void)disconnectAfterReading;
340 |
341 | /**
342 | * Disconnects after all pending writes have completed.
343 | * After calling this, the read and write methods will do nothing.
344 | * The socket will disconnect even if there are still pending reads.
345 | **/
346 | - (void)disconnectAfterWriting;
347 |
348 | /**
349 | * Disconnects after all pending reads and writes have completed.
350 | * After calling this, the read and write methods will do nothing.
351 | **/
352 | - (void)disconnectAfterReadingAndWriting;
353 |
354 | #pragma mark Diagnostics
355 |
356 | /**
357 | * Returns whether the socket is disconnected or connected.
358 | *
359 | * A disconnected socket may be recycled.
360 | * That is, it can be used again for connecting or listening.
361 | *
362 | * If a socket is in the process of connecting, it may be neither disconnected nor connected.
363 | **/
364 | @property (atomic, readonly) BOOL isDisconnected;
365 | @property (atomic, readonly) BOOL isConnected;
366 |
367 | /**
368 | * Returns the local or remote host and port to which this socket is connected, or nil and 0 if not connected.
369 | * The host will be an IP address.
370 | **/
371 | @property (atomic, readonly, nullable) NSString *connectedHost;
372 | @property (atomic, readonly) uint16_t connectedPort;
373 | @property (atomic, readonly, nullable) NSURL *connectedUrl;
374 |
375 | @property (atomic, readonly, nullable) NSString *localHost;
376 | @property (atomic, readonly) uint16_t localPort;
377 |
378 | /**
379 | * Returns the local or remote address to which this socket is connected,
380 | * specified as a sockaddr structure wrapped in a NSData object.
381 | *
382 | * @seealso connectedHost
383 | * @seealso connectedPort
384 | * @seealso localHost
385 | * @seealso localPort
386 | **/
387 | @property (atomic, readonly, nullable) NSData *connectedAddress;
388 | @property (atomic, readonly, nullable) NSData *localAddress;
389 |
390 | /**
391 | * Returns whether the socket is IPv4 or IPv6.
392 | * An accepting socket may be both.
393 | **/
394 | @property (atomic, readonly) BOOL isIPv4;
395 | @property (atomic, readonly) BOOL isIPv6;
396 |
397 | /**
398 | * Returns whether or not the socket has been secured via SSL/TLS.
399 | *
400 | * See also the startTLS method.
401 | **/
402 | @property (atomic, readonly) BOOL isSecure;
403 |
404 | #pragma mark Reading
405 |
406 | // The readData and writeData methods won't block (they are asynchronous).
407 | //
408 | // When a read is complete the socket:didReadData:withTag: delegate method is dispatched on the delegateQueue.
409 | // When a write is complete the socket:didWriteDataWithTag: delegate method is dispatched on the delegateQueue.
410 | //
411 | // You may optionally set a timeout for any read/write operation. (To not timeout, use a negative time interval.)
412 | // If a read/write opertion times out, the corresponding "socket:shouldTimeout..." delegate method
413 | // is called to optionally allow you to extend the timeout.
414 | // Upon a timeout, the "socket:didDisconnectWithError:" method is called
415 | //
416 | // The tag is for your convenience.
417 | // You can use it as an array index, step number, state id, pointer, etc.
418 |
419 | /**
420 | * Reads the first available bytes that become available on the socket.
421 | *
422 | * If the timeout value is negative, the read operation will not use a timeout.
423 | **/
424 | - (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag;
425 |
426 | /**
427 | * Reads the first available bytes that become available on the socket.
428 | * The bytes will be appended to the given byte buffer starting at the given offset.
429 | * The given buffer will automatically be increased in size if needed.
430 | *
431 | * If the timeout value is negative, the read operation will not use a timeout.
432 | * If the buffer if nil, the socket will create a buffer for you.
433 | *
434 | * If the bufferOffset is greater than the length of the given buffer,
435 | * the method will do nothing, and the delegate will not be called.
436 | *
437 | * If you pass a buffer, you must not alter it in any way while the socket is using it.
438 | * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer.
439 | * That is, it will reference the bytes that were appended to the given buffer via
440 | * the method [NSData dataWithBytesNoCopy:length:freeWhenDone:NO].
441 | **/
442 | - (void)readDataWithTimeout:(NSTimeInterval)timeout
443 | buffer:(nullable NSMutableData *)buffer
444 | bufferOffset:(NSUInteger)offset
445 | tag:(long)tag;
446 |
447 | /**
448 | * Reads the first available bytes that become available on the socket.
449 | * The bytes will be appended to the given byte buffer starting at the given offset.
450 | * The given buffer will automatically be increased in size if needed.
451 | * A maximum of length bytes will be read.
452 | *
453 | * If the timeout value is negative, the read operation will not use a timeout.
454 | * If the buffer if nil, a buffer will automatically be created for you.
455 | * If maxLength is zero, no length restriction is enforced.
456 | *
457 | * If the bufferOffset is greater than the length of the given buffer,
458 | * the method will do nothing, and the delegate will not be called.
459 | *
460 | * If you pass a buffer, you must not alter it in any way while the socket is using it.
461 | * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer.
462 | * That is, it will reference the bytes that were appended to the given buffer via
463 | * the method [NSData dataWithBytesNoCopy:length:freeWhenDone:NO].
464 | **/
465 | - (void)readDataWithTimeout:(NSTimeInterval)timeout
466 | buffer:(nullable NSMutableData *)buffer
467 | bufferOffset:(NSUInteger)offset
468 | maxLength:(NSUInteger)length
469 | tag:(long)tag;
470 |
471 | /**
472 | * Reads the given number of bytes.
473 | *
474 | * If the timeout value is negative, the read operation will not use a timeout.
475 | *
476 | * If the length is 0, this method does nothing and the delegate is not called.
477 | **/
478 | - (void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;
479 |
480 | /**
481 | * Reads the given number of bytes.
482 | * The bytes will be appended to the given byte buffer starting at the given offset.
483 | * The given buffer will automatically be increased in size if needed.
484 | *
485 | * If the timeout value is negative, the read operation will not use a timeout.
486 | * If the buffer if nil, a buffer will automatically be created for you.
487 | *
488 | * If the length is 0, this method does nothing and the delegate is not called.
489 | * If the bufferOffset is greater than the length of the given buffer,
490 | * the method will do nothing, and the delegate will not be called.
491 | *
492 | * If you pass a buffer, you must not alter it in any way while AsyncSocket is using it.
493 | * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer.
494 | * That is, it will reference the bytes that were appended to the given buffer via
495 | * the method [NSData dataWithBytesNoCopy:length:freeWhenDone:NO].
496 | **/
497 | - (void)readDataToLength:(NSUInteger)length
498 | withTimeout:(NSTimeInterval)timeout
499 | buffer:(nullable NSMutableData *)buffer
500 | bufferOffset:(NSUInteger)offset
501 | tag:(long)tag;
502 |
503 | /**
504 | * Reads bytes until (and including) the passed "data" parameter, which acts as a separator.
505 | *
506 | * If the timeout value is negative, the read operation will not use a timeout.
507 | *
508 | * If you pass nil or zero-length data as the "data" parameter,
509 | * the method will do nothing (except maybe print a warning), and the delegate will not be called.
510 | *
511 | * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter.
512 | * If you're developing your own custom protocol, be sure your separator can not occur naturally as
513 | * part of the data between separators.
514 | * For example, imagine you want to send several small documents over a socket.
515 | * Using CRLF as a separator is likely unwise, as a CRLF could easily exist within the documents.
516 | * In this particular example, it would be better to use a protocol similar to HTTP with
517 | * a header that includes the length of the document.
518 | * Also be careful that your separator cannot occur naturally as part of the encoding for a character.
519 | *
520 | * The given data (separator) parameter should be immutable.
521 | * For performance reasons, the socket will retain it, not copy it.
522 | * So if it is immutable, don't modify it while the socket is using it.
523 | **/
524 | - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
525 |
526 | /**
527 | * Reads bytes until (and including) the passed "data" parameter, which acts as a separator.
528 | * The bytes will be appended to the given byte buffer starting at the given offset.
529 | * The given buffer will automatically be increased in size if needed.
530 | *
531 | * If the timeout value is negative, the read operation will not use a timeout.
532 | * If the buffer if nil, a buffer will automatically be created for you.
533 | *
534 | * If the bufferOffset is greater than the length of the given buffer,
535 | * the method will do nothing (except maybe print a warning), and the delegate will not be called.
536 | *
537 | * If you pass a buffer, you must not alter it in any way while the socket is using it.
538 | * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer.
539 | * That is, it will reference the bytes that were appended to the given buffer via
540 | * the method [NSData dataWithBytesNoCopy:length:freeWhenDone:NO].
541 | *
542 | * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter.
543 | * If you're developing your own custom protocol, be sure your separator can not occur naturally as
544 | * part of the data between separators.
545 | * For example, imagine you want to send several small documents over a socket.
546 | * Using CRLF as a separator is likely unwise, as a CRLF could easily exist within the documents.
547 | * In this particular example, it would be better to use a protocol similar to HTTP with
548 | * a header that includes the length of the document.
549 | * Also be careful that your separator cannot occur naturally as part of the encoding for a character.
550 | *
551 | * The given data (separator) parameter should be immutable.
552 | * For performance reasons, the socket will retain it, not copy it.
553 | * So if it is immutable, don't modify it while the socket is using it.
554 | **/
555 | - (void)readDataToData:(NSData *)data
556 | withTimeout:(NSTimeInterval)timeout
557 | buffer:(nullable NSMutableData *)buffer
558 | bufferOffset:(NSUInteger)offset
559 | tag:(long)tag;
560 |
561 | /**
562 | * Reads bytes until (and including) the passed "data" parameter, which acts as a separator.
563 | *
564 | * If the timeout value is negative, the read operation will not use a timeout.
565 | *
566 | * If maxLength is zero, no length restriction is enforced.
567 | * Otherwise if maxLength bytes are read without completing the read,
568 | * it is treated similarly to a timeout - the socket is closed with a GCDAsyncSocketReadMaxedOutError.
569 | * The read will complete successfully if exactly maxLength bytes are read and the given data is found at the end.
570 | *
571 | * If you pass nil or zero-length data as the "data" parameter,
572 | * the method will do nothing (except maybe print a warning), and the delegate will not be called.
573 | * If you pass a maxLength parameter that is less than the length of the data parameter,
574 | * the method will do nothing (except maybe print a warning), and the delegate will not be called.
575 | *
576 | * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter.
577 | * If you're developing your own custom protocol, be sure your separator can not occur naturally as
578 | * part of the data between separators.
579 | * For example, imagine you want to send several small documents over a socket.
580 | * Using CRLF as a separator is likely unwise, as a CRLF could easily exist within the documents.
581 | * In this particular example, it would be better to use a protocol similar to HTTP with
582 | * a header that includes the length of the document.
583 | * Also be careful that your separator cannot occur naturally as part of the encoding for a character.
584 | *
585 | * The given data (separator) parameter should be immutable.
586 | * For performance reasons, the socket will retain it, not copy it.
587 | * So if it is immutable, don't modify it while the socket is using it.
588 | **/
589 | - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(NSUInteger)length tag:(long)tag;
590 |
591 | /**
592 | * Reads bytes until (and including) the passed "data" parameter, which acts as a separator.
593 | * The bytes will be appended to the given byte buffer starting at the given offset.
594 | * The given buffer will automatically be increased in size if needed.
595 | *
596 | * If the timeout value is negative, the read operation will not use a timeout.
597 | * If the buffer if nil, a buffer will automatically be created for you.
598 | *
599 | * If maxLength is zero, no length restriction is enforced.
600 | * Otherwise if maxLength bytes are read without completing the read,
601 | * it is treated similarly to a timeout - the socket is closed with a GCDAsyncSocketReadMaxedOutError.
602 | * The read will complete successfully if exactly maxLength bytes are read and the given data is found at the end.
603 | *
604 | * If you pass a maxLength parameter that is less than the length of the data (separator) parameter,
605 | * the method will do nothing (except maybe print a warning), and the delegate will not be called.
606 | * If the bufferOffset is greater than the length of the given buffer,
607 | * the method will do nothing (except maybe print a warning), and the delegate will not be called.
608 | *
609 | * If you pass a buffer, you must not alter it in any way while the socket is using it.
610 | * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer.
611 | * That is, it will reference the bytes that were appended to the given buffer via
612 | * the method [NSData dataWithBytesNoCopy:length:freeWhenDone:NO].
613 | *
614 | * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter.
615 | * If you're developing your own custom protocol, be sure your separator can not occur naturally as
616 | * part of the data between separators.
617 | * For example, imagine you want to send several small documents over a socket.
618 | * Using CRLF as a separator is likely unwise, as a CRLF could easily exist within the documents.
619 | * In this particular example, it would be better to use a protocol similar to HTTP with
620 | * a header that includes the length of the document.
621 | * Also be careful that your separator cannot occur naturally as part of the encoding for a character.
622 | *
623 | * The given data (separator) parameter should be immutable.
624 | * For performance reasons, the socket will retain it, not copy it.
625 | * So if it is immutable, don't modify it while the socket is using it.
626 | **/
627 | - (void)readDataToData:(NSData *)data
628 | withTimeout:(NSTimeInterval)timeout
629 | buffer:(nullable NSMutableData *)buffer
630 | bufferOffset:(NSUInteger)offset
631 | maxLength:(NSUInteger)length
632 | tag:(long)tag;
633 |
634 | /**
635 | * Returns progress of the current read, from 0.0 to 1.0, or NaN if no current read (use isnan() to check).
636 | * The parameters "tag", "done" and "total" will be filled in if they aren't NULL.
637 | **/
638 | - (float)progressOfReadReturningTag:(nullable long *)tagPtr bytesDone:(nullable NSUInteger *)donePtr total:(nullable NSUInteger *)totalPtr;
639 |
640 | #pragma mark Writing
641 |
642 | /**
643 | * Writes data to the socket, and calls the delegate when finished.
644 | *
645 | * If you pass in nil or zero-length data, this method does nothing and the delegate will not be called.
646 | * If the timeout value is negative, the write operation will not use a timeout.
647 | *
648 | * Thread-Safety Note:
649 | * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while
650 | * the socket is writing it. In other words, it's not safe to alter the data until after the delegate method
651 | * socket:didWriteDataWithTag: is invoked signifying that this particular write operation has completed.
652 | * This is due to the fact that GCDAsyncSocket does NOT copy the data. It simply retains it.
653 | * This is for performance reasons. Often times, if NSMutableData is passed, it is because
654 | * a request/response was built up in memory. Copying this data adds an unwanted/unneeded overhead.
655 | * If you need to write data from an immutable buffer, and you need to alter the buffer before the socket
656 | * completes writing the bytes (which is NOT immediately after this method returns, but rather at a later time
657 | * when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method.
658 | **/
659 | - (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
660 |
661 | /**
662 | * Returns progress of the current write, from 0.0 to 1.0, or NaN if no current write (use isnan() to check).
663 | * The parameters "tag", "done" and "total" will be filled in if they aren't NULL.
664 | **/
665 | - (float)progressOfWriteReturningTag:(nullable long *)tagPtr bytesDone:(nullable NSUInteger *)donePtr total:(nullable NSUInteger *)totalPtr;
666 |
667 | #pragma mark Security
668 |
669 | /**
670 | * Secures the connection using SSL/TLS.
671 | *
672 | * This method may be called at any time, and the TLS handshake will occur after all pending reads and writes
673 | * are finished. This allows one the option of sending a protocol dependent StartTLS message, and queuing
674 | * the upgrade to TLS at the same time, without having to wait for the write to finish.
675 | * Any reads or writes scheduled after this method is called will occur over the secured connection.
676 | *
677 | * ==== The available TOP-LEVEL KEYS are:
678 | *
679 | * - GCDAsyncSocketManuallyEvaluateTrust
680 | * The value must be of type NSNumber, encapsulating a BOOL value.
681 | * If you set this to YES, then the underlying SecureTransport system will not evaluate the SecTrustRef of the peer.
682 | * Instead it will pause at the moment evaulation would typically occur,
683 | * and allow us to handle the security evaluation however we see fit.
684 | * So GCDAsyncSocket will invoke the delegate method socket:shouldTrustPeer: passing the SecTrustRef.
685 | *
686 | * Note that if you set this option, then all other configuration keys are ignored.
687 | * Evaluation will be completely up to you during the socket:didReceiveTrust:completionHandler: delegate method.
688 | *
689 | * For more information on trust evaluation see:
690 | * Apple's Technical Note TN2232 - HTTPS Server Trust Evaluation
691 | * https://developer.apple.com/library/ios/technotes/tn2232/_index.html
692 | *
693 | * If unspecified, the default value is NO.
694 | *
695 | * - GCDAsyncSocketUseCFStreamForTLS (iOS only)
696 | * The value must be of type NSNumber, encapsulating a BOOL value.
697 | * By default GCDAsyncSocket will use the SecureTransport layer to perform encryption.
698 | * This gives us more control over the security protocol (many more configuration options),
699 | * plus it allows us to optimize things like sys calls and buffer allocation.
700 | *
701 | * However, if you absolutely must, you can instruct GCDAsyncSocket to use the old-fashioned encryption
702 | * technique by going through the CFStream instead. So instead of using SecureTransport, GCDAsyncSocket
703 | * will instead setup a CFRead/CFWriteStream. And then set the kCFStreamPropertySSLSettings property
704 | * (via CFReadStreamSetProperty / CFWriteStreamSetProperty) and will pass the given options to this method.
705 | *
706 | * Thus all the other keys in the given dictionary will be ignored by GCDAsyncSocket,
707 | * and will passed directly CFReadStreamSetProperty / CFWriteStreamSetProperty.
708 | * For more infomation on these keys, please see the documentation for kCFStreamPropertySSLSettings.
709 | *
710 | * If unspecified, the default value is NO.
711 | *
712 | * ==== The available CONFIGURATION KEYS are:
713 | *
714 | * - kCFStreamSSLPeerName
715 | * The value must be of type NSString.
716 | * It should match the name in the X.509 certificate given by the remote party.
717 | * See Apple's documentation for SSLSetPeerDomainName.
718 | *
719 | * - kCFStreamSSLCertificates
720 | * The value must be of type NSArray.
721 | * See Apple's documentation for SSLSetCertificate.
722 | *
723 | * - kCFStreamSSLIsServer
724 | * The value must be of type NSNumber, encapsulationg a BOOL value.
725 | * See Apple's documentation for SSLCreateContext for iOS.
726 | * This is optional for iOS. If not supplied, a NO value is the default.
727 | * This is not needed for Mac OS X, and the value is ignored.
728 | *
729 | * - GCDAsyncSocketSSLPeerID
730 | * The value must be of type NSData.
731 | * You must set this value if you want to use TLS session resumption.
732 | * See Apple's documentation for SSLSetPeerID.
733 | *
734 | * - GCDAsyncSocketSSLProtocolVersionMin
735 | * - GCDAsyncSocketSSLProtocolVersionMax
736 | * The value(s) must be of type NSNumber, encapsulting a SSLProtocol value.
737 | * See Apple's documentation for SSLSetProtocolVersionMin & SSLSetProtocolVersionMax.
738 | * See also the SSLProtocol typedef.
739 | *
740 | * - GCDAsyncSocketSSLSessionOptionFalseStart
741 | * The value must be of type NSNumber, encapsulating a BOOL value.
742 | * See Apple's documentation for kSSLSessionOptionFalseStart.
743 | *
744 | * - GCDAsyncSocketSSLSessionOptionSendOneByteRecord
745 | * The value must be of type NSNumber, encapsulating a BOOL value.
746 | * See Apple's documentation for kSSLSessionOptionSendOneByteRecord.
747 | *
748 | * - GCDAsyncSocketSSLCipherSuites
749 | * The values must be of type NSArray.
750 | * Each item within the array must be a NSNumber, encapsulating
751 | * See Apple's documentation for SSLSetEnabledCiphers.
752 | * See also the SSLCipherSuite typedef.
753 | *
754 | * - GCDAsyncSocketSSLDiffieHellmanParameters (Mac OS X only)
755 | * The value must be of type NSData.
756 | * See Apple's documentation for SSLSetDiffieHellmanParams.
757 | *
758 | * ==== The following UNAVAILABLE KEYS are: (with throw an exception)
759 | *
760 | * - kCFStreamSSLAllowsAnyRoot (UNAVAILABLE)
761 | * You MUST use manual trust evaluation instead (see GCDAsyncSocketManuallyEvaluateTrust).
762 | * Corresponding deprecated method: SSLSetAllowsAnyRoot
763 | *
764 | * - kCFStreamSSLAllowsExpiredRoots (UNAVAILABLE)
765 | * You MUST use manual trust evaluation instead (see GCDAsyncSocketManuallyEvaluateTrust).
766 | * Corresponding deprecated method: SSLSetAllowsExpiredRoots
767 | *
768 | * - kCFStreamSSLAllowsExpiredCertificates (UNAVAILABLE)
769 | * You MUST use manual trust evaluation instead (see GCDAsyncSocketManuallyEvaluateTrust).
770 | * Corresponding deprecated method: SSLSetAllowsExpiredCerts
771 | *
772 | * - kCFStreamSSLValidatesCertificateChain (UNAVAILABLE)
773 | * You MUST use manual trust evaluation instead (see GCDAsyncSocketManuallyEvaluateTrust).
774 | * Corresponding deprecated method: SSLSetEnableCertVerify
775 | *
776 | * - kCFStreamSSLLevel (UNAVAILABLE)
777 | * You MUST use GCDAsyncSocketSSLProtocolVersionMin & GCDAsyncSocketSSLProtocolVersionMin instead.
778 | * Corresponding deprecated method: SSLSetProtocolVersionEnabled
779 | *
780 | *
781 | * Please refer to Apple's documentation for corresponding SSLFunctions.
782 | *
783 | * If you pass in nil or an empty dictionary, the default settings will be used.
784 | *
785 | * IMPORTANT SECURITY NOTE:
786 | * The default settings will check to make sure the remote party's certificate is signed by a
787 | * trusted 3rd party certificate agency (e.g. verisign) and that the certificate is not expired.
788 | * However it will not verify the name on the certificate unless you
789 | * give it a name to verify against via the kCFStreamSSLPeerName key.
790 | * The security implications of this are important to understand.
791 | * Imagine you are attempting to create a secure connection to MySecureServer.com,
792 | * but your socket gets directed to MaliciousServer.com because of a hacked DNS server.
793 | * If you simply use the default settings, and MaliciousServer.com has a valid certificate,
794 | * the default settings will not detect any problems since the certificate is valid.
795 | * To properly secure your connection in this particular scenario you
796 | * should set the kCFStreamSSLPeerName property to "MySecureServer.com".
797 | *
798 | * You can also perform additional validation in socketDidSecure.
799 | **/
800 | - (void)startTLS:(nullable NSDictionary *)tlsSettings;
801 |
802 | #pragma mark Advanced
803 |
804 | /**
805 | * Traditionally sockets are not closed until the conversation is over.
806 | * However, it is technically possible for the remote enpoint to close its write stream.
807 | * Our socket would then be notified that there is no more data to be read,
808 | * but our socket would still be writeable and the remote endpoint could continue to receive our data.
809 | *
810 | * The argument for this confusing functionality stems from the idea that a client could shut down its
811 | * write stream after sending a request to the server, thus notifying the server there are to be no further requests.
812 | * In practice, however, this technique did little to help server developers.
813 | *
814 | * To make matters worse, from a TCP perspective there is no way to tell the difference from a read stream close
815 | * and a full socket close. They both result in the TCP stack receiving a FIN packet. The only way to tell
816 | * is by continuing to write to the socket. If it was only a read stream close, then writes will continue to work.
817 | * Otherwise an error will be occur shortly (when the remote end sends us a RST packet).
818 | *
819 | * In addition to the technical challenges and confusion, many high level socket/stream API's provide
820 | * no support for dealing with the problem. If the read stream is closed, the API immediately declares the
821 | * socket to be closed, and shuts down the write stream as well. In fact, this is what Apple's CFStream API does.
822 | * It might sound like poor design at first, but in fact it simplifies development.
823 | *
824 | * The vast majority of the time if the read stream is closed it's because the remote endpoint closed its socket.
825 | * Thus it actually makes sense to close the socket at this point.
826 | * And in fact this is what most networking developers want and expect to happen.
827 | * However, if you are writing a server that interacts with a plethora of clients,
828 | * you might encounter a client that uses the discouraged technique of shutting down its write stream.
829 | * If this is the case, you can set this property to NO,
830 | * and make use of the socketDidCloseReadStream delegate method.
831 | *
832 | * The default value is YES.
833 | **/
834 | @property (atomic, assign, readwrite) BOOL autoDisconnectOnClosedReadStream;
835 |
836 | /**
837 | * GCDAsyncSocket maintains thread safety by using an internal serial dispatch_queue.
838 | * In most cases, the instance creates this queue itself.
839 | * However, to allow for maximum flexibility, the internal queue may be passed in the init method.
840 | * This allows for some advanced options such as controlling socket priority via target queues.
841 | * However, when one begins to use target queues like this, they open the door to some specific deadlock issues.
842 | *
843 | * For example, imagine there are 2 queues:
844 | * dispatch_queue_t socketQueue;
845 | * dispatch_queue_t socketTargetQueue;
846 | *
847 | * If you do this (pseudo-code):
848 | * socketQueue.targetQueue = socketTargetQueue;
849 | *
850 | * Then all socketQueue operations will actually get run on the given socketTargetQueue.
851 | * This is fine and works great in most situations.
852 | * But if you run code directly from within the socketTargetQueue that accesses the socket,
853 | * you could potentially get deadlock. Imagine the following code:
854 | *
855 | * - (BOOL)socketHasSomething
856 | * {
857 | * __block BOOL result = NO;
858 | * dispatch_block_t block = ^{
859 | * result = [self someInternalMethodToBeRunOnlyOnSocketQueue];
860 | * }
861 | * if (is_executing_on_queue(socketQueue))
862 | * block();
863 | * else
864 | * dispatch_sync(socketQueue, block);
865 | *
866 | * return result;
867 | * }
868 | *
869 | * What happens if you call this method from the socketTargetQueue? The result is deadlock.
870 | * This is because the GCD API offers no mechanism to discover a queue's targetQueue.
871 | * Thus we have no idea if our socketQueue is configured with a targetQueue.
872 | * If we had this information, we could easily avoid deadlock.
873 | * But, since these API's are missing or unfeasible, you'll have to explicitly set it.
874 | *
875 | * IF you pass a socketQueue via the init method,
876 | * AND you've configured the passed socketQueue with a targetQueue,
877 | * THEN you should pass the end queue in the target hierarchy.
878 | *
879 | * For example, consider the following queue hierarchy:
880 | * socketQueue -> ipQueue -> moduleQueue
881 | *
882 | * This example demonstrates priority shaping within some server.
883 | * All incoming client connections from the same IP address are executed on the same target queue.
884 | * And all connections for a particular module are executed on the same target queue.
885 | * Thus, the priority of all networking for the entire module can be changed on the fly.
886 | * Additionally, networking traffic from a single IP cannot monopolize the module.
887 | *
888 | * Here's how you would accomplish something like that:
889 | * - (dispatch_queue_t)newSocketQueueForConnectionFromAddress:(NSData *)address onSocket:(GCDAsyncSocket *)sock
890 | * {
891 | * dispatch_queue_t socketQueue = dispatch_queue_create("", NULL);
892 | * dispatch_queue_t ipQueue = [self ipQueueForAddress:address];
893 | *
894 | * dispatch_set_target_queue(socketQueue, ipQueue);
895 | * dispatch_set_target_queue(iqQueue, moduleQueue);
896 | *
897 | * return socketQueue;
898 | * }
899 | * - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
900 | * {
901 | * [clientConnections addObject:newSocket];
902 | * [newSocket markSocketQueueTargetQueue:moduleQueue];
903 | * }
904 | *
905 | * Note: This workaround is ONLY needed if you intend to execute code directly on the ipQueue or moduleQueue.
906 | * This is often NOT the case, as such queues are used solely for execution shaping.
907 | **/
908 | - (void)markSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreConfiguredTargetQueue;
909 | - (void)unmarkSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreviouslyConfiguredTargetQueue;
910 |
911 | /**
912 | * It's not thread-safe to access certain variables from outside the socket's internal queue.
913 | *
914 | * For example, the socket file descriptor.
915 | * File descriptors are simply integers which reference an index in the per-process file table.
916 | * However, when one requests a new file descriptor (by opening a file or socket),
917 | * the file descriptor returned is guaranteed to be the lowest numbered unused descriptor.
918 | * So if we're not careful, the following could be possible:
919 | *
920 | * - Thread A invokes a method which returns the socket's file descriptor.
921 | * - The socket is closed via the socket's internal queue on thread B.
922 | * - Thread C opens a file, and subsequently receives the file descriptor that was previously the socket's FD.
923 | * - Thread A is now accessing/altering the file instead of the socket.
924 | *
925 | * In addition to this, other variables are not actually objects,
926 | * and thus cannot be retained/released or even autoreleased.
927 | * An example is the sslContext, of type SSLContextRef, which is actually a malloc'd struct.
928 | *
929 | * Although there are internal variables that make it difficult to maintain thread-safety,
930 | * it is important to provide access to these variables
931 | * to ensure this class can be used in a wide array of environments.
932 | * This method helps to accomplish this by invoking the current block on the socket's internal queue.
933 | * The methods below can be invoked from within the block to access
934 | * those generally thread-unsafe internal variables in a thread-safe manner.
935 | * The given block will be invoked synchronously on the socket's internal queue.
936 | *
937 | * If you save references to any protected variables and use them outside the block, you do so at your own peril.
938 | **/
939 | - (void)performBlock:(dispatch_block_t)block;
940 |
941 | /**
942 | * These methods are only available from within the context of a performBlock: invocation.
943 | * See the documentation for the performBlock: method above.
944 | *
945 | * Provides access to the socket's file descriptor(s).
946 | * If the socket is a server socket (is accepting incoming connections),
947 | * it might actually have multiple internal socket file descriptors - one for IPv4 and one for IPv6.
948 | **/
949 | - (int)socketFD;
950 | - (int)socket4FD;
951 | - (int)socket6FD;
952 |
953 | #if TARGET_OS_IPHONE
954 |
955 | /**
956 | * These methods are only available from within the context of a performBlock: invocation.
957 | * See the documentation for the performBlock: method above.
958 | *
959 | * Provides access to the socket's internal CFReadStream/CFWriteStream.
960 | *
961 | * These streams are only used as workarounds for specific iOS shortcomings:
962 | *
963 | * - Apple has decided to keep the SecureTransport framework private is iOS.
964 | * This means the only supplied way to do SSL/TLS is via CFStream or some other API layered on top of it.
965 | * Thus, in order to provide SSL/TLS support on iOS we are forced to rely on CFStream,
966 | * instead of the preferred and faster and more powerful SecureTransport.
967 | *
968 | * - If a socket doesn't have backgrounding enabled, and that socket is closed while the app is backgrounded,
969 | * Apple only bothers to notify us via the CFStream API.
970 | * The faster and more powerful GCD API isn't notified properly in this case.
971 | *
972 | * See also: (BOOL)enableBackgroundingOnSocket
973 | **/
974 | - (nullable CFReadStreamRef)readStream;
975 | - (nullable CFWriteStreamRef)writeStream;
976 |
977 | /**
978 | * This method is only available from within the context of a performBlock: invocation.
979 | * See the documentation for the performBlock: method above.
980 | *
981 | * Configures the socket to allow it to operate when the iOS application has been backgrounded.
982 | * In other words, this method creates a read & write stream, and invokes:
983 | *
984 | * CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
985 | * CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
986 | *
987 | * Returns YES if successful, NO otherwise.
988 | *
989 | * Note: Apple does not officially support backgrounding server sockets.
990 | * That is, if your socket is accepting incoming connections, Apple does not officially support
991 | * allowing iOS applications to accept incoming connections while an app is backgrounded.
992 | *
993 | * Example usage:
994 | *
995 | * - (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port
996 | * {
997 | * [asyncSocket performBlock:^{
998 | * [asyncSocket enableBackgroundingOnSocket];
999 | * }];
1000 | * }
1001 | **/
1002 | - (BOOL)enableBackgroundingOnSocket;
1003 |
1004 | #endif
1005 |
1006 | /**
1007 | * This method is only available from within the context of a performBlock: invocation.
1008 | * See the documentation for the performBlock: method above.
1009 | *
1010 | * Provides access to the socket's SSLContext, if SSL/TLS has been started on the socket.
1011 | **/
1012 | - (nullable SSLContextRef)sslContext;
1013 |
1014 | #pragma mark Utilities
1015 |
1016 | /**
1017 | * The address lookup utility used by the class.
1018 | * This method is synchronous, so it's recommended you use it on a background thread/queue.
1019 | *
1020 | * The special strings "localhost" and "loopback" return the loopback address for IPv4 and IPv6.
1021 | *
1022 | * @returns
1023 | * A mutable array with all IPv4 and IPv6 addresses returned by getaddrinfo.
1024 | * The addresses are specifically for TCP connections.
1025 | * You can filter the addresses, if needed, using the other utility methods provided by the class.
1026 | **/
1027 | + (nullable NSMutableArray *)lookupHost:(NSString *)host port:(uint16_t)port error:(NSError **)errPtr;
1028 |
1029 | /**
1030 | * Extracting host and port information from raw address data.
1031 | **/
1032 |
1033 | + (nullable NSString *)hostFromAddress:(NSData *)address;
1034 | + (uint16_t)portFromAddress:(NSData *)address;
1035 |
1036 | + (BOOL)isIPv4Address:(NSData *)address;
1037 | + (BOOL)isIPv6Address:(NSData *)address;
1038 |
1039 | + (BOOL)getHost:( NSString * __nullable * __nullable)hostPtr port:(nullable uint16_t *)portPtr fromAddress:(NSData *)address;
1040 |
1041 | + (BOOL)getHost:(NSString * __nullable * __nullable)hostPtr port:(nullable uint16_t *)portPtr family:(nullable sa_family_t *)afPtr fromAddress:(NSData *)address;
1042 |
1043 | /**
1044 | * A few common line separators, for use with the readDataToData:... methods.
1045 | **/
1046 | + (NSData *)CRLFData; // 0x0D0A
1047 | + (NSData *)CRData; // 0x0D
1048 | + (NSData *)LFData; // 0x0A
1049 | + (NSData *)ZeroData; // 0x00
1050 |
1051 | @end
1052 |
1053 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1054 | #pragma mark -
1055 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1056 |
1057 | @protocol GCDAsyncSocketDelegate
1058 | @optional
1059 |
1060 | /**
1061 | * This method is called immediately prior to socket:didAcceptNewSocket:.
1062 | * It optionally allows a listening socket to specify the socketQueue for a new accepted socket.
1063 | * If this method is not implemented, or returns NULL, the new accepted socket will create its own default queue.
1064 | *
1065 | * Since you cannot autorelease a dispatch_queue,
1066 | * this method uses the "new" prefix in its name to specify that the returned queue has been retained.
1067 | *
1068 | * Thus you could do something like this in the implementation:
1069 | * return dispatch_queue_create("MyQueue", NULL);
1070 | *
1071 | * If you are placing multiple sockets on the same queue,
1072 | * then care should be taken to increment the retain count each time this method is invoked.
1073 | *
1074 | * For example, your implementation might look something like this:
1075 | * dispatch_retain(myExistingQueue);
1076 | * return myExistingQueue;
1077 | **/
1078 | - (nullable dispatch_queue_t)newSocketQueueForConnectionFromAddress:(NSData *)address onSocket:(GCDAsyncSocket *)sock;
1079 |
1080 | /**
1081 | * Called when a socket accepts a connection.
1082 | * Another socket is automatically spawned to handle it.
1083 | *
1084 | * You must retain the newSocket if you wish to handle the connection.
1085 | * Otherwise the newSocket instance will be released and the spawned connection will be closed.
1086 | *
1087 | * By default the new socket will have the same delegate and delegateQueue.
1088 | * You may, of course, change this at any time.
1089 | **/
1090 | - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket;
1091 |
1092 | /**
1093 | * Called when a socket connects and is ready for reading and writing.
1094 | * The host parameter will be an IP address, not a DNS name.
1095 | **/
1096 | - (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port;
1097 |
1098 | /**
1099 | * Called when a socket connects and is ready for reading and writing.
1100 | * The host parameter will be an IP address, not a DNS name.
1101 | **/
1102 | - (void)socket:(GCDAsyncSocket *)sock didConnectToUrl:(NSURL *)url;
1103 |
1104 | /**
1105 | * Called when a socket has completed reading the requested data into memory.
1106 | * Not called if there is an error.
1107 | **/
1108 | - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag;
1109 |
1110 | /**
1111 | * Called when a socket has read in data, but has not yet completed the read.
1112 | * This would occur if using readToData: or readToLength: methods.
1113 | * It may be used to for things such as updating progress bars.
1114 | **/
1115 | - (void)socket:(GCDAsyncSocket *)sock didReadPartialDataOfLength:(NSUInteger)partialLength tag:(long)tag;
1116 |
1117 | /**
1118 | * Called when a socket has completed writing the requested data. Not called if there is an error.
1119 | **/
1120 | - (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag;
1121 |
1122 | /**
1123 | * Called when a socket has written some data, but has not yet completed the entire write.
1124 | * It may be used to for things such as updating progress bars.
1125 | **/
1126 | - (void)socket:(GCDAsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag;
1127 |
1128 | /**
1129 | * Called if a read operation has reached its timeout without completing.
1130 | * This method allows you to optionally extend the timeout.
1131 | * If you return a positive time interval (> 0) the read's timeout will be extended by the given amount.
1132 | * If you don't implement this method, or return a non-positive time interval (<= 0) the read will timeout as usual.
1133 | *
1134 | * The elapsed parameter is the sum of the original timeout, plus any additions previously added via this method.
1135 | * The length parameter is the number of bytes that have been read so far for the read operation.
1136 | *
1137 | * Note that this method may be called multiple times for a single read if you return positive numbers.
1138 | **/
1139 | - (NSTimeInterval)socket:(GCDAsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag
1140 | elapsed:(NSTimeInterval)elapsed
1141 | bytesDone:(NSUInteger)length;
1142 |
1143 | /**
1144 | * Called if a write operation has reached its timeout without completing.
1145 | * This method allows you to optionally extend the timeout.
1146 | * If you return a positive time interval (> 0) the write's timeout will be extended by the given amount.
1147 | * If you don't implement this method, or return a non-positive time interval (<= 0) the write will timeout as usual.
1148 | *
1149 | * The elapsed parameter is the sum of the original timeout, plus any additions previously added via this method.
1150 | * The length parameter is the number of bytes that have been written so far for the write operation.
1151 | *
1152 | * Note that this method may be called multiple times for a single write if you return positive numbers.
1153 | **/
1154 | - (NSTimeInterval)socket:(GCDAsyncSocket *)sock shouldTimeoutWriteWithTag:(long)tag
1155 | elapsed:(NSTimeInterval)elapsed
1156 | bytesDone:(NSUInteger)length;
1157 |
1158 | /**
1159 | * Conditionally called if the read stream closes, but the write stream may still be writeable.
1160 | *
1161 | * This delegate method is only called if autoDisconnectOnClosedReadStream has been set to NO.
1162 | * See the discussion on the autoDisconnectOnClosedReadStream method for more information.
1163 | **/
1164 | - (void)socketDidCloseReadStream:(GCDAsyncSocket *)sock;
1165 |
1166 | /**
1167 | * Called when a socket disconnects with or without error.
1168 | *
1169 | * If you call the disconnect method, and the socket wasn't already disconnected,
1170 | * then an invocation of this delegate method will be enqueued on the delegateQueue
1171 | * before the disconnect method returns.
1172 | *
1173 | * Note: If the GCDAsyncSocket instance is deallocated while it is still connected,
1174 | * and the delegate is not also deallocated, then this method will be invoked,
1175 | * but the sock parameter will be nil. (It must necessarily be nil since it is no longer available.)
1176 | * This is a generally rare, but is possible if one writes code like this:
1177 | *
1178 | * asyncSocket = nil; // I'm implicitly disconnecting the socket
1179 | *
1180 | * In this case it may preferrable to nil the delegate beforehand, like this:
1181 | *
1182 | * asyncSocket.delegate = nil; // Don't invoke my delegate method
1183 | * asyncSocket = nil; // I'm implicitly disconnecting the socket
1184 | *
1185 | * Of course, this depends on how your state machine is configured.
1186 | **/
1187 | - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(nullable NSError *)err;
1188 |
1189 | /**
1190 | * Called after the socket has successfully completed SSL/TLS negotiation.
1191 | * This method is not called unless you use the provided startTLS method.
1192 | *
1193 | * If a SSL/TLS negotiation fails (invalid certificate, etc) then the socket will immediately close,
1194 | * and the socketDidDisconnect:withError: delegate method will be called with the specific SSL error code.
1195 | **/
1196 | - (void)socketDidSecure:(GCDAsyncSocket *)sock;
1197 |
1198 | /**
1199 | * Allows a socket delegate to hook into the TLS handshake and manually validate the peer it's connecting to.
1200 | *
1201 | * This is only called if startTLS is invoked with options that include:
1202 | * - GCDAsyncSocketManuallyEvaluateTrust == YES
1203 | *
1204 | * Typically the delegate will use SecTrustEvaluate (and related functions) to properly validate the peer.
1205 | *
1206 | * Note from Apple's documentation:
1207 | * Because [SecTrustEvaluate] might look on the network for certificates in the certificate chain,
1208 | * [it] might block while attempting network access. You should never call it from your main thread;
1209 | * call it only from within a function running on a dispatch queue or on a separate thread.
1210 | *
1211 | * Thus this method uses a completionHandler block rather than a normal return value.
1212 | * The completionHandler block is thread-safe, and may be invoked from a background queue/thread.
1213 | * It is safe to invoke the completionHandler block even if the socket has been closed.
1214 | **/
1215 | - (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust
1216 | completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler;
1217 |
1218 | @end
1219 | NS_ASSUME_NONNULL_END
1220 |
--------------------------------------------------------------------------------
/iOS_GetUDID_Demo/Server/CocoaAsyncSocket/GCDAsyncUdpSocket.h:
--------------------------------------------------------------------------------
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
12 | #import
13 | #import
14 | #import
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_ENUM(NSInteger, 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
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 )aDelegate delegateQueue:(nullable dispatch_queue_t)dq;
184 | - (instancetype)initWithDelegate:(nullable id )aDelegate delegateQueue:(nullable dispatch_queue_t)dq socketQueue:(nullable dispatch_queue_t)sq;
185 |
186 | #pragma mark Configuration
187 |
188 | - (nullable id )delegate;
189 | - (void)setDelegate:(nullable id )delegate;
190 | - (void)synchronouslySetDelegate:(nullable id )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 __nullable * __nullable)delegatePtr delegateQueue:(dispatch_queue_t __nullable * __nullable)delegateQueuePtr;
197 | - (void)setDelegate:(nullable id )delegate delegateQueue:(nullable dispatch_queue_t)delegateQueue;
198 | - (void)synchronouslySetDelegate:(nullable id )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 | #pragma mark Reuse Port
494 |
495 | /**
496 | * By default, only one socket can be bound to a given IP address + port at a time.
497 | * To enable multiple processes to simultaneously bind to the same address+port,
498 | * you need to enable this functionality in the socket. All processes that wish to
499 | * use the address+port simultaneously must all enable reuse port on the socket
500 | * bound to that port.
501 | **/
502 | - (BOOL)enableReusePort:(BOOL)flag error:(NSError **)errPtr;
503 |
504 | #pragma mark Broadcast
505 |
506 | /**
507 | * By default, the underlying socket in the OS will not allow you to send broadcast messages.
508 | * In order to send broadcast messages, you need to enable this functionality in the socket.
509 | *
510 | * A broadcast is a UDP message to addresses like "192.168.255.255" or "255.255.255.255" that is
511 | * delivered to every host on the network.
512 | * The reason this is generally disabled by default (by the OS) is to prevent
513 | * accidental broadcast messages from flooding the network.
514 | **/
515 | - (BOOL)enableBroadcast:(BOOL)flag error:(NSError **)errPtr;
516 |
517 | #pragma mark Sending
518 |
519 | /**
520 | * Asynchronously sends the given data, with the given timeout and tag.
521 | *
522 | * This method may only be used with a connected socket.
523 | * Recall that connecting is optional for a UDP socket.
524 | * For connected sockets, data can only be sent to the connected address.
525 | * For non-connected sockets, the remote destination is specified for each packet.
526 | * For more information about optionally connecting udp sockets, see the documentation for the connect methods above.
527 | *
528 | * @param data
529 | * The data to send.
530 | * If data is nil or zero-length, this method does nothing.
531 | * If passing NSMutableData, please read the thread-safety notice below.
532 | *
533 | * @param timeout
534 | * The timeout for the send opeartion.
535 | * If the timeout value is negative, the send operation will not use a timeout.
536 | *
537 | * @param tag
538 | * The tag is for your convenience.
539 | * It is not sent or received over the socket in any manner what-so-ever.
540 | * It is reported back as a parameter in the udpSocket:didSendDataWithTag:
541 | * or udpSocket:didNotSendDataWithTag:dueToError: methods.
542 | * You can use it as an array index, state id, type constant, etc.
543 | *
544 | *
545 | * Thread-Safety Note:
546 | * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while
547 | * the socket is sending it. In other words, it's not safe to alter the data until after the delegate method
548 | * udpSocket:didSendDataWithTag: or udpSocket:didNotSendDataWithTag:dueToError: is invoked signifying
549 | * that this particular send operation has completed.
550 | * This is due to the fact that GCDAsyncUdpSocket does NOT copy the data.
551 | * It simply retains it for performance reasons.
552 | * Often times, if NSMutableData is passed, it is because a request/response was built up in memory.
553 | * Copying this data adds an unwanted/unneeded overhead.
554 | * If you need to write data from an immutable buffer, and you need to alter the buffer before the socket
555 | * completes sending the bytes (which is NOT immediately after this method returns, but rather at a later time
556 | * when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method.
557 | **/
558 | - (void)sendData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
559 |
560 | /**
561 | * Asynchronously sends the given data, with the given timeout and tag, to the given host and port.
562 | *
563 | * This method cannot be used with a connected socket.
564 | * Recall that connecting is optional for a UDP socket.
565 | * For connected sockets, data can only be sent to the connected address.
566 | * For non-connected sockets, the remote destination is specified for each packet.
567 | * For more information about optionally connecting udp sockets, see the documentation for the connect methods above.
568 | *
569 | * @param data
570 | * The data to send.
571 | * If data is nil or zero-length, this method does nothing.
572 | * If passing NSMutableData, please read the thread-safety notice below.
573 | *
574 | * @param host
575 | * The destination to send the udp packet to.
576 | * May be specified as a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2").
577 | * You may also use the convenience strings of "loopback" or "localhost".
578 | *
579 | * @param port
580 | * The port of the host to send to.
581 | *
582 | * @param timeout
583 | * The timeout for the send opeartion.
584 | * If the timeout value is negative, the send operation will not use a timeout.
585 | *
586 | * @param tag
587 | * The tag is for your convenience.
588 | * It is not sent or received over the socket in any manner what-so-ever.
589 | * It is reported back as a parameter in the udpSocket:didSendDataWithTag:
590 | * or udpSocket:didNotSendDataWithTag:dueToError: methods.
591 | * You can use it as an array index, state id, type constant, etc.
592 | *
593 | *
594 | * Thread-Safety Note:
595 | * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while
596 | * the socket is sending it. In other words, it's not safe to alter the data until after the delegate method
597 | * udpSocket:didSendDataWithTag: or udpSocket:didNotSendDataWithTag:dueToError: is invoked signifying
598 | * that this particular send operation has completed.
599 | * This is due to the fact that GCDAsyncUdpSocket does NOT copy the data.
600 | * It simply retains it for performance reasons.
601 | * Often times, if NSMutableData is passed, it is because a request/response was built up in memory.
602 | * Copying this data adds an unwanted/unneeded overhead.
603 | * If you need to write data from an immutable buffer, and you need to alter the buffer before the socket
604 | * completes sending the bytes (which is NOT immediately after this method returns, but rather at a later time
605 | * when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method.
606 | **/
607 | - (void)sendData:(NSData *)data
608 | toHost:(NSString *)host
609 | port:(uint16_t)port
610 | withTimeout:(NSTimeInterval)timeout
611 | tag:(long)tag;
612 |
613 | /**
614 | * Asynchronously sends the given data, with the given timeout and tag, to the given address.
615 | *
616 | * This method cannot be used with a connected socket.
617 | * Recall that connecting is optional for a UDP socket.
618 | * For connected sockets, data can only be sent to the connected address.
619 | * For non-connected sockets, the remote destination is specified for each packet.
620 | * For more information about optionally connecting udp sockets, see the documentation for the connect methods above.
621 | *
622 | * @param data
623 | * The data to send.
624 | * If data is nil or zero-length, this method does nothing.
625 | * If passing NSMutableData, please read the thread-safety notice below.
626 | *
627 | * @param remoteAddr
628 | * The address to send the data to (specified as a sockaddr structure wrapped in a NSData object).
629 | *
630 | * @param timeout
631 | * The timeout for the send opeartion.
632 | * If the timeout value is negative, the send operation will not use a timeout.
633 | *
634 | * @param tag
635 | * The tag is for your convenience.
636 | * It is not sent or received over the socket in any manner what-so-ever.
637 | * It is reported back as a parameter in the udpSocket:didSendDataWithTag:
638 | * or udpSocket:didNotSendDataWithTag:dueToError: methods.
639 | * You can use it as an array index, state id, type constant, etc.
640 | *
641 | *
642 | * Thread-Safety Note:
643 | * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while
644 | * the socket is sending it. In other words, it's not safe to alter the data until after the delegate method
645 | * udpSocket:didSendDataWithTag: or udpSocket:didNotSendDataWithTag:dueToError: is invoked signifying
646 | * that this particular send operation has completed.
647 | * This is due to the fact that GCDAsyncUdpSocket does NOT copy the data.
648 | * It simply retains it for performance reasons.
649 | * Often times, if NSMutableData is passed, it is because a request/response was built up in memory.
650 | * Copying this data adds an unwanted/unneeded overhead.
651 | * If you need to write data from an immutable buffer, and you need to alter the buffer before the socket
652 | * completes sending the bytes (which is NOT immediately after this method returns, but rather at a later time
653 | * when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method.
654 | **/
655 | - (void)sendData:(NSData *)data toAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout tag:(long)tag;
656 |
657 | /**
658 | * You may optionally set a send filter for the socket.
659 | * A filter can provide several interesting possibilities:
660 | *
661 | * 1. Optional caching of resolved addresses for domain names.
662 | * The cache could later be consulted, resulting in fewer system calls to getaddrinfo.
663 | *
664 | * 2. Reusable modules of code for bandwidth monitoring.
665 | *
666 | * 3. Sometimes traffic shapers are needed to simulate real world environments.
667 | * A filter allows you to write custom code to simulate such environments.
668 | * The ability to code this yourself is especially helpful when your simulated environment
669 | * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router),
670 | * or the system tools to handle this aren't available (e.g. on a mobile device).
671 | *
672 | * For more information about GCDAsyncUdpSocketSendFilterBlock, see the documentation for its typedef.
673 | * To remove a previously set filter, invoke this method and pass a nil filterBlock and NULL filterQueue.
674 | *
675 | * Note: This method invokes setSendFilter:withQueue:isAsynchronous: (documented below),
676 | * passing YES for the isAsynchronous parameter.
677 | **/
678 | - (void)setSendFilter:(nullable GCDAsyncUdpSocketSendFilterBlock)filterBlock withQueue:(nullable dispatch_queue_t)filterQueue;
679 |
680 | /**
681 | * The receive filter can be run via dispatch_async or dispatch_sync.
682 | * Most typical situations call for asynchronous operation.
683 | *
684 | * However, there are a few situations in which synchronous operation is preferred.
685 | * Such is the case when the filter is extremely minimal and fast.
686 | * This is because dispatch_sync is faster than dispatch_async.
687 | *
688 | * If you choose synchronous operation, be aware of possible deadlock conditions.
689 | * Since the socket queue is executing your block via dispatch_sync,
690 | * then you cannot perform any tasks which may invoke dispatch_sync on the socket queue.
691 | * For example, you can't query properties on the socket.
692 | **/
693 | - (void)setSendFilter:(nullable GCDAsyncUdpSocketSendFilterBlock)filterBlock
694 | withQueue:(nullable dispatch_queue_t)filterQueue
695 | isAsynchronous:(BOOL)isAsynchronous;
696 |
697 | #pragma mark Receiving
698 |
699 | /**
700 | * There are two modes of operation for receiving packets: one-at-a-time & continuous.
701 | *
702 | * In one-at-a-time mode, you call receiveOnce everytime your delegate is ready to process an incoming udp packet.
703 | * Receiving packets one-at-a-time may be better suited for implementing certain state machine code,
704 | * where your state machine may not always be ready to process incoming packets.
705 | *
706 | * In continuous mode, the delegate is invoked immediately everytime incoming udp packets are received.
707 | * Receiving packets continuously is better suited to real-time streaming applications.
708 | *
709 | * You may switch back and forth between one-at-a-time mode and continuous mode.
710 | * If the socket is currently in continuous mode, calling this method will switch it to one-at-a-time mode.
711 | *
712 | * When a packet is received (and not filtered by the optional receive filter),
713 | * the delegate method (udpSocket:didReceiveData:fromAddress:withFilterContext:) is invoked.
714 | *
715 | * If the socket is able to begin receiving packets, this method returns YES.
716 | * Otherwise it returns NO, and sets the errPtr with appropriate error information.
717 | *
718 | * An example error:
719 | * You created a udp socket to act as a server, and immediately called receive.
720 | * You forgot to first bind the socket to a port number, and received a error with a message like:
721 | * "Must bind socket before you can receive data."
722 | **/
723 | - (BOOL)receiveOnce:(NSError **)errPtr;
724 |
725 | /**
726 | * There are two modes of operation for receiving packets: one-at-a-time & continuous.
727 | *
728 | * In one-at-a-time mode, you call receiveOnce everytime your delegate is ready to process an incoming udp packet.
729 | * Receiving packets one-at-a-time may be better suited for implementing certain state machine code,
730 | * where your state machine may not always be ready to process incoming packets.
731 | *
732 | * In continuous mode, the delegate is invoked immediately everytime incoming udp packets are received.
733 | * Receiving packets continuously is better suited to real-time streaming applications.
734 | *
735 | * You may switch back and forth between one-at-a-time mode and continuous mode.
736 | * If the socket is currently in one-at-a-time mode, calling this method will switch it to continuous mode.
737 | *
738 | * For every received packet (not filtered by the optional receive filter),
739 | * the delegate method (udpSocket:didReceiveData:fromAddress:withFilterContext:) is invoked.
740 | *
741 | * If the socket is able to begin receiving packets, this method returns YES.
742 | * Otherwise it returns NO, and sets the errPtr with appropriate error information.
743 | *
744 | * An example error:
745 | * You created a udp socket to act as a server, and immediately called receive.
746 | * You forgot to first bind the socket to a port number, and received a error with a message like:
747 | * "Must bind socket before you can receive data."
748 | **/
749 | - (BOOL)beginReceiving:(NSError **)errPtr;
750 |
751 | /**
752 | * If the socket is currently receiving (beginReceiving has been called), this method pauses the receiving.
753 | * That is, it won't read any more packets from the underlying OS socket until beginReceiving is called again.
754 | *
755 | * Important Note:
756 | * GCDAsyncUdpSocket may be running in parallel with your code.
757 | * That is, your delegate is likely running on a separate thread/dispatch_queue.
758 | * When you invoke this method, GCDAsyncUdpSocket may have already dispatched delegate methods to be invoked.
759 | * Thus, if those delegate methods have already been dispatch_async'd,
760 | * your didReceive delegate method may still be invoked after this method has been called.
761 | * You should be aware of this, and program defensively.
762 | **/
763 | - (void)pauseReceiving;
764 |
765 | /**
766 | * You may optionally set a receive filter for the socket.
767 | * This receive filter may be set to run in its own queue (independent of delegate queue).
768 | *
769 | * A filter can provide several useful features.
770 | *
771 | * 1. Many times udp packets need to be parsed.
772 | * Since the filter can run in its own independent queue, you can parallelize this parsing quite easily.
773 | * The end result is a parallel socket io, datagram parsing, and packet processing.
774 | *
775 | * 2. Many times udp packets are discarded because they are duplicate/unneeded/unsolicited.
776 | * The filter can prevent such packets from arriving at the delegate.
777 | * And because the filter can run in its own independent queue, this doesn't slow down the delegate.
778 | *
779 | * - Since the udp protocol does not guarantee delivery, udp packets may be lost.
780 | * Many protocols built atop udp thus provide various resend/re-request algorithms.
781 | * This sometimes results in duplicate packets arriving.
782 | * A filter may allow you to architect the duplicate detection code to run in parallel to normal processing.
783 | *
784 | * - Since the udp socket may be connectionless, its possible for unsolicited packets to arrive.
785 | * Such packets need to be ignored.
786 | *
787 | * 3. Sometimes traffic shapers are needed to simulate real world environments.
788 | * A filter allows you to write custom code to simulate such environments.
789 | * The ability to code this yourself is especially helpful when your simulated environment
790 | * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router),
791 | * or the system tools to handle this aren't available (e.g. on a mobile device).
792 | *
793 | * Example:
794 | *
795 | * GCDAsyncUdpSocketReceiveFilterBlock filter = ^BOOL (NSData *data, NSData *address, id *context) {
796 | *
797 | * MyProtocolMessage *msg = [MyProtocol parseMessage:data];
798 | *
799 | * *context = response;
800 | * return (response != nil);
801 | * };
802 | * [udpSocket setReceiveFilter:filter withQueue:myParsingQueue];
803 | *
804 | * For more information about GCDAsyncUdpSocketReceiveFilterBlock, see the documentation for its typedef.
805 | * To remove a previously set filter, invoke this method and pass a nil filterBlock and NULL filterQueue.
806 | *
807 | * Note: This method invokes setReceiveFilter:withQueue:isAsynchronous: (documented below),
808 | * passing YES for the isAsynchronous parameter.
809 | **/
810 | - (void)setReceiveFilter:(nullable GCDAsyncUdpSocketReceiveFilterBlock)filterBlock withQueue:(nullable dispatch_queue_t)filterQueue;
811 |
812 | /**
813 | * The receive filter can be run via dispatch_async or dispatch_sync.
814 | * Most typical situations call for asynchronous operation.
815 | *
816 | * However, there are a few situations in which synchronous operation is preferred.
817 | * Such is the case when the filter is extremely minimal and fast.
818 | * This is because dispatch_sync is faster than dispatch_async.
819 | *
820 | * If you choose synchronous operation, be aware of possible deadlock conditions.
821 | * Since the socket queue is executing your block via dispatch_sync,
822 | * then you cannot perform any tasks which may invoke dispatch_sync on the socket queue.
823 | * For example, you can't query properties on the socket.
824 | **/
825 | - (void)setReceiveFilter:(nullable GCDAsyncUdpSocketReceiveFilterBlock)filterBlock
826 | withQueue:(nullable dispatch_queue_t)filterQueue
827 | isAsynchronous:(BOOL)isAsynchronous;
828 |
829 | #pragma mark Closing
830 |
831 | /**
832 | * Immediately closes the underlying socket.
833 | * Any pending send operations are discarded.
834 | *
835 | * The GCDAsyncUdpSocket instance may optionally be used again.
836 | * (it will setup/configure/use another unnderlying BSD socket).
837 | **/
838 | - (void)close;
839 |
840 | /**
841 | * Closes the underlying socket after all pending send operations have been sent.
842 | *
843 | * The GCDAsyncUdpSocket instance may optionally be used again.
844 | * (it will setup/configure/use another unnderlying BSD socket).
845 | **/
846 | - (void)closeAfterSending;
847 |
848 | #pragma mark Advanced
849 | /**
850 | * GCDAsyncSocket maintains thread safety by using an internal serial dispatch_queue.
851 | * In most cases, the instance creates this queue itself.
852 | * However, to allow for maximum flexibility, the internal queue may be passed in the init method.
853 | * This allows for some advanced options such as controlling socket priority via target queues.
854 | * However, when one begins to use target queues like this, they open the door to some specific deadlock issues.
855 | *
856 | * For example, imagine there are 2 queues:
857 | * dispatch_queue_t socketQueue;
858 | * dispatch_queue_t socketTargetQueue;
859 | *
860 | * If you do this (pseudo-code):
861 | * socketQueue.targetQueue = socketTargetQueue;
862 | *
863 | * Then all socketQueue operations will actually get run on the given socketTargetQueue.
864 | * This is fine and works great in most situations.
865 | * But if you run code directly from within the socketTargetQueue that accesses the socket,
866 | * you could potentially get deadlock. Imagine the following code:
867 | *
868 | * - (BOOL)socketHasSomething
869 | * {
870 | * __block BOOL result = NO;
871 | * dispatch_block_t block = ^{
872 | * result = [self someInternalMethodToBeRunOnlyOnSocketQueue];
873 | * }
874 | * if (is_executing_on_queue(socketQueue))
875 | * block();
876 | * else
877 | * dispatch_sync(socketQueue, block);
878 | *
879 | * return result;
880 | * }
881 | *
882 | * What happens if you call this method from the socketTargetQueue? The result is deadlock.
883 | * This is because the GCD API offers no mechanism to discover a queue's targetQueue.
884 | * Thus we have no idea if our socketQueue is configured with a targetQueue.
885 | * If we had this information, we could easily avoid deadlock.
886 | * But, since these API's are missing or unfeasible, you'll have to explicitly set it.
887 | *
888 | * IF you pass a socketQueue via the init method,
889 | * AND you've configured the passed socketQueue with a targetQueue,
890 | * THEN you should pass the end queue in the target hierarchy.
891 | *
892 | * For example, consider the following queue hierarchy:
893 | * socketQueue -> ipQueue -> moduleQueue
894 | *
895 | * This example demonstrates priority shaping within some server.
896 | * All incoming client connections from the same IP address are executed on the same target queue.
897 | * And all connections for a particular module are executed on the same target queue.
898 | * Thus, the priority of all networking for the entire module can be changed on the fly.
899 | * Additionally, networking traffic from a single IP cannot monopolize the module.
900 | *
901 | * Here's how you would accomplish something like that:
902 | * - (dispatch_queue_t)newSocketQueueForConnectionFromAddress:(NSData *)address onSocket:(GCDAsyncSocket *)sock
903 | * {
904 | * dispatch_queue_t socketQueue = dispatch_queue_create("", NULL);
905 | * dispatch_queue_t ipQueue = [self ipQueueForAddress:address];
906 | *
907 | * dispatch_set_target_queue(socketQueue, ipQueue);
908 | * dispatch_set_target_queue(iqQueue, moduleQueue);
909 | *
910 | * return socketQueue;
911 | * }
912 | * - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
913 | * {
914 | * [clientConnections addObject:newSocket];
915 | * [newSocket markSocketQueueTargetQueue:moduleQueue];
916 | * }
917 | *
918 | * Note: This workaround is ONLY needed if you intend to execute code directly on the ipQueue or moduleQueue.
919 | * This is often NOT the case, as such queues are used solely for execution shaping.
920 | **/
921 | - (void)markSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreConfiguredTargetQueue;
922 | - (void)unmarkSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreviouslyConfiguredTargetQueue;
923 |
924 | /**
925 | * It's not thread-safe to access certain variables from outside the socket's internal queue.
926 | *
927 | * For example, the socket file descriptor.
928 | * File descriptors are simply integers which reference an index in the per-process file table.
929 | * However, when one requests a new file descriptor (by opening a file or socket),
930 | * the file descriptor returned is guaranteed to be the lowest numbered unused descriptor.
931 | * So if we're not careful, the following could be possible:
932 | *
933 | * - Thread A invokes a method which returns the socket's file descriptor.
934 | * - The socket is closed via the socket's internal queue on thread B.
935 | * - Thread C opens a file, and subsequently receives the file descriptor that was previously the socket's FD.
936 | * - Thread A is now accessing/altering the file instead of the socket.
937 | *
938 | * In addition to this, other variables are not actually objects,
939 | * and thus cannot be retained/released or even autoreleased.
940 | * An example is the sslContext, of type SSLContextRef, which is actually a malloc'd struct.
941 | *
942 | * Although there are internal variables that make it difficult to maintain thread-safety,
943 | * it is important to provide access to these variables
944 | * to ensure this class can be used in a wide array of environments.
945 | * This method helps to accomplish this by invoking the current block on the socket's internal queue.
946 | * The methods below can be invoked from within the block to access
947 | * those generally thread-unsafe internal variables in a thread-safe manner.
948 | * The given block will be invoked synchronously on the socket's internal queue.
949 | *
950 | * If you save references to any protected variables and use them outside the block, you do so at your own peril.
951 | **/
952 | - (void)performBlock:(dispatch_block_t)block;
953 |
954 | /**
955 | * These methods are only available from within the context of a performBlock: invocation.
956 | * See the documentation for the performBlock: method above.
957 | *
958 | * Provides access to the socket's file descriptor(s).
959 | * If the socket isn't connected, or explicity bound to a particular interface,
960 | * it might actually have multiple internal socket file descriptors - one for IPv4 and one for IPv6.
961 | **/
962 | - (int)socketFD;
963 | - (int)socket4FD;
964 | - (int)socket6FD;
965 |
966 | #if TARGET_OS_IPHONE
967 |
968 | /**
969 | * These methods are only available from within the context of a performBlock: invocation.
970 | * See the documentation for the performBlock: method above.
971 | *
972 | * Returns (creating if necessary) a CFReadStream/CFWriteStream for the internal socket.
973 | *
974 | * Generally GCDAsyncUdpSocket doesn't use CFStream. (It uses the faster GCD API's.)
975 | * However, if you need one for any reason,
976 | * these methods are a convenient way to get access to a safe instance of one.
977 | **/
978 | - (nullable CFReadStreamRef)readStream;
979 | - (nullable CFWriteStreamRef)writeStream;
980 |
981 | /**
982 | * This method is only available from within the context of a performBlock: invocation.
983 | * See the documentation for the performBlock: method above.
984 | *
985 | * Configures the socket to allow it to operate when the iOS application has been backgrounded.
986 | * In other words, this method creates a read & write stream, and invokes:
987 | *
988 | * CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
989 | * CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
990 | *
991 | * Returns YES if successful, NO otherwise.
992 | *
993 | * Example usage:
994 | *
995 | * [asyncUdpSocket performBlock:^{
996 | * [asyncUdpSocket enableBackgroundingOnSocket];
997 | * }];
998 | *
999 | *
1000 | * NOTE : Apple doesn't currently support backgrounding UDP sockets. (Only TCP for now).
1001 | **/
1002 | //- (BOOL)enableBackgroundingOnSockets;
1003 |
1004 | #endif
1005 |
1006 | #pragma mark Utilities
1007 |
1008 | /**
1009 | * Extracting host/port/family information from raw address data.
1010 | **/
1011 |
1012 | + (nullable NSString *)hostFromAddress:(NSData *)address;
1013 | + (uint16_t)portFromAddress:(NSData *)address;
1014 | + (int)familyFromAddress:(NSData *)address;
1015 |
1016 | + (BOOL)isIPv4Address:(NSData *)address;
1017 | + (BOOL)isIPv6Address:(NSData *)address;
1018 |
1019 | + (BOOL)getHost:(NSString * __nullable * __nullable)hostPtr port:(uint16_t * __nullable)portPtr fromAddress:(NSData *)address;
1020 | + (BOOL)getHost:(NSString * __nullable * __nullable)hostPtr port:(uint16_t * __nullable)portPtr family:(int * __nullable)afPtr fromAddress:(NSData *)address;
1021 |
1022 | @end
1023 |
1024 | NS_ASSUME_NONNULL_END
1025 |
--------------------------------------------------------------------------------
/iOS_GetUDID_Demo/Server/SFWebServer/SFWebServer.h:
--------------------------------------------------------------------------------
1 | //
2 | // SFWebServer.h
3 | // SFWebServer
4 | //
5 | // Created by www.skyfox.org on 2017/6/19.
6 | // Copyright © 2017年 Jakey. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "SFWebServerRouter.h"
11 | #import "SFWebServerRequest.h"
12 | #import "SFWebServerRespone.h"
13 | #import "GCDAsyncSocket.h"
14 | @interface SFWebServer : NSObject
15 | @property(nonatomic, readonly) NSUInteger port;
16 | @property(nonatomic, readonly) NSString *host;
17 | @property(nonatomic, readonly) NSString *address;
18 |
19 |
20 | - (instancetype)init __attribute__((unavailable("Forbidden use init!")));
21 | + (SFWebServer*)shared;
22 |
23 |
24 | + (instancetype)startWithPort:(NSInteger)port;
25 |
26 | - (void)router:(NSString*)method basePath:(NSString*)basePath handler:(SFWebServerRouterHandler)handler;
27 |
28 | - (void)router:(NSString*)method path:(NSString*)path handler:(SFWebServerRouterHandler)handler;
29 |
30 | - (void)router:(NSString*)method filename:(NSString*)filename handler:(SFWebServerRouterHandler)handler;
31 |
32 | - (void)router:(NSString*)method extension:(NSString*)extension handler:(SFWebServerRouterHandler)handler;
33 | @end
34 |
--------------------------------------------------------------------------------
/iOS_GetUDID_Demo/Server/SFWebServer/SFWebServer.m:
--------------------------------------------------------------------------------
1 | //
2 | // SFWebServer.m
3 | // SFWebServer
4 | //
5 | // Created by www.skyfox.org on 2017/6/19.
6 | // Copyright © 2017年 Jakey. All rights reserved.
7 | //
8 |
9 | #import "SFWebServer.h"
10 | #import
11 | @interface SFWebServer()
12 | {
13 | GCDAsyncSocket *_server;
14 | NSMutableArray *_routers;
15 | }
16 | @end
17 | @implementation SFWebServer
18 | + (SFWebServer*)shared
19 | {
20 | static SFWebServer *shared = nil;
21 | static dispatch_once_t onceToken;
22 | dispatch_once(&onceToken, ^{
23 | shared = [[self alloc] init];
24 | });
25 | return shared;
26 | }
27 | - (NSDictionary*)getTableData{
28 | return nil;
29 | }
30 | + (instancetype)startWithPort:(NSInteger)port{
31 | SFWebServer *server = [[SFWebServer shared] initWithPort:port];
32 | [server startServer];
33 | return server;
34 | }
35 |
36 | - (BOOL)startServer{
37 | _server = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
38 | NSError *error = nil;
39 | if ([_server acceptOnPort:self.port error:&error]) {
40 | NSLog(@"server start on %@:%zd",_server.localHost,_server.localPort);
41 | _host = _server.localHost;
42 | return YES;
43 | }else{
44 | NSLog(@"error %@",error);
45 | return NO;
46 | }
47 | return NO;
48 | }
49 |
50 | - (instancetype)initWithPort:(NSInteger)port{
51 | self = [super init];
52 | if (self) {
53 | _port = port;
54 | }
55 | return self;
56 | }
57 | -(NSString *)address{
58 | return [NSString stringWithFormat:@"http://%@:%zd",[SFWebServer shared].host,[SFWebServer shared].port];
59 | }
60 |
61 | - (void)router:(NSString*)method basePath:(NSString*)basePath handler:(SFWebServerRouterHandler)handler{
62 | [self _router:method path:basePath type:@"basepath" handler:handler];
63 | }
64 | - (void)router:(NSString*)method path:(NSString*)path handler:(SFWebServerRouterHandler)handler{
65 | [self _router:method path:path type:@"url" handler:handler];
66 | }
67 | - (void)router:(NSString*)method filename:(NSString*)filename handler:(SFWebServerRouterHandler)handler{
68 | NSString *path = [[NSBundle mainBundle]pathForResource:filename ofType:[filename pathExtension]];
69 | [self _router:method path:path type:@"url" handler:handler];
70 | }
71 | - (void)router:(NSString*)method extension:(NSString*)extension handler:(SFWebServerRouterHandler)handler{
72 | [self _router:method path:extension type:@"extension" handler:handler];
73 | }
74 |
75 | - (void)_router:(NSString*)method path:(NSString*)path type:(NSString*)type handler:(SFWebServerRouterHandler)handler{
76 | SFWebServerRouter *router = [[SFWebServerRouter alloc]init];
77 | router.method = method;
78 | router.path = path?:@"/";
79 | router.handler = handler;
80 | router.type = type?:@"url";
81 | if (!_routers) {
82 | _routers = [NSMutableArray array];
83 | }
84 | [_routers addObject:router];
85 | }
86 |
87 | -(void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket{
88 | NSLog(@"didAcceptNewSocket");
89 | NSLog(@"newSocket %@ %@ %zd",newSocket.userData,newSocket.localHost,newSocket.localPort);
90 |
91 |
92 | // NSMutableString *serverStr = [NSMutableString string];
93 | // [serverStr appendString:@"connect\n"];
94 | // [newSocket writeData:[serverStr dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];
95 | [newSocket readDataWithTimeout:-1 tag:0];
96 | }
97 | - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{
98 | NSLog(@"didReadData");
99 | NSLog(@"sock %@ %@ %zd",sock.userData,sock.localHost,sock.localPort);
100 |
101 | SFWebServerRequest *request = [[SFWebServerRequest alloc]initWithData:data];
102 |
103 | BOOL found = NO;
104 | for (SFWebServerRouter *router in _routers)
105 | {
106 | if ([router.method isEqualToString:request.method] && router.handler)
107 | {
108 | if ([router.type isEqualToString:@"url"])
109 | {
110 | if ([router.path isEqualToString:request.path]) {
111 | SFWebServerRespone *respone = router.handler(request);
112 | [sock writeData:respone.data withTimeout:-1 tag:0];
113 | found = YES;
114 | }
115 | }
116 | else if ([router.type isEqualToString:@"extension"]) {
117 | if ([request.path hasSuffix:router.path]) {
118 | SFWebServerRespone *respone = router.handler(request);
119 | [sock writeData:respone.data withTimeout:-1 tag:0];
120 | found = YES;
121 | }
122 | }
123 | else if ([router.type isEqualToString:@"basepath"]) {
124 | if ([request.path hasPrefix:router.path]) {
125 | SFWebServerRespone *respone = router.handler(request);
126 | [sock writeData:respone.data withTimeout:-1 tag:0];
127 | found = YES;
128 | }
129 | }
130 | }
131 | }
132 | if (!found) {
133 | NSLog(@"request.path %@ 404",request.path);
134 | SFWebServerRespone *respone = [[SFWebServerRespone alloc]initWithHTML:@"401"];
135 | respone.statusCode = 404;
136 | [sock writeData:respone.data withTimeout:-1 tag:0];
137 | }
138 | }
139 | - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
140 | {
141 |
142 | }
143 | @end
144 |
--------------------------------------------------------------------------------
/iOS_GetUDID_Demo/Server/SFWebServer/SFWebServerRequest.h:
--------------------------------------------------------------------------------
1 | //
2 | // SFWebServerRequest.h
3 | // SFWebServer
4 | //
5 | // Created by www.skyfox.org on 2017/6/19.
6 | // Copyright © 2017年 Jakey. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface SFWebServerRequest : NSObject
12 | @property (nonatomic,copy) NSMutableDictionary *headers;
13 | @property (nonatomic,copy) NSString *headerString;
14 | @property (nonatomic,copy) NSString *method;
15 | @property (nonatomic,copy) NSString *path;
16 | @property (nonatomic,copy) NSString *body;
17 | @property (nonatomic,strong) NSData *rawData;
18 | @property (nonatomic,copy) NSString *HTTPVersion;
19 | - (instancetype)initWithData:(NSData*)data;
20 | @end
21 |
--------------------------------------------------------------------------------
/iOS_GetUDID_Demo/Server/SFWebServer/SFWebServerRequest.m:
--------------------------------------------------------------------------------
1 | //
2 | // SFWebServerRequest.m
3 | // SFWebServer
4 | //
5 | // Created by www.skyfox.org on 2017/6/19.
6 | // Copyright © 2017年 Jakey. All rights reserved.
7 | //
8 |
9 | #import "SFWebServerRequest.h"
10 |
11 | @implementation SFWebServerRequest
12 | - (instancetype)initWithData:(NSData*)data{
13 | self = [super init];
14 | if (self) {
15 | NSString *string = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
16 | if (!string) {
17 | string = [[NSString alloc]initWithData:data encoding:NSISOLatin1StringEncoding];
18 |
19 | }
20 | self.headerString = string;
21 | NSArray *headerArray = [string componentsSeparatedByString:@"\r\n"];
22 | NSString *info = [headerArray firstObject];
23 |
24 | _headers = [NSMutableDictionary dictionary];
25 | if (info) {
26 | NSArray *infoArray = [info componentsSeparatedByString:@" "];
27 | if ([infoArray count] == 3)
28 | {
29 | _method = infoArray[0];
30 | _path = infoArray[1];
31 | _HTTPVersion = infoArray[2];
32 | }
33 | for (int i = 1; i<[headerArray count]; i++) {
34 | NSString *h = [headerArray objectAtIndex:i];
35 | NSString *key = [[h componentsSeparatedByString:@": "] firstObject];
36 | NSString *value = [[h componentsSeparatedByString:@": "] lastObject];
37 | _headers[key] = value;
38 | }
39 | }
40 | if (!string) {
41 | _method = @"GET";
42 | _path = @"/";
43 | _HTTPVersion = @"HTTP/1.1";
44 | }
45 | self.rawData = data;
46 | NSLog(@"SFWebServerRequest path:%@",_path);
47 | }
48 | return self;
49 | }
50 |
51 | @end
52 |
--------------------------------------------------------------------------------
/iOS_GetUDID_Demo/Server/SFWebServer/SFWebServerRespone.h:
--------------------------------------------------------------------------------
1 | //
2 | // SFWebServerRespone.h
3 | // SFWebServer
4 | //
5 | // Created by www.skyfox.org on 2017/6/19.
6 | // Copyright © 2017年 Jakey. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface SFWebServerRespone : NSObject
12 | @property (nonatomic,copy) NSString *html;
13 | @property (nonatomic,strong) NSData *htmlData;
14 | @property (nonatomic,assign) NSInteger statusCode;
15 | @property (nonatomic,strong) NSData *data;
16 | @property (nonatomic,copy) NSString *contentType;
17 | @property (nonatomic,copy) NSString *location;
18 |
19 | @property (nonatomic,assign) NSUInteger contentLength;
20 | @property (nonatomic,copy) NSString *fileName;
21 |
22 | - (instancetype)initWithHTML:(NSString*)html;
23 | - (instancetype)initWithHTMLData:(NSData*)htmlData;
24 | - (instancetype)initWithFile:(NSString*)filePath;
25 | - (instancetype)initWithFileName:(NSString*)fileName;
26 | @end
27 |
--------------------------------------------------------------------------------
/iOS_GetUDID_Demo/Server/SFWebServer/SFWebServerRespone.m:
--------------------------------------------------------------------------------
1 | //
2 | // SFWebServerRespone.m
3 | // SFWebServer
4 | //
5 | // Created by www.skyfox.org on 2017/6/19.
6 | // Copyright © 2017年 Jakey. All rights reserved.
7 | //
8 |
9 | #import "SFWebServerRespone.h"
10 |
11 | @implementation SFWebServerRespone
12 | - (instancetype)init
13 | {
14 | self = [super init];
15 | if (self) {
16 | _statusCode = 200;
17 | }
18 | return self;
19 | }
20 | - (instancetype)initWithHTML:(NSString*)html{
21 | self = [super init];
22 | if (self) {
23 | _html = html;
24 | _statusCode = 200;
25 | }
26 | return self;
27 | }
28 | - (instancetype)initWithHTMLData:(NSData*)htmlData{
29 | self = [super init];
30 | if (self) {
31 | _htmlData = htmlData;
32 | _statusCode = 200;
33 | }
34 | return self;
35 | }
36 | - (instancetype)initWithFileName:(NSString*)fileName{
37 | self = [super init];
38 | if (self) {
39 | _htmlData = [NSData dataWithContentsOfFile:[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:fileName]];
40 | _contentType = [[self class] detectMimeType:fileName];
41 | _statusCode = 200;
42 | }
43 | return self;
44 | }
45 | - (instancetype)initWithFile:(NSString*)filePath{
46 | self = [super init];
47 | if (self) {
48 | _htmlData = [NSData dataWithContentsOfFile:filePath];
49 | _contentType = [[self class] detectMimeType:filePath];
50 | _statusCode = 200;
51 | }
52 | return self;
53 | }
54 | //static inline NSString* _EscapeHTMLString(NSString* string) {
55 | // return [string stringByReplacingOccurrencesOfString:@"\"" withString:@"""];
56 | //}
57 | //- (instancetype)initWithStatusCode:(NSInteger)statusCode underlyingError:(NSError*)underlyingError messageFormat:(NSString*)format arguments:(va_list)arguments {
58 | // NSString* message = [[NSString alloc] initWithFormat:format arguments:arguments];
59 | // NSString* title = [NSString stringWithFormat:@"HTTP Error %i", (int)statusCode];
60 | // NSString* error = underlyingError ? [NSString stringWithFormat:@"[%@] %@ (%li)", underlyingError.domain, _EscapeHTMLString(underlyingError.localizedDescription), (long)underlyingError.code] : @"";
61 | // NSString* html = [NSString stringWithFormat:@"%@ %@: %@ %@ ",
62 | // title, title, _EscapeHTMLString(message), error];
63 | // if ((self = [self initWithHTML:html])) {
64 | // self.statusCode = statusCode;
65 | // }
66 | // return self;
67 | //}
68 | -(NSData *)data{
69 |
70 | // _htmlData?:[_html dataUsingEncoding:NSUTF8StringEncoding]
71 | if (self.htmlData) {
72 | self.html = [[NSString alloc]initWithData:_htmlData encoding:NSUTF8StringEncoding];
73 | self.contentLength = self.htmlData.length;
74 | }else{
75 | NSData *data = [_html dataUsingEncoding:NSUTF8StringEncoding];
76 | self.contentLength = data.length;
77 | }
78 | if (self.fileName) {
79 | NSMutableString *res = [NSMutableString string];
80 | [res appendString:[NSString stringWithFormat:@"HTTP/1.0 %zd\n",_statusCode]];
81 | [res appendString:@"Accept-Ranges:bytes\n"];
82 | [res appendString:[NSString stringWithFormat:@"Content-Type: %@; charset=UTF-8\n",self.contentType?:@"text/html"]];
83 | [res appendString:[NSString stringWithFormat:@"Content-Length:%zd\n",self.contentLength]];
84 | [res appendString:[NSString stringWithFormat:@"Content-Disposition: attachment; filename=%@\n",self.fileName]];
85 | [res appendString:@"\n"];
86 |
87 | NSMutableData *r = [[res dataUsingEncoding:NSUTF8StringEncoding] mutableCopy];
88 | [r appendData:_htmlData];
89 | return r;
90 | }else{
91 | NSMutableString *res = [NSMutableString string];
92 | [res appendString:[NSString stringWithFormat:@"HTTP/1.0 %zd\n",_statusCode]];
93 | [res appendString:@"Accept-Ranges:bytes\n"];
94 | [res appendString:[NSString stringWithFormat:@"Content-Type: %@; charset=UTF-8\n",self.contentType?:@"text/html"]];
95 | [res appendString:[NSString stringWithFormat:@"Content-Length:%zd\n",self.contentLength]];
96 | if (self.location) {
97 | [res appendString:[NSString stringWithFormat:@"Location:%@\n",self.location]];
98 | }
99 | [res appendString:@"\n"];
100 | [res appendString:self.html?:@""];
101 |
102 | return [res dataUsingEncoding:NSUTF8StringEncoding];
103 |
104 | }
105 | }
106 | + (NSString*)detectMimeType:(NSString *)fileName{
107 | if (fileName.length==0) {
108 | return nil;
109 | } else if ([fileName hasSuffix:@".html"]) {
110 | return @"text/html";
111 | } else if ([fileName hasSuffix:@".js"]) {
112 | return @"application/javascript";
113 | } else if ([fileName hasSuffix:@".css"]) {
114 | return @"text/css";
115 | } else {
116 | return @"application/octet-stream";
117 | }
118 | }
119 | @end
120 |
--------------------------------------------------------------------------------
/iOS_GetUDID_Demo/Server/SFWebServer/SFWebServerRouter.h:
--------------------------------------------------------------------------------
1 | //
2 | // SFWebServerRouter.h
3 | // SFWebServer
4 | //
5 | // Created by www.skyfox.org on 2017/6/19.
6 | // Copyright © 2017年 Jakey. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #import "SFWebServerRequest.h"
12 | #import "SFWebServerRespone.h"
13 |
14 |
15 | typedef SFWebServerRespone* (^SFWebServerRouterHandler)(SFWebServerRequest *request);
16 |
17 | @interface SFWebServerRouter : NSObject
18 | @property (nonatomic,copy) NSString *type;
19 | @property (nonatomic,copy) NSString *method;
20 | @property (nonatomic,copy) NSString *path;
21 | @property (nonatomic,copy) SFWebServerRouterHandler handler;
22 | @end
23 |
--------------------------------------------------------------------------------
/iOS_GetUDID_Demo/Server/SFWebServer/SFWebServerRouter.m:
--------------------------------------------------------------------------------
1 | //
2 | // SFWebServerRouter.m
3 | // SFWebServer
4 | //
5 | // Created by www.skyfox.org on 2017/6/19.
6 | // Copyright © 2017年 Jakey. All rights reserved.
7 | //
8 |
9 | #import "SFWebServerRouter.h"
10 |
11 | @implementation SFWebServerRouter
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/iOS_GetUDID_Demo/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // iOS_GetUDID_Demo
4 | //
5 | // Created by 黄云碧 on 2019/3/10.
6 | // Copyright © 2019 黄云碧. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : UIViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/iOS_GetUDID_Demo/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // iOS_GetUDID_Demo
4 | //
5 | // Created by 黄云碧 on 2019/3/10.
6 | // Copyright © 2019 黄云碧. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 |
11 | @interface ViewController ()
12 |
13 | @end
14 |
15 | @implementation ViewController
16 |
17 | - (void)viewDidLoad {
18 | [super viewDidLoad];
19 | // Do any additional setup after loading the view, typically from a nib.
20 | }
21 |
22 | - (IBAction)getUDID:(id)sender {
23 |
24 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"http://127.0.0.1:6699/udid.get"]];
25 |
26 | }
27 |
28 | @end
29 |
--------------------------------------------------------------------------------
/iOS_GetUDID_Demo/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // iOS_GetUDID_Demo
4 | //
5 | // Created by 黄云碧 on 2019/3/10.
6 | // Copyright © 2019 黄云碧. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/iOS_GetUDID_Demo/udid.mobileconfig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PayloadContent
6 |
7 | URL
8 | http://127.0.0.1:6699/receive.do
9 | DeviceAttributes
10 |
11 | SERIAL
12 | MAC_ADDRESS_EN0
13 | UDID
14 | IMEI
15 | ICCID
16 | VERSION
17 | PRODUCT
18 |
19 |
20 | PayloadOrganization
21 | dev.skyfox.org
22 | PayloadDisplayName
23 | 查询设备UDID
24 | PayloadVersion
25 | 1
26 | PayloadUUID
27 | 3C4DC7D2-E475-3375-489C-0BB8D737A653
28 | PayloadIdentifier
29 | cn.com.aaaa
30 | PayloadDescription
31 | 本文件仅用来获取设备ID
32 | PayloadType
33 | Profile Service
34 |
35 |
36 |
--------------------------------------------------------------------------------