├── .gitignore ├── AQSocket.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── AQSocket ├── AQSocket-Prefix.pch ├── AQSocket.h ├── AQSocket.m ├── AQSocketIOChannel.h ├── AQSocketIOChannel.m ├── AQSocketReader+PrivateInternal.h ├── AQSocketReader.h └── AQSocketReader.m ├── AppledocSettings.plist ├── LICENSE ├── README.markdown ├── Templates ├── docset │ └── Contents │ │ ├── Resources │ │ ├── Documents │ │ │ └── documents-template │ │ ├── nodes-template.xml │ │ └── tokens-template.xml │ │ └── info-template.plist └── html │ ├── css │ └── styles.css │ ├── document-template.html │ ├── hierarchy-template.html │ ├── img │ ├── button_bar_background.png │ ├── disclosure.png │ ├── disclosure_open.png │ └── title_background.png │ ├── index-template.html │ └── object-template.html └── build-docset.sh /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | AQSocket.xcodeproj/xcuserdata 3 | AQSocket.xcodeproj/project.xcworkspace/xcuserdata -------------------------------------------------------------------------------- /AQSocket.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 38634F3D15473DBD007DA652 /* AQSocketIOChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = 38634F3B15473DBD007DA652 /* AQSocketIOChannel.h */; }; 11 | 38634F3E15473DBD007DA652 /* AQSocketIOChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = 38634F3C15473DBD007DA652 /* AQSocketIOChannel.m */; }; 12 | 3888F2B2149ED04E00DC886C /* AQSocketReader+PrivateInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 3888F2B1149ED04E00DC886C /* AQSocketReader+PrivateInternal.h */; }; 13 | 38FFA31D149BCAB800B0D4D0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 38FFA31C149BCAB800B0D4D0 /* Foundation.framework */; }; 14 | 38FFA323149BCAB800B0D4D0 /* AQSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 38FFA322149BCAB800B0D4D0 /* AQSocket.m */; }; 15 | 38FFA32B149BD9CF00B0D4D0 /* AQSocketReader.h in Headers */ = {isa = PBXBuildFile; fileRef = 38FFA329149BD9CF00B0D4D0 /* AQSocketReader.h */; }; 16 | 38FFA32C149BD9CF00B0D4D0 /* AQSocketReader.m in Sources */ = {isa = PBXBuildFile; fileRef = 38FFA32A149BD9CF00B0D4D0 /* AQSocketReader.m */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 38634F3B15473DBD007DA652 /* AQSocketIOChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AQSocketIOChannel.h; sourceTree = ""; }; 21 | 38634F3C15473DBD007DA652 /* AQSocketIOChannel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AQSocketIOChannel.m; sourceTree = ""; }; 22 | 3888F2B1149ED04E00DC886C /* AQSocketReader+PrivateInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "AQSocketReader+PrivateInternal.h"; sourceTree = ""; }; 23 | 38FFA319149BCAB800B0D4D0 /* libAQSocket.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libAQSocket.a; sourceTree = BUILT_PRODUCTS_DIR; }; 24 | 38FFA31C149BCAB800B0D4D0 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 25 | 38FFA320149BCAB800B0D4D0 /* AQSocket-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AQSocket-Prefix.pch"; sourceTree = ""; }; 26 | 38FFA321149BCAB800B0D4D0 /* AQSocket.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AQSocket.h; sourceTree = ""; }; 27 | 38FFA322149BCAB800B0D4D0 /* AQSocket.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AQSocket.m; sourceTree = ""; }; 28 | 38FFA329149BD9CF00B0D4D0 /* AQSocketReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AQSocketReader.h; sourceTree = ""; }; 29 | 38FFA32A149BD9CF00B0D4D0 /* AQSocketReader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AQSocketReader.m; sourceTree = ""; }; 30 | /* End PBXFileReference section */ 31 | 32 | /* Begin PBXFrameworksBuildPhase section */ 33 | 38FFA316149BCAB800B0D4D0 /* Frameworks */ = { 34 | isa = PBXFrameworksBuildPhase; 35 | buildActionMask = 2147483647; 36 | files = ( 37 | 38FFA31D149BCAB800B0D4D0 /* Foundation.framework in Frameworks */, 38 | ); 39 | runOnlyForDeploymentPostprocessing = 0; 40 | }; 41 | /* End PBXFrameworksBuildPhase section */ 42 | 43 | /* Begin PBXGroup section */ 44 | 38FFA30E149BCAB800B0D4D0 = { 45 | isa = PBXGroup; 46 | children = ( 47 | 38FFA31E149BCAB800B0D4D0 /* AQSocket */, 48 | 38FFA31B149BCAB800B0D4D0 /* Frameworks */, 49 | 38FFA31A149BCAB800B0D4D0 /* Products */, 50 | ); 51 | sourceTree = ""; 52 | }; 53 | 38FFA31A149BCAB800B0D4D0 /* Products */ = { 54 | isa = PBXGroup; 55 | children = ( 56 | 38FFA319149BCAB800B0D4D0 /* libAQSocket.a */, 57 | ); 58 | name = Products; 59 | sourceTree = ""; 60 | }; 61 | 38FFA31B149BCAB800B0D4D0 /* Frameworks */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | 38FFA31C149BCAB800B0D4D0 /* Foundation.framework */, 65 | ); 66 | name = Frameworks; 67 | sourceTree = ""; 68 | }; 69 | 38FFA31E149BCAB800B0D4D0 /* AQSocket */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | 38FFA321149BCAB800B0D4D0 /* AQSocket.h */, 73 | 38FFA322149BCAB800B0D4D0 /* AQSocket.m */, 74 | 38FFA329149BD9CF00B0D4D0 /* AQSocketReader.h */, 75 | 38FFA32A149BD9CF00B0D4D0 /* AQSocketReader.m */, 76 | 3888F2B1149ED04E00DC886C /* AQSocketReader+PrivateInternal.h */, 77 | 38634F3B15473DBD007DA652 /* AQSocketIOChannel.h */, 78 | 38634F3C15473DBD007DA652 /* AQSocketIOChannel.m */, 79 | 38FFA31F149BCAB800B0D4D0 /* Supporting Files */, 80 | ); 81 | path = AQSocket; 82 | sourceTree = ""; 83 | }; 84 | 38FFA31F149BCAB800B0D4D0 /* Supporting Files */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | 38FFA320149BCAB800B0D4D0 /* AQSocket-Prefix.pch */, 88 | ); 89 | name = "Supporting Files"; 90 | sourceTree = ""; 91 | }; 92 | /* End PBXGroup section */ 93 | 94 | /* Begin PBXHeadersBuildPhase section */ 95 | 38FFA317149BCAB800B0D4D0 /* Headers */ = { 96 | isa = PBXHeadersBuildPhase; 97 | buildActionMask = 2147483647; 98 | files = ( 99 | 38FFA32B149BD9CF00B0D4D0 /* AQSocketReader.h in Headers */, 100 | 3888F2B2149ED04E00DC886C /* AQSocketReader+PrivateInternal.h in Headers */, 101 | 38634F3D15473DBD007DA652 /* AQSocketIOChannel.h in Headers */, 102 | ); 103 | runOnlyForDeploymentPostprocessing = 0; 104 | }; 105 | /* End PBXHeadersBuildPhase section */ 106 | 107 | /* Begin PBXNativeTarget section */ 108 | 38FFA318149BCAB800B0D4D0 /* AQSocket */ = { 109 | isa = PBXNativeTarget; 110 | buildConfigurationList = 38FFA326149BCAB800B0D4D0 /* Build configuration list for PBXNativeTarget "AQSocket" */; 111 | buildPhases = ( 112 | 38FFA315149BCAB800B0D4D0 /* Sources */, 113 | 38FFA316149BCAB800B0D4D0 /* Frameworks */, 114 | 38FFA317149BCAB800B0D4D0 /* Headers */, 115 | ); 116 | buildRules = ( 117 | ); 118 | dependencies = ( 119 | ); 120 | name = AQSocket; 121 | productName = AQSocket; 122 | productReference = 38FFA319149BCAB800B0D4D0 /* libAQSocket.a */; 123 | productType = "com.apple.product-type.library.static"; 124 | }; 125 | /* End PBXNativeTarget section */ 126 | 127 | /* Begin PBXProject section */ 128 | 38FFA310149BCAB800B0D4D0 /* Project object */ = { 129 | isa = PBXProject; 130 | attributes = { 131 | LastUpgradeCheck = 0440; 132 | ORGANIZATIONNAME = "Jim Dovey"; 133 | }; 134 | buildConfigurationList = 38FFA313149BCAB800B0D4D0 /* Build configuration list for PBXProject "AQSocket" */; 135 | compatibilityVersion = "Xcode 3.2"; 136 | developmentRegion = English; 137 | hasScannedForEncodings = 0; 138 | knownRegions = ( 139 | en, 140 | ); 141 | mainGroup = 38FFA30E149BCAB800B0D4D0; 142 | productRefGroup = 38FFA31A149BCAB800B0D4D0 /* Products */; 143 | projectDirPath = ""; 144 | projectRoot = ""; 145 | targets = ( 146 | 38FFA318149BCAB800B0D4D0 /* AQSocket */, 147 | ); 148 | }; 149 | /* End PBXProject section */ 150 | 151 | /* Begin PBXSourcesBuildPhase section */ 152 | 38FFA315149BCAB800B0D4D0 /* Sources */ = { 153 | isa = PBXSourcesBuildPhase; 154 | buildActionMask = 2147483647; 155 | files = ( 156 | 38FFA323149BCAB800B0D4D0 /* AQSocket.m in Sources */, 157 | 38FFA32C149BD9CF00B0D4D0 /* AQSocketReader.m in Sources */, 158 | 38634F3E15473DBD007DA652 /* AQSocketIOChannel.m in Sources */, 159 | ); 160 | runOnlyForDeploymentPostprocessing = 0; 161 | }; 162 | /* End PBXSourcesBuildPhase section */ 163 | 164 | /* Begin XCBuildConfiguration section */ 165 | 38FFA324149BCAB800B0D4D0 /* Debug */ = { 166 | isa = XCBuildConfiguration; 167 | buildSettings = { 168 | ALWAYS_SEARCH_USER_PATHS = NO; 169 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 170 | CLANG_ENABLE_OBJC_ARC = YES; 171 | COPY_PHASE_STRIP = NO; 172 | GCC_C_LANGUAGE_STANDARD = gnu99; 173 | GCC_DYNAMIC_NO_PIC = NO; 174 | GCC_OPTIMIZATION_LEVEL = 0; 175 | GCC_PREPROCESSOR_DEFINITIONS = ( 176 | "DEBUG=1", 177 | "$(inherited)", 178 | ); 179 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 180 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 181 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 182 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 183 | GCC_WARN_UNUSED_VARIABLE = YES; 184 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 185 | SDKROOT = iphoneos; 186 | }; 187 | name = Debug; 188 | }; 189 | 38FFA325149BCAB800B0D4D0 /* Release */ = { 190 | isa = XCBuildConfiguration; 191 | buildSettings = { 192 | ALWAYS_SEARCH_USER_PATHS = NO; 193 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 194 | CLANG_ENABLE_OBJC_ARC = YES; 195 | COPY_PHASE_STRIP = YES; 196 | GCC_C_LANGUAGE_STANDARD = gnu99; 197 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 198 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 199 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 200 | GCC_WARN_UNUSED_VARIABLE = YES; 201 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 202 | SDKROOT = iphoneos; 203 | VALIDATE_PRODUCT = YES; 204 | }; 205 | name = Release; 206 | }; 207 | 38FFA327149BCAB800B0D4D0 /* Debug */ = { 208 | isa = XCBuildConfiguration; 209 | buildSettings = { 210 | DSTROOT = /tmp/AQSocket.dst; 211 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 212 | GCC_PREFIX_HEADER = "AQSocket/AQSocket-Prefix.pch"; 213 | OTHER_LDFLAGS = "-ObjC"; 214 | PRODUCT_NAME = "$(TARGET_NAME)"; 215 | SKIP_INSTALL = YES; 216 | }; 217 | name = Debug; 218 | }; 219 | 38FFA328149BCAB800B0D4D0 /* Release */ = { 220 | isa = XCBuildConfiguration; 221 | buildSettings = { 222 | DSTROOT = /tmp/AQSocket.dst; 223 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 224 | GCC_PREFIX_HEADER = "AQSocket/AQSocket-Prefix.pch"; 225 | OTHER_LDFLAGS = "-ObjC"; 226 | PRODUCT_NAME = "$(TARGET_NAME)"; 227 | SKIP_INSTALL = YES; 228 | }; 229 | name = Release; 230 | }; 231 | /* End XCBuildConfiguration section */ 232 | 233 | /* Begin XCConfigurationList section */ 234 | 38FFA313149BCAB800B0D4D0 /* Build configuration list for PBXProject "AQSocket" */ = { 235 | isa = XCConfigurationList; 236 | buildConfigurations = ( 237 | 38FFA324149BCAB800B0D4D0 /* Debug */, 238 | 38FFA325149BCAB800B0D4D0 /* Release */, 239 | ); 240 | defaultConfigurationIsVisible = 0; 241 | defaultConfigurationName = Release; 242 | }; 243 | 38FFA326149BCAB800B0D4D0 /* Build configuration list for PBXNativeTarget "AQSocket" */ = { 244 | isa = XCConfigurationList; 245 | buildConfigurations = ( 246 | 38FFA327149BCAB800B0D4D0 /* Debug */, 247 | 38FFA328149BCAB800B0D4D0 /* Release */, 248 | ); 249 | defaultConfigurationIsVisible = 0; 250 | defaultConfigurationName = Release; 251 | }; 252 | /* End XCConfigurationList section */ 253 | }; 254 | rootObject = 38FFA310149BCAB800B0D4D0 /* Project object */; 255 | } 256 | -------------------------------------------------------------------------------- /AQSocket.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AQSocket/AQSocket-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'AQSocket' target in the 'AQSocket' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | 7 | # import 8 | 9 | # define USING_MRR (!__has_feature(objc_arc)) 10 | 11 | # if defined(OS_OBJECT_USE_OBJC) && OS_OBJECT_USE_OBJC 12 | # define DISPATCH_USES_ARC !USING_MRR 13 | # else 14 | # define DISPATCH_USES_ARC 0 15 | # endif 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /AQSocket/AQSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // AQSocket.h 3 | // AQSocket 4 | // 5 | // Created by Jim Dovey on 11-12-16. 6 | // Copyright (c) 2011 Jim Dovey Inc. All rights reserved. 7 | // 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions 10 | // are met: 11 | // 12 | // Redistributions of source code must retain the above copyright notice, 13 | // this list of conditions and the following disclaimer. 14 | // 15 | // Redistributions in binary form must reproduce the above copyright 16 | // notice, this list of conditions and the following disclaimer in the 17 | // documentation and/or other materials provided with the distribution. 18 | // 19 | // Neither the name of the project's author nor the names of its 20 | // contributors may be used to endorse or promote products derived from 21 | // this software without specific prior written permission. 22 | // 23 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29 | // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31 | // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | // 35 | 36 | #import 37 | #import 38 | 39 | // A special reader class where you can check and opt not to read some input 40 | // until *n* bytes are actually available. If you leave some bytes unread, 41 | // the next time data arrives on the socket those bytes will still be available 42 | // to read from the new AQSocketReader instance. 43 | @class AQSocketReader; 44 | 45 | typedef enum 46 | { 47 | AQSocketEventConnected, /// info == nil 48 | AQSocketEventDisconnected, /// info == nil 49 | AQSocketEventConnectionFailed, /// info == NSError 50 | AQSocketEventAcceptedNewConnection, /// info == new accepted AQSocket 51 | AQSocketEventDataAvailable, /// info == AQSocketReader 52 | AQSocketErrorEncountered, /// info == NSError 53 | 54 | } AQSocketEvent; 55 | 56 | typedef enum 57 | { 58 | AQSocketUnconnected, /// The socket is ready to connect but none has yet been initiated. 59 | AQSocketConnecting, /// The socket is waiting for a connection to complete. 60 | AQSocketListening, /// The socket is bound to a local address/port and is waiting to accept new connections. 61 | AQSocketConnected, /// The socket has successfully connected to a remote host and can be used to send and receive data. 62 | AQSocketDisconnected /// The socket has disconnected and is no longer usable. 63 | 64 | } AQSocketStatus; 65 | 66 | /** A block-based event handler. This is called for connection events (for both 67 | client and server-side sockets) and for incoming data. Note that it does NOT 68 | advertise writability-- writing happens via a separate, sequenced, 69 | complete-write API. 70 | @param event The event which prompted this callback. 71 | @param info An object containing more information. See AQSocketEvent for information on the info parameter's type for each event. 72 | */ 73 | typedef void (^AQSocketEventHandler)(AQSocketEvent event, id info); 74 | 75 | @interface AQSocket : NSObject 76 | 77 | /** @name Initializers */ 78 | 79 | /** The designated initializer. This will always assume the user wants IPv6 or 80 | IPv4 protocol sockets, the latter provided by an 6to4 wrapper. The caller can 81 | specify SOCK_STREAM to get a TCP socket or SOCK_DGRAM to use UDP. A destination 82 | address is not required at this point-- that is provided to one of the connect 83 | methods specified below, such as connectToAddress:error:. 84 | @param type A socket type, i.e. SOCK_STREAM for TCP or SOCK_DGRAM for UDP. 85 | @result A new unconnected AQSocket instance. 86 | */ 87 | - (id) initWithSocketType: (int) type; 88 | 89 | /** 90 | Initializes a new AQSocket instance of type SOCK_STREAM using TCP over 91 | IPv6 (or 4to6). 92 | @see initWithSocketType: 93 | @result A new unconnected AQSocket instance. 94 | */ 95 | - (id) init; 96 | 97 | /** @name Properties */ 98 | 99 | /// The event handler for this socket. Must be set non-zero before the socket can 100 | /// be connected or otherwise used. See AQSocketEventHandler for more discussion. 101 | @property (nonatomic, copy) AQSocketEventHandler eventHandler; 102 | 103 | /** @name Connections */ 104 | 105 | /** 106 | Connects the socket to its remote server asynchronously, notifying success or 107 | failure via the installed eventHandler. Should the socket not be configured with 108 | an event handler yet, or should it already be connected/connecting, then upon 109 | return the `error` parameter will be initialized to a new autoreleased NSError 110 | instance containing detailed information. 111 | @param saddr A socket address structure containing a remote server address. 112 | @param error If this method returns `NO`, then on return this value contains 113 | an NSError object detailing the error. 114 | @result `YES` if the connection initialization takes place, `NO` if something prevented it from doing so. 115 | */ 116 | - (BOOL) connectToAddress: (struct sockaddr *) saddr 117 | error: (NSError **) error; 118 | 119 | /** 120 | This method allows the creation of a listening socket, which will accept() 121 | incoming connection requests on either the loopback interface or the appropriate 122 | IPv4/IPV6 'any' address. It will choose its own port, which can be obtained using 123 | the `port` property. 124 | @param useLoopback If `YES`, bind to the loopback interface. Otherwise, use 'any'. 125 | @param error If this method returns `NO`, then on return this value contains 126 | an NSError object detailing the error. 127 | @result `YES` if the connection could be bound, `NO` otherwise. 128 | */ 129 | - (BOOL) listenForConnections: (BOOL) useLoopback error: (NSError **) error; 130 | 131 | /** 132 | This is a wrapper around connectToAddress:error: which allows 133 | the caller to pass a DNS hostname to specify the destination for the connection. 134 | @param hostname The DNS hostname of the server to which to attempt connection. 135 | @param port The port number to which to connect. 136 | @param error If this method returns `NO`, then on return this value contains 137 | an NSError object detailing the error. 138 | @result Returns `YES` if the async connection attempt began, `NO` otherwise. 139 | */ 140 | - (BOOL) connectToHost: (NSString *) hostname 141 | port: (UInt16) port 142 | error: (NSError **) error; 143 | 144 | /** 145 | This is a wrapper around connectToAddress:withTimeout:error: which allows 146 | the caller to pass a numeric IPv4 or IPv6 address to specify the destination 147 | for the connection. 148 | @param address The IPv4/6 address of the server to which to attempt connection. 149 | @param port The port number to which to connect. 150 | @param error If this method returns `NO`, then on return this value contains 151 | an NSError object detailing the error. 152 | @result Returns `YES` if the async connection attempt began, `NO` otherwise. 153 | */ 154 | - (BOOL) connectToIPAddress: (NSString *) address 155 | port: (UInt16) port 156 | error: (NSError **) error; 157 | 158 | /** 159 | Closes the socket and ceases all handling of input and output. 160 | */ 161 | - (void) close; 162 | 163 | /** 164 | Returns the connection status of the socket. 165 | @result One of the following values: 166 | * `AQSocketUnconnected`: 167 | The socket is ready to connect but none has yet been initiated. 168 | * `AQSocketConnecting`: 169 | The socket is waiting for a connection to complete. 170 | * `AQSocketListening`: 171 | The socket is bound to a local address/port and is waiting to accept new connections. 172 | * `AQSocketConnected`: 173 | The socket has successfully connected to a remote host and can be used to send and receive data. 174 | * `AQSocketDisconnected`: 175 | The socket has disconnected and is no longer usable. 176 | */ 177 | @property (nonatomic, readonly) AQSocketStatus status; 178 | 179 | /** @name Data Transmission */ 180 | 181 | /** 182 | Writes bytes to the socket. This will enqueue the write to an ordered serial 183 | queue, and is guaranteed to write everything in a single go, monitoring 184 | writability states as necessary should it ever encounter a full output buffer at 185 | the socket level. Should an unrecoverable error occur, the `completionHandler` 186 | block will be called with an appropriate NSError parameter. Should everything 187 | write correctly, `completionHandler` will be invoked with a `nil` NSError 188 | parameter. 189 | 190 | Note that error states encountered while writing data will NOT be reported via 191 | the eventHandler callback block property, only by the `completionHandler` passed 192 | to this method. 193 | 194 | @param bytes The data to write on the socket. 195 | @param completionHandler A callback method to invoke upon write completion or error. 196 | 197 | @exception NSInternalInconsistencyException If the socket is not connected, or is a server-side listening socket. 198 | */ 199 | - (void) writeBytes: (NSData *) bytes 200 | completion: (void (^)(NSData * unwritten, NSError * error)) completionHandler; 201 | 202 | @end 203 | -------------------------------------------------------------------------------- /AQSocket/AQSocket.m: -------------------------------------------------------------------------------- 1 | // 2 | // AQSocket.m 3 | // AQSocket 4 | // 5 | // Created by Jim Dovey on 11-12-16. 6 | // Copyright (c) 2011 Jim Dovey. All rights reserved. 7 | // 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions 10 | // are met: 11 | // 12 | // Redistributions of source code must retain the above copyright notice, 13 | // this list of conditions and the following disclaimer. 14 | // 15 | // Redistributions in binary form must reproduce the above copyright 16 | // notice, this list of conditions and the following disclaimer in the 17 | // documentation and/or other materials provided with the distribution. 18 | // 19 | // Neither the name of the project's author nor the names of its 20 | // contributors may be used to endorse or promote products derived from 21 | // this software without specific prior written permission. 22 | // 23 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29 | // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31 | // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | // 35 | 36 | #import "AQSocket.h" 37 | #import "AQSocketReader.h" 38 | #import "AQSocketReader+PrivateInternal.h" 39 | #import "AQSocketIOChannel.h" 40 | #import 41 | #import 42 | #import 43 | #import 44 | #import 45 | 46 | // See -connectToAddress:port:error: for discussion. 47 | #if TARGET_OS_IPHONE 48 | #import 49 | #else 50 | #import 51 | #endif 52 | 53 | @interface AQSocket (CFSocketConnectionCallback) 54 | - (void) connectedSuccessfully; 55 | - (void) connectionFailedWithError: (SInt32) err; 56 | @end 57 | 58 | @interface AQSocket (CFSocketAcceptCallback) 59 | - (void) acceptNewConnection: (CFSocketNativeHandle) clientSock; 60 | @end 61 | 62 | static void _CFSocketConnectionCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) 63 | { 64 | if ( type != kCFSocketConnectCallBack ) 65 | return; 66 | 67 | AQSocket * aqsock = (__bridge AQSocket *)info; 68 | if ( data == NULL ) 69 | [aqsock connectedSuccessfully]; 70 | else 71 | [aqsock connectionFailedWithError: *((SInt32 *)data)]; 72 | } 73 | 74 | static void _CFSocketAcceptCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) 75 | { 76 | if ( type != kCFSocketAcceptCallBack ) 77 | return; 78 | 79 | AQSocket * aqsock = (__bridge AQSocket *)info; 80 | [aqsock acceptNewConnection: *((CFSocketNativeHandle *)data)]; 81 | } 82 | 83 | static BOOL _SocketAddressFromString(NSString * addrStr, BOOL isNumeric, UInt16 port, struct sockaddr_storage * outAddr, NSError * __autoreleasing* outError) 84 | { 85 | // Flags for getaddrinfo(): 86 | // 87 | // `AI_ADDRCONFIG`: Only return IPv4 or IPv6 if the local host has configured 88 | // interfaces for those types. 89 | // 90 | // `AI_V4MAPPED`: If no IPv6 addresses found, return an IPv4-mapped IPv6 91 | // address for any IPv4 addresses found. 92 | int flags = AI_ADDRCONFIG|AI_V4MAPPED; 93 | 94 | // If providing a numeric IPv4 or IPv6 string, tell getaddrinfo() not to 95 | // do DNS name lookup. 96 | if ( isNumeric ) 97 | flags |= AI_NUMERICHOST; 98 | 99 | // We're assuming TCP at this point 100 | struct addrinfo hints = { 101 | .ai_flags = flags, 102 | .ai_family = AF_INET6, // using AF_INET6 with 4to6 support 103 | .ai_socktype = SOCK_STREAM, 104 | .ai_protocol = IPPROTO_TCP 105 | }; 106 | 107 | struct addrinfo *pLookup = NULL; 108 | 109 | // Hrm-- this is synchronous, which is required since we can't init asynchronously 110 | int err = getaddrinfo([addrStr UTF8String], NULL, &hints, &pLookup); 111 | if ( err != 0 ) 112 | { 113 | NSLog(@"Error from getaddrinfo() for address %@: %s", addrStr, gai_strerror(err)); 114 | if ( outError != NULL ) 115 | { 116 | NSDictionary * userInfo = [[NSDictionary alloc] initWithObjectsAndKeys: [NSString stringWithUTF8String: gai_strerror(err)], NSLocalizedDescriptionKey, nil]; 117 | *outError = [NSError errorWithDomain: @"GetAddrInfoErrorDomain" code: err userInfo: userInfo]; 118 | #if USING_MRR 119 | [userInfo release]; 120 | #endif 121 | } 122 | 123 | return ( NO ); 124 | } 125 | 126 | // Copy the returned address to the output parameter 127 | memcpy(outAddr, pLookup->ai_addr, pLookup->ai_addr->sa_len); 128 | 129 | switch ( outAddr->ss_family ) 130 | { 131 | case AF_INET: 132 | { 133 | struct sockaddr_in *p = (struct sockaddr_in *)outAddr; 134 | p->sin_port = htons(port); // remember to put in network byte-order! 135 | break; 136 | } 137 | case AF_INET6: 138 | { 139 | struct sockaddr_in6 *p = (struct sockaddr_in6 *)outAddr; 140 | p->sin6_port = htons(port); // network byte order again 141 | break; 142 | } 143 | default: 144 | return ( NO ); 145 | } 146 | 147 | // Have to release the returned address information here 148 | freeaddrinfo(pLookup); 149 | return ( 0 ); 150 | } 151 | 152 | #pragma mark - 153 | 154 | @implementation AQSocket 155 | { 156 | int _socketType; 157 | int _socketProtocol; 158 | AQSocketStatus _status; 159 | CFSocketRef _socketRef; 160 | CFRunLoopSourceRef _socketRunloopSource; 161 | AQSocketIOChannel * _socketIO; 162 | AQSocketReader * _socketReader; 163 | } 164 | 165 | @synthesize eventHandler, status=_status; 166 | 167 | - (id) initWithSocketType: (int) type 168 | { 169 | NSParameterAssert(type == SOCK_STREAM || type == SOCK_DGRAM); 170 | self = [super init]; 171 | if ( self == nil ) 172 | return ( nil ); 173 | 174 | _socketType = type; 175 | _socketProtocol = (type == SOCK_STREAM ? IPPROTO_TCP : IPPROTO_UDP); 176 | 177 | _status = AQSocketUnconnected; 178 | 179 | return ( self ); 180 | } 181 | 182 | - (id) init 183 | { 184 | return ( [self initWithSocketType: SOCK_STREAM] ); 185 | } 186 | 187 | - (id) initWithConnectedSocket: (CFSocketNativeHandle) nativeSocket 188 | { 189 | int socktype = SOCK_STREAM; 190 | socklen_t len = 0; 191 | 192 | getsockopt(nativeSocket, SOL_SOCKET, SO_TYPE, &socktype, &len); 193 | self = [self initWithSocketType: socktype]; 194 | if ( self == nil ) 195 | return ( nil ); 196 | 197 | // don't send SIGPIPE 198 | int nosigpipe = 1; 199 | setsockopt(nativeSocket, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(nosigpipe)); 200 | 201 | CFSocketContext ctx = { 0, (__bridge void *)self, NULL, NULL, CFCopyDescription }; 202 | _socketRef = CFSocketCreateWithNative(kCFAllocatorDefault, nativeSocket, 0, NULL, &ctx); 203 | [self connectedSuccessfully]; // setup the io channel for data notifications etc. 204 | 205 | return ( self ); 206 | } 207 | 208 | - (void) dealloc 209 | { 210 | // disconnected == unusable, where unconnected == ready to connect. 211 | _status = AQSocketDisconnected; 212 | 213 | if ( _socketRef != NULL ) 214 | { 215 | // Not got around to initializing the dispatch IO channel yet, 216 | // so we will have to release the socket ref manually. 217 | CFRelease(_socketRef); 218 | } 219 | if ( _socketRunloopSource != NULL ) 220 | { 221 | // Ensure the source is no longer scheduled in any run loops. 222 | CFRunLoopRemoveSource(CFRunLoopGetMain(), _socketRunloopSource, kCFRunLoopDefaultMode); 223 | #if 0 224 | CFRunLoopRemoveSource(CFRunLoopGetMain(), _socketRunloopSource, (__bridge CFStringRef) 225 | #if TARGET_OS_IPHONE 226 | UITrackingRunLoopMode 227 | #else 228 | NSEventTrackingRunLoopMode 229 | #endif 230 | ); 231 | #endif 232 | 233 | // Now we can safely release our reference to it. 234 | CFRelease(_socketRunloopSource); 235 | } 236 | #if USING_MRR 237 | [_socketReader release]; 238 | [super dealloc]; 239 | #endif 240 | } 241 | 242 | - (BOOL) listenForConnections: (BOOL) useLoopback error: (NSError **) error 243 | { 244 | if ( _status != AQSocketUnconnected ) 245 | { 246 | if ( error != NULL ) 247 | { 248 | NSDictionary * info = [[NSDictionary alloc] initWithObjectsAndKeys: NSLocalizedString(@"Socket is already in use.", @"Socket connection error"), NSLocalizedDescriptionKey, nil]; 249 | *error = [NSError errorWithDomain: NSCocoaErrorDomain code: _status userInfo: info]; 250 | #if USING_MRR 251 | [info release]; 252 | #endif 253 | } 254 | } 255 | 256 | // This is only initialized once we have successfully set up the connection. 257 | if ( _socketIO != nil ) 258 | { 259 | if ( error != NULL ) 260 | { 261 | NSDictionary * info = [[NSDictionary alloc] initWithObjectsAndKeys: NSLocalizedString(@"Already connected.", @"Connection error"), NSLocalizedDescriptionKey, nil]; 262 | *error = [NSError errorWithDomain: NSCocoaErrorDomain code: AQSocketConnected userInfo: info]; 263 | #if USING_MRR 264 | [info release]; 265 | #endif 266 | } 267 | 268 | return ( NO ); 269 | } 270 | 271 | struct sockaddr_in saddr = {0}; 272 | NSData * sockData = [[NSData alloc] initWithBytesNoCopy: &saddr length: sizeof(struct sockaddr_in) freeWhenDone: NO]; 273 | 274 | // Create a local address to which we'll bind. Sticking with IPv4 for now. 275 | struct sockaddr_in *pIn = &saddr; 276 | pIn->sin_family = AF_INET; 277 | pIn->sin_len = sizeof(struct sockaddr_in); 278 | pIn->sin_port = 0; 279 | pIn->sin_addr.s_addr = htonl((useLoopback ? INADDR_LOOPBACK : INADDR_ANY)); 280 | 281 | // Create the socket with the appropriate socket family from the address 282 | // structure. 283 | CFSocketContext ctx = { 284 | .version = 0, 285 | .info = (__bridge void *)self, // just a plain bridge cast 286 | .retain = CFRetain, 287 | .release = CFRelease, 288 | .copyDescription = CFCopyDescription 289 | }; 290 | 291 | _socketRef = CFSocketCreate(kCFAllocatorDefault, saddr.sin_family, _socketType, _socketProtocol, kCFSocketAcceptCallBack, _CFSocketAcceptCallBack, &ctx); 292 | if ( _socketRef == NULL ) 293 | { 294 | // We failed to create the socket, so build an error (if appropriate) 295 | // and return `NO`. 296 | if ( error != NULL ) 297 | { 298 | // This error code is -1004. 299 | *error = [NSError errorWithDomain: NSURLErrorDomain code: NSURLErrorCannotConnectToHost userInfo: nil]; 300 | } 301 | 302 | #if USING_MRR 303 | [sockData release]; 304 | #endif 305 | return ( NO ); 306 | } 307 | 308 | // Create a runloop source for the socket reference and bind it to a 309 | // runloop that's guaranteed to be running some time in the future: the main 310 | // one. 311 | _socketRunloopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _socketRef, 0); 312 | CFRunLoopAddSource(CFRunLoopGetMain(), _socketRunloopSource, kCFRunLoopDefaultMode); 313 | 314 | // We also want to ensure that the connection callback fires during 315 | // input event tracking. There are different constants for this on iOS and 316 | // OS X, so I've used a compiler switch for that. 317 | #if 0 318 | CFRunLoopAddSource(CFRunLoopGetMain(), _socketRunloopSource, (__bridge CFStringRef) 319 | #if TARGET_OS_IPHONE 320 | UITrackingRunLoopMode 321 | #else 322 | NSEventTrackingRunLoopMode 323 | #endif 324 | ); 325 | #endif 326 | CFSocketError sockErr = CFSocketSetAddress(_socketRef, (__bridge CFDataRef)sockData); 327 | #if USING_MRR 328 | [sockData release]; 329 | #endif 330 | if ( sockErr != kCFSocketSuccess ) 331 | return ( NO ); 332 | 333 | // Find out what port we were assigned. 334 | struct sockaddr_in myAddr = {0}; 335 | socklen_t slen = sizeof(struct sockaddr_in); 336 | getsockname(CFSocketGetNative(_socketRef), (struct sockaddr *)&myAddr, &slen); 337 | 338 | NSLog(@"Using port %hu", ntohs(myAddr.sin_port)); 339 | 340 | CFSocketSetSocketFlags(_socketRef, kCFSocketAutomaticallyReenableAcceptCallBack); 341 | CFSocketEnableCallBacks(_socketRef, kCFSocketAcceptCallBack); 342 | 343 | // Record the change in status. 344 | _status = AQSocketListening; 345 | 346 | return ( YES ); 347 | } 348 | 349 | - (BOOL) connectToAddress: (struct sockaddr *) saddr 350 | error: (NSError **) error 351 | { 352 | if ( _status != AQSocketUnconnected ) 353 | { 354 | if ( error != NULL ) 355 | { 356 | NSDictionary * info = [[NSDictionary alloc] initWithObjectsAndKeys: NSLocalizedString(@"Socket is already in use.", @"Socket connection error"), NSLocalizedDescriptionKey, nil]; 357 | *error = [NSError errorWithDomain: NSCocoaErrorDomain code: _status userInfo: info]; 358 | #if USING_MRR 359 | [info release]; 360 | #endif 361 | } 362 | } 363 | 364 | // We're only initializing this member once we successfully connect 365 | if ( _socketIO != nil ) 366 | { 367 | if ( error != NULL ) 368 | { 369 | NSDictionary * info = [[NSDictionary alloc] initWithObjectsAndKeys: NSLocalizedString(@"Already connected.", @"Connection error"), NSLocalizedDescriptionKey, nil]; 370 | *error = [NSError errorWithDomain: NSCocoaErrorDomain code: 1 userInfo: info]; 371 | #if USING_MRR 372 | [info release]; 373 | #endif 374 | } 375 | 376 | return ( NO ); 377 | } 378 | 379 | // We require that an event handler be set, so we can notify 380 | // connection success 381 | if ( self.eventHandler == nil ) 382 | { 383 | if ( error != NULL ) 384 | { 385 | NSDictionary * info = [[NSDictionary alloc] initWithObjectsAndKeys: NSLocalizedString(@"No event handler provided.", @"Connection error"), NSLocalizedDescriptionKey, nil]; 386 | *error = [NSError errorWithDomain: NSCocoaErrorDomain code: 2 userInfo: info]; 387 | #if USING_MRR 388 | [info release]; 389 | #endif 390 | } 391 | 392 | return ( NO ); 393 | } 394 | 395 | // Create the socket with the appropriate socket family from the address 396 | // structure. 397 | CFSocketContext ctx = { 398 | .version = 0, 399 | .info = (__bridge void *)self, // just a plain bridge cast 400 | .retain = CFRetain, 401 | .release = CFRelease, 402 | .copyDescription = CFCopyDescription 403 | }; 404 | 405 | _socketRef = CFSocketCreate(kCFAllocatorDefault, saddr->sa_family, _socketType, _socketProtocol, kCFSocketConnectCallBack, _CFSocketConnectionCallBack, &ctx); 406 | if ( _socketRef == NULL ) 407 | { 408 | // We failed to create the socket, so build an error (if appropriate) 409 | // and return `NO`. 410 | if ( error != NULL ) 411 | { 412 | // This error code is -1004. 413 | *error = [NSError errorWithDomain: NSURLErrorDomain code: NSURLErrorCannotConnectToHost userInfo: nil]; 414 | } 415 | 416 | return ( NO ); 417 | } 418 | 419 | // Create a runloop source for the socket reference and bind it to a 420 | // runloop that's guaranteed to be running some time in the future: the main 421 | // one. 422 | _socketRunloopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _socketRef, 0); 423 | CFRunLoopAddSource(CFRunLoopGetMain(), _socketRunloopSource, kCFRunLoopDefaultMode); 424 | 425 | // We also want to ensure that the connection callback fires during 426 | // input event tracking. There are different constants for this on iOS and 427 | // OS X, so I've used a compiler switch for that. 428 | #if 0 429 | CFRunLoopAddSource(CFRunLoopGetMain(), _socketRunloopSource, (__bridge CFStringRef) 430 | #if TARGET_OS_IPHONE 431 | UITrackingRunLoopMode 432 | #else 433 | NSEventTrackingRunLoopMode 434 | #endif 435 | ); 436 | #endif 437 | // Start the connection process. 438 | // Let's fire off the connection attempt and wait for the socket to become 439 | // readable, which means the connection succeeded. The timeout value of 440 | // -1 means 'do it in the background', meaning our C callback will be invoked 441 | // when the connection attempt succeeds or fails. 442 | // Note that in this instance I'm using a plain __bridge cast for `data`, so 443 | // that no retain/release operations or change of ownership is implied. 444 | NSData * data = [[NSData alloc] initWithBytesNoCopy: saddr 445 | length: saddr->sa_len 446 | freeWhenDone: NO]; 447 | CFSocketError err = CFSocketConnectToAddress(_socketRef, (__bridge CFDataRef)data, -1); 448 | #if USING_MRR 449 | [data release]; 450 | #endif 451 | 452 | if ( err != kCFSocketSuccess ) 453 | { 454 | NSLog(@"Error connecting socket: %ld", err); 455 | 456 | if ( error != NULL ) 457 | { 458 | if ( err == kCFSocketError ) 459 | { 460 | // Try to get hold of the underlying error from the raw socket. 461 | int sockerr = 0; 462 | socklen_t len = sizeof(sockerr); 463 | if ( getsockopt(CFSocketGetNative(_socketRef), SOL_SOCKET, SO_ERROR, &sockerr, &len) == -1 ) 464 | { 465 | // Yes, this is cheating. 466 | sockerr = errno; 467 | } 468 | 469 | // The CoreFoundation CFErrorRef (toll-free-bridged with NSError) 470 | // actually fills in the userInfo for POSIX errors from a 471 | // localized table. Neat, huh? 472 | *error = [NSError errorWithDomain: NSPOSIXErrorDomain code: sockerr userInfo: nil]; 473 | } 474 | else 475 | { 476 | // By definition, it's a timeout. 477 | // I'm not returning a userInfo here: code is -1001 478 | *error = [NSError errorWithDomain: NSURLErrorDomain code: NSURLErrorTimedOut userInfo: nil]; 479 | } 480 | } 481 | 482 | return ( NO ); 483 | } 484 | 485 | // Update our connection status. 486 | _status = AQSocketConnecting; 487 | 488 | // The asynchronous connection attempt has begun. 489 | return ( YES ); 490 | } 491 | 492 | - (BOOL) connectToHost: (NSString *) hostname 493 | port: (UInt16) port 494 | error: (NSError **) error 495 | { 496 | NSParameterAssert([hostname length] != 0); 497 | struct sockaddr_storage addrStore = {0}; 498 | 499 | // Convert the hostname into a socket address. This method installs the 500 | // port number into any returned socket address, and generates an NSError 501 | // for us should something go wrong. 502 | if ( _SocketAddressFromString(hostname, NO, port, &addrStore, error) == NO ) 503 | return ( NO ); 504 | 505 | return ( [self connectToAddress: (struct sockaddr *)&addrStore 506 | error: error] ); 507 | } 508 | 509 | - (BOOL) connectToIPAddress: (NSString *) address 510 | port: (UInt16) port 511 | error: (NSError **) error 512 | { 513 | NSParameterAssert([address length] != 0); 514 | struct sockaddr_storage addrStore = {0}; 515 | 516 | // Convert the hostname into a socket address. This method installs the 517 | // port number into any returned socket address, and generates an NSError 518 | // for us should something go wrong. 519 | if ( _SocketAddressFromString(address, YES, port, &addrStore, error) == NO ) 520 | return ( NO ); 521 | 522 | return ( [self connectToAddress: (struct sockaddr *)&addrStore 523 | error: error] ); 524 | } 525 | 526 | - (void) close 527 | { 528 | [_socketIO close]; 529 | #if USING_MRR 530 | [_socketIO release]; 531 | #endif 532 | _socketIO = nil; 533 | 534 | if ( _socketRef != NULL ) 535 | { 536 | int sock = CFSocketGetNative(_socketRef); 537 | CFRelease(_socketRef); 538 | _socketRef = NULL; 539 | close(sock); 540 | } 541 | } 542 | 543 | - (void) writeBytes: (NSData *) bytes 544 | completion: (void (^)(NSData *, NSError *)) completionHandler 545 | { 546 | NSParameterAssert([bytes length] != 0); 547 | 548 | if ( _socketIO == nil ) 549 | { 550 | [NSException raise: NSInternalInconsistencyException format: @"-[%@ %@]: socket is not connected.", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; 551 | } 552 | 553 | // Pass the write along to our IO channel, along with the completion/unsent handler. 554 | [_socketIO writeData: bytes withCompletion: completionHandler]; 555 | } 556 | 557 | - (void) setEventHandler: (AQSocketEventHandler) anEventHandler 558 | { 559 | #if USING_MRR 560 | anEventHandler = [anEventHandler copy]; 561 | AQSocketEventHandler oldHandler = eventHandler; 562 | #endif 563 | eventHandler = anEventHandler; 564 | #if USING_MRR 565 | [oldHandler release]; 566 | #endif 567 | 568 | if ( anEventHandler != nil && [_socketReader length] > 0 ) 569 | { 570 | #if USING_MRR 571 | [_socketReader retain]; 572 | #endif 573 | anEventHandler(AQSocketEventDataAvailable, _socketReader); 574 | #if USING_MRR 575 | [_socketReader release]; 576 | #endif 577 | } 578 | } 579 | 580 | - (NSString *) description 581 | { 582 | struct sockaddr_storage sockname = {0}; 583 | struct sockaddr_storage peername = {0}; 584 | socklen_t socknamelen = sockname.ss_len = sizeof(struct sockaddr_storage); 585 | socklen_t peernamelen = peername.ss_len = sizeof(struct sockaddr_storage); 586 | 587 | getsockname(CFSocketGetNative(_socketRef), (struct sockaddr *)&sockname, &socknamelen); 588 | getpeername(CFSocketGetNative(_socketRef), (struct sockaddr *)&peername, &peernamelen); 589 | 590 | char socknamestr[INET6_ADDRSTRLEN]; 591 | char peernamestr[INET6_ADDRSTRLEN]; 592 | inet_ntop(sockname.ss_family, &sockname, socknamestr, INET6_ADDRSTRLEN); 593 | inet_ntop(peername.ss_family, &peername, peernamestr, INET6_ADDRSTRLEN); 594 | 595 | socknamestr[INET6_ADDRSTRLEN-1] = '\0'; 596 | peernamestr[INET6_ADDRSTRLEN-1] = '\0'; 597 | 598 | uint16_t sockport = 0, peerport = 0; 599 | if ( sockname.ss_family == AF_INET ) 600 | { 601 | struct sockaddr_in *p = (struct sockaddr_in *)&sockname; 602 | sockport = ntohs(p->sin_port); 603 | } 604 | else 605 | { 606 | struct sockaddr_in6 *p = (struct sockaddr_in6 *)&sockname; 607 | sockport = ntohs(p->sin6_port); 608 | } 609 | if ( peername.ss_family == AF_INET ) 610 | { 611 | struct sockaddr_in *p = (struct sockaddr_in *)&peername; 612 | peerport = ntohs(p->sin_port); 613 | } 614 | else 615 | { 616 | struct sockaddr_in6 *p = (struct sockaddr_in6 *)&peername; 617 | peerport = ntohs(p->sin6_port); 618 | } 619 | 620 | NSString * myAddr = [NSString stringWithFormat: @"%s:%hu", socknamestr, sockport]; 621 | NSString * peerAddr = [NSString stringWithFormat: @"%s:%hu", peernamestr, peerport]; 622 | 623 | static NSArray * __statusStrings = nil; 624 | static dispatch_once_t onceToken; 625 | dispatch_once(&onceToken, ^{ 626 | __statusStrings = [[NSArray alloc] initWithObjects: @"AQSocketUnconnected", @"AQSocketConnecting", @"AQSocketListening", @"AQSocketConnected", @"AQSocketDisconnected", nil]; 627 | }); 628 | 629 | return ( [NSString stringWithFormat: @"%@: {status=%@, addr=%@, peer=%@}", [super description], [__statusStrings objectAtIndex: _status], myAddr, peerAddr] ); 630 | } 631 | 632 | @end 633 | 634 | @implementation AQSocket (CFSocketConnectionCallback) 635 | 636 | - (void) connectedSuccessfully 637 | { 638 | // Now that we're connected, we must set up a couple of things: 639 | // 640 | // 1. The dispatch IO channel through which we will handle reads and writes. 641 | // 2. The AQSocketReader object which will serve as our read buffer. 642 | // 3. The dispatch source which will notify us of incoming data. 643 | 644 | // Before all that though, we'll remove the CFSocketRef from the runloop. 645 | if ( _socketRunloopSource != NULL ) 646 | { 647 | CFRunLoopRemoveSource(CFRunLoopGetMain(), _socketRunloopSource, kCFRunLoopDefaultMode); 648 | #if 0 649 | CFRunLoopRemoveSource(CFRunLoopGetMain(), _socketRunloopSource, (__bridge CFStringRef) 650 | #if TARGET_OS_IPHONE 651 | UITrackingRunLoopMode 652 | #else 653 | NSEventTrackingRunLoopMode 654 | #endif 655 | ); 656 | #endif 657 | // All done with this one now. 658 | CFRelease(_socketRunloopSource); 659 | _socketRunloopSource = NULL; 660 | } 661 | 662 | // Note that we are now connected. 663 | _status = AQSocketConnected; 664 | 665 | // First, the IO channel. We will have its cleanup handler release the 666 | // CFSocketRef for us; in other words, the IO channel now owns the 667 | // CFSocketRef. 668 | _socketIO = [[AQSocketIOChannel alloc] initWithNativeSocket: CFSocketGetNative(_socketRef) cleanupHandler: ^{ 669 | // all done with the socket reference, make it noticeably go away. 670 | if ( _socketRef != NULL ) 671 | { 672 | CFRelease(_socketRef); 673 | _socketRef = NULL; 674 | } 675 | }]; 676 | 677 | // Next the socket reader object. This will keep track of all the data blobs 678 | // returned via dispatch_io_read(), providing peek support to the upper 679 | // protocol layers. 680 | // Note that we initialize it as a stack variable initially, which we use in the block 681 | // below to avoid a retain-cycle. 682 | AQSocketReader * aSocketReader = [AQSocketReader new]; 683 | #if USING_MRR 684 | _socketReader = [aSocketReader retain]; 685 | #else 686 | _socketReader = aSocketReader; 687 | #endif 688 | 689 | __maybe_weak AQSocket *weakSelf = self; 690 | 691 | // Now we install a callback to tell us when new data arrives. 692 | _socketIO.readHandler = ^(NSData * data, NSError * error){ 693 | AQSocket *strongSelf = weakSelf; 694 | NSLog(@"Incoming data on %@: %lu bytes", strongSelf, [data length]); 695 | if ( data != nil ) 696 | { 697 | if ( [data length] != 0 ) 698 | { 699 | [aSocketReader appendData: data]; // if using a dispatch data wrapper, this method will notice and do the right thing automatically 700 | if ( strongSelf.eventHandler != nil ) 701 | strongSelf.eventHandler(AQSocketEventDataAvailable, aSocketReader); 702 | } 703 | else 704 | { 705 | // socket closed 706 | NSLog(@"zero-length data received, closing socket"); 707 | dispatch_async(dispatch_get_main_queue(), ^{ 708 | [strongSelf close]; 709 | }); 710 | } 711 | } 712 | else if ( error != nil ) 713 | { 714 | NSLog(@"Error on %@; %@", strongSelf, error); 715 | if ( strongSelf.eventHandler != nil ) 716 | strongSelf.eventHandler(AQSocketErrorEncountered, error); 717 | } 718 | }; 719 | } 720 | 721 | - (void) connectionFailedWithError: (SInt32) err 722 | { 723 | NSError * info = [NSError errorWithDomain: NSPOSIXErrorDomain code: err userInfo: nil]; 724 | self.eventHandler(AQSocketEventConnectionFailed, info); 725 | 726 | // Get rid of the socket now, since we might try to re-connect, which will 727 | // create a new CFSocketRef. 728 | CFRelease(_socketRef); _socketRef = NULL; 729 | } 730 | 731 | @end 732 | 733 | @implementation AQSocket (CFSocketAcceptCallback) 734 | 735 | - (void) acceptNewConnection: (CFSocketNativeHandle) clientSock 736 | { 737 | AQSocket * child = [[AQSocket alloc] initWithConnectedSocket: clientSock]; 738 | if ( child == nil ) 739 | return; 740 | 741 | // Inform the client about the appearance of the child socket. 742 | // It's up to the client to keep it around -- we just pass it on as appropriate. 743 | self.eventHandler(AQSocketEventAcceptedNewConnection, child); 744 | #if USING_MRR 745 | [child release]; 746 | #endif 747 | } 748 | 749 | @end 750 | -------------------------------------------------------------------------------- /AQSocket/AQSocketIOChannel.h: -------------------------------------------------------------------------------- 1 | // 2 | // AQSocketIOChannel.h 3 | // AQSocket 4 | // 5 | // Created by Jim Dovey on 2012-04-24. 6 | // Copyright (c) 2012 Jim Dovey. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AQSocketReader.h" 11 | 12 | @interface AQSocketIOChannel : NSObject 13 | { 14 | CFSocketNativeHandle _nativeSocket; 15 | dispatch_queue_t _q; // a serial queue upon which notifiers will be enqueued for rigidly serialized calls 16 | void (^_cleanupHandler)(void); 17 | void (^_readHandler)(NSData *, NSError *); 18 | } 19 | - (id) initWithNativeSocket: (CFSocketNativeHandle) nativeSocket cleanupHandler: (void (^)(void)) cleanupHandler; 20 | - (void) writeData: (NSData *) data withCompletion: (void (^)(NSData * unsentData, NSError *error)) completion; 21 | @property (nonatomic, copy) void (^readHandler)(NSData *data, NSError *error); 22 | - (void) close; 23 | @end 24 | 25 | // A sly little NSData class simplifying the dispatch_data_t -> NSData transition. 26 | @interface _AQDispatchData : NSData 27 | { 28 | dispatch_data_t _ddata; 29 | const void * _buf; 30 | size_t _len; 31 | } 32 | - (id) initWithDispatchData: (dispatch_data_t) ddata; 33 | - (dispatch_data_t) _aq_getDispatchData; 34 | @end 35 | -------------------------------------------------------------------------------- /AQSocket/AQSocketIOChannel.m: -------------------------------------------------------------------------------- 1 | // 2 | // AQSocketIOChannel.m 3 | // AQSocket 4 | // 5 | // Created by Jim Dovey on 2012-04-24. 6 | // Copyright (c) 2012 Jim Dovey. All rights reserved. 7 | // 8 | 9 | #import "AQSocketIOChannel.h" 10 | #import "AQSocketReader+PrivateInternal.h" 11 | #import "AQSocket.h" 12 | #import 13 | 14 | @implementation _AQDispatchData 15 | 16 | - (id) initWithDispatchData: (dispatch_data_t) ddata 17 | { 18 | self = [super init]; 19 | if ( self == nil ) 20 | return ( nil ); 21 | 22 | // First off, ensure we have a contiguous range of bytes to deal with. 23 | _ddata = dispatch_data_create_map(ddata, &_buf, &_len); 24 | 25 | return ( self ); 26 | } 27 | 28 | #if !DISPATCH_USES_ARC 29 | - (void) dealloc 30 | { 31 | if ( _ddata != NULL ) 32 | dispatch_release(_ddata); 33 | #if USING_MRR 34 | [super dealloc]; 35 | #endif 36 | } 37 | #endif 38 | 39 | - (const void *) bytes 40 | { 41 | return ( _buf ); 42 | } 43 | 44 | - (NSUInteger) length 45 | { 46 | return ( _len ); 47 | } 48 | 49 | - (dispatch_data_t) _aq_getDispatchData 50 | { 51 | return ( _ddata ); 52 | } 53 | 54 | @end 55 | 56 | #pragma mark - 57 | 58 | @interface AQSocketDispatchIOChannel : AQSocketIOChannel 59 | @end 60 | 61 | @interface AQSocketLegacyIOChannel : AQSocketIOChannel 62 | @end 63 | 64 | @implementation AQSocketIOChannel 65 | 66 | @synthesize readHandler=_readHandler; 67 | 68 | + (id) allocWithZone: (NSZone *) zone 69 | { 70 | if ( [self class] == [AQSocketIOChannel class] ) 71 | { 72 | if ( dispatch_io_read != NULL ) 73 | return ( [AQSocketDispatchIOChannel allocWithZone: zone] ); 74 | 75 | return ( [AQSocketLegacyIOChannel allocWithZone: zone] ); 76 | } 77 | 78 | return ( [super allocWithZone: zone] ); 79 | } 80 | 81 | - (id) initWithNativeSocket: (CFSocketNativeHandle) nativeSocket cleanupHandler: (void (^)(void)) cleanupHandler 82 | { 83 | self = [super init]; 84 | if ( self == nil ) 85 | return ( nil ); 86 | 87 | _nativeSocket = nativeSocket; 88 | _cleanupHandler = [cleanupHandler copy]; 89 | 90 | // Create a serial queue upon which all notification/completion blocks will run. 91 | _q = dispatch_queue_create("me.alanquatermain.AQSocketIOChannel", DISPATCH_QUEUE_SERIAL); 92 | 93 | // Point this serial queue at the high-priority global queue for speedy handling. 94 | dispatch_set_target_queue(_q, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)); 95 | 96 | return ( self ); 97 | } 98 | 99 | #if USING_MRR || DISPATCH_USES_ARC == 0 100 | - (void) dealloc 101 | { 102 | #if DISPATCH_USES_ARC == 0 103 | dispatch_release(_q); 104 | _q = NULL; 105 | #endif 106 | #if USING_MRR 107 | // Note that we don't *run* the cleanup handler here-- it's up to subclasses to do that as is appropriate. 108 | [_cleanupHandler release]; 109 | [_readHandler release]; 110 | [super dealloc]; 111 | #endif 112 | } 113 | #endif 114 | 115 | - (void) close 116 | { 117 | [NSException raise: @"SubclassMustImplementException" format: @"Subclass of %@ is expected to implement %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; 118 | } 119 | 120 | - (void) writeData: (NSData *) data withCompletion: (void (^)(NSData *, NSError *)) completion 121 | { 122 | [NSException raise: @"SubclassMustImplementException" format: @"Subclass of %@ is expected to implement %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; 123 | } 124 | 125 | @end 126 | 127 | @implementation AQSocketDispatchIOChannel 128 | { 129 | dispatch_io_t _io; 130 | } 131 | 132 | - (void) dealloc 133 | { 134 | [self close]; 135 | #if USING_MRR 136 | [super dealloc]; 137 | #endif 138 | } 139 | 140 | - (void) close 141 | { 142 | if ( _io != NULL ) 143 | { 144 | // swap out our _io variable so we don't get an invalid access should the cleanup handler called by dispatch_io_close() also call into here 145 | dispatch_io_t io = _io; 146 | _io = NULL; 147 | 148 | dispatch_io_close(io, DISPATCH_IO_STOP); 149 | #if DISPATCH_USES_ARC == 0 150 | dispatch_release(io); 151 | #endif 152 | } 153 | } 154 | 155 | - (void) setReadHandler: (void (^)(NSData *, NSError *)) inReadHandler 156 | { 157 | [super setReadHandler: inReadHandler]; 158 | [self close]; 159 | 160 | if ( _readHandler == nil ) 161 | return; 162 | 163 | _io = dispatch_io_create(DISPATCH_IO_STREAM, _nativeSocket, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(int error) { 164 | if ( error != 0 ) 165 | NSLog(@"Error in dispatch IO channel causing its shutdown: %d", error); 166 | _cleanupHandler(); 167 | }); 168 | 169 | // we want to return any time *any* bytes are read... 170 | // similarly we do NOT want output data to be held back if it's smaller than a certain amount 171 | dispatch_io_set_low_water(_io, 1); 172 | 173 | // install the read callback 174 | dispatch_io_read(_io, 0, SIZE_MAX, _q, ^(bool done, dispatch_data_t data, int error) { 175 | NSLog(@"dispatch read for %@: %lu bytes, error %d", self, (data == NULL ? 0ul : dispatch_data_get_size(data)), error); 176 | if ( _readHandler == nil ) 177 | return; 178 | 179 | NSError * nsError = nil; 180 | if ( error != 0 ) 181 | nsError = [[NSError alloc] initWithDomain: NSPOSIXErrorDomain code: error userInfo: nil]; 182 | 183 | NSData * nsData = nil; 184 | if ( data != NULL ) 185 | nsData = [[_AQDispatchData alloc] initWithDispatchData: data]; 186 | 187 | if ( done || (data != NULL && dispatch_data_get_size(data) != 0) ) 188 | _readHandler(nsData, nsError); 189 | 190 | #if USING_MRR 191 | [nsError release]; 192 | [nsData release]; 193 | #endif 194 | }); 195 | } 196 | 197 | - (void) writeData: (NSData *) data withCompletion: (void (^)(NSData *, NSError *)) completionHandler 198 | { 199 | // Ensure that the completion handler block is on the heap, not the stack 200 | void (^completionCopy)(NSData *, NSError *) = [completionHandler copy]; 201 | 202 | if ( _io == NULL ) 203 | { 204 | if ( completionCopy == nil ) 205 | return; 206 | 207 | dispatch_async(_q, ^{ 208 | NSDictionary * userInfo = [[NSDictionary alloc] initWithObjectsAndKeys: NSLocalizedString(@"IO channel is no longer connected", @"IO channel error"), NSLocalizedFailureReasonErrorKey, nil]; 209 | NSError * error = [NSError errorWithDomain: NSURLErrorDomain code: NSURLErrorNetworkConnectionLost userInfo: userInfo]; 210 | #if USING_MRR 211 | [userInfo release]; 212 | #endif 213 | completionCopy(nil, error); 214 | }); 215 | 216 | return; 217 | } 218 | 219 | // This copy call ensures we have an immutable data object. If we were 220 | // passed an immutable NSData, the copy is actually only a retain. 221 | // We convert it to a CFDataRef in order to get manual reference counting 222 | // semantics in order to keep the data object alive until the dispatch_data_t 223 | // in which we're using it is itself released. 224 | NSData * copiedData = [data copy]; 225 | CFDataRef staticData = CFBridgingRetain(copiedData); 226 | 227 | dispatch_data_t ddata = dispatch_data_create(CFDataGetBytePtr(staticData), CFDataGetLength(staticData), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 228 | // When the dispatch data object is deallocated, release our CFData ref. 229 | CFRelease(staticData); 230 | }); 231 | 232 | #if USING_MRR 233 | // In manual retain/release mode, the call to [data copy] returns +1, and the CFBridgingRetain() returns +1. The block above 234 | // releases the reference from CFBridgingRetain(), so we need to perform another release to match the [data copy] call. 235 | [copiedData release]; 236 | #endif 237 | 238 | dispatch_io_write(_io, 0, ddata, _q, ^(_Bool done, dispatch_data_t data, int error) { 239 | if ( completionCopy == nil ) 240 | return; // no point going further, really 241 | 242 | NSError * nsError = nil; 243 | if ( error != 0 && error != EAGAIN ) 244 | nsError = [NSError errorWithDomain: NSPOSIXErrorDomain code: error userInfo: nil]; 245 | 246 | if ( done ) 247 | { 248 | // All the data was written successfully. 249 | completionCopy(nil, nsError); 250 | return; 251 | } 252 | 253 | // the handler will be re-enqueued with the remaining data at this point 254 | 255 | /* 256 | // Here we are once again relying upon CFErrorRef's magical 'fill in 257 | // the POSIX error userInfo' functionality. 258 | NSError * errObj = [NSError errorWithDomain: NSPOSIXErrorDomain code: error userInfo: nil]; 259 | 260 | NSData * unwritten = nil; 261 | if ( data != NULL ) 262 | unwritten = [[_AQDispatchData alloc] initWithDispatchData: data]; 263 | 264 | completionCopy(unwritten, errObj); 265 | #if USING_MRR 266 | [unwritten release]; 267 | #endif 268 | */ 269 | }); 270 | 271 | #if DISPATCH_USES_ARC == 0 272 | //dispatch_release(ddata); 273 | #endif 274 | 275 | #if USING_MRR 276 | // This has been captured by the block now, so we can release it. 277 | [completionCopy release]; 278 | #endif 279 | } 280 | 281 | @end 282 | 283 | #pragma mark - 284 | 285 | @implementation AQSocketLegacyIOChannel 286 | { 287 | dispatch_source_t _readerSource; 288 | } 289 | 290 | - (id) initWithNativeSocket: (CFSocketNativeHandle) nativeSocket cleanupHandler: (void (^)(void)) cleanupHandler 291 | { 292 | self = [super initWithNativeSocket: nativeSocket cleanupHandler: cleanupHandler]; 293 | if ( self == nil ) 294 | return ( nil ); 295 | 296 | _readerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, _nativeSocket, 0, _q); 297 | // leave it suspended until we get a read handler installed 298 | 299 | return ( self ); 300 | } 301 | 302 | - (void) dealloc 303 | { 304 | if ( _readerSource != NULL ) 305 | { 306 | dispatch_source_cancel(_readerSource); 307 | #if DISPATCH_USES_ARC == 0 308 | dispatch_release(_readerSource); 309 | #endif 310 | _readerSource = NULL; 311 | } 312 | #if USING_MRR 313 | [super dealloc]; 314 | #endif 315 | } 316 | 317 | - (void) close 318 | { 319 | if ( _readHandler != NULL ) 320 | { 321 | // this runs the cleanup handler, if any 322 | dispatch_source_cancel(_readerSource); 323 | #if DISPATCH_USES_ARC == 0 324 | dispatch_release(_readerSource); 325 | #endif 326 | _readerSource = NULL; 327 | } 328 | } 329 | 330 | - (void) setReadHandler: (void (^)(NSData *, NSError *)) readHandler 331 | { 332 | // Always suspend the reader source before swapping out an existing handler. 333 | // This also has the effect of mirroring the dispatch_resume() call after installing the event/cancel handlers. 334 | if ( _readHandler != nil ) 335 | dispatch_suspend(_readerSource); 336 | 337 | [super setReadHandler: readHandler]; 338 | if ( _readHandler == nil ) 339 | return; 340 | 341 | if ( _readerSource == NULL ) 342 | return; 343 | 344 | dispatch_source_set_cancel_handler(_readerSource, ^{ 345 | if ( _cleanupHandler != nil ) 346 | _cleanupHandler(); 347 | }); 348 | 349 | dispatch_source_set_event_handler(_readerSource, ^{ 350 | // read as much data as possible 351 | int flags = fcntl(_nativeSocket, F_GETFL, 0); 352 | BOOL isNonBlocking = ((flags & O_NONBLOCK) == O_NONBLOCK); 353 | if ( !isNonBlocking ) 354 | { 355 | // make it use nonblocking IO so we can receive zero-length data 356 | flags |= O_NONBLOCK; 357 | fcntl(_nativeSocket, F_SETFL, flags); 358 | } 359 | 360 | NSMutableData * data = [NSMutableData new]; 361 | NSError * error = nil; 362 | 363 | #define READ_BUFLEN 1024*8 364 | uint8_t buf[READ_BUFLEN]; 365 | ssize_t nread = 0; 366 | while ( (nread = recv(_nativeSocket, buf, READ_BUFLEN, 0)) > 0 ) 367 | { 368 | [data appendBytes:buf length:nread]; 369 | } 370 | 371 | if ( nread < 0 ) 372 | { 373 | // don't send errors for EAGAIN-- we just finished reading data is all, it's not an error that needs handling further up the chain 374 | int err = errno; 375 | if ( err != EAGAIN ) 376 | error = [[NSError alloc] initWithDomain: NSPOSIXErrorDomain code: err userInfo: nil]; 377 | } 378 | 379 | _readHandler(data, error); 380 | 381 | #if USING_MRR 382 | [data release]; 383 | [error release]; 384 | #endif 385 | 386 | // reset to blocking mode if appropriate 387 | if ( !isNonBlocking ) 388 | { 389 | flags &= ~O_NONBLOCK; 390 | fcntl(_nativeSocket, F_SETFL, flags); 391 | } 392 | }); 393 | 394 | dispatch_resume(_readerSource); 395 | } 396 | 397 | - (void) writeData: (NSData *) data withCompletion: (void (^)(NSData *, NSError *)) completion 398 | { 399 | // Ensure the completion block is on the heap, not the stack. 400 | void (^completionCopy)(NSData *, NSError *) = [completion copy]; 401 | 402 | // Run the write operation in the background. We use our serial queue to avoid spawning 1001 threads blocking on select(). 403 | dispatch_async(_q, ^{ 404 | ssize_t totalSent = 0; 405 | const uint8_t *p = [data bytes]; 406 | size_t len = [data length]; 407 | 408 | while (totalSent < [data length]) 409 | { 410 | ssize_t numSent = send(_nativeSocket, p, len, 0); 411 | int err = errno; 412 | if ( numSent < 0 && err != EAGAIN ) 413 | { 414 | NSError * error = [NSError errorWithDomain: NSPOSIXErrorDomain code: errno userInfo: nil]; 415 | dispatch_async(_q, ^{ 416 | completionCopy([data subdataWithRange: NSMakeRange(totalSent, len)], error); 417 | }); 418 | break; 419 | } 420 | else 421 | { 422 | if ( numSent >= 0 ) 423 | { 424 | totalSent += numSent; 425 | len -= numSent; 426 | } 427 | 428 | if ( totalSent == [data length] ) 429 | { 430 | // all done, no errors 431 | dispatch_async(_q, ^{ 432 | completionCopy(nil, nil); 433 | }); 434 | } 435 | else // sent partial data-- block until we receive more 436 | { 437 | fd_set wfds, efds; 438 | FD_ZERO(&wfds); 439 | FD_ZERO(&efds); 440 | FD_SET(_nativeSocket, &wfds); 441 | FD_SET(_nativeSocket, &efds); 442 | 443 | if ( select(_nativeSocket+1, NULL, &wfds, &efds, NULL) < 0 ) 444 | { 445 | // an error occurred. 446 | NSError * error = [NSError errorWithDomain: NSPOSIXErrorDomain code: errno userInfo: nil]; 447 | dispatch_async(_q, ^{ 448 | completionCopy([data subdataWithRange: NSMakeRange(totalSent, len)], error); 449 | }); 450 | 451 | break; 452 | } 453 | 454 | if ( FD_ISSET(_nativeSocket, &wfds) ) 455 | continue; // room available to write, so let's use it 456 | 457 | if ( FD_ISSET(_nativeSocket, &efds) ) 458 | { 459 | // an error occurred. 460 | int sockerr = 0; 461 | socklen_t slen = sizeof(int); 462 | getsockopt(_nativeSocket, SOL_SOCKET, SO_ERROR, &sockerr, &slen); 463 | 464 | NSError * error = [NSError errorWithDomain: NSPOSIXErrorDomain code: sockerr userInfo: nil]; 465 | dispatch_async(_q, ^{ 466 | completionCopy([data subdataWithRange: NSMakeRange(totalSent, len)], error); 467 | }); 468 | 469 | break; 470 | } 471 | } 472 | } 473 | } 474 | }); 475 | 476 | #if USING_MRR 477 | // This has been captured by the block now, so we can release it. 478 | [completionCopy release]; 479 | #endif 480 | } 481 | 482 | @end 483 | -------------------------------------------------------------------------------- /AQSocket/AQSocketReader+PrivateInternal.h: -------------------------------------------------------------------------------- 1 | // 2 | // AQSocketReader+PrivateInternal.h 3 | // AQSocket 4 | // 5 | // Created by Jim Dovey on 11-12-18. 6 | // Copyright (c) 2011 Jim Dovey. All rights reserved. 7 | // 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions 10 | // are met: 11 | // 12 | // Redistributions of source code must retain the above copyright notice, 13 | // this list of conditions and the following disclaimer. 14 | // 15 | // Redistributions in binary form must reproduce the above copyright 16 | // notice, this list of conditions and the following disclaimer in the 17 | // documentation and/or other materials provided with the distribution. 18 | // 19 | // Neither the name of the project's author nor the names of its 20 | // contributors may be used to endorse or promote products derived from 21 | // this software without specific prior written permission. 22 | // 23 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29 | // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31 | // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | // 35 | 36 | #import "AQSocketReader.h" 37 | 38 | @interface AQSocketReader (PrivateInternal) 39 | - (void) appendDispatchData: (dispatch_data_t) data; 40 | - (void) appendData: (NSData *) data; 41 | @end 42 | -------------------------------------------------------------------------------- /AQSocket/AQSocketReader.h: -------------------------------------------------------------------------------- 1 | // 2 | // AQSocketReader.h 3 | // AQSocket 4 | // 5 | // Created by Jim Dovey on 11-12-16. 6 | // Copyright (c) 2011 Jim Dovey. All rights reserved. 7 | // 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions 10 | // are met: 11 | // 12 | // Redistributions of source code must retain the above copyright notice, 13 | // this list of conditions and the following disclaimer. 14 | // 15 | // Redistributions in binary form must reproduce the above copyright 16 | // notice, this list of conditions and the following disclaimer in the 17 | // documentation and/or other materials provided with the distribution. 18 | // 19 | // Neither the name of the project's author nor the names of its 20 | // contributors may be used to endorse or promote products derived from 21 | // this software without specific prior written permission. 22 | // 23 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29 | // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31 | // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | // 35 | 36 | #import 37 | 38 | /// A special reader class where you can check and opt not to read some input 39 | /// until *n* bytes are actually available. If you leave some bytes unread, 40 | /// the next time data arrives on the socket those bytes will still be available 41 | /// to read from the new AQSocketReader instance. 42 | @interface AQSocketReader : NSObject 43 | 44 | /// Returns the total number of bytes available to read at this time. 45 | @property (nonatomic, readonly) NSUInteger length; 46 | 47 | /** 48 | Peeks at a number of available bytes. This method does not actually remove 49 | the returned bytes from any protocol buffers, so they will still be returned 50 | from a succeeding call to -readBytes: or -readBytes:size:. 51 | @param count The number of bytes at which to peek. 52 | @return An NSData object containing the peeked-at bytes, or `nil`. 53 | */ 54 | - (NSData *) peekBytes: (NSUInteger) count; 55 | 56 | /** 57 | Reads a number of bytes of data, removing them from the protocol buffers. 58 | @param count The number of bytes to read. 59 | @return An NSData object containing the read bytes, or `nil`. 60 | */ 61 | - (NSData *) readBytes: (NSUInteger) count; 62 | 63 | /** 64 | Reads a number of bytes of data into a caller-supplied buffer. 65 | @param buffer A pre-allocated buffer of at least `bufSize` bytes. 66 | @param bufSize The number of bytes allocated in `buffer`. 67 | @return The number of bytes copied into `buffer`. 68 | */ 69 | - (NSInteger) readBytes: (uint8_t *) buffer size: (NSUInteger) bufSize; 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /AQSocket/AQSocketReader.m: -------------------------------------------------------------------------------- 1 | // 2 | // AQSocketReader.m 3 | // AQSocket 4 | // 5 | // Created by Jim Dovey on 11-12-16. 6 | // Copyright (c) 2011 Jim Dovey. All rights reserved. 7 | // 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions 10 | // are met: 11 | // 12 | // Redistributions of source code must retain the above copyright notice, 13 | // this list of conditions and the following disclaimer. 14 | // 15 | // Redistributions in binary form must reproduce the above copyright 16 | // notice, this list of conditions and the following disclaimer in the 17 | // documentation and/or other materials provided with the distribution. 18 | // 19 | // Neither the name of the project's author nor the names of its 20 | // contributors may be used to endorse or promote products derived from 21 | // this software without specific prior written permission. 22 | // 23 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29 | // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31 | // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | // 35 | 36 | #import "AQSocketReader.h" 37 | #import "AQSocketReader+PrivateInternal.h" 38 | #import "AQSocketIOChannel.h" 39 | #import 40 | 41 | #define LOCKED(block) do { \ 42 | dispatch_semaphore_wait(self.lock, DISPATCH_TIME_FOREVER); \ 43 | @try { \ 44 | block(); \ 45 | } @finally { \ 46 | dispatch_semaphore_signal(self.lock); \ 47 | } \ 48 | } while (0) 49 | 50 | @interface AQSocketDispatchDataReader : AQSocketReader 51 | @end 52 | 53 | @interface AQSocketReader () 54 | @property (nonatomic, assign) size_t offset; 55 | @property (nonatomic, readonly) dispatch_semaphore_t lock; 56 | @end 57 | 58 | @implementation AQSocketReader 59 | { 60 | NSMutableData * _mutableData; 61 | size_t _offset; 62 | dispatch_semaphore_t _lock; 63 | } 64 | 65 | @synthesize offset=_offset, lock=_lock; 66 | 67 | + (id) allocWithZone: (NSZone *) zone 68 | { 69 | if ( self == [AQSocketReader class] && dispatch_data_create != 0 ) 70 | { 71 | return ( [AQSocketDispatchDataReader allocWithZone: zone] ); 72 | } 73 | 74 | return ( [super allocWithZone: zone] ); 75 | } 76 | 77 | - (id) init 78 | { 79 | self = [super init]; 80 | if ( self == nil ) 81 | return ( nil ); 82 | 83 | // By default, it has a NULL dispatch data object, since it's empty to start. 84 | 85 | // create a critical section lock 86 | _lock = dispatch_semaphore_create(1); 87 | 88 | return ( self ); 89 | } 90 | 91 | - (void) dealloc 92 | { 93 | #if DISPATCH_USES_ARC == 0 94 | if ( _lock != NULL ) 95 | { 96 | dispatch_release(_lock); 97 | _lock = NULL; 98 | } 99 | #endif 100 | #if USING_MRR 101 | [_mutableData release]; 102 | [super dealloc]; 103 | #endif 104 | } 105 | 106 | - (NSUInteger) length 107 | { 108 | return ( [_mutableData length] ); 109 | } 110 | 111 | - (NSData *) peekBytes: (NSUInteger) count 112 | { 113 | if ( count == 0 ) 114 | return ( nil ); 115 | 116 | __block NSData * result = nil; 117 | LOCKED(^{ 118 | result = [_mutableData subdataWithRange: NSMakeRange(0, MIN(count, [_mutableData length]))]; 119 | }); 120 | 121 | return ( result ); 122 | } 123 | 124 | - (NSData *) readBytes: (NSUInteger) count 125 | { 126 | if ( count == 0 ) 127 | return ( nil ); 128 | 129 | __block NSData * result = nil; 130 | LOCKED(^{ 131 | if ( count < [_mutableData length] ) 132 | { 133 | NSRange r = NSMakeRange(0, count); 134 | result = [_mutableData subdataWithRange: r]; 135 | [_mutableData replaceBytesInRange: r withBytes: NULL length: 0]; 136 | } 137 | else 138 | { 139 | result = [_mutableData copy]; 140 | [_mutableData setLength: 0]; 141 | #if USING_MRR 142 | [result autorelease]; 143 | #endif 144 | } 145 | }); 146 | 147 | return ( result ); 148 | } 149 | 150 | - (NSInteger) readBytes: (uint8_t *) buffer size: (NSUInteger) bufSize 151 | { 152 | if ( bufSize == 0 || buffer == NULL ) 153 | return ( 0 ); 154 | 155 | __block NSInteger copied = 0; 156 | LOCKED(^{ 157 | copied = MIN(bufSize, [_mutableData length]); 158 | if ( copied == 0 ) 159 | return; 160 | 161 | [_mutableData getBytes: buffer length: bufSize]; 162 | if ( copied == [_mutableData length] ) 163 | [_mutableData setLength: 0]; 164 | else 165 | [_mutableData replaceBytesInRange: NSMakeRange(0, copied) withBytes: NULL length: 0]; 166 | }); 167 | 168 | return ( copied ); 169 | } 170 | 171 | @end 172 | 173 | @implementation AQSocketReader (PrivateInternal) 174 | 175 | - (void) appendDispatchData: (dispatch_data_t) data 176 | { 177 | LOCKED(^{ 178 | if ( _mutableData == nil ) 179 | _mutableData = [[NSMutableData alloc] initWithCapacity: dispatch_data_get_size(data)]; 180 | 181 | // append all regions within the dispatch data 182 | dispatch_data_apply(data, ^bool(dispatch_data_t region, size_t offset, const void *buffer, size_t size) { 183 | [_mutableData appendBytes: buffer length: size]; 184 | return ( true ); 185 | }); 186 | }); 187 | } 188 | 189 | - (void) appendData: (NSData *) data 190 | { 191 | LOCKED(^{ 192 | if ( _mutableData == nil ) 193 | { 194 | _mutableData = [data mutableCopy]; 195 | } 196 | else 197 | { 198 | [_mutableData appendData: data]; 199 | } 200 | }); 201 | } 202 | 203 | @end 204 | 205 | #pragma mark - 206 | 207 | @implementation AQSocketDispatchDataReader 208 | { 209 | dispatch_data_t _data; 210 | } 211 | 212 | #if DISPATCH_USES_ARC == 0 213 | - (void) dealloc 214 | { 215 | if ( _data != NULL ) 216 | { 217 | dispatch_release(_data); 218 | _data = NULL; 219 | } 220 | #if USING_MRR 221 | [super dealloc]; 222 | #endif 223 | } 224 | #endif 225 | 226 | - (NSUInteger) length 227 | { 228 | if ( _data == NULL ) 229 | return ( 0 ); 230 | 231 | // we keep track of an offset to handle full reads of partial data regions 232 | return ( (NSUInteger)dispatch_data_get_size(_data) - self.offset ); 233 | } 234 | 235 | - (size_t) _copyBytes: (uint8_t *) buffer length: (size_t) length 236 | { 237 | __block size_t copied = 0; 238 | 239 | // iterate the regions in our data object until we've read `count` bytes 240 | dispatch_data_apply(_data, ^bool(dispatch_data_t region, size_t off, const void * buf, size_t size) { 241 | if ( off + self.offset > length ) 242 | return ( false ); 243 | 244 | // if there's nothing in the output buffer yet, take our global read offset into account 245 | if ( copied == 0 && self.offset != 0 ) 246 | { 247 | // tweak all the variables at once, to ease checking later on 248 | buf = (const void *)((const uint8_t *)buf + self.offset); 249 | size -= self.offset; 250 | off += self.offset; 251 | } 252 | 253 | size_t stillNeeded = copied - length; 254 | size_t sizeToCopy = stillNeeded > size ? size : size - stillNeeded; 255 | 256 | if ( sizeToCopy > 0 ) 257 | { 258 | memcpy(buffer + copied, buf, sizeToCopy); 259 | copied += sizeToCopy; 260 | } 261 | 262 | // if this region satisfied the read, then cease iteration 263 | if ( off + size >= length ) 264 | return ( false ); 265 | 266 | // otherwise, continue on to the next region 267 | return ( true ); 268 | }); 269 | 270 | return ( copied ); 271 | } 272 | 273 | - (void) _removeBytesOfLength: (NSUInteger) length 274 | { 275 | // NB: this is called while the lock is already held. 276 | if ( _data == NULL ) 277 | return; 278 | 279 | self.offset = self.offset - length; 280 | 281 | if ( self.length == length ) 282 | { 283 | #if DISPATCH_USES_ARC == 0 284 | dispatch_release(_data); 285 | #endif 286 | _data = NULL; 287 | return; 288 | } 289 | 290 | // simple version: just create a subrange data object 291 | dispatch_data_t newData = dispatch_data_create_subrange(_data, length, self.length - length); 292 | if ( newData == NULL ) 293 | return; // ARGH! 294 | #if DISPATCH_USES_ARC == 0 295 | dispatch_release(_data); 296 | #endif 297 | _data = newData; 298 | } 299 | 300 | - (NSData *) peekBytes: (NSUInteger) count 301 | { 302 | if ( _data == NULL ) 303 | return ( nil ); 304 | 305 | NSMutableData * result = [NSMutableData dataWithCapacity: count]; 306 | LOCKED(^{ 307 | [self _copyBytes: (uint8_t *)[result mutableBytes] 308 | length: count]; 309 | }); 310 | 311 | return ( result ); 312 | } 313 | 314 | - (NSData *) readBytes: (NSUInteger) count 315 | { 316 | if ( _data == NULL ) 317 | return ( nil ); 318 | 319 | NSMutableData * result = [NSMutableData dataWithLength: count]; 320 | 321 | LOCKED(^{ 322 | size_t copied = [self _copyBytes: (uint8_t*)[result mutableBytes] 323 | length: count]; 324 | [result setLength: copied]; 325 | self.offset = self.offset + copied; 326 | 327 | // adjust content parameters while the lock is still held 328 | [self _removeBytesOfLength: copied]; 329 | }); 330 | 331 | return ( result ); 332 | } 333 | 334 | - (NSInteger) readBytes: (uint8_t *) buffer size: (NSUInteger) bufSize 335 | { 336 | if ( _data == NULL ) 337 | return ( 0 ); 338 | 339 | __block NSInteger numRead = 0; 340 | LOCKED(^{ 341 | numRead = [self _copyBytes: buffer length: bufSize]; 342 | [self _removeBytesOfLength: bufSize]; 343 | }); 344 | 345 | return ( numRead ); 346 | } 347 | 348 | @end 349 | 350 | @implementation AQSocketDispatchDataReader (PrivateInternal) 351 | 352 | - (void) appendDispatchData: (dispatch_data_t) appendedData 353 | { 354 | LOCKED(^{ 355 | if ( _data == NULL ) 356 | { 357 | #if DISPATCH_USES_ARC == 0 358 | dispatch_retain(appendedData); 359 | #endif 360 | _data = appendedData; 361 | } 362 | else 363 | { 364 | dispatch_data_t newData = dispatch_data_create_concat(_data, appendedData); 365 | if ( newData != NULL ) 366 | { 367 | #if DISPATCH_USES_ARC == 0 368 | dispatch_release(_data); 369 | #endif 370 | _data = newData; 371 | } 372 | } 373 | }); 374 | } 375 | 376 | - (void) appendData: (NSData *) data 377 | { 378 | if ( [data length] == 0 ) 379 | return; 380 | 381 | if ( [data isKindOfClass: [_AQDispatchData class]] ) 382 | { 383 | _AQDispatchData * __data = (_AQDispatchData *)data; 384 | dispatch_data_t ddata = [__data _aq_getDispatchData]; 385 | [self appendDispatchData: ddata]; 386 | return; 387 | } 388 | 389 | // Ensure we have an immutable data object. If it's already immutable, this -copy just does -retain. 390 | NSData * dataCopy = [data copy]; 391 | dispatch_data_t ddata = dispatch_data_create([dataCopy bytes], [dataCopy length], dispatch_get_main_queue(), ^{ 392 | #if USING_MRR 393 | [dataCopy release]; 394 | #endif 395 | }); 396 | 397 | if ( ddata == NULL ) 398 | return; 399 | 400 | [self appendDispatchData: ddata]; 401 | #if DISPATCH_USES_ARC == 0 402 | dispatch_release(ddata); 403 | #endif 404 | } 405 | 406 | @end 407 | -------------------------------------------------------------------------------- /AppledocSettings.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | --company-id 6 | net.alanquatermain 7 | --logformat 8 | 1 9 | --print-settings 10 | 11 | --project-company 12 | Jim Dovey 13 | --project-name 14 | AQSocket 15 | --repeat-first-par 16 | 17 | --templates 18 | ./Templates 19 | --verbose 20 | 3 21 | --merge-categories 22 | 23 | --keep-merged-sections 24 | 25 | --keep-undocumented-objects 26 | 27 | --keep-undocumented-members 28 | 29 | --project-version 30 | 0.1 31 | 32 | 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | AQSocket 2 | Copyright (c) 2011 Jim Dovey. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | Neither the name of Kobo Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # AQSocket 2 | #### Copyright (c) 2011 Jim Dovey. All Rights Reserved. 3 | 4 | AQSocket is a simple Objective-C socket class written to make use of dispatch I/O channels on sockets. It uses a CFSocketRef to provide asynchronous connection, and uses dispatch sources and dispatch I/O channels to implement asynchronous read and write operations. -------------------------------------------------------------------------------- /Templates/docset/Contents/Resources/Documents/documents-template: -------------------------------------------------------------------------------- 1 | This is used only as placeholder for location of Documents directory! -------------------------------------------------------------------------------- /Templates/docset/Contents/Resources/nodes-template.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{projectName}} 6 | {{indexFilename}} 7 | 8 | {{#hasClasses}} 9 | 10 | {{strings/docset/classesTitle}} 11 | {{indexFilename}} 12 | 13 | {{#classes}}{{>NodeRef}} 14 | {{/classes}} 15 | 16 | 17 | {{/hasClasses}} 18 | {{#hasCategories}} 19 | 20 | {{strings/docset/categoriesTitle}} 21 | {{indexFilename}} 22 | 23 | {{#categories}}{{>NodeRef}} 24 | {{/categories}} 25 | 26 | 27 | {{/hasCategories}} 28 | {{#hasProtocols}} 29 | 30 | {{strings/docset/protocolsTitle}} 31 | {{indexFilename}} 32 | 33 | {{#protocols}}{{>NodeRef}} 34 | {{/protocols}} 35 | 36 | 37 | {{/hasProtocols}} 38 | 39 | 40 | 41 | 42 | {{#classes}}{{>Node}} 43 | {{/classes}} 44 | {{#categories}}{{>Node}} 45 | {{/categories}} 46 | {{#protocols}}{{>Node}} 47 | {{/protocols}} 48 | 49 | 50 | 51 | Section Node 52 | 53 | {{name}} 54 | {{path}} 55 | 56 | EndSection 57 | 58 | Section NodeRef 59 | 60 | EndSection 61 | -------------------------------------------------------------------------------- /Templates/docset/Contents/Resources/tokens-template.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{#object}} 5 | 6 | {{>TokenIdentifier}} 7 | {{>Abstract}} 8 | {{>DeclaredIn}} 9 | {{>RelatedTokens}} 10 | {{#refid}}{{/refid}} 11 | 12 | {{/object}} 13 | {{#members}} 14 | 15 | {{>TokenIdentifier}} 16 | {{>Abstract}} 17 | {{>DeclaredIn}} 18 | {{>RelatedTokens}} 19 | {{>MethodDeclaration}} 20 | {{#hasParameters}} 21 | {{#parameters}} 22 | {{name}} 23 | {{>Abstract}} 24 | {{/parameters}} 25 | {{/hasParameters}} 26 | {{#returnValue}}{{>Abstract}}{{/returnValue}} 27 | {{#anchor}}{{anchor}}{{/anchor}} 28 | 29 | {{/members}} 30 | 31 | 32 | 33 | Section TokenIdentifier 34 | {{identifier}} 35 | EndSection 36 | 37 | Section DeclaredIn 38 | {{declaredin}} 39 | EndSection 40 | 41 | Section RelatedTokens 42 | {{#hasRelatedTokens}} 43 | 44 | {{#relatedTokens}}{{.}} 45 | {{/relatedTokens}} 46 | 47 | {{/hasRelatedTokens}} 48 | EndSection 49 | 50 | Section Abstract 51 | {{#abstract}}{{>GBCommentComponentsList}}{{/abstract}} 52 | EndSection 53 | 54 | Section MethodDeclaration 55 | {{#formattedComponents}}{{value}}{{/formattedComponents}} 56 | EndSection 57 | 58 | Section GBCommentComponentsList 59 | {{#components}}{{&textValue}}{{/components}} 60 | EndSection 61 | 62 | -------------------------------------------------------------------------------- /Templates/docset/Contents/info-template.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | {{#bundleIdentifier}}CFBundleIdentifier 8 | {{bundleIdentifier}}{{/bundleIdentifier}} 9 | {{#bundleName}}CFBundleName 10 | {{bundleName}}{{/bundleName}} 11 | {{#bundleVersion}}CFBundleShortVersionString 12 | {{bundleVersion}} 13 | CFBundleVersion 14 | {{bundleVersion}}{{/bundleVersion}} 15 | {{#certificateIssuer}}DocSetCertificateIssuer 16 | {{certificateIssuer}}{{/certificateIssuer}} 17 | {{#certificateSigner}}DocSetCertificateSigner 18 | {{certificateSigner}}{{/certificateSigner}} 19 | {{#description}}DocSetDescription 20 | {{description}}{{/description}} 21 | {{#fallbackURL}}DocSetFallbackURL 22 | {{fallbackURL}}{{/fallbackURL}} 23 | {{#feedName}}DocSetFeedName 24 | {{feedName}}{{/feedName}} 25 | {{#feedURL}}DocSetFeedURL 26 | {{feedURL}}{{/feedURL}} 27 | {{#minimumXcodeVersion}}DocSetMinimumXcodeVersion 28 | {{minimumXcodeVersion}}{{/minimumXcodeVersion}} 29 | {{#platformFamily}}DocSetPlatformFamily 30 | {{platformFamily}}{{/platformFamily}} 31 | {{#publisherIdentifier}}DocSetPublisherIdentifier 32 | {{publisherIdentifier}}{{/publisherIdentifier}} 33 | {{#publisherName}}DocSetPublisherName 34 | {{publisherName}}{{/publisherName}} 35 | {{#copyrightMessage}}NSHumanReadableCopyright 36 | {{copyrightMessage}}{{/copyrightMessage}} 37 | 38 | 39 | -------------------------------------------------------------------------------- /Templates/html/css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; 3 | font-size: 13px; 4 | color: #000; 5 | } 6 | 7 | code { 8 | font-family: Courier, Consolas, monospace; 9 | font-size: 13px; 10 | color: #666; 11 | } 12 | 13 | pre { 14 | font-family: Courier, Consolas, monospace; 15 | font-size: 13px; 16 | line-height: 18px; 17 | tab-interval: 0.5em; 18 | border: 1px solid #C7CFD5; 19 | background-color: #F1F5F9; 20 | color: #666; 21 | padding: 0.3em 1em; 22 | } 23 | 24 | ul { 25 | list-style-type: square; 26 | } 27 | 28 | li { 29 | margin-bottom: 10px; 30 | } 31 | 32 | p:first-child { 33 | margin-top: 0.5em; 34 | } 35 | 36 | a code, 37 | a { 38 | text-decoration: none; 39 | color: #36C; 40 | } 41 | 42 | a:hover code, 43 | a:hover { 44 | text-decoration: underline; 45 | color: #36C; 46 | } 47 | 48 | /* @group Common page elements */ 49 | 50 | #top_header { 51 | height: 55px; 52 | left: 0; 53 | min-width: 598px; 54 | position: absolute; 55 | right: 0; 56 | top: 0; 57 | z-index: 900; 58 | } 59 | 60 | #footer { 61 | clear: both; 62 | padding-top: 20px; 63 | text-align: center; 64 | } 65 | 66 | #contents { 67 | border-top: 1px solid #2B334F; 68 | position: absolute; 69 | top: 56px; 70 | left: 0; 71 | right: 0; 72 | bottom: 0; 73 | overflow-x: hidden; 74 | overflow-y: auto; 75 | padding-left: 2em; 76 | padding-right: 2em; 77 | padding-top: 1em; 78 | min-width: 550px; 79 | } 80 | 81 | #contents.isShowingTOC { 82 | left: 230px; 83 | min-width: 320px; 84 | } 85 | 86 | #overview_contents { 87 | padding-left: 2em; 88 | padding-right: 2em; 89 | padding-top: 1em; 90 | min-width: 550px; 91 | } 92 | 93 | .copyright { 94 | font-size: 12px; 95 | } 96 | 97 | .generator { 98 | font-size: 11px; 99 | } 100 | 101 | .main-navigation ul li { 102 | display: inline; 103 | margin-left: 15px; 104 | list-style: none; 105 | } 106 | 107 | .navigation-top { 108 | clear: both; 109 | float: right; 110 | } 111 | 112 | .navigation-bottom { 113 | clear: both; 114 | float: right; 115 | margin-top: 20px; 116 | margin-bottom: -10px; 117 | } 118 | 119 | .open > .disclosure { 120 | background-image: url("../img/disclosure_open.png"); 121 | } 122 | 123 | .disclosure { 124 | background: url("../img/disclosure.png") no-repeat scroll 0 0; 125 | } 126 | 127 | .disclosure, .nodisclosure { 128 | display: inline-block; 129 | height: 8px; 130 | margin-right: 5px; 131 | position: relative; 132 | width: 9px; 133 | } 134 | 135 | /* @end */ 136 | 137 | /* @group Header */ 138 | 139 | #top_header #title { 140 | background: url("../img/title_background.png") repeat-x 0 0 #8A98A9; 141 | border-bottom: 1px solid #B6B6B6; 142 | height: 25px; 143 | overflow: hidden; 144 | } 145 | 146 | #top_header h1 { 147 | font-size: 115%; 148 | font-weight: normal; 149 | margin: 0; 150 | padding: 3px 0 2px; 151 | text-align: center; 152 | text-shadow: 0 1px 0 #D5D5D5; 153 | white-space: nowrap; 154 | } 155 | 156 | #headerButtons { 157 | background-color: #D8D8D8; 158 | background-image: url("../img/button_bar_background.png"); 159 | border-bottom: 1px solid #EDEDED; 160 | border-top: 1px solid #2B334F; 161 | font-size: 8pt; 162 | height: 28px; 163 | left: 0; 164 | list-style: none outside none; 165 | margin: 0; 166 | overflow: hidden; 167 | padding: 0; 168 | position: absolute; 169 | right: 0; 170 | top: 26px; 171 | } 172 | 173 | #headerButtons li { 174 | background-repeat: no-repeat; 175 | display: inline; 176 | margin-top: 0; 177 | margin-bottom: 0; 178 | padding: 0; 179 | } 180 | 181 | #toc_button button { 182 | border-color: #ACACAC; 183 | border-style: none solid none none; 184 | border-width: 0 1px 0 0; 185 | height: 28px; 186 | margin: 0; 187 | padding-left: 30px; 188 | text-align: left; 189 | width: 230px; 190 | } 191 | 192 | li#jumpto_button { 193 | left: 230px; 194 | margin-left: 0; 195 | position: absolute; 196 | } 197 | 198 | li#jumpto_button select { 199 | height: 22px; 200 | margin: 5px 2px 0 10px; 201 | max-width: 300px; 202 | } 203 | 204 | /* @end */ 205 | 206 | /* @group Table of contents */ 207 | 208 | #tocContainer.isShowingTOC { 209 | border-right: 1px solid #ACACAC; 210 | display: block; 211 | overflow-x: hidden; 212 | overflow-y: auto; 213 | padding: 0; 214 | } 215 | 216 | #tocContainer { 217 | background-color: #E4EBF7; 218 | border-top: 1px solid #2B334F; 219 | bottom: 0; 220 | display: none; 221 | left: 0; 222 | overflow: hidden; 223 | position: absolute; 224 | top: 56px; 225 | width: 229px; 226 | } 227 | 228 | #tocContainer > ul#toc { 229 | font-size: 11px; 230 | margin: 0; 231 | padding: 12px 0 18px; 232 | width: 209px; 233 | -moz-user-select: none; 234 | -webkit-user-select: none; 235 | user-select: none; 236 | } 237 | 238 | #tocContainer > ul#toc > li { 239 | margin: 0; 240 | padding: 0 0 7px 30px; 241 | text-indent: -15px; 242 | } 243 | 244 | #tocContainer > ul#toc > li > .sectionName a { 245 | color: #000000; 246 | font-weight: bold; 247 | } 248 | 249 | #tocContainer > ul#toc > li > .sectionName a:hover { 250 | text-decoration: none; 251 | } 252 | 253 | #tocContainer > ul#toc li.children > ul { 254 | display: none; 255 | height: 0; 256 | } 257 | 258 | #tocContainer > ul#toc > li > ul { 259 | margin: 0; 260 | padding: 0; 261 | } 262 | 263 | #tocContainer > ul#toc > li > ul, ul#toc > li > ul > li { 264 | margin-left: 0; 265 | margin-bottom: 0; 266 | padding-left: 15px; 267 | } 268 | 269 | #tocContainer > ul#toc > li ul { 270 | list-style: none; 271 | margin-right: 0; 272 | padding-right: 0; 273 | } 274 | 275 | #tocContainer > ul#toc li.children.open > ul { 276 | display: block; 277 | height: auto; 278 | margin-left: -15px; 279 | padding-left: 0; 280 | } 281 | 282 | #tocContainer > ul#toc > li > ul, ul#toc > li > ul > li { 283 | margin-left: 0; 284 | padding-left: 15px; 285 | } 286 | 287 | #tocContainer li ul li { 288 | margin-top: 0.583em; 289 | overflow: hidden; 290 | text-overflow: ellipsis; 291 | white-space: nowrap; 292 | } 293 | 294 | #tocContainer li ul li span.sectionName { 295 | white-space: normal; 296 | } 297 | 298 | #tocContainer > ul#toc > li > ul > li > .sectionName a { 299 | font-weight: bold; 300 | } 301 | 302 | #tocContainer > ul#toc > li > ul a { 303 | color: #4F4F4F; 304 | } 305 | 306 | /* @end */ 307 | 308 | /* @group Index formatting */ 309 | 310 | .index-title { 311 | font-size: 13px; 312 | font-weight: normal; 313 | } 314 | 315 | .index-column { 316 | float: left; 317 | width: 30%; 318 | min-width: 200px; 319 | font-size: 11px; 320 | } 321 | 322 | .index-column ul { 323 | margin: 8px 0 0 0; 324 | padding: 0; 325 | list-style: none; 326 | } 327 | 328 | .index-column ul li { 329 | margin: 0 0 3px 0; 330 | padding: 0; 331 | } 332 | 333 | .hierarchy-column { 334 | min-width: 400px; 335 | } 336 | 337 | .hierarchy-column ul { 338 | margin: 3px 0 0 15px; 339 | } 340 | 341 | .hierarchy-column ul li { 342 | list-style-type: square; 343 | } 344 | 345 | /* @end */ 346 | 347 | /* @group Common formatting elements */ 348 | 349 | .title { 350 | font-weight: normal; 351 | font-size: 250%; 352 | margin-top:0; 353 | } 354 | 355 | .subtitle, 356 | .index-overview h1 { 357 | font-weight: normal; 358 | font-size: 180%; 359 | color: #3C4C6C; 360 | border-bottom: 1px solid #5088C5; 361 | } 362 | 363 | .subsubtitle { 364 | font-weight: normal; 365 | font-size: 145%; 366 | height: 0.7em; 367 | } 368 | 369 | .warning { 370 | border: 1px solid #5088C5; 371 | background-color: #F0F3F7; 372 | margin-bottom: 0.5em; 373 | padding: 0em 0.8em; 374 | } 375 | 376 | .bug { 377 | border: 1px solid #000; 378 | background-color: #ffffcc; 379 | margin-bottom: 0.5em; 380 | padding: 0em 0.8em; 381 | } 382 | 383 | /* @end */ 384 | 385 | /* @group Common layout */ 386 | 387 | .section { 388 | margin-top: 3em; 389 | } 390 | 391 | /* @end */ 392 | 393 | /* @group Object specification section */ 394 | 395 | .section-specification { 396 | margin-left: 2.5em; 397 | margin-right: 2.5em; 398 | font-size: 12px; 399 | } 400 | 401 | .section-specification table { 402 | border-top: 1px solid #d6e0e5; 403 | } 404 | 405 | .section-specification td { 406 | vertical-align: top; 407 | border-bottom: 1px solid #d6e0e5; 408 | padding: .6em; 409 | } 410 | 411 | .section-specification .specification-title { 412 | font-weight: bold; 413 | } 414 | 415 | /* @end */ 416 | 417 | /* @group Tasks section */ 418 | 419 | .task-list { 420 | list-style-type: none; 421 | padding-left: 0px; 422 | } 423 | 424 | .task-list li { 425 | margin-bottom: 3px; 426 | } 427 | 428 | .task-item-suffix { 429 | color: #996; 430 | font-size: 12px; 431 | font-style: italic; 432 | margin-left: 0.5em; 433 | } 434 | 435 | span.tooltip span.tooltip { 436 | font-size: 1.0em; 437 | display: none; 438 | padding: 0.2em 0.3em; 439 | border: 1px solid #aaa; 440 | background-color: #fdfec8; 441 | color: #000; 442 | text-align: left; 443 | } 444 | 445 | span.tooltip span.tooltip p { 446 | margin: 0.1em 0.3em; 447 | } 448 | 449 | span.tooltip:hover span.tooltip { 450 | display: block; 451 | position: absolute; 452 | margin-left: 2em; 453 | } 454 | 455 | /* @end */ 456 | 457 | /* @group Method section */ 458 | 459 | .section-method { 460 | margin-top: 2.3em; 461 | } 462 | 463 | .method-title { 464 | margin-bottom: 1.5em; 465 | } 466 | 467 | .method-subtitle { 468 | margin-top: 0.7em; 469 | margin-bottom: 0.2em; 470 | } 471 | 472 | .method-subsection p { 473 | margin-top: 0.4em; 474 | margin-bottom: 0.8em; 475 | } 476 | 477 | .method-declaration { 478 | margin-top:1.182em; 479 | margin-bottom:.909em; 480 | } 481 | 482 | .method-declaration code { 483 | font:14px Courier, Consolas, monospace; 484 | color:#000; 485 | } 486 | 487 | .declaration { 488 | color: #000; 489 | } 490 | 491 | .argument-def { 492 | margin-top: 0.3em; 493 | margin-bottom: 0.3em; 494 | } 495 | 496 | .argument-def dd { 497 | margin-left: 1.25em; 498 | } 499 | 500 | .see-also-section ul { 501 | list-style-type: none; 502 | padding-left: 0px; 503 | margin-top: 0; 504 | } 505 | 506 | .see-also-section li { 507 | margin-bottom: 3px; 508 | } 509 | 510 | .see-also-section p { 511 | margin-top: 0; 512 | margin-bottom: 0; 513 | } 514 | 515 | .declared-in-ref { 516 | color: #666; 517 | } 518 | 519 | /* @end */ 520 | 521 | -------------------------------------------------------------------------------- /Templates/html/document-template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{page/title}} 5 | 6 | 7 | 8 | 9 | {{#strings/appledocData}}{{/strings/appledocData}} 10 | 11 | 12 |
13 | 14 |
15 | {{#object/comment}}{{#longDescription}}{{>GBCommentComponentsList}}{{/longDescription}}{{/object/comment}} 16 |
17 |
18 | 19 | 20 | 21 | 22 | Section GBCommentComponentsList 23 | {{#components}}{{>GBCommentComponent}}{{/components}} 24 | EndSection 25 | 26 | Section GBCommentComponent 27 | {{&htmlValue}} 28 | EndSection 29 | -------------------------------------------------------------------------------- /Templates/html/hierarchy-template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{page/title}} 5 | 6 | 7 | {{#strings/appledocData}}{{/strings/appledocData}} 8 | 9 | 10 |
11 |
12 | 15 | 20 |
21 | {{#hasClasses}} 22 |
23 |

{{strings/hierarchyPage/classesTitle}}

24 | {{>Classes}} 25 |
26 | {{/hasClasses}} 27 | 28 | {{#hasProtocolsOrCategories}} 29 |
30 | {{#hasProtocols}} 31 |

{{strings/hierarchyPage/protocolsTitle}}

32 |
    33 | {{#protocols}} 34 |
  • {{title}}
  • 35 | {{/protocols}} 36 |
37 | {{/hasProtocols}} 38 | {{#hasCategories}} 39 |

{{strings/hierarchyPage/categoriesTitle}}

40 |
    41 | {{#categories}} 42 |
  • {{title}}
  • 43 | {{/categories}} 44 |
45 | {{/hasCategories}} 46 |
47 | {{/hasProtocolsOrCategories}} 48 |
49 | 52 | 62 |
63 |
64 | 65 | 66 | 67 | Section Classes 68 | {{#hasClasses}} 69 | 74 | {{/hasClasses}} 75 | EndSection 76 | 77 | Section Navigation 78 | Previous 79 | EndSection -------------------------------------------------------------------------------- /Templates/html/img/button_bar_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanQuatermain/AQSocket/b9f4e62b23485d46cab510b041047fc87d187a15/Templates/html/img/button_bar_background.png -------------------------------------------------------------------------------- /Templates/html/img/disclosure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanQuatermain/AQSocket/b9f4e62b23485d46cab510b041047fc87d187a15/Templates/html/img/disclosure.png -------------------------------------------------------------------------------- /Templates/html/img/disclosure_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanQuatermain/AQSocket/b9f4e62b23485d46cab510b041047fc87d187a15/Templates/html/img/disclosure_open.png -------------------------------------------------------------------------------- /Templates/html/img/title_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanQuatermain/AQSocket/b9f4e62b23485d46cab510b041047fc87d187a15/Templates/html/img/title_background.png -------------------------------------------------------------------------------- /Templates/html/index-template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{page/title}} 5 | 6 | 7 | {{#strings/appledocData}}{{/strings/appledocData}} 8 | 9 | 10 |
11 |
12 | 15 | 20 |
21 | {{#indexDescription}} 22 |
23 | {{#comment}} 24 | {{#hasLongDescription}} 25 | {{#longDescription}}{{#components}}{{&htmlValue}}{{/components}}{{/longDescription}} 26 | {{/hasLongDescription}} 27 | {{/comment}} 28 |
29 | {{/indexDescription}} 30 | 31 | {{#hasClasses}} 32 |
33 |

{{strings/indexPage/classesTitle}}

34 |
    35 | {{#classes}} 36 |
  • {{title}}
  • 37 | {{/classes}} 38 |
39 |
40 | {{/hasClasses}} 41 | 42 | {{#hasProtocolsOrCategories}} 43 |
44 | {{#hasProtocols}} 45 |

{{strings/indexPage/protocolsTitle}}

46 |
    47 | {{#protocols}} 48 |
  • {{title}}
  • 49 | {{/protocols}} 50 |
51 | {{/hasProtocols}} 52 | {{#hasCategories}} 53 |

{{strings/indexPage/categoriesTitle}}

54 |
    55 | {{#categories}} 56 |
  • {{title}}
  • 57 | {{/categories}} 58 |
59 | {{/hasCategories}} 60 |
61 | {{/hasProtocolsOrCategories}} 62 |
63 | 66 | 76 |
77 |
78 | 79 | 80 | 81 | Section Navigation 82 | Next 83 | EndSection -------------------------------------------------------------------------------- /Templates/html/object-template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{page/title}} 5 | 6 | 7 | 8 | 9 | {{#strings/appledocData}}{{/strings/appledocData}} 10 | 11 | 12 |
13 | 16 | 24 |
25 | 30 |
31 |
32 | 33 | 36 | 41 |
42 | {{#page/specifications}} 43 | {{#used}}
{{/used}} 44 | {{#values}}{{>ObjectSpecification}}{{/values}} 45 | {{#used}}
{{/used}} 46 | {{/page/specifications}} 47 | 48 | {{#object/comment}} 49 | {{#hasLongDescription}} 50 |
51 | 52 |

{{strings/objectOverview/title}}

53 | {{#longDescription}}{{>GBCommentComponentsList}}{{/longDescription}} 54 |
55 | {{/hasLongDescription}} 56 | {{/object/comment}} 57 | 58 | {{#object/methods}} 59 | {{#hasSections}} 60 |
61 | 62 |

{{strings/objectTasks/title}}

63 | {{#sections}} 64 | 65 | {{#sectionName}}{{/sectionName}} 66 | {{>TaskTitle}} 67 |
    68 | {{#methods}}{{>TaskMethod}}{{/methods}} 69 |
70 | {{/sections}} 71 |
72 | {{/hasSections}} 73 | {{/object/methods}} 74 | 75 | {{#object/methods}} 76 | {{#hasProperties}} 77 |
78 | 79 |

{{strings/objectMethods/propertiesTitle}}

80 | {{#properties}} 81 | {{>Method}} 82 | {{/properties}} 83 |
84 | {{/hasProperties}} 85 | 86 | {{#hasClassMethods}} 87 |
88 | 89 |

{{strings/objectMethods/classMethodsTitle}}

90 | {{#classMethods}} 91 | {{>Method}} 92 | {{/classMethods}} 93 |
94 | {{/hasClassMethods}} 95 | 96 | {{#hasInstanceMethods}} 97 |
98 | 99 |

{{strings/objectMethods/instanceMethodsTitle}}

100 | {{#instanceMethods}} 101 | {{>Method}} 102 | {{/instanceMethods}} 103 |
104 | {{/hasInstanceMethods}} 105 | {{/object/methods}} 106 |
107 | 110 | 119 |
120 |
121 | 204 | 205 | 206 | 207 | 208 | Section Method 209 |
210 | 211 |

{{methodSelector}}

212 | 213 | {{#comment}} 214 | {{#hasShortDescription}} 215 |
216 | {{#shortDescription}}{{>GBCommentComponent}}{{/shortDescription}} 217 |
218 | {{/hasShortDescription}} 219 | 220 |
{{>MethodDeclaration}}
221 | 222 | {{#hasMethodParameters}} 223 |
224 |

{{strings/objectMethods/parametersTitle}}

225 | {{#methodParameters}} 226 |
227 |
{{argumentName}}
228 |
{{#argumentDescription}}{{>GBCommentComponentsList}}{{/argumentDescription}}
229 |
230 | {{/methodParameters}} 231 |
232 | {{/hasMethodParameters}} 233 | 234 | {{#hasMethodResult}} 235 |
236 |

{{strings/objectMethods/resultTitle}}

237 | {{#methodResult}}{{>GBCommentComponentsList}}{{/methodResult}} 238 |
239 | {{/hasMethodResult}} 240 | 241 | {{#hasLongDescription}} 242 |
243 |

{{strings/objectMethods/discussionTitle}}

244 | {{#longDescription}}{{>GBCommentComponentsList}}{{/longDescription}} 245 |
246 | {{/hasLongDescription}} 247 | 248 | {{#hasMethodExceptions}} 249 |
250 |

{{strings/objectMethods/exceptionsTitle}}

251 | {{#methodExceptions}} 252 |
253 |
{{argumentName}}
254 |
{{#argumentDescription}}{{>GBCommentComponentsList}}{{/argumentDescription}}
255 |
256 | {{/methodExceptions}} 257 |
258 | {{/hasMethodExceptions}} 259 | 260 | {{#hasRelatedItems}} 261 |
262 |

{{strings/objectMethods/seeAlsoTitle}}

263 |
    264 | {{#relatedItems/components}} 265 |
  • {{>GBCommentComponent}}
  • 266 | {{/relatedItems/components}} 267 |
268 |
269 | {{/hasRelatedItems}} 270 | 271 | {{#prefferedSourceInfo}} 272 |
273 |

{{strings/objectMethods/declaredInTitle}}

274 | {{filename}}
275 |
276 | {{/prefferedSourceInfo}} 277 | {{/comment}} 278 |
279 | EndSection 280 | 281 | Section MethodDeclaration 282 | {{#formattedComponents}}{{#emphasized}}{{/emphasized}}{{#href}}{{/href}}{{value}}{{#href}}{{/href}}{{#emphasized}}{{/emphasized}}{{/formattedComponents}} 283 | EndSection 284 | 285 | 286 | Section TaskTitle 287 | {{#hasMultipleSections}}

{{#sectionName}}{{.}}{{/sectionName}}{{^sectionName}}{{strings/objectTasks/otherMethodsSectionName}}{{/sectionName}}

{{/hasMultipleSections}} 288 | {{^hasMultipleSections}}{{#sectionName}}

{{.}}

{{/sectionName}}{{/hasMultipleSections}} 289 | EndSection 290 | 291 | Section TaskMethod 292 |
  • 293 | 294 | {{>TaskSelector}} 295 | {{#comment}}{{#hasShortDescription}}{{#shortDescription}}{{>GBCommentComponent}}{{/shortDescription}}{{/hasShortDescription}}{{/comment}} 296 | 297 | {{#isProperty}}{{strings/objectTasks/property}}{{/isProperty}} 298 | {{#isRequired}}{{strings/objectTasks/requiredMethod}}{{/isRequired}} 299 |
  • 300 | EndSection 301 | 302 | Section TaskSelector 303 | {{#isInstanceMethod}}– {{/isInstanceMethod}}{{#isClassMethod}}+ {{/isClassMethod}}{{#isProperty}}  {{/isProperty}}{{methodSelector}} 304 | EndSection 305 | 306 | 307 | Section GBCommentComponentsList 308 | {{#components}}{{>GBCommentComponent}}{{/components}} 309 | EndSection 310 | 311 | Section GBCommentComponent 312 | {{&htmlValue}} 313 | EndSection 314 | 315 | Section ObjectSpecification 316 | 317 | {{title}} 318 | {{#values}}{{#href}}{{/href}}{{string}}{{#href}}{{/href}}{{&delimiter}}{{/values}} 319 | 320 | EndSection 321 | 322 | 323 | Section Navigation 324 | 328 | EndSection 329 | 330 | Section JumpTo 331 | 366 | EndSection 367 | 368 | Section TableOfContents 369 | {{#object/comment}} 370 |
  • {{strings/objectOverview/title}}
  • 371 | {{/object/comment}} 372 | 373 | {{#object/methods}} 374 | {{#hasSections}} 375 |
  • {{strings/objectTasks/title}}
  • 380 | {{/hasSections}} 381 | {{/object/methods}} 382 | 383 | {{#object/methods}} 384 | {{#hasProperties}} 385 |
  • {{strings/objectMethods/propertiesTitle}}
  • 390 | {{/hasProperties}} 391 | 392 | {{#hasClassMethods}} 393 |
  • {{strings/objectMethods/classMethodsTitle}}
  • 398 | {{/hasClassMethods}} 399 | 400 | {{#hasInstanceMethods}} 401 |
  • {{strings/objectMethods/instanceMethodsTitle}}
  • 406 | {{/hasInstanceMethods}} 407 | {{/object/methods}} 408 | EndSection -------------------------------------------------------------------------------- /build-docset.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | appledoc ./AppledocSettings.plist ./AQSocket/*.h 3 | --------------------------------------------------------------------------------