├── README.md ├── ServiceApp.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ ├── Joymake.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ │ └── wangguopeng.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ ├── Joymake.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ └── xcschememanagement.plist │ └── wangguopeng.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── ServiceApp.xcscheme │ └── xcschememanagement.plist ├── ServiceApp ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── LifeWork128@1x.png │ │ ├── LifeWork128@2x.png │ │ ├── LifeWork32@1x.png │ │ └── LifeWork32@2x.png │ └── Contents.json ├── Base.lproj │ └── MainMenu.xib ├── GCDAsyncSocket+category.h ├── GCDAsyncSocket+category.m ├── GCDAsyncSocket.h ├── GCDAsyncSocket.m ├── GCDAsyncUdpSocket.h ├── GCDAsyncUdpSocket.m ├── Info.plist ├── LifeWork.png ├── MainWindow.h ├── MainWindow.m ├── SerViceAPP.h ├── SerViceAPP.m └── main.m ├── ServiceAppTests ├── Info.plist └── ServiceAppTests.m └── ServiceAppUITests ├── Info.plist └── ServiceAppUITests.m /README.md: -------------------------------------------------------------------------------- 1 | # socketServer [->简书讲解](http://www.jianshu.com/p/81fd2464b14c) 2 | ![socket通讯](http://upload-images.jianshu.io/upload_images/1488115-aabd39cf6983688a.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 3 | 4 | 一款用于测试socket数据的mac服务器 app 5 | 6 | 1.打开应用后,会自动获取本机ip并显示,端口默认8080 7 | 8 | 2.点击"开始监控",进行端口8080的数据监听 9 | 10 | 3.当服务器接收到数据后会显示来源地址、端口以及数据内容,如果有多个socket连接到本服务器,则会进行数据转发 11 | 12 | 比如a、b、c同时连接socket服务器,那么 13 | 14 | 当a向服务器传送数据时,服务器会将数据转发给b、c,同理b发送数据时a和c也可收到 15 | 16 | -------------------------------------------------------------------------------- /ServiceApp.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2F5B50EB1EB0B29000C79DEE /* GCDAsyncSocket+category.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F5B50EA1EB0B29000C79DEE /* GCDAsyncSocket+category.m */; }; 11 | 2F88073D1E56A0E20029FB4B /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F88073C1E56A0E20029FB4B /* AppDelegate.m */; }; 12 | 2F8807401E56A0E20029FB4B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F88073F1E56A0E20029FB4B /* main.m */; }; 13 | 2F8807421E56A0E20029FB4B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2F8807411E56A0E20029FB4B /* Assets.xcassets */; }; 14 | 2F8807451E56A0E20029FB4B /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2F8807431E56A0E20029FB4B /* MainMenu.xib */; }; 15 | 2F8807501E56A0E30029FB4B /* ServiceAppTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F88074F1E56A0E30029FB4B /* ServiceAppTests.m */; }; 16 | 2F88075B1E56A0E30029FB4B /* ServiceAppUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F88075A1E56A0E30029FB4B /* ServiceAppUITests.m */; }; 17 | 2F88076A1E56A2C60029FB4B /* MainWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F8807691E56A2C60029FB4B /* MainWindow.m */; }; 18 | 2F88076F1E56A9F20029FB4B /* GCDAsyncSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F88076C1E56A9F20029FB4B /* GCDAsyncSocket.m */; }; 19 | 2F8807701E56A9F20029FB4B /* GCDAsyncUdpSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F88076E1E56A9F20029FB4B /* GCDAsyncUdpSocket.m */; }; 20 | 2F8807751E56AB8F0029FB4B /* SerViceAPP.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F8807741E56AB8F0029FB4B /* SerViceAPP.m */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | 2F88074C1E56A0E20029FB4B /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = 2F8807301E56A0E20029FB4B /* Project object */; 27 | proxyType = 1; 28 | remoteGlobalIDString = 2F8807371E56A0E20029FB4B; 29 | remoteInfo = ServiceApp; 30 | }; 31 | 2F8807571E56A0E30029FB4B /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = 2F8807301E56A0E20029FB4B /* Project object */; 34 | proxyType = 1; 35 | remoteGlobalIDString = 2F8807371E56A0E20029FB4B; 36 | remoteInfo = ServiceApp; 37 | }; 38 | /* End PBXContainerItemProxy section */ 39 | 40 | /* Begin PBXFileReference section */ 41 | 2F5B50E91EB0B29000C79DEE /* GCDAsyncSocket+category.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GCDAsyncSocket+category.h"; sourceTree = ""; }; 42 | 2F5B50EA1EB0B29000C79DEE /* GCDAsyncSocket+category.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GCDAsyncSocket+category.m"; sourceTree = ""; }; 43 | 2F8807381E56A0E20029FB4B /* ServiceApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ServiceApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | 2F88073B1E56A0E20029FB4B /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 45 | 2F88073C1E56A0E20029FB4B /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 46 | 2F88073F1E56A0E20029FB4B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 47 | 2F8807411E56A0E20029FB4B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 2F8807441E56A0E20029FB4B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 49 | 2F8807461E56A0E20029FB4B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | 2F88074B1E56A0E20029FB4B /* ServiceAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ServiceAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | 2F88074F1E56A0E30029FB4B /* ServiceAppTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ServiceAppTests.m; sourceTree = ""; }; 52 | 2F8807511E56A0E30029FB4B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 53 | 2F8807561E56A0E30029FB4B /* ServiceAppUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ServiceAppUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | 2F88075A1E56A0E30029FB4B /* ServiceAppUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ServiceAppUITests.m; sourceTree = ""; }; 55 | 2F88075C1E56A0E30029FB4B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 56 | 2F8807681E56A2C60029FB4B /* MainWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainWindow.h; sourceTree = ""; }; 57 | 2F8807691E56A2C60029FB4B /* MainWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainWindow.m; sourceTree = ""; }; 58 | 2F88076B1E56A9F20029FB4B /* GCDAsyncSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDAsyncSocket.h; sourceTree = ""; }; 59 | 2F88076C1E56A9F20029FB4B /* GCDAsyncSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDAsyncSocket.m; sourceTree = ""; }; 60 | 2F88076D1E56A9F20029FB4B /* GCDAsyncUdpSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDAsyncUdpSocket.h; sourceTree = ""; }; 61 | 2F88076E1E56A9F20029FB4B /* GCDAsyncUdpSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDAsyncUdpSocket.m; sourceTree = ""; }; 62 | 2F8807731E56AB8F0029FB4B /* SerViceAPP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SerViceAPP.h; sourceTree = ""; }; 63 | 2F8807741E56AB8F0029FB4B /* SerViceAPP.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SerViceAPP.m; sourceTree = ""; }; 64 | /* End PBXFileReference section */ 65 | 66 | /* Begin PBXFrameworksBuildPhase section */ 67 | 2F8807351E56A0E20029FB4B /* Frameworks */ = { 68 | isa = PBXFrameworksBuildPhase; 69 | buildActionMask = 2147483647; 70 | files = ( 71 | ); 72 | runOnlyForDeploymentPostprocessing = 0; 73 | }; 74 | 2F8807481E56A0E20029FB4B /* Frameworks */ = { 75 | isa = PBXFrameworksBuildPhase; 76 | buildActionMask = 2147483647; 77 | files = ( 78 | ); 79 | runOnlyForDeploymentPostprocessing = 0; 80 | }; 81 | 2F8807531E56A0E30029FB4B /* Frameworks */ = { 82 | isa = PBXFrameworksBuildPhase; 83 | buildActionMask = 2147483647; 84 | files = ( 85 | ); 86 | runOnlyForDeploymentPostprocessing = 0; 87 | }; 88 | /* End PBXFrameworksBuildPhase section */ 89 | 90 | /* Begin PBXGroup section */ 91 | 2F88072F1E56A0E20029FB4B = { 92 | isa = PBXGroup; 93 | children = ( 94 | 2F88073A1E56A0E20029FB4B /* ServiceApp */, 95 | 2F88074E1E56A0E20029FB4B /* ServiceAppTests */, 96 | 2F8807591E56A0E30029FB4B /* ServiceAppUITests */, 97 | 2F8807391E56A0E20029FB4B /* Products */, 98 | ); 99 | sourceTree = ""; 100 | }; 101 | 2F8807391E56A0E20029FB4B /* Products */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 2F8807381E56A0E20029FB4B /* ServiceApp.app */, 105 | 2F88074B1E56A0E20029FB4B /* ServiceAppTests.xctest */, 106 | 2F8807561E56A0E30029FB4B /* ServiceAppUITests.xctest */, 107 | ); 108 | name = Products; 109 | sourceTree = ""; 110 | }; 111 | 2F88073A1E56A0E20029FB4B /* ServiceApp */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 2F88073B1E56A0E20029FB4B /* AppDelegate.h */, 115 | 2F88073C1E56A0E20029FB4B /* AppDelegate.m */, 116 | 2F88076B1E56A9F20029FB4B /* GCDAsyncSocket.h */, 117 | 2F88076C1E56A9F20029FB4B /* GCDAsyncSocket.m */, 118 | 2F88076D1E56A9F20029FB4B /* GCDAsyncUdpSocket.h */, 119 | 2F88076E1E56A9F20029FB4B /* GCDAsyncUdpSocket.m */, 120 | 2F8807731E56AB8F0029FB4B /* SerViceAPP.h */, 121 | 2F8807741E56AB8F0029FB4B /* SerViceAPP.m */, 122 | 2F5B50E91EB0B29000C79DEE /* GCDAsyncSocket+category.h */, 123 | 2F5B50EA1EB0B29000C79DEE /* GCDAsyncSocket+category.m */, 124 | 2F8807681E56A2C60029FB4B /* MainWindow.h */, 125 | 2F8807691E56A2C60029FB4B /* MainWindow.m */, 126 | 2F8807411E56A0E20029FB4B /* Assets.xcassets */, 127 | 2F8807431E56A0E20029FB4B /* MainMenu.xib */, 128 | 2F8807461E56A0E20029FB4B /* Info.plist */, 129 | 2F88073E1E56A0E20029FB4B /* Supporting Files */, 130 | ); 131 | path = ServiceApp; 132 | sourceTree = ""; 133 | }; 134 | 2F88073E1E56A0E20029FB4B /* Supporting Files */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | 2F88073F1E56A0E20029FB4B /* main.m */, 138 | ); 139 | name = "Supporting Files"; 140 | sourceTree = ""; 141 | }; 142 | 2F88074E1E56A0E20029FB4B /* ServiceAppTests */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | 2F88074F1E56A0E30029FB4B /* ServiceAppTests.m */, 146 | 2F8807511E56A0E30029FB4B /* Info.plist */, 147 | ); 148 | path = ServiceAppTests; 149 | sourceTree = ""; 150 | }; 151 | 2F8807591E56A0E30029FB4B /* ServiceAppUITests */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 2F88075A1E56A0E30029FB4B /* ServiceAppUITests.m */, 155 | 2F88075C1E56A0E30029FB4B /* Info.plist */, 156 | ); 157 | path = ServiceAppUITests; 158 | sourceTree = ""; 159 | }; 160 | /* End PBXGroup section */ 161 | 162 | /* Begin PBXNativeTarget section */ 163 | 2F8807371E56A0E20029FB4B /* ServiceApp */ = { 164 | isa = PBXNativeTarget; 165 | buildConfigurationList = 2F88075F1E56A0E30029FB4B /* Build configuration list for PBXNativeTarget "ServiceApp" */; 166 | buildPhases = ( 167 | 2F8807341E56A0E20029FB4B /* Sources */, 168 | 2F8807351E56A0E20029FB4B /* Frameworks */, 169 | 2F8807361E56A0E20029FB4B /* Resources */, 170 | ); 171 | buildRules = ( 172 | ); 173 | dependencies = ( 174 | ); 175 | name = ServiceApp; 176 | productName = ServiceApp; 177 | productReference = 2F8807381E56A0E20029FB4B /* ServiceApp.app */; 178 | productType = "com.apple.product-type.application"; 179 | }; 180 | 2F88074A1E56A0E20029FB4B /* ServiceAppTests */ = { 181 | isa = PBXNativeTarget; 182 | buildConfigurationList = 2F8807621E56A0E30029FB4B /* Build configuration list for PBXNativeTarget "ServiceAppTests" */; 183 | buildPhases = ( 184 | 2F8807471E56A0E20029FB4B /* Sources */, 185 | 2F8807481E56A0E20029FB4B /* Frameworks */, 186 | 2F8807491E56A0E20029FB4B /* Resources */, 187 | ); 188 | buildRules = ( 189 | ); 190 | dependencies = ( 191 | 2F88074D1E56A0E20029FB4B /* PBXTargetDependency */, 192 | ); 193 | name = ServiceAppTests; 194 | productName = ServiceAppTests; 195 | productReference = 2F88074B1E56A0E20029FB4B /* ServiceAppTests.xctest */; 196 | productType = "com.apple.product-type.bundle.unit-test"; 197 | }; 198 | 2F8807551E56A0E30029FB4B /* ServiceAppUITests */ = { 199 | isa = PBXNativeTarget; 200 | buildConfigurationList = 2F8807651E56A0E30029FB4B /* Build configuration list for PBXNativeTarget "ServiceAppUITests" */; 201 | buildPhases = ( 202 | 2F8807521E56A0E30029FB4B /* Sources */, 203 | 2F8807531E56A0E30029FB4B /* Frameworks */, 204 | 2F8807541E56A0E30029FB4B /* Resources */, 205 | ); 206 | buildRules = ( 207 | ); 208 | dependencies = ( 209 | 2F8807581E56A0E30029FB4B /* PBXTargetDependency */, 210 | ); 211 | name = ServiceAppUITests; 212 | productName = ServiceAppUITests; 213 | productReference = 2F8807561E56A0E30029FB4B /* ServiceAppUITests.xctest */; 214 | productType = "com.apple.product-type.bundle.ui-testing"; 215 | }; 216 | /* End PBXNativeTarget section */ 217 | 218 | /* Begin PBXProject section */ 219 | 2F8807301E56A0E20029FB4B /* Project object */ = { 220 | isa = PBXProject; 221 | attributes = { 222 | LastUpgradeCheck = 0820; 223 | ORGANIZATIONNAME = joymake; 224 | TargetAttributes = { 225 | 2F8807371E56A0E20029FB4B = { 226 | CreatedOnToolsVersion = 8.2.1; 227 | DevelopmentTeam = 4SR64HN66V; 228 | ProvisioningStyle = Automatic; 229 | }; 230 | 2F88074A1E56A0E20029FB4B = { 231 | CreatedOnToolsVersion = 8.2.1; 232 | DevelopmentTeam = 4SR64HN66V; 233 | ProvisioningStyle = Automatic; 234 | TestTargetID = 2F8807371E56A0E20029FB4B; 235 | }; 236 | 2F8807551E56A0E30029FB4B = { 237 | CreatedOnToolsVersion = 8.2.1; 238 | DevelopmentTeam = 4SR64HN66V; 239 | ProvisioningStyle = Automatic; 240 | TestTargetID = 2F8807371E56A0E20029FB4B; 241 | }; 242 | }; 243 | }; 244 | buildConfigurationList = 2F8807331E56A0E20029FB4B /* Build configuration list for PBXProject "ServiceApp" */; 245 | compatibilityVersion = "Xcode 3.2"; 246 | developmentRegion = English; 247 | hasScannedForEncodings = 0; 248 | knownRegions = ( 249 | en, 250 | Base, 251 | ); 252 | mainGroup = 2F88072F1E56A0E20029FB4B; 253 | productRefGroup = 2F8807391E56A0E20029FB4B /* Products */; 254 | projectDirPath = ""; 255 | projectRoot = ""; 256 | targets = ( 257 | 2F8807371E56A0E20029FB4B /* ServiceApp */, 258 | 2F88074A1E56A0E20029FB4B /* ServiceAppTests */, 259 | 2F8807551E56A0E30029FB4B /* ServiceAppUITests */, 260 | ); 261 | }; 262 | /* End PBXProject section */ 263 | 264 | /* Begin PBXResourcesBuildPhase section */ 265 | 2F8807361E56A0E20029FB4B /* Resources */ = { 266 | isa = PBXResourcesBuildPhase; 267 | buildActionMask = 2147483647; 268 | files = ( 269 | 2F8807421E56A0E20029FB4B /* Assets.xcassets in Resources */, 270 | 2F8807451E56A0E20029FB4B /* MainMenu.xib in Resources */, 271 | ); 272 | runOnlyForDeploymentPostprocessing = 0; 273 | }; 274 | 2F8807491E56A0E20029FB4B /* Resources */ = { 275 | isa = PBXResourcesBuildPhase; 276 | buildActionMask = 2147483647; 277 | files = ( 278 | ); 279 | runOnlyForDeploymentPostprocessing = 0; 280 | }; 281 | 2F8807541E56A0E30029FB4B /* Resources */ = { 282 | isa = PBXResourcesBuildPhase; 283 | buildActionMask = 2147483647; 284 | files = ( 285 | ); 286 | runOnlyForDeploymentPostprocessing = 0; 287 | }; 288 | /* End PBXResourcesBuildPhase section */ 289 | 290 | /* Begin PBXSourcesBuildPhase section */ 291 | 2F8807341E56A0E20029FB4B /* Sources */ = { 292 | isa = PBXSourcesBuildPhase; 293 | buildActionMask = 2147483647; 294 | files = ( 295 | 2F88076F1E56A9F20029FB4B /* GCDAsyncSocket.m in Sources */, 296 | 2F8807701E56A9F20029FB4B /* GCDAsyncUdpSocket.m in Sources */, 297 | 2F5B50EB1EB0B29000C79DEE /* GCDAsyncSocket+category.m in Sources */, 298 | 2F8807401E56A0E20029FB4B /* main.m in Sources */, 299 | 2F8807751E56AB8F0029FB4B /* SerViceAPP.m in Sources */, 300 | 2F88073D1E56A0E20029FB4B /* AppDelegate.m in Sources */, 301 | 2F88076A1E56A2C60029FB4B /* MainWindow.m in Sources */, 302 | ); 303 | runOnlyForDeploymentPostprocessing = 0; 304 | }; 305 | 2F8807471E56A0E20029FB4B /* Sources */ = { 306 | isa = PBXSourcesBuildPhase; 307 | buildActionMask = 2147483647; 308 | files = ( 309 | 2F8807501E56A0E30029FB4B /* ServiceAppTests.m in Sources */, 310 | ); 311 | runOnlyForDeploymentPostprocessing = 0; 312 | }; 313 | 2F8807521E56A0E30029FB4B /* Sources */ = { 314 | isa = PBXSourcesBuildPhase; 315 | buildActionMask = 2147483647; 316 | files = ( 317 | 2F88075B1E56A0E30029FB4B /* ServiceAppUITests.m in Sources */, 318 | ); 319 | runOnlyForDeploymentPostprocessing = 0; 320 | }; 321 | /* End PBXSourcesBuildPhase section */ 322 | 323 | /* Begin PBXTargetDependency section */ 324 | 2F88074D1E56A0E20029FB4B /* PBXTargetDependency */ = { 325 | isa = PBXTargetDependency; 326 | target = 2F8807371E56A0E20029FB4B /* ServiceApp */; 327 | targetProxy = 2F88074C1E56A0E20029FB4B /* PBXContainerItemProxy */; 328 | }; 329 | 2F8807581E56A0E30029FB4B /* PBXTargetDependency */ = { 330 | isa = PBXTargetDependency; 331 | target = 2F8807371E56A0E20029FB4B /* ServiceApp */; 332 | targetProxy = 2F8807571E56A0E30029FB4B /* PBXContainerItemProxy */; 333 | }; 334 | /* End PBXTargetDependency section */ 335 | 336 | /* Begin PBXVariantGroup section */ 337 | 2F8807431E56A0E20029FB4B /* MainMenu.xib */ = { 338 | isa = PBXVariantGroup; 339 | children = ( 340 | 2F8807441E56A0E20029FB4B /* Base */, 341 | ); 342 | name = MainMenu.xib; 343 | sourceTree = ""; 344 | }; 345 | /* End PBXVariantGroup section */ 346 | 347 | /* Begin XCBuildConfiguration section */ 348 | 2F88075D1E56A0E30029FB4B /* Debug */ = { 349 | isa = XCBuildConfiguration; 350 | buildSettings = { 351 | ALWAYS_SEARCH_USER_PATHS = NO; 352 | CLANG_ANALYZER_NONNULL = YES; 353 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 354 | CLANG_CXX_LIBRARY = "libc++"; 355 | CLANG_ENABLE_MODULES = YES; 356 | CLANG_ENABLE_OBJC_ARC = YES; 357 | CLANG_WARN_BOOL_CONVERSION = YES; 358 | CLANG_WARN_CONSTANT_CONVERSION = YES; 359 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 360 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 361 | CLANG_WARN_EMPTY_BODY = YES; 362 | CLANG_WARN_ENUM_CONVERSION = YES; 363 | CLANG_WARN_INFINITE_RECURSION = YES; 364 | CLANG_WARN_INT_CONVERSION = YES; 365 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 366 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 367 | CLANG_WARN_UNREACHABLE_CODE = YES; 368 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 369 | CODE_SIGN_IDENTITY = "-"; 370 | COPY_PHASE_STRIP = NO; 371 | DEBUG_INFORMATION_FORMAT = dwarf; 372 | ENABLE_STRICT_OBJC_MSGSEND = YES; 373 | ENABLE_TESTABILITY = YES; 374 | GCC_C_LANGUAGE_STANDARD = gnu99; 375 | GCC_DYNAMIC_NO_PIC = NO; 376 | GCC_NO_COMMON_BLOCKS = YES; 377 | GCC_OPTIMIZATION_LEVEL = 0; 378 | GCC_PREPROCESSOR_DEFINITIONS = ( 379 | "DEBUG=1", 380 | "$(inherited)", 381 | ); 382 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 383 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 384 | GCC_WARN_UNDECLARED_SELECTOR = YES; 385 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 386 | GCC_WARN_UNUSED_FUNCTION = YES; 387 | GCC_WARN_UNUSED_VARIABLE = YES; 388 | MACOSX_DEPLOYMENT_TARGET = 10.12; 389 | MTL_ENABLE_DEBUG_INFO = YES; 390 | ONLY_ACTIVE_ARCH = YES; 391 | SDKROOT = macosx; 392 | }; 393 | name = Debug; 394 | }; 395 | 2F88075E1E56A0E30029FB4B /* Release */ = { 396 | isa = XCBuildConfiguration; 397 | buildSettings = { 398 | ALWAYS_SEARCH_USER_PATHS = NO; 399 | CLANG_ANALYZER_NONNULL = YES; 400 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 401 | CLANG_CXX_LIBRARY = "libc++"; 402 | CLANG_ENABLE_MODULES = YES; 403 | CLANG_ENABLE_OBJC_ARC = YES; 404 | CLANG_WARN_BOOL_CONVERSION = YES; 405 | CLANG_WARN_CONSTANT_CONVERSION = YES; 406 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 407 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 408 | CLANG_WARN_EMPTY_BODY = YES; 409 | CLANG_WARN_ENUM_CONVERSION = YES; 410 | CLANG_WARN_INFINITE_RECURSION = YES; 411 | CLANG_WARN_INT_CONVERSION = YES; 412 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 413 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 414 | CLANG_WARN_UNREACHABLE_CODE = YES; 415 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 416 | CODE_SIGN_IDENTITY = "-"; 417 | COPY_PHASE_STRIP = NO; 418 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 419 | ENABLE_NS_ASSERTIONS = NO; 420 | ENABLE_STRICT_OBJC_MSGSEND = YES; 421 | GCC_C_LANGUAGE_STANDARD = gnu99; 422 | GCC_NO_COMMON_BLOCKS = YES; 423 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 424 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 425 | GCC_WARN_UNDECLARED_SELECTOR = YES; 426 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 427 | GCC_WARN_UNUSED_FUNCTION = YES; 428 | GCC_WARN_UNUSED_VARIABLE = YES; 429 | MACOSX_DEPLOYMENT_TARGET = 10.12; 430 | MTL_ENABLE_DEBUG_INFO = NO; 431 | SDKROOT = macosx; 432 | }; 433 | name = Release; 434 | }; 435 | 2F8807601E56A0E30029FB4B /* Debug */ = { 436 | isa = XCBuildConfiguration; 437 | buildSettings = { 438 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 439 | COMBINE_HIDPI_IMAGES = YES; 440 | DEVELOPMENT_TEAM = 4SR64HN66V; 441 | INFOPLIST_FILE = ServiceApp/Info.plist; 442 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 443 | PRODUCT_BUNDLE_IDENTIFIER = com.syswinPush.TOON.enterprise.ServiceApp; 444 | PRODUCT_NAME = "$(TARGET_NAME)"; 445 | }; 446 | name = Debug; 447 | }; 448 | 2F8807611E56A0E30029FB4B /* Release */ = { 449 | isa = XCBuildConfiguration; 450 | buildSettings = { 451 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 452 | COMBINE_HIDPI_IMAGES = YES; 453 | DEVELOPMENT_TEAM = 4SR64HN66V; 454 | INFOPLIST_FILE = ServiceApp/Info.plist; 455 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 456 | PRODUCT_BUNDLE_IDENTIFIER = com.syswinPush.TOON.enterprise.ServiceApp; 457 | PRODUCT_NAME = "$(TARGET_NAME)"; 458 | }; 459 | name = Release; 460 | }; 461 | 2F8807631E56A0E30029FB4B /* Debug */ = { 462 | isa = XCBuildConfiguration; 463 | buildSettings = { 464 | BUNDLE_LOADER = "$(TEST_HOST)"; 465 | COMBINE_HIDPI_IMAGES = YES; 466 | DEVELOPMENT_TEAM = 4SR64HN66V; 467 | INFOPLIST_FILE = ServiceAppTests/Info.plist; 468 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 469 | PRODUCT_BUNDLE_IDENTIFIER = com.syswinPush.TOON.enterprise.ServiceAppTests; 470 | PRODUCT_NAME = "$(TARGET_NAME)"; 471 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ServiceApp.app/Contents/MacOS/ServiceApp"; 472 | }; 473 | name = Debug; 474 | }; 475 | 2F8807641E56A0E30029FB4B /* Release */ = { 476 | isa = XCBuildConfiguration; 477 | buildSettings = { 478 | BUNDLE_LOADER = "$(TEST_HOST)"; 479 | COMBINE_HIDPI_IMAGES = YES; 480 | DEVELOPMENT_TEAM = 4SR64HN66V; 481 | INFOPLIST_FILE = ServiceAppTests/Info.plist; 482 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 483 | PRODUCT_BUNDLE_IDENTIFIER = com.syswinPush.TOON.enterprise.ServiceAppTests; 484 | PRODUCT_NAME = "$(TARGET_NAME)"; 485 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ServiceApp.app/Contents/MacOS/ServiceApp"; 486 | }; 487 | name = Release; 488 | }; 489 | 2F8807661E56A0E30029FB4B /* Debug */ = { 490 | isa = XCBuildConfiguration; 491 | buildSettings = { 492 | COMBINE_HIDPI_IMAGES = YES; 493 | DEVELOPMENT_TEAM = 4SR64HN66V; 494 | INFOPLIST_FILE = ServiceAppUITests/Info.plist; 495 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 496 | PRODUCT_BUNDLE_IDENTIFIER = com.syswinPush.TOON.enterprise.ServiceAppUITests; 497 | PRODUCT_NAME = "$(TARGET_NAME)"; 498 | TEST_TARGET_NAME = ServiceApp; 499 | }; 500 | name = Debug; 501 | }; 502 | 2F8807671E56A0E30029FB4B /* Release */ = { 503 | isa = XCBuildConfiguration; 504 | buildSettings = { 505 | COMBINE_HIDPI_IMAGES = YES; 506 | DEVELOPMENT_TEAM = 4SR64HN66V; 507 | INFOPLIST_FILE = ServiceAppUITests/Info.plist; 508 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 509 | PRODUCT_BUNDLE_IDENTIFIER = com.syswinPush.TOON.enterprise.ServiceAppUITests; 510 | PRODUCT_NAME = "$(TARGET_NAME)"; 511 | TEST_TARGET_NAME = ServiceApp; 512 | }; 513 | name = Release; 514 | }; 515 | /* End XCBuildConfiguration section */ 516 | 517 | /* Begin XCConfigurationList section */ 518 | 2F8807331E56A0E20029FB4B /* Build configuration list for PBXProject "ServiceApp" */ = { 519 | isa = XCConfigurationList; 520 | buildConfigurations = ( 521 | 2F88075D1E56A0E30029FB4B /* Debug */, 522 | 2F88075E1E56A0E30029FB4B /* Release */, 523 | ); 524 | defaultConfigurationIsVisible = 0; 525 | defaultConfigurationName = Release; 526 | }; 527 | 2F88075F1E56A0E30029FB4B /* Build configuration list for PBXNativeTarget "ServiceApp" */ = { 528 | isa = XCConfigurationList; 529 | buildConfigurations = ( 530 | 2F8807601E56A0E30029FB4B /* Debug */, 531 | 2F8807611E56A0E30029FB4B /* Release */, 532 | ); 533 | defaultConfigurationIsVisible = 0; 534 | defaultConfigurationName = Release; 535 | }; 536 | 2F8807621E56A0E30029FB4B /* Build configuration list for PBXNativeTarget "ServiceAppTests" */ = { 537 | isa = XCConfigurationList; 538 | buildConfigurations = ( 539 | 2F8807631E56A0E30029FB4B /* Debug */, 540 | 2F8807641E56A0E30029FB4B /* Release */, 541 | ); 542 | defaultConfigurationIsVisible = 0; 543 | defaultConfigurationName = Release; 544 | }; 545 | 2F8807651E56A0E30029FB4B /* Build configuration list for PBXNativeTarget "ServiceAppUITests" */ = { 546 | isa = XCConfigurationList; 547 | buildConfigurations = ( 548 | 2F8807661E56A0E30029FB4B /* Debug */, 549 | 2F8807671E56A0E30029FB4B /* Release */, 550 | ); 551 | defaultConfigurationIsVisible = 0; 552 | defaultConfigurationName = Release; 553 | }; 554 | /* End XCConfigurationList section */ 555 | }; 556 | rootObject = 2F8807301E56A0E20029FB4B /* Project object */; 557 | } 558 | -------------------------------------------------------------------------------- /ServiceApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ServiceApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ServiceApp.xcodeproj/project.xcworkspace/xcuserdata/Joymake.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joymakee/macSocketServer/0ecb59d9f8fafcbd22f6f43fe39f79b908dc031e/ServiceApp.xcodeproj/project.xcworkspace/xcuserdata/Joymake.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ServiceApp.xcodeproj/project.xcworkspace/xcuserdata/wangguopeng.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joymakee/macSocketServer/0ecb59d9f8fafcbd22f6f43fe39f79b908dc031e/ServiceApp.xcodeproj/project.xcworkspace/xcuserdata/wangguopeng.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ServiceApp.xcodeproj/xcuserdata/Joymake.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /ServiceApp.xcodeproj/xcuserdata/Joymake.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ServiceApp.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | ServiceApp.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /ServiceApp.xcodeproj/xcuserdata/wangguopeng.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 18 | 19 | 20 | 22 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /ServiceApp.xcodeproj/xcuserdata/wangguopeng.xcuserdatad/xcschemes/ServiceApp.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /ServiceApp.xcodeproj/xcuserdata/wangguopeng.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ServiceApp.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 2F8807371E56A0E20029FB4B 16 | 17 | primary 18 | 19 | 20 | 2F88074A1E56A0E20029FB4B 21 | 22 | primary 23 | 24 | 25 | 2F8807551E56A0E30029FB4B 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /ServiceApp/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // ServiceApp 4 | // 5 | // Created by wangguopeng on 2017/2/17. 6 | // Copyright © 2017年 joymake. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : NSObject 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /ServiceApp/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // ServiceApp 4 | // 5 | // Created by wangguopeng on 2017/2/17. 6 | // Copyright © 2017年 joymake. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @property (weak) IBOutlet NSWindow *window; 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 19 | // Insert code here to initialize your application 20 | } 21 | 22 | 23 | - (void)applicationWillTerminate:(NSNotification *)aNotification { 24 | // Insert code here to tear down your application 25 | } 26 | 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /ServiceApp/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "size" : "32x32", 15 | "idiom" : "mac", 16 | "filename" : "LifeWork32@1x的.png", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "size" : "32x32", 21 | "idiom" : "mac", 22 | "filename" : "LifeWork32@2x的.png", 23 | "scale" : "2x" 24 | }, 25 | { 26 | "size" : "128x128", 27 | "idiom" : "mac", 28 | "filename" : "LifeWork128@1x.png", 29 | "scale" : "1x" 30 | }, 31 | { 32 | "size" : "128x128", 33 | "idiom" : "mac", 34 | "filename" : "LifeWork128@2x.png", 35 | "scale" : "2x" 36 | }, 37 | { 38 | "idiom" : "mac", 39 | "size" : "256x256", 40 | "scale" : "1x" 41 | }, 42 | { 43 | "idiom" : "mac", 44 | "size" : "256x256", 45 | "scale" : "2x" 46 | }, 47 | { 48 | "idiom" : "mac", 49 | "size" : "512x512", 50 | "scale" : "1x" 51 | }, 52 | { 53 | "idiom" : "mac", 54 | "size" : "512x512", 55 | "scale" : "2x" 56 | } 57 | ], 58 | "info" : { 59 | "version" : 1, 60 | "author" : "xcode" 61 | } 62 | } -------------------------------------------------------------------------------- /ServiceApp/Assets.xcassets/AppIcon.appiconset/LifeWork128@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joymakee/macSocketServer/0ecb59d9f8fafcbd22f6f43fe39f79b908dc031e/ServiceApp/Assets.xcassets/AppIcon.appiconset/LifeWork128@1x.png -------------------------------------------------------------------------------- /ServiceApp/Assets.xcassets/AppIcon.appiconset/LifeWork128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joymakee/macSocketServer/0ecb59d9f8fafcbd22f6f43fe39f79b908dc031e/ServiceApp/Assets.xcassets/AppIcon.appiconset/LifeWork128@2x.png -------------------------------------------------------------------------------- /ServiceApp/Assets.xcassets/AppIcon.appiconset/LifeWork32@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joymakee/macSocketServer/0ecb59d9f8fafcbd22f6f43fe39f79b908dc031e/ServiceApp/Assets.xcassets/AppIcon.appiconset/LifeWork32@1x.png -------------------------------------------------------------------------------- /ServiceApp/Assets.xcassets/AppIcon.appiconset/LifeWork32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joymakee/macSocketServer/0ecb59d9f8fafcbd22f6f43fe39f79b908dc031e/ServiceApp/Assets.xcassets/AppIcon.appiconset/LifeWork32@2x.png -------------------------------------------------------------------------------- /ServiceApp/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /ServiceApp/GCDAsyncSocket+category.h: -------------------------------------------------------------------------------- 1 | // 2 | // GCDAsyncSocket+category.h 3 | // ServiceApp 4 | // 5 | // Created by wangguopeng on 2017/4/26. 6 | // Copyright © 2017年 joymake. All rights reserved. 7 | // 8 | 9 | #import "GCDAsyncSocket.h" 10 | 11 | @interface GCDAsyncSocket (category) 12 | @property (nonatomic,strong)NSDate *timeNew; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /ServiceApp/GCDAsyncSocket+category.m: -------------------------------------------------------------------------------- 1 | // 2 | // GCDAsyncSocket+category.m 3 | // ServiceApp 4 | // 5 | // Created by wangguopeng on 2017/4/26. 6 | // Copyright © 2017年 joymake. All rights reserved. 7 | // 8 | 9 | #import "GCDAsyncSocket+category.h" 10 | #import 11 | 12 | @implementation GCDAsyncSocket (category) 13 | -(void)setTimeNew:(NSDate *)timeNew{ 14 | objc_setAssociatedObject(self, _cmd, timeNew, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 15 | } 16 | 17 | -(NSDate *)timeNew{ 18 | return objc_getAssociatedObject(self, @selector(setTimeNew:)); 19 | } 20 | @end 21 | -------------------------------------------------------------------------------- /ServiceApp/GCDAsyncSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // GCDAsyncSocket.h 3 | // 4 | // This class is in the public domain. 5 | // Originally created by Robbie Hanson in Q3 2010. 6 | // Updated and maintained by Deusty LLC and the Apple development community. 7 | // 8 | // https://github.com/robbiehanson/CocoaAsyncSocket 9 | // 10 | 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | 17 | #include // AF_INET, AF_INET6 18 | 19 | @class GCDAsyncReadPacket; 20 | @class GCDAsyncWritePacket; 21 | @class GCDAsyncSocketPreBuffer; 22 | 23 | extern NSString *const GCDAsyncSocketException; 24 | extern NSString *const GCDAsyncSocketErrorDomain; 25 | 26 | extern NSString *const GCDAsyncSocketQueueName; 27 | extern NSString *const GCDAsyncSocketThreadName; 28 | 29 | extern NSString *const GCDAsyncSocketManuallyEvaluateTrust; 30 | #if TARGET_OS_IPHONE 31 | extern NSString *const GCDAsyncSocketUseCFStreamForTLS; 32 | #endif 33 | #define GCDAsyncSocketSSLPeerName (NSString *)kCFStreamSSLPeerName 34 | #define GCDAsyncSocketSSLCertificates (NSString *)kCFStreamSSLCertificates 35 | #define GCDAsyncSocketSSLIsServer (NSString *)kCFStreamSSLIsServer 36 | extern NSString *const GCDAsyncSocketSSLPeerID; 37 | extern NSString *const GCDAsyncSocketSSLProtocolVersionMin; 38 | extern NSString *const GCDAsyncSocketSSLProtocolVersionMax; 39 | extern NSString *const GCDAsyncSocketSSLSessionOptionFalseStart; 40 | extern NSString *const GCDAsyncSocketSSLSessionOptionSendOneByteRecord; 41 | extern NSString *const GCDAsyncSocketSSLCipherSuites; 42 | #if !TARGET_OS_IPHONE 43 | extern NSString *const GCDAsyncSocketSSLDiffieHellmanParameters; 44 | #endif 45 | 46 | #define GCDAsyncSocketLoggingContext 65535 47 | 48 | 49 | typedef NS_ENUM(NSInteger, GCDAsyncSocketError) { 50 | GCDAsyncSocketNoError = 0, // Never used 51 | GCDAsyncSocketBadConfigError, // Invalid configuration 52 | GCDAsyncSocketBadParamError, // Invalid parameter was passed 53 | GCDAsyncSocketConnectTimeoutError, // A connect operation timed out 54 | GCDAsyncSocketReadTimeoutError, // A read operation timed out 55 | GCDAsyncSocketWriteTimeoutError, // A write operation timed out 56 | GCDAsyncSocketReadMaxedOutError, // Reached set maxLength without completing 57 | GCDAsyncSocketClosedError, // The remote peer closed the connection 58 | GCDAsyncSocketOtherError, // Description provided in userInfo 59 | }; 60 | 61 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 62 | #pragma mark - 63 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 64 | 65 | @interface GCDAsyncSocket : NSObject 66 | 67 | /** 68 | * GCDAsyncSocket uses the standard delegate paradigm, 69 | * but executes all delegate callbacks on a given delegate dispatch queue. 70 | * This allows for maximum concurrency, while at the same time providing easy thread safety. 71 | * 72 | * You MUST set a delegate AND delegate dispatch queue before attempting to 73 | * use the socket, or you will get an error. 74 | * 75 | * The socket queue is optional. 76 | * If you pass NULL, GCDAsyncSocket will automatically create it's own socket queue. 77 | * If you choose to provide a socket queue, the socket queue must not be a concurrent queue. 78 | * If you choose to provide a socket queue, and the socket queue has a configured target queue, 79 | * then please see the discussion for the method markSocketQueueTargetQueue. 80 | * 81 | * The delegate queue and socket queue can optionally be the same. 82 | **/ 83 | - (id)init; 84 | - (id)initWithSocketQueue:(dispatch_queue_t)sq; 85 | - (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq; 86 | - (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq socketQueue:(dispatch_queue_t)sq; 87 | 88 | #pragma mark Configuration 89 | 90 | @property (atomic, weak, readwrite) id delegate; 91 | #if OS_OBJECT_USE_OBJC 92 | @property (atomic, strong, readwrite) dispatch_queue_t delegateQueue; 93 | #else 94 | @property (atomic, assign, readwrite) dispatch_queue_t delegateQueue; 95 | #endif 96 | 97 | - (void)getDelegate:(id *)delegatePtr delegateQueue:(dispatch_queue_t *)delegateQueuePtr; 98 | - (void)setDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue; 99 | 100 | /** 101 | * If you are setting the delegate to nil within the delegate's dealloc method, 102 | * you may need to use the synchronous versions below. 103 | **/ 104 | - (void)synchronouslySetDelegate:(id)delegate; 105 | - (void)synchronouslySetDelegateQueue:(dispatch_queue_t)delegateQueue; 106 | - (void)synchronouslySetDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue; 107 | 108 | /** 109 | * By default, both IPv4 and IPv6 are enabled. 110 | * 111 | * For accepting incoming connections, this means GCDAsyncSocket automatically supports both protocols, 112 | * and can simulataneously accept incoming connections on either protocol. 113 | * 114 | * For outgoing connections, this means GCDAsyncSocket can connect to remote hosts running either protocol. 115 | * If a DNS lookup returns only IPv4 results, GCDAsyncSocket will automatically use IPv4. 116 | * If a DNS lookup returns only IPv6 results, GCDAsyncSocket will automatically use IPv6. 117 | * If a DNS lookup returns both IPv4 and IPv6 results, the preferred protocol will be chosen. 118 | * By default, the preferred protocol is IPv4, but may be configured as desired. 119 | **/ 120 | 121 | @property (atomic, assign, readwrite, getter=isIPv4Enabled) BOOL IPv4Enabled; 122 | @property (atomic, assign, readwrite, getter=isIPv6Enabled) BOOL IPv6Enabled; 123 | 124 | @property (atomic, assign, readwrite, getter=isIPv4PreferredOverIPv6) BOOL IPv4PreferredOverIPv6; 125 | 126 | /** 127 | * User data allows you to associate arbitrary information with the socket. 128 | * This data is not used internally by socket in any way. 129 | **/ 130 | @property (atomic, strong, readwrite) id userData; 131 | 132 | #pragma mark Accepting 133 | 134 | /** 135 | * Tells the socket to begin listening and accepting connections on the given port. 136 | * When a connection is accepted, a new instance of GCDAsyncSocket will be spawned to handle it, 137 | * and the socket:didAcceptNewSocket: delegate method will be invoked. 138 | * 139 | * The socket will listen on all available interfaces (e.g. wifi, ethernet, etc) 140 | **/ 141 | - (BOOL)acceptOnPort:(uint16_t)port error:(NSError **)errPtr; 142 | 143 | /** 144 | * This method is the same as acceptOnPort:error: with the 145 | * additional option of specifying which interface to listen on. 146 | * 147 | * For example, you could specify that the socket should only accept connections over ethernet, 148 | * and not other interfaces such as wifi. 149 | * 150 | * The interface may be specified by name (e.g. "en1" or "lo0") or by IP address (e.g. "192.168.4.34"). 151 | * You may also use the special strings "localhost" or "loopback" to specify that 152 | * the socket only accept connections from the local machine. 153 | * 154 | * You can see the list of interfaces via the command line utility "ifconfig", 155 | * or programmatically via the getifaddrs() function. 156 | * 157 | * To accept connections on any interface pass nil, or simply use the acceptOnPort:error: method. 158 | **/ 159 | - (BOOL)acceptOnInterface:(NSString *)interface port:(uint16_t)port error:(NSError **)errPtr; 160 | 161 | /** 162 | * Tells the socket to begin listening and accepting connections on the unix domain at the given url. 163 | * When a connection is accepted, a new instance of GCDAsyncSocket will be spawned to handle it, 164 | * and the socket:didAcceptNewSocket: delegate method will be invoked. 165 | * 166 | * The socket will listen on all available interfaces (e.g. wifi, ethernet, etc) 167 | **/ 168 | - (BOOL)acceptOnUrl:(NSURL *)url error:(NSError **)errPtr; 169 | 170 | #pragma mark Connecting 171 | 172 | /** 173 | * Connects to the given host and port. 174 | * 175 | * This method invokes connectToHost:onPort:viaInterface:withTimeout:error: 176 | * and uses the default interface, and no timeout. 177 | **/ 178 | - (BOOL)connectToHost:(NSString *)host onPort:(uint16_t)port error:(NSError **)errPtr; 179 | 180 | /** 181 | * Connects to the given host and port with an optional timeout. 182 | * 183 | * This method invokes connectToHost:onPort:viaInterface:withTimeout:error: and uses the default interface. 184 | **/ 185 | - (BOOL)connectToHost:(NSString *)host 186 | onPort:(uint16_t)port 187 | withTimeout:(NSTimeInterval)timeout 188 | error:(NSError **)errPtr; 189 | 190 | /** 191 | * Connects to the given host & port, via the optional interface, with an optional timeout. 192 | * 193 | * The host may be a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2"). 194 | * The host may also be the special strings "localhost" or "loopback" to specify connecting 195 | * to a service on the local machine. 196 | * 197 | * The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35"). 198 | * The interface may also be used to specify the local port (see below). 199 | * 200 | * To not time out use a negative time interval. 201 | * 202 | * This method will return NO if an error is detected, and set the error pointer (if one was given). 203 | * Possible errors would be a nil host, invalid interface, or socket is already connected. 204 | * 205 | * If no errors are detected, this method will start a background connect operation and immediately return YES. 206 | * The delegate callbacks are used to notify you when the socket connects, or if the host was unreachable. 207 | * 208 | * Since this class supports queued reads and writes, you can immediately start reading and/or writing. 209 | * All read/write operations will be queued, and upon socket connection, 210 | * the operations will be dequeued and processed in order. 211 | * 212 | * The interface may optionally contain a port number at the end of the string, separated by a colon. 213 | * This allows you to specify the local port that should be used for the outgoing connection. (read paragraph to end) 214 | * To specify both interface and local port: "en1:8082" or "192.168.4.35:2424". 215 | * To specify only local port: ":8082". 216 | * Please note this is an advanced feature, and is somewhat hidden on purpose. 217 | * You should understand that 99.999% of the time you should NOT specify the local port for an outgoing connection. 218 | * If you think you need to, there is a very good chance you have a fundamental misunderstanding somewhere. 219 | * Local ports do NOT need to match remote ports. In fact, they almost never do. 220 | * This feature is here for networking professionals using very advanced techniques. 221 | **/ 222 | - (BOOL)connectToHost:(NSString *)host 223 | onPort:(uint16_t)port 224 | viaInterface:(NSString *)interface 225 | withTimeout:(NSTimeInterval)timeout 226 | error:(NSError **)errPtr; 227 | 228 | /** 229 | * Connects to the given address, specified as a sockaddr structure wrapped in a NSData object. 230 | * For example, a NSData object returned from NSNetService's addresses method. 231 | * 232 | * If you have an existing struct sockaddr you can convert it to a NSData object like so: 233 | * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len]; 234 | * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len]; 235 | * 236 | * This method invokes connectToAdd 237 | **/ 238 | - (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr; 239 | 240 | /** 241 | * This method is the same as connectToAddress:error: with an additional timeout option. 242 | * To not time out use a negative time interval, or simply use the connectToAddress:error: method. 243 | **/ 244 | - (BOOL)connectToAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr; 245 | 246 | /** 247 | * Connects to the given address, using the specified interface and timeout. 248 | * 249 | * The address is specified as a sockaddr structure wrapped in a NSData object. 250 | * For example, a NSData object returned from NSNetService's addresses method. 251 | * 252 | * If you have an existing struct sockaddr you can convert it to a NSData object like so: 253 | * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len]; 254 | * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len]; 255 | * 256 | * The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35"). 257 | * The interface may also be used to specify the local port (see below). 258 | * 259 | * The timeout is optional. To not time out use a negative time interval. 260 | * 261 | * This method will return NO if an error is detected, and set the error pointer (if one was given). 262 | * Possible errors would be a nil host, invalid interface, or socket is already connected. 263 | * 264 | * If no errors are detected, this method will start a background connect operation and immediately return YES. 265 | * The delegate callbacks are used to notify you when the socket connects, or if the host was unreachable. 266 | * 267 | * Since this class supports queued reads and writes, you can immediately start reading and/or writing. 268 | * All read/write operations will be queued, and upon socket connection, 269 | * the operations will be dequeued and processed in order. 270 | * 271 | * The interface may optionally contain a port number at the end of the string, separated by a colon. 272 | * This allows you to specify the local port that should be used for the outgoing connection. (read paragraph to end) 273 | * To specify both interface and local port: "en1:8082" or "192.168.4.35:2424". 274 | * To specify only local port: ":8082". 275 | * Please note this is an advanced feature, and is somewhat hidden on purpose. 276 | * You should understand that 99.999% of the time you should NOT specify the local port for an outgoing connection. 277 | * If you think you need to, there is a very good chance you have a fundamental misunderstanding somewhere. 278 | * Local ports do NOT need to match remote ports. In fact, they almost never do. 279 | * This feature is here for networking professionals using very advanced techniques. 280 | **/ 281 | - (BOOL)connectToAddress:(NSData *)remoteAddr 282 | viaInterface:(NSString *)interface 283 | withTimeout:(NSTimeInterval)timeout 284 | error:(NSError **)errPtr; 285 | /** 286 | * Connects to the unix domain socket at the given url, using the specified timeout. 287 | */ 288 | - (BOOL)connectToUrl:(NSURL *)url withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr; 289 | 290 | #pragma mark Disconnecting 291 | 292 | /** 293 | * Disconnects immediately (synchronously). Any pending reads or writes are dropped. 294 | * 295 | * If the socket is not already disconnected, an invocation to the socketDidDisconnect:withError: delegate method 296 | * will be queued onto the delegateQueue asynchronously (behind any previously queued delegate methods). 297 | * In other words, the disconnected delegate method will be invoked sometime shortly after this method returns. 298 | * 299 | * Please note the recommended way of releasing a GCDAsyncSocket instance (e.g. in a dealloc method) 300 | * [asyncSocket setDelegate:nil]; 301 | * [asyncSocket disconnect]; 302 | * [asyncSocket release]; 303 | * 304 | * If you plan on disconnecting the socket, and then immediately asking it to connect again, 305 | * you'll likely want to do so like this: 306 | * [asyncSocket setDelegate:nil]; 307 | * [asyncSocket disconnect]; 308 | * [asyncSocket setDelegate:self]; 309 | * [asyncSocket connect...]; 310 | **/ 311 | - (void)disconnect; 312 | 313 | /** 314 | * Disconnects after all pending reads have completed. 315 | * After calling this, the read and write methods will do nothing. 316 | * The socket will disconnect even if there are still pending writes. 317 | **/ 318 | - (void)disconnectAfterReading; 319 | 320 | /** 321 | * Disconnects after all pending writes have completed. 322 | * After calling this, the read and write methods will do nothing. 323 | * The socket will disconnect even if there are still pending reads. 324 | **/ 325 | - (void)disconnectAfterWriting; 326 | 327 | /** 328 | * Disconnects after all pending reads and writes have completed. 329 | * After calling this, the read and write methods will do nothing. 330 | **/ 331 | - (void)disconnectAfterReadingAndWriting; 332 | 333 | #pragma mark Diagnostics 334 | 335 | /** 336 | * Returns whether the socket is disconnected or connected. 337 | * 338 | * A disconnected socket may be recycled. 339 | * That is, it can used again for connecting or listening. 340 | * 341 | * If a socket is in the process of connecting, it may be neither disconnected nor connected. 342 | **/ 343 | @property (atomic, readonly) BOOL isDisconnected; 344 | @property (atomic, readonly) BOOL isConnected; 345 | 346 | /** 347 | * Returns the local or remote host and port to which this socket is connected, or nil and 0 if not connected. 348 | * The host will be an IP address. 349 | **/ 350 | @property (atomic, readonly) NSString *connectedHost; 351 | @property (atomic, readonly) uint16_t connectedPort; 352 | @property (atomic, readonly) NSURL *connectedUrl; 353 | 354 | @property (atomic, readonly) NSString *localHost; 355 | @property (atomic, readonly) uint16_t localPort; 356 | 357 | /** 358 | * Returns the local or remote address to which this socket is connected, 359 | * specified as a sockaddr structure wrapped in a NSData object. 360 | * 361 | * @seealso connectedHost 362 | * @seealso connectedPort 363 | * @seealso localHost 364 | * @seealso localPort 365 | **/ 366 | @property (atomic, readonly) NSData *connectedAddress; 367 | @property (atomic, readonly) NSData *localAddress; 368 | 369 | /** 370 | * Returns whether the socket is IPv4 or IPv6. 371 | * An accepting socket may be both. 372 | **/ 373 | @property (atomic, readonly) BOOL isIPv4; 374 | @property (atomic, readonly) BOOL isIPv6; 375 | 376 | /** 377 | * Returns whether or not the socket has been secured via SSL/TLS. 378 | * 379 | * See also the startTLS method. 380 | **/ 381 | @property (atomic, readonly) BOOL isSecure; 382 | 383 | #pragma mark Reading 384 | 385 | // The readData and writeData methods won't block (they are asynchronous). 386 | // 387 | // When a read is complete the socket:didReadData:withTag: delegate method is dispatched on the delegateQueue. 388 | // When a write is complete the socket:didWriteDataWithTag: delegate method is dispatched on the delegateQueue. 389 | // 390 | // You may optionally set a timeout for any read/write operation. (To not timeout, use a negative time interval.) 391 | // If a read/write opertion times out, the corresponding "socket:shouldTimeout..." delegate method 392 | // is called to optionally allow you to extend the timeout. 393 | // Upon a timeout, the "socket:didDisconnectWithError:" method is called 394 | // 395 | // The tag is for your convenience. 396 | // You can use it as an array index, step number, state id, pointer, etc. 397 | 398 | /** 399 | * Reads the first available bytes that become available on the socket. 400 | * 401 | * If the timeout value is negative, the read operation will not use a timeout. 402 | **/ 403 | - (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag; 404 | 405 | /** 406 | * Reads the first available bytes that become available on the socket. 407 | * The bytes will be appended to the given byte buffer starting at the given offset. 408 | * The given buffer will automatically be increased in size if needed. 409 | * 410 | * If the timeout value is negative, the read operation will not use a timeout. 411 | * If the buffer if nil, the socket will create a buffer for you. 412 | * 413 | * If the bufferOffset is greater than the length of the given buffer, 414 | * the method will do nothing, and the delegate will not be called. 415 | * 416 | * If you pass a buffer, you must not alter it in any way while the socket is using it. 417 | * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer. 418 | * That is, it will reference the bytes that were appended to the given buffer via 419 | * the method [NSData dataWithBytesNoCopy:length:freeWhenDone:NO]. 420 | **/ 421 | - (void)readDataWithTimeout:(NSTimeInterval)timeout 422 | buffer:(NSMutableData *)buffer 423 | bufferOffset:(NSUInteger)offset 424 | tag:(long)tag; 425 | 426 | /** 427 | * Reads the first available bytes that become available on the socket. 428 | * The bytes will be appended to the given byte buffer starting at the given offset. 429 | * The given buffer will automatically be increased in size if needed. 430 | * A maximum of length bytes will be read. 431 | * 432 | * If the timeout value is negative, the read operation will not use a timeout. 433 | * If the buffer if nil, a buffer will automatically be created for you. 434 | * If maxLength is zero, no length restriction is enforced. 435 | * 436 | * If the bufferOffset is greater than the length of the given buffer, 437 | * the method will do nothing, and the delegate will not be called. 438 | * 439 | * If you pass a buffer, you must not alter it in any way while the socket is using it. 440 | * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer. 441 | * That is, it will reference the bytes that were appended to the given buffer via 442 | * the method [NSData dataWithBytesNoCopy:length:freeWhenDone:NO]. 443 | **/ 444 | - (void)readDataWithTimeout:(NSTimeInterval)timeout 445 | buffer:(NSMutableData *)buffer 446 | bufferOffset:(NSUInteger)offset 447 | maxLength:(NSUInteger)length 448 | tag:(long)tag; 449 | 450 | /** 451 | * Reads the given number of bytes. 452 | * 453 | * If the timeout value is negative, the read operation will not use a timeout. 454 | * 455 | * If the length is 0, this method does nothing and the delegate is not called. 456 | **/ 457 | - (void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag; 458 | 459 | /** 460 | * Reads the given number of bytes. 461 | * The bytes will be appended to the given byte buffer starting at the given offset. 462 | * The given buffer will automatically be increased in size if needed. 463 | * 464 | * If the timeout value is negative, the read operation will not use a timeout. 465 | * If the buffer if nil, a buffer will automatically be created for you. 466 | * 467 | * If the length is 0, this method does nothing and the delegate is not called. 468 | * If the bufferOffset is greater than the length of the given buffer, 469 | * the method will do nothing, and the delegate will not be called. 470 | * 471 | * If you pass a buffer, you must not alter it in any way while AsyncSocket is using it. 472 | * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer. 473 | * That is, it will reference the bytes that were appended to the given buffer via 474 | * the method [NSData dataWithBytesNoCopy:length:freeWhenDone:NO]. 475 | **/ 476 | - (void)readDataToLength:(NSUInteger)length 477 | withTimeout:(NSTimeInterval)timeout 478 | buffer:(NSMutableData *)buffer 479 | bufferOffset:(NSUInteger)offset 480 | tag:(long)tag; 481 | 482 | /** 483 | * Reads bytes until (and including) the passed "data" parameter, which acts as a separator. 484 | * 485 | * If the timeout value is negative, the read operation will not use a timeout. 486 | * 487 | * If you pass nil or zero-length data as the "data" parameter, 488 | * the method will do nothing (except maybe print a warning), and the delegate will not be called. 489 | * 490 | * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter. 491 | * If you're developing your own custom protocol, be sure your separator can not occur naturally as 492 | * part of the data between separators. 493 | * For example, imagine you want to send several small documents over a socket. 494 | * Using CRLF as a separator is likely unwise, as a CRLF could easily exist within the documents. 495 | * In this particular example, it would be better to use a protocol similar to HTTP with 496 | * a header that includes the length of the document. 497 | * Also be careful that your separator cannot occur naturally as part of the encoding for a character. 498 | * 499 | * The given data (separator) parameter should be immutable. 500 | * For performance reasons, the socket will retain it, not copy it. 501 | * So if it is immutable, don't modify it while the socket is using it. 502 | **/ 503 | - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; 504 | 505 | /** 506 | * Reads bytes until (and including) the passed "data" parameter, which acts as a separator. 507 | * The bytes will be appended to the given byte buffer starting at the given offset. 508 | * The given buffer will automatically be increased in size if needed. 509 | * 510 | * If the timeout value is negative, the read operation will not use a timeout. 511 | * If the buffer if nil, a buffer will automatically be created for you. 512 | * 513 | * If the bufferOffset is greater than the length of the given buffer, 514 | * the method will do nothing (except maybe print a warning), and the delegate will not be called. 515 | * 516 | * If you pass a buffer, you must not alter it in any way while the socket is using it. 517 | * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer. 518 | * That is, it will reference the bytes that were appended to the given buffer via 519 | * the method [NSData dataWithBytesNoCopy:length:freeWhenDone:NO]. 520 | * 521 | * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter. 522 | * If you're developing your own custom protocol, be sure your separator can not occur naturally as 523 | * part of the data between separators. 524 | * For example, imagine you want to send several small documents over a socket. 525 | * Using CRLF as a separator is likely unwise, as a CRLF could easily exist within the documents. 526 | * In this particular example, it would be better to use a protocol similar to HTTP with 527 | * a header that includes the length of the document. 528 | * Also be careful that your separator cannot occur naturally as part of the encoding for a character. 529 | * 530 | * The given data (separator) parameter should be immutable. 531 | * For performance reasons, the socket will retain it, not copy it. 532 | * So if it is immutable, don't modify it while the socket is using it. 533 | **/ 534 | - (void)readDataToData:(NSData *)data 535 | withTimeout:(NSTimeInterval)timeout 536 | buffer:(NSMutableData *)buffer 537 | bufferOffset:(NSUInteger)offset 538 | tag:(long)tag; 539 | 540 | /** 541 | * Reads bytes until (and including) the passed "data" parameter, which acts as a separator. 542 | * 543 | * If the timeout value is negative, the read operation will not use a timeout. 544 | * 545 | * If maxLength is zero, no length restriction is enforced. 546 | * Otherwise if maxLength bytes are read without completing the read, 547 | * it is treated similarly to a timeout - the socket is closed with a GCDAsyncSocketReadMaxedOutError. 548 | * The read will complete successfully if exactly maxLength bytes are read and the given data is found at the end. 549 | * 550 | * If you pass nil or zero-length data as the "data" parameter, 551 | * the method will do nothing (except maybe print a warning), and the delegate will not be called. 552 | * If you pass a maxLength parameter that is less than the length of the data parameter, 553 | * the method will do nothing (except maybe print a warning), and the delegate will not be called. 554 | * 555 | * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter. 556 | * If you're developing your own custom protocol, be sure your separator can not occur naturally as 557 | * part of the data between separators. 558 | * For example, imagine you want to send several small documents over a socket. 559 | * Using CRLF as a separator is likely unwise, as a CRLF could easily exist within the documents. 560 | * In this particular example, it would be better to use a protocol similar to HTTP with 561 | * a header that includes the length of the document. 562 | * Also be careful that your separator cannot occur naturally as part of the encoding for a character. 563 | * 564 | * The given data (separator) parameter should be immutable. 565 | * For performance reasons, the socket will retain it, not copy it. 566 | * So if it is immutable, don't modify it while the socket is using it. 567 | **/ 568 | - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(NSUInteger)length tag:(long)tag; 569 | 570 | /** 571 | * Reads bytes until (and including) the passed "data" parameter, which acts as a separator. 572 | * The bytes will be appended to the given byte buffer starting at the given offset. 573 | * The given buffer will automatically be increased in size if needed. 574 | * 575 | * If the timeout value is negative, the read operation will not use a timeout. 576 | * If the buffer if nil, a buffer will automatically be created for you. 577 | * 578 | * If maxLength is zero, no length restriction is enforced. 579 | * Otherwise if maxLength bytes are read without completing the read, 580 | * it is treated similarly to a timeout - the socket is closed with a GCDAsyncSocketReadMaxedOutError. 581 | * The read will complete successfully if exactly maxLength bytes are read and the given data is found at the end. 582 | * 583 | * If you pass a maxLength parameter that is less than the length of the data (separator) parameter, 584 | * the method will do nothing (except maybe print a warning), and the delegate will not be called. 585 | * If the bufferOffset is greater than the length of the given buffer, 586 | * the method will do nothing (except maybe print a warning), and the delegate will not be called. 587 | * 588 | * If you pass a buffer, you must not alter it in any way while the socket is using it. 589 | * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer. 590 | * That is, it will reference the bytes that were appended to the given buffer via 591 | * the method [NSData dataWithBytesNoCopy:length:freeWhenDone:NO]. 592 | * 593 | * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter. 594 | * If you're developing your own custom protocol, be sure your separator can not occur naturally as 595 | * part of the data between separators. 596 | * For example, imagine you want to send several small documents over a socket. 597 | * Using CRLF as a separator is likely unwise, as a CRLF could easily exist within the documents. 598 | * In this particular example, it would be better to use a protocol similar to HTTP with 599 | * a header that includes the length of the document. 600 | * Also be careful that your separator cannot occur naturally as part of the encoding for a character. 601 | * 602 | * The given data (separator) parameter should be immutable. 603 | * For performance reasons, the socket will retain it, not copy it. 604 | * So if it is immutable, don't modify it while the socket is using it. 605 | **/ 606 | - (void)readDataToData:(NSData *)data 607 | withTimeout:(NSTimeInterval)timeout 608 | buffer:(NSMutableData *)buffer 609 | bufferOffset:(NSUInteger)offset 610 | maxLength:(NSUInteger)length 611 | tag:(long)tag; 612 | 613 | /** 614 | * Returns progress of the current read, from 0.0 to 1.0, or NaN if no current read (use isnan() to check). 615 | * The parameters "tag", "done" and "total" will be filled in if they aren't NULL. 616 | **/ 617 | - (float)progressOfReadReturningTag:(long *)tagPtr bytesDone:(NSUInteger *)donePtr total:(NSUInteger *)totalPtr; 618 | 619 | #pragma mark Writing 620 | 621 | /** 622 | * Writes data to the socket, and calls the delegate when finished. 623 | * 624 | * If you pass in nil or zero-length data, this method does nothing and the delegate will not be called. 625 | * If the timeout value is negative, the write operation will not use a timeout. 626 | * 627 | * Thread-Safety Note: 628 | * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while 629 | * the socket is writing it. In other words, it's not safe to alter the data until after the delegate method 630 | * socket:didWriteDataWithTag: is invoked signifying that this particular write operation has completed. 631 | * This is due to the fact that GCDAsyncSocket does NOT copy the data. It simply retains it. 632 | * This is for performance reasons. Often times, if NSMutableData is passed, it is because 633 | * a request/response was built up in memory. Copying this data adds an unwanted/unneeded overhead. 634 | * If you need to write data from an immutable buffer, and you need to alter the buffer before the socket 635 | * completes writing the bytes (which is NOT immediately after this method returns, but rather at a later time 636 | * when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method. 637 | **/ 638 | - (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; 639 | 640 | /** 641 | * Returns progress of the current write, from 0.0 to 1.0, or NaN if no current write (use isnan() to check). 642 | * The parameters "tag", "done" and "total" will be filled in if they aren't NULL. 643 | **/ 644 | - (float)progressOfWriteReturningTag:(long *)tagPtr bytesDone:(NSUInteger *)donePtr total:(NSUInteger *)totalPtr; 645 | 646 | #pragma mark Security 647 | 648 | /** 649 | * Secures the connection using SSL/TLS. 650 | * 651 | * This method may be called at any time, and the TLS handshake will occur after all pending reads and writes 652 | * are finished. This allows one the option of sending a protocol dependent StartTLS message, and queuing 653 | * the upgrade to TLS at the same time, without having to wait for the write to finish. 654 | * Any reads or writes scheduled after this method is called will occur over the secured connection. 655 | * 656 | * ==== The available TOP-LEVEL KEYS are: 657 | * 658 | * - GCDAsyncSocketManuallyEvaluateTrust 659 | * The value must be of type NSNumber, encapsulating a BOOL value. 660 | * If you set this to YES, then the underlying SecureTransport system will not evaluate the SecTrustRef of the peer. 661 | * Instead it will pause at the moment evaulation would typically occur, 662 | * and allow us to handle the security evaluation however we see fit. 663 | * So GCDAsyncSocket will invoke the delegate method socket:shouldTrustPeer: passing the SecTrustRef. 664 | * 665 | * Note that if you set this option, then all other configuration keys are ignored. 666 | * Evaluation will be completely up to you during the socket:didReceiveTrust:completionHandler: delegate method. 667 | * 668 | * For more information on trust evaluation see: 669 | * Apple's Technical Note TN2232 - HTTPS Server Trust Evaluation 670 | * https://developer.apple.com/library/ios/technotes/tn2232/_index.html 671 | * 672 | * If unspecified, the default value is NO. 673 | * 674 | * - GCDAsyncSocketUseCFStreamForTLS (iOS only) 675 | * The value must be of type NSNumber, encapsulating a BOOL value. 676 | * By default GCDAsyncSocket will use the SecureTransport layer to perform encryption. 677 | * This gives us more control over the security protocol (many more configuration options), 678 | * plus it allows us to optimize things like sys calls and buffer allocation. 679 | * 680 | * However, if you absolutely must, you can instruct GCDAsyncSocket to use the old-fashioned encryption 681 | * technique by going through the CFStream instead. So instead of using SecureTransport, GCDAsyncSocket 682 | * will instead setup a CFRead/CFWriteStream. And then set the kCFStreamPropertySSLSettings property 683 | * (via CFReadStreamSetProperty / CFWriteStreamSetProperty) and will pass the given options to this method. 684 | * 685 | * Thus all the other keys in the given dictionary will be ignored by GCDAsyncSocket, 686 | * and will passed directly CFReadStreamSetProperty / CFWriteStreamSetProperty. 687 | * For more infomation on these keys, please see the documentation for kCFStreamPropertySSLSettings. 688 | * 689 | * If unspecified, the default value is NO. 690 | * 691 | * ==== The available CONFIGURATION KEYS are: 692 | * 693 | * - kCFStreamSSLPeerName 694 | * The value must be of type NSString. 695 | * It should match the name in the X.509 certificate given by the remote party. 696 | * See Apple's documentation for SSLSetPeerDomainName. 697 | * 698 | * - kCFStreamSSLCertificates 699 | * The value must be of type NSArray. 700 | * See Apple's documentation for SSLSetCertificate. 701 | * 702 | * - kCFStreamSSLIsServer 703 | * The value must be of type NSNumber, encapsulationg a BOOL value. 704 | * See Apple's documentation for SSLCreateContext for iOS. 705 | * This is optional for iOS. If not supplied, a NO value is the default. 706 | * This is not needed for Mac OS X, and the value is ignored. 707 | * 708 | * - GCDAsyncSocketSSLPeerID 709 | * The value must be of type NSData. 710 | * You must set this value if you want to use TLS session resumption. 711 | * See Apple's documentation for SSLSetPeerID. 712 | * 713 | * - GCDAsyncSocketSSLProtocolVersionMin 714 | * - GCDAsyncSocketSSLProtocolVersionMax 715 | * The value(s) must be of type NSNumber, encapsulting a SSLProtocol value. 716 | * See Apple's documentation for SSLSetProtocolVersionMin & SSLSetProtocolVersionMax. 717 | * See also the SSLProtocol typedef. 718 | * 719 | * - GCDAsyncSocketSSLSessionOptionFalseStart 720 | * The value must be of type NSNumber, encapsulating a BOOL value. 721 | * See Apple's documentation for kSSLSessionOptionFalseStart. 722 | * 723 | * - GCDAsyncSocketSSLSessionOptionSendOneByteRecord 724 | * The value must be of type NSNumber, encapsulating a BOOL value. 725 | * See Apple's documentation for kSSLSessionOptionSendOneByteRecord. 726 | * 727 | * - GCDAsyncSocketSSLCipherSuites 728 | * The values must be of type NSArray. 729 | * Each item within the array must be a NSNumber, encapsulating 730 | * See Apple's documentation for SSLSetEnabledCiphers. 731 | * See also the SSLCipherSuite typedef. 732 | * 733 | * - GCDAsyncSocketSSLDiffieHellmanParameters (Mac OS X only) 734 | * The value must be of type NSData. 735 | * See Apple's documentation for SSLSetDiffieHellmanParams. 736 | * 737 | * ==== The following UNAVAILABLE KEYS are: (with throw an exception) 738 | * 739 | * - kCFStreamSSLAllowsAnyRoot (UNAVAILABLE) 740 | * You MUST use manual trust evaluation instead (see GCDAsyncSocketManuallyEvaluateTrust). 741 | * Corresponding deprecated method: SSLSetAllowsAnyRoot 742 | * 743 | * - kCFStreamSSLAllowsExpiredRoots (UNAVAILABLE) 744 | * You MUST use manual trust evaluation instead (see GCDAsyncSocketManuallyEvaluateTrust). 745 | * Corresponding deprecated method: SSLSetAllowsExpiredRoots 746 | * 747 | * - kCFStreamSSLAllowsExpiredCertificates (UNAVAILABLE) 748 | * You MUST use manual trust evaluation instead (see GCDAsyncSocketManuallyEvaluateTrust). 749 | * Corresponding deprecated method: SSLSetAllowsExpiredCerts 750 | * 751 | * - kCFStreamSSLValidatesCertificateChain (UNAVAILABLE) 752 | * You MUST use manual trust evaluation instead (see GCDAsyncSocketManuallyEvaluateTrust). 753 | * Corresponding deprecated method: SSLSetEnableCertVerify 754 | * 755 | * - kCFStreamSSLLevel (UNAVAILABLE) 756 | * You MUST use GCDAsyncSocketSSLProtocolVersionMin & GCDAsyncSocketSSLProtocolVersionMin instead. 757 | * Corresponding deprecated method: SSLSetProtocolVersionEnabled 758 | * 759 | * 760 | * Please refer to Apple's documentation for corresponding SSLFunctions. 761 | * 762 | * If you pass in nil or an empty dictionary, the default settings will be used. 763 | * 764 | * IMPORTANT SECURITY NOTE: 765 | * The default settings will check to make sure the remote party's certificate is signed by a 766 | * trusted 3rd party certificate agency (e.g. verisign) and that the certificate is not expired. 767 | * However it will not verify the name on the certificate unless you 768 | * give it a name to verify against via the kCFStreamSSLPeerName key. 769 | * The security implications of this are important to understand. 770 | * Imagine you are attempting to create a secure connection to MySecureServer.com, 771 | * but your socket gets directed to MaliciousServer.com because of a hacked DNS server. 772 | * If you simply use the default settings, and MaliciousServer.com has a valid certificate, 773 | * the default settings will not detect any problems since the certificate is valid. 774 | * To properly secure your connection in this particular scenario you 775 | * should set the kCFStreamSSLPeerName property to "MySecureServer.com". 776 | * 777 | * You can also perform additional validation in socketDidSecure. 778 | **/ 779 | - (void)startTLS:(NSDictionary *)tlsSettings; 780 | 781 | #pragma mark Advanced 782 | 783 | /** 784 | * Traditionally sockets are not closed until the conversation is over. 785 | * However, it is technically possible for the remote enpoint to close its write stream. 786 | * Our socket would then be notified that there is no more data to be read, 787 | * but our socket would still be writeable and the remote endpoint could continue to receive our data. 788 | * 789 | * The argument for this confusing functionality stems from the idea that a client could shut down its 790 | * write stream after sending a request to the server, thus notifying the server there are to be no further requests. 791 | * In practice, however, this technique did little to help server developers. 792 | * 793 | * To make matters worse, from a TCP perspective there is no way to tell the difference from a read stream close 794 | * and a full socket close. They both result in the TCP stack receiving a FIN packet. The only way to tell 795 | * is by continuing to write to the socket. If it was only a read stream close, then writes will continue to work. 796 | * Otherwise an error will be occur shortly (when the remote end sends us a RST packet). 797 | * 798 | * In addition to the technical challenges and confusion, many high level socket/stream API's provide 799 | * no support for dealing with the problem. If the read stream is closed, the API immediately declares the 800 | * socket to be closed, and shuts down the write stream as well. In fact, this is what Apple's CFStream API does. 801 | * It might sound like poor design at first, but in fact it simplifies development. 802 | * 803 | * The vast majority of the time if the read stream is closed it's because the remote endpoint closed its socket. 804 | * Thus it actually makes sense to close the socket at this point. 805 | * And in fact this is what most networking developers want and expect to happen. 806 | * However, if you are writing a server that interacts with a plethora of clients, 807 | * you might encounter a client that uses the discouraged technique of shutting down its write stream. 808 | * If this is the case, you can set this property to NO, 809 | * and make use of the socketDidCloseReadStream delegate method. 810 | * 811 | * The default value is YES. 812 | **/ 813 | @property (atomic, assign, readwrite) BOOL autoDisconnectOnClosedReadStream; 814 | 815 | /** 816 | * GCDAsyncSocket maintains thread safety by using an internal serial dispatch_queue. 817 | * In most cases, the instance creates this queue itself. 818 | * However, to allow for maximum flexibility, the internal queue may be passed in the init method. 819 | * This allows for some advanced options such as controlling socket priority via target queues. 820 | * However, when one begins to use target queues like this, they open the door to some specific deadlock issues. 821 | * 822 | * For example, imagine there are 2 queues: 823 | * dispatch_queue_t socketQueue; 824 | * dispatch_queue_t socketTargetQueue; 825 | * 826 | * If you do this (pseudo-code): 827 | * socketQueue.targetQueue = socketTargetQueue; 828 | * 829 | * Then all socketQueue operations will actually get run on the given socketTargetQueue. 830 | * This is fine and works great in most situations. 831 | * But if you run code directly from within the socketTargetQueue that accesses the socket, 832 | * you could potentially get deadlock. Imagine the following code: 833 | * 834 | * - (BOOL)socketHasSomething 835 | * { 836 | * __block BOOL result = NO; 837 | * dispatch_block_t block = ^{ 838 | * result = [self someInternalMethodToBeRunOnlyOnSocketQueue]; 839 | * } 840 | * if (is_executing_on_queue(socketQueue)) 841 | * block(); 842 | * else 843 | * dispatch_sync(socketQueue, block); 844 | * 845 | * return result; 846 | * } 847 | * 848 | * What happens if you call this method from the socketTargetQueue? The result is deadlock. 849 | * This is because the GCD API offers no mechanism to discover a queue's targetQueue. 850 | * Thus we have no idea if our socketQueue is configured with a targetQueue. 851 | * If we had this information, we could easily avoid deadlock. 852 | * But, since these API's are missing or unfeasible, you'll have to explicitly set it. 853 | * 854 | * IF you pass a socketQueue via the init method, 855 | * AND you've configured the passed socketQueue with a targetQueue, 856 | * THEN you should pass the end queue in the target hierarchy. 857 | * 858 | * For example, consider the following queue hierarchy: 859 | * socketQueue -> ipQueue -> moduleQueue 860 | * 861 | * This example demonstrates priority shaping within some server. 862 | * All incoming client connections from the same IP address are executed on the same target queue. 863 | * And all connections for a particular module are executed on the same target queue. 864 | * Thus, the priority of all networking for the entire module can be changed on the fly. 865 | * Additionally, networking traffic from a single IP cannot monopolize the module. 866 | * 867 | * Here's how you would accomplish something like that: 868 | * - (dispatch_queue_t)newSocketQueueForConnectionFromAddress:(NSData *)address onSocket:(GCDAsyncSocket *)sock 869 | * { 870 | * dispatch_queue_t socketQueue = dispatch_queue_create("", NULL); 871 | * dispatch_queue_t ipQueue = [self ipQueueForAddress:address]; 872 | * 873 | * dispatch_set_target_queue(socketQueue, ipQueue); 874 | * dispatch_set_target_queue(iqQueue, moduleQueue); 875 | * 876 | * return socketQueue; 877 | * } 878 | * - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket 879 | * { 880 | * [clientConnections addObject:newSocket]; 881 | * [newSocket markSocketQueueTargetQueue:moduleQueue]; 882 | * } 883 | * 884 | * Note: This workaround is ONLY needed if you intend to execute code directly on the ipQueue or moduleQueue. 885 | * This is often NOT the case, as such queues are used solely for execution shaping. 886 | **/ 887 | - (void)markSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreConfiguredTargetQueue; 888 | - (void)unmarkSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreviouslyConfiguredTargetQueue; 889 | 890 | /** 891 | * It's not thread-safe to access certain variables from outside the socket's internal queue. 892 | * 893 | * For example, the socket file descriptor. 894 | * File descriptors are simply integers which reference an index in the per-process file table. 895 | * However, when one requests a new file descriptor (by opening a file or socket), 896 | * the file descriptor returned is guaranteed to be the lowest numbered unused descriptor. 897 | * So if we're not careful, the following could be possible: 898 | * 899 | * - Thread A invokes a method which returns the socket's file descriptor. 900 | * - The socket is closed via the socket's internal queue on thread B. 901 | * - Thread C opens a file, and subsequently receives the file descriptor that was previously the socket's FD. 902 | * - Thread A is now accessing/altering the file instead of the socket. 903 | * 904 | * In addition to this, other variables are not actually objects, 905 | * and thus cannot be retained/released or even autoreleased. 906 | * An example is the sslContext, of type SSLContextRef, which is actually a malloc'd struct. 907 | * 908 | * Although there are internal variables that make it difficult to maintain thread-safety, 909 | * it is important to provide access to these variables 910 | * to ensure this class can be used in a wide array of environments. 911 | * This method helps to accomplish this by invoking the current block on the socket's internal queue. 912 | * The methods below can be invoked from within the block to access 913 | * those generally thread-unsafe internal variables in a thread-safe manner. 914 | * The given block will be invoked synchronously on the socket's internal queue. 915 | * 916 | * If you save references to any protected variables and use them outside the block, you do so at your own peril. 917 | **/ 918 | - (void)performBlock:(dispatch_block_t)block; 919 | 920 | /** 921 | * These methods are only available from within the context of a performBlock: invocation. 922 | * See the documentation for the performBlock: method above. 923 | * 924 | * Provides access to the socket's file descriptor(s). 925 | * If the socket is a server socket (is accepting incoming connections), 926 | * it might actually have multiple internal socket file descriptors - one for IPv4 and one for IPv6. 927 | **/ 928 | - (int)socketFD; 929 | - (int)socket4FD; 930 | - (int)socket6FD; 931 | 932 | #if TARGET_OS_IPHONE 933 | 934 | /** 935 | * These methods are only available from within the context of a performBlock: invocation. 936 | * See the documentation for the performBlock: method above. 937 | * 938 | * Provides access to the socket's internal CFReadStream/CFWriteStream. 939 | * 940 | * These streams are only used as workarounds for specific iOS shortcomings: 941 | * 942 | * - Apple has decided to keep the SecureTransport framework private is iOS. 943 | * This means the only supplied way to do SSL/TLS is via CFStream or some other API layered on top of it. 944 | * Thus, in order to provide SSL/TLS support on iOS we are forced to rely on CFStream, 945 | * instead of the preferred and faster and more powerful SecureTransport. 946 | * 947 | * - If a socket doesn't have backgrounding enabled, and that socket is closed while the app is backgrounded, 948 | * Apple only bothers to notify us via the CFStream API. 949 | * The faster and more powerful GCD API isn't notified properly in this case. 950 | * 951 | * See also: (BOOL)enableBackgroundingOnSocket 952 | **/ 953 | - (CFReadStreamRef)readStream; 954 | - (CFWriteStreamRef)writeStream; 955 | 956 | /** 957 | * This method is only available from within the context of a performBlock: invocation. 958 | * See the documentation for the performBlock: method above. 959 | * 960 | * Configures the socket to allow it to operate when the iOS application has been backgrounded. 961 | * In other words, this method creates a read & write stream, and invokes: 962 | * 963 | * CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); 964 | * CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); 965 | * 966 | * Returns YES if successful, NO otherwise. 967 | * 968 | * Note: Apple does not officially support backgrounding server sockets. 969 | * That is, if your socket is accepting incoming connections, Apple does not officially support 970 | * allowing iOS applications to accept incoming connections while an app is backgrounded. 971 | * 972 | * Example usage: 973 | * 974 | * - (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port 975 | * { 976 | * [asyncSocket performBlock:^{ 977 | * [asyncSocket enableBackgroundingOnSocket]; 978 | * }]; 979 | * } 980 | **/ 981 | - (BOOL)enableBackgroundingOnSocket; 982 | 983 | #endif 984 | 985 | /** 986 | * This method is only available from within the context of a performBlock: invocation. 987 | * See the documentation for the performBlock: method above. 988 | * 989 | * Provides access to the socket's SSLContext, if SSL/TLS has been started on the socket. 990 | **/ 991 | - (SSLContextRef)sslContext; 992 | 993 | #pragma mark Utilities 994 | 995 | /** 996 | * The address lookup utility used by the class. 997 | * This method is synchronous, so it's recommended you use it on a background thread/queue. 998 | * 999 | * The special strings "localhost" and "loopback" return the loopback address for IPv4 and IPv6. 1000 | * 1001 | * @returns 1002 | * A mutable array with all IPv4 and IPv6 addresses returned by getaddrinfo. 1003 | * The addresses are specifically for TCP connections. 1004 | * You can filter the addresses, if needed, using the other utility methods provided by the class. 1005 | **/ 1006 | + (NSMutableArray *)lookupHost:(NSString *)host port:(uint16_t)port error:(NSError **)errPtr; 1007 | 1008 | /** 1009 | * Extracting host and port information from raw address data. 1010 | **/ 1011 | 1012 | + (NSString *)hostFromAddress:(NSData *)address; 1013 | + (uint16_t)portFromAddress:(NSData *)address; 1014 | 1015 | + (BOOL)isIPv4Address:(NSData *)address; 1016 | + (BOOL)isIPv6Address:(NSData *)address; 1017 | 1018 | + (BOOL)getHost:(NSString **)hostPtr port:(uint16_t *)portPtr fromAddress:(NSData *)address; 1019 | 1020 | + (BOOL)getHost:(NSString **)hostPtr port:(uint16_t *)portPtr family:(sa_family_t *)afPtr fromAddress:(NSData *)address; 1021 | 1022 | /** 1023 | * A few common line separators, for use with the readDataToData:... methods. 1024 | **/ 1025 | + (NSData *)CRLFData; // 0x0D0A 1026 | + (NSData *)CRData; // 0x0D 1027 | + (NSData *)LFData; // 0x0A 1028 | + (NSData *)ZeroData; // 0x00 1029 | 1030 | @end 1031 | 1032 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 1033 | #pragma mark - 1034 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 1035 | 1036 | @protocol GCDAsyncSocketDelegate 1037 | @optional 1038 | 1039 | /** 1040 | * This method is called immediately prior to socket:didAcceptNewSocket:. 1041 | * It optionally allows a listening socket to specify the socketQueue for a new accepted socket. 1042 | * If this method is not implemented, or returns NULL, the new accepted socket will create its own default queue. 1043 | * 1044 | * Since you cannot autorelease a dispatch_queue, 1045 | * this method uses the "new" prefix in its name to specify that the returned queue has been retained. 1046 | * 1047 | * Thus you could do something like this in the implementation: 1048 | * return dispatch_queue_create("MyQueue", NULL); 1049 | * 1050 | * If you are placing multiple sockets on the same queue, 1051 | * then care should be taken to increment the retain count each time this method is invoked. 1052 | * 1053 | * For example, your implementation might look something like this: 1054 | * dispatch_retain(myExistingQueue); 1055 | * return myExistingQueue; 1056 | **/ 1057 | - (dispatch_queue_t)newSocketQueueForConnectionFromAddress:(NSData *)address onSocket:(GCDAsyncSocket *)sock; 1058 | 1059 | /** 1060 | * Called when a socket accepts a connection. 1061 | * Another socket is automatically spawned to handle it. 1062 | * 1063 | * You must retain the newSocket if you wish to handle the connection. 1064 | * Otherwise the newSocket instance will be released and the spawned connection will be closed. 1065 | * 1066 | * By default the new socket will have the same delegate and delegateQueue. 1067 | * You may, of course, change this at any time. 1068 | **/ 1069 | - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket; 1070 | 1071 | /** 1072 | * Called when a socket connects and is ready for reading and writing. 1073 | * The host parameter will be an IP address, not a DNS name. 1074 | **/ 1075 | - (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port; 1076 | 1077 | /** 1078 | * Called when a socket connects and is ready for reading and writing. 1079 | * The host parameter will be an IP address, not a DNS name. 1080 | **/ 1081 | - (void)socket:(GCDAsyncSocket *)sock didConnectToUrl:(NSURL *)url; 1082 | 1083 | /** 1084 | * Called when a socket has completed reading the requested data into memory. 1085 | * Not called if there is an error. 1086 | **/ 1087 | - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag; 1088 | 1089 | /** 1090 | * Called when a socket has read in data, but has not yet completed the read. 1091 | * This would occur if using readToData: or readToLength: methods. 1092 | * It may be used to for things such as updating progress bars. 1093 | **/ 1094 | - (void)socket:(GCDAsyncSocket *)sock didReadPartialDataOfLength:(NSUInteger)partialLength tag:(long)tag; 1095 | 1096 | /** 1097 | * Called when a socket has completed writing the requested data. Not called if there is an error. 1098 | **/ 1099 | - (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag; 1100 | 1101 | /** 1102 | * Called when a socket has written some data, but has not yet completed the entire write. 1103 | * It may be used to for things such as updating progress bars. 1104 | **/ 1105 | - (void)socket:(GCDAsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag; 1106 | 1107 | /** 1108 | * Called if a read operation has reached its timeout without completing. 1109 | * This method allows you to optionally extend the timeout. 1110 | * If you return a positive time interval (> 0) the read's timeout will be extended by the given amount. 1111 | * If you don't implement this method, or return a non-positive time interval (<= 0) the read will timeout as usual. 1112 | * 1113 | * The elapsed parameter is the sum of the original timeout, plus any additions previously added via this method. 1114 | * The length parameter is the number of bytes that have been read so far for the read operation. 1115 | * 1116 | * Note that this method may be called multiple times for a single read if you return positive numbers. 1117 | **/ 1118 | - (NSTimeInterval)socket:(GCDAsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag 1119 | elapsed:(NSTimeInterval)elapsed 1120 | bytesDone:(NSUInteger)length; 1121 | 1122 | /** 1123 | * Called if a write operation has reached its timeout without completing. 1124 | * This method allows you to optionally extend the timeout. 1125 | * If you return a positive time interval (> 0) the write's timeout will be extended by the given amount. 1126 | * If you don't implement this method, or return a non-positive time interval (<= 0) the write will timeout as usual. 1127 | * 1128 | * The elapsed parameter is the sum of the original timeout, plus any additions previously added via this method. 1129 | * The length parameter is the number of bytes that have been written so far for the write operation. 1130 | * 1131 | * Note that this method may be called multiple times for a single write if you return positive numbers. 1132 | **/ 1133 | - (NSTimeInterval)socket:(GCDAsyncSocket *)sock shouldTimeoutWriteWithTag:(long)tag 1134 | elapsed:(NSTimeInterval)elapsed 1135 | bytesDone:(NSUInteger)length; 1136 | 1137 | /** 1138 | * Conditionally called if the read stream closes, but the write stream may still be writeable. 1139 | * 1140 | * This delegate method is only called if autoDisconnectOnClosedReadStream has been set to NO. 1141 | * See the discussion on the autoDisconnectOnClosedReadStream method for more information. 1142 | **/ 1143 | - (void)socketDidCloseReadStream:(GCDAsyncSocket *)sock; 1144 | 1145 | /** 1146 | * Called when a socket disconnects with or without error. 1147 | * 1148 | * If you call the disconnect method, and the socket wasn't already disconnected, 1149 | * then an invocation of this delegate method will be enqueued on the delegateQueue 1150 | * before the disconnect method returns. 1151 | * 1152 | * Note: If the GCDAsyncSocket instance is deallocated while it is still connected, 1153 | * and the delegate is not also deallocated, then this method will be invoked, 1154 | * but the sock parameter will be nil. (It must necessarily be nil since it is no longer available.) 1155 | * This is a generally rare, but is possible if one writes code like this: 1156 | * 1157 | * asyncSocket = nil; // I'm implicitly disconnecting the socket 1158 | * 1159 | * In this case it may preferrable to nil the delegate beforehand, like this: 1160 | * 1161 | * asyncSocket.delegate = nil; // Don't invoke my delegate method 1162 | * asyncSocket = nil; // I'm implicitly disconnecting the socket 1163 | * 1164 | * Of course, this depends on how your state machine is configured. 1165 | **/ 1166 | - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err; 1167 | 1168 | /** 1169 | * Called after the socket has successfully completed SSL/TLS negotiation. 1170 | * This method is not called unless you use the provided startTLS method. 1171 | * 1172 | * If a SSL/TLS negotiation fails (invalid certificate, etc) then the socket will immediately close, 1173 | * and the socketDidDisconnect:withError: delegate method will be called with the specific SSL error code. 1174 | **/ 1175 | - (void)socketDidSecure:(GCDAsyncSocket *)sock; 1176 | 1177 | /** 1178 | * Allows a socket delegate to hook into the TLS handshake and manually validate the peer it's connecting to. 1179 | * 1180 | * This is only called if startTLS is invoked with options that include: 1181 | * - GCDAsyncSocketManuallyEvaluateTrust == YES 1182 | * 1183 | * Typically the delegate will use SecTrustEvaluate (and related functions) to properly validate the peer. 1184 | * 1185 | * Note from Apple's documentation: 1186 | * Because [SecTrustEvaluate] might look on the network for certificates in the certificate chain, 1187 | * [it] might block while attempting network access. You should never call it from your main thread; 1188 | * call it only from within a function running on a dispatch queue or on a separate thread. 1189 | * 1190 | * Thus this method uses a completionHandler block rather than a normal return value. 1191 | * The completionHandler block is thread-safe, and may be invoked from a background queue/thread. 1192 | * It is safe to invoke the completionHandler block even if the socket has been closed. 1193 | **/ 1194 | - (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust 1195 | completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler; 1196 | 1197 | @end 1198 | -------------------------------------------------------------------------------- /ServiceApp/GCDAsyncUdpSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // GCDAsyncUdpSocket 3 | // 4 | // This class is in the public domain. 5 | // Originally created by Robbie Hanson of Deusty LLC. 6 | // Updated and maintained by Deusty LLC and the Apple development community. 7 | // 8 | // https://github.com/robbiehanson/CocoaAsyncSocket 9 | // 10 | 11 | #import 12 | #import 13 | #import 14 | #import 15 | 16 | extern NSString *const GCDAsyncUdpSocketException; 17 | extern NSString *const GCDAsyncUdpSocketErrorDomain; 18 | 19 | extern NSString *const GCDAsyncUdpSocketQueueName; 20 | extern NSString *const GCDAsyncUdpSocketThreadName; 21 | 22 | typedef NS_ENUM(NSInteger, GCDAsyncUdpSocketError) { 23 | GCDAsyncUdpSocketNoError = 0, // Never used 24 | GCDAsyncUdpSocketBadConfigError, // Invalid configuration 25 | GCDAsyncUdpSocketBadParamError, // Invalid parameter was passed 26 | GCDAsyncUdpSocketSendTimeoutError, // A send operation timed out 27 | GCDAsyncUdpSocketClosedError, // The socket was closed 28 | GCDAsyncUdpSocketOtherError, // Description provided in userInfo 29 | }; 30 | 31 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 32 | #pragma mark - 33 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 34 | 35 | @class GCDAsyncUdpSocket; 36 | 37 | @protocol GCDAsyncUdpSocketDelegate 38 | @optional 39 | 40 | /** 41 | * By design, UDP is a connectionless protocol, and connecting is not needed. 42 | * However, you may optionally choose to connect to a particular host for reasons 43 | * outlined in the documentation for the various connect methods listed above. 44 | * 45 | * This method is called if one of the connect methods are invoked, and the connection is successful. 46 | **/ 47 | - (void)udpSocket:(GCDAsyncUdpSocket *)sock didConnectToAddress:(NSData *)address; 48 | 49 | /** 50 | * By design, UDP is a connectionless protocol, and connecting is not needed. 51 | * However, you may optionally choose to connect to a particular host for reasons 52 | * outlined in the documentation for the various connect methods listed above. 53 | * 54 | * This method is called if one of the connect methods are invoked, and the connection fails. 55 | * This may happen, for example, if a domain name is given for the host and the domain name is unable to be resolved. 56 | **/ 57 | - (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotConnect:(NSError *)error; 58 | 59 | /** 60 | * Called when the datagram with the given tag has been sent. 61 | **/ 62 | - (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag; 63 | 64 | /** 65 | * Called if an error occurs while trying to send a datagram. 66 | * This could be due to a timeout, or something more serious such as the data being too large to fit in a sigle packet. 67 | **/ 68 | - (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError *)error; 69 | 70 | /** 71 | * Called when the socket has received the requested datagram. 72 | **/ 73 | - (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data 74 | fromAddress:(NSData *)address 75 | withFilterContext:(id)filterContext; 76 | 77 | /** 78 | * Called when the socket is closed. 79 | **/ 80 | - (void)udpSocketDidClose:(GCDAsyncUdpSocket *)sock withError:(NSError *)error; 81 | 82 | @end 83 | 84 | /** 85 | * You may optionally set a receive filter for the socket. 86 | * A filter can provide several useful features: 87 | * 88 | * 1. Many times udp packets need to be parsed. 89 | * Since the filter can run in its own independent queue, you can parallelize this parsing quite easily. 90 | * The end result is a parallel socket io, datagram parsing, and packet processing. 91 | * 92 | * 2. Many times udp packets are discarded because they are duplicate/unneeded/unsolicited. 93 | * The filter can prevent such packets from arriving at the delegate. 94 | * And because the filter can run in its own independent queue, this doesn't slow down the delegate. 95 | * 96 | * - Since the udp protocol does not guarantee delivery, udp packets may be lost. 97 | * Many protocols built atop udp thus provide various resend/re-request algorithms. 98 | * This sometimes results in duplicate packets arriving. 99 | * A filter may allow you to architect the duplicate detection code to run in parallel to normal processing. 100 | * 101 | * - Since the udp socket may be connectionless, its possible for unsolicited packets to arrive. 102 | * Such packets need to be ignored. 103 | * 104 | * 3. Sometimes traffic shapers are needed to simulate real world environments. 105 | * A filter allows you to write custom code to simulate such environments. 106 | * The ability to code this yourself is especially helpful when your simulated environment 107 | * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), 108 | * or the system tools to handle this aren't available (e.g. on a mobile device). 109 | * 110 | * @param data - The packet that was received. 111 | * @param address - The address the data was received from. 112 | * See utilities section for methods to extract info from address. 113 | * @param context - Out parameter you may optionally set, which will then be passed to the delegate method. 114 | * For example, filter block can parse the data and then, 115 | * pass the parsed data to the delegate. 116 | * 117 | * @returns - YES if the received packet should be passed onto the delegate. 118 | * NO if the received packet should be discarded, and not reported to the delegete. 119 | * 120 | * Example: 121 | * 122 | * GCDAsyncUdpSocketReceiveFilterBlock filter = ^BOOL (NSData *data, NSData *address, id *context) { 123 | * 124 | * MyProtocolMessage *msg = [MyProtocol parseMessage:data]; 125 | * 126 | * *context = response; 127 | * return (response != nil); 128 | * }; 129 | * [udpSocket setReceiveFilter:filter withQueue:myParsingQueue]; 130 | * 131 | **/ 132 | typedef BOOL (^GCDAsyncUdpSocketReceiveFilterBlock)(NSData *data, NSData *address, id *context); 133 | 134 | /** 135 | * You may optionally set a send filter for the socket. 136 | * A filter can provide several interesting possibilities: 137 | * 138 | * 1. Optional caching of resolved addresses for domain names. 139 | * The cache could later be consulted, resulting in fewer system calls to getaddrinfo. 140 | * 141 | * 2. Reusable modules of code for bandwidth monitoring. 142 | * 143 | * 3. Sometimes traffic shapers are needed to simulate real world environments. 144 | * A filter allows you to write custom code to simulate such environments. 145 | * The ability to code this yourself is especially helpful when your simulated environment 146 | * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), 147 | * or the system tools to handle this aren't available (e.g. on a mobile device). 148 | * 149 | * @param data - The packet that was received. 150 | * @param address - The address the data was received from. 151 | * See utilities section for methods to extract info from address. 152 | * @param tag - The tag that was passed in the send method. 153 | * 154 | * @returns - YES if the packet should actually be sent over the socket. 155 | * NO if the packet should be silently dropped (not sent over the socket). 156 | * 157 | * Regardless of the return value, the delegate will be informed that the packet was successfully sent. 158 | * 159 | **/ 160 | typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, long tag); 161 | 162 | 163 | @interface GCDAsyncUdpSocket : NSObject 164 | 165 | /** 166 | * GCDAsyncUdpSocket uses the standard delegate paradigm, 167 | * but executes all delegate callbacks on a given delegate dispatch queue. 168 | * This allows for maximum concurrency, while at the same time providing easy thread safety. 169 | * 170 | * You MUST set a delegate AND delegate dispatch queue before attempting to 171 | * use the socket, or you will get an error. 172 | * 173 | * The socket queue is optional. 174 | * If you pass NULL, GCDAsyncSocket will automatically create its own socket queue. 175 | * If you choose to provide a socket queue, the socket queue must not be a concurrent queue, 176 | * then please see the discussion for the method markSocketQueueTargetQueue. 177 | * 178 | * The delegate queue and socket queue can optionally be the same. 179 | **/ 180 | - (id)init; 181 | - (id)initWithSocketQueue:(dispatch_queue_t)sq; 182 | - (id)initWithDelegate:(id )aDelegate delegateQueue:(dispatch_queue_t)dq; 183 | - (id)initWithDelegate:(id )aDelegate delegateQueue:(dispatch_queue_t)dq socketQueue:(dispatch_queue_t)sq; 184 | 185 | #pragma mark Configuration 186 | 187 | - (id )delegate; 188 | - (void)setDelegate:(id )delegate; 189 | - (void)synchronouslySetDelegate:(id )delegate; 190 | 191 | - (dispatch_queue_t)delegateQueue; 192 | - (void)setDelegateQueue:(dispatch_queue_t)delegateQueue; 193 | - (void)synchronouslySetDelegateQueue:(dispatch_queue_t)delegateQueue; 194 | 195 | - (void)getDelegate:(id *)delegatePtr delegateQueue:(dispatch_queue_t *)delegateQueuePtr; 196 | - (void)setDelegate:(id )delegate delegateQueue:(dispatch_queue_t)delegateQueue; 197 | - (void)synchronouslySetDelegate:(id )delegate delegateQueue:(dispatch_queue_t)delegateQueue; 198 | 199 | /** 200 | * By default, both IPv4 and IPv6 are enabled. 201 | * 202 | * This means GCDAsyncUdpSocket automatically supports both protocols, 203 | * and can send to IPv4 or IPv6 addresses, 204 | * as well as receive over IPv4 and IPv6. 205 | * 206 | * For operations that require DNS resolution, GCDAsyncUdpSocket supports both IPv4 and IPv6. 207 | * If a DNS lookup returns only IPv4 results, GCDAsyncUdpSocket will automatically use IPv4. 208 | * If a DNS lookup returns only IPv6 results, GCDAsyncUdpSocket will automatically use IPv6. 209 | * If a DNS lookup returns both IPv4 and IPv6 results, then the protocol used depends on the configured preference. 210 | * If IPv4 is preferred, then IPv4 is used. 211 | * If IPv6 is preferred, then IPv6 is used. 212 | * If neutral, then the first IP version in the resolved array will be used. 213 | * 214 | * Starting with Mac OS X 10.7 Lion and iOS 5, the default IP preference is neutral. 215 | * On prior systems the default IP preference is IPv4. 216 | **/ 217 | - (BOOL)isIPv4Enabled; 218 | - (void)setIPv4Enabled:(BOOL)flag; 219 | 220 | - (BOOL)isIPv6Enabled; 221 | - (void)setIPv6Enabled:(BOOL)flag; 222 | 223 | - (BOOL)isIPv4Preferred; 224 | - (BOOL)isIPv6Preferred; 225 | - (BOOL)isIPVersionNeutral; 226 | 227 | - (void)setPreferIPv4; 228 | - (void)setPreferIPv6; 229 | - (void)setIPVersionNeutral; 230 | 231 | /** 232 | * Gets/Sets the maximum size of the buffer that will be allocated for receive operations. 233 | * The default maximum size is 9216 bytes. 234 | * 235 | * The theoretical maximum size of any IPv4 UDP packet is UINT16_MAX = 65535. 236 | * The theoretical maximum size of any IPv6 UDP packet is UINT32_MAX = 4294967295. 237 | * 238 | * Since the OS/GCD notifies us of the size of each received UDP packet, 239 | * the actual allocated buffer size for each packet is exact. 240 | * And in practice the size of UDP packets is generally much smaller than the max. 241 | * Indeed most protocols will send and receive packets of only a few bytes, 242 | * or will set a limit on the size of packets to prevent fragmentation in the IP layer. 243 | * 244 | * If you set the buffer size too small, the sockets API in the OS will silently discard 245 | * any extra data, and you will not be notified of the error. 246 | **/ 247 | - (uint16_t)maxReceiveIPv4BufferSize; 248 | - (void)setMaxReceiveIPv4BufferSize:(uint16_t)max; 249 | 250 | - (uint32_t)maxReceiveIPv6BufferSize; 251 | - (void)setMaxReceiveIPv6BufferSize:(uint32_t)max; 252 | 253 | /** 254 | * User data allows you to associate arbitrary information with the socket. 255 | * This data is not used internally in any way. 256 | **/ 257 | - (id)userData; 258 | - (void)setUserData:(id)arbitraryUserData; 259 | 260 | #pragma mark Diagnostics 261 | 262 | /** 263 | * Returns the local address info for the socket. 264 | * 265 | * The localAddress method returns a sockaddr structure wrapped in a NSData object. 266 | * The localHost method returns the human readable IP address as a string. 267 | * 268 | * Note: Address info may not be available until after the socket has been binded, connected 269 | * or until after data has been sent. 270 | **/ 271 | - (NSData *)localAddress; 272 | - (NSString *)localHost; 273 | - (uint16_t)localPort; 274 | 275 | - (NSData *)localAddress_IPv4; 276 | - (NSString *)localHost_IPv4; 277 | - (uint16_t)localPort_IPv4; 278 | 279 | - (NSData *)localAddress_IPv6; 280 | - (NSString *)localHost_IPv6; 281 | - (uint16_t)localPort_IPv6; 282 | 283 | /** 284 | * Returns the remote address info for the socket. 285 | * 286 | * The connectedAddress method returns a sockaddr structure wrapped in a NSData object. 287 | * The connectedHost method returns the human readable IP address as a string. 288 | * 289 | * Note: Since UDP is connectionless by design, connected address info 290 | * will not be available unless the socket is explicitly connected to a remote host/port. 291 | * If the socket is not connected, these methods will return nil / 0. 292 | **/ 293 | - (NSData *)connectedAddress; 294 | - (NSString *)connectedHost; 295 | - (uint16_t)connectedPort; 296 | 297 | /** 298 | * Returns whether or not this socket has been connected to a single host. 299 | * By design, UDP is a connectionless protocol, and connecting is not needed. 300 | * If connected, the socket will only be able to send/receive data to/from the connected host. 301 | **/ 302 | - (BOOL)isConnected; 303 | 304 | /** 305 | * Returns whether or not this socket has been closed. 306 | * The only way a socket can be closed is if you explicitly call one of the close methods. 307 | **/ 308 | - (BOOL)isClosed; 309 | 310 | /** 311 | * Returns whether or not this socket is IPv4. 312 | * 313 | * By default this will be true, unless: 314 | * - IPv4 is disabled (via setIPv4Enabled:) 315 | * - The socket is explicitly bound to an IPv6 address 316 | * - The socket is connected to an IPv6 address 317 | **/ 318 | - (BOOL)isIPv4; 319 | 320 | /** 321 | * Returns whether or not this socket is IPv6. 322 | * 323 | * By default this will be true, unless: 324 | * - IPv6 is disabled (via setIPv6Enabled:) 325 | * - The socket is explicitly bound to an IPv4 address 326 | * _ The socket is connected to an IPv4 address 327 | * 328 | * This method will also return false on platforms that do not support IPv6. 329 | * Note: The iPhone does not currently support IPv6. 330 | **/ 331 | - (BOOL)isIPv6; 332 | 333 | #pragma mark Binding 334 | 335 | /** 336 | * Binds the UDP socket to the given port. 337 | * Binding should be done for server sockets that receive data prior to sending it. 338 | * Client sockets can skip binding, 339 | * as the OS will automatically assign the socket an available port when it starts sending data. 340 | * 341 | * You may optionally pass a port number of zero to immediately bind the socket, 342 | * yet still allow the OS to automatically assign an available port. 343 | * 344 | * You cannot bind a socket after its been connected. 345 | * You can only bind a socket once. 346 | * You can still connect a socket (if desired) after binding. 347 | * 348 | * On success, returns YES. 349 | * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass NULL for errPtr. 350 | **/ 351 | - (BOOL)bindToPort:(uint16_t)port error:(NSError **)errPtr; 352 | 353 | /** 354 | * Binds the UDP socket to the given port and optional interface. 355 | * Binding should be done for server sockets that receive data prior to sending it. 356 | * Client sockets can skip binding, 357 | * as the OS will automatically assign the socket an available port when it starts sending data. 358 | * 359 | * You may optionally pass a port number of zero to immediately bind the socket, 360 | * yet still allow the OS to automatically assign an available port. 361 | * 362 | * The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35"). 363 | * You may also use the special strings "localhost" or "loopback" to specify that 364 | * the socket only accept packets from the local machine. 365 | * 366 | * You cannot bind a socket after its been connected. 367 | * You can only bind a socket once. 368 | * You can still connect a socket (if desired) after binding. 369 | * 370 | * On success, returns YES. 371 | * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass NULL for errPtr. 372 | **/ 373 | - (BOOL)bindToPort:(uint16_t)port interface:(NSString *)interface error:(NSError **)errPtr; 374 | 375 | /** 376 | * Binds the UDP socket to the given address, specified as a sockaddr structure wrapped in a NSData object. 377 | * 378 | * If you have an existing struct sockaddr you can convert it to a NSData object like so: 379 | * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len]; 380 | * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len]; 381 | * 382 | * Binding should be done for server sockets that receive data prior to sending it. 383 | * Client sockets can skip binding, 384 | * as the OS will automatically assign the socket an available port when it starts sending data. 385 | * 386 | * You cannot bind a socket after its been connected. 387 | * You can only bind a socket once. 388 | * You can still connect a socket (if desired) after binding. 389 | * 390 | * On success, returns YES. 391 | * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass NULL for errPtr. 392 | **/ 393 | - (BOOL)bindToAddress:(NSData *)localAddr error:(NSError **)errPtr; 394 | 395 | #pragma mark Connecting 396 | 397 | /** 398 | * Connects the UDP socket to the given host and port. 399 | * By design, UDP is a connectionless protocol, and connecting is not needed. 400 | * 401 | * Choosing to connect to a specific host/port has the following effect: 402 | * - You will only be able to send data to the connected host/port. 403 | * - You will only be able to receive data from the connected host/port. 404 | * - You will receive ICMP messages that come from the connected host/port, such as "connection refused". 405 | * 406 | * The actual process of connecting a UDP socket does not result in any communication on the socket. 407 | * It simply changes the internal state of the socket. 408 | * 409 | * You cannot bind a socket after it has been connected. 410 | * You can only connect a socket once. 411 | * 412 | * The host may be a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2"). 413 | * 414 | * This method is asynchronous as it requires a DNS lookup to resolve the given host name. 415 | * If an obvious error is detected, this method immediately returns NO and sets errPtr. 416 | * If you don't care about the error, you can pass nil for errPtr. 417 | * Otherwise, this method returns YES and begins the asynchronous connection process. 418 | * The result of the asynchronous connection process will be reported via the delegate methods. 419 | **/ 420 | - (BOOL)connectToHost:(NSString *)host onPort:(uint16_t)port error:(NSError **)errPtr; 421 | 422 | /** 423 | * Connects the UDP socket to the given address, specified as a sockaddr structure wrapped in a NSData object. 424 | * 425 | * If you have an existing struct sockaddr you can convert it to a NSData object like so: 426 | * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len]; 427 | * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len]; 428 | * 429 | * By design, UDP is a connectionless protocol, and connecting is not needed. 430 | * 431 | * Choosing to connect to a specific address has the following effect: 432 | * - You will only be able to send data to the connected address. 433 | * - You will only be able to receive data from the connected address. 434 | * - You will receive ICMP messages that come from the connected address, such as "connection refused". 435 | * 436 | * Connecting a UDP socket does not result in any communication on the socket. 437 | * It simply changes the internal state of the socket. 438 | * 439 | * You cannot bind a socket after its been connected. 440 | * You can only connect a socket once. 441 | * 442 | * On success, returns YES. 443 | * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr. 444 | * 445 | * Note: Unlike the connectToHost:onPort:error: method, this method does not require a DNS lookup. 446 | * Thus when this method returns, the connection has either failed or fully completed. 447 | * In other words, this method is synchronous, unlike the asynchronous connectToHost::: method. 448 | * However, for compatibility and simplification of delegate code, if this method returns YES 449 | * then the corresponding delegate method (udpSocket:didConnectToHost:port:) is still invoked. 450 | **/ 451 | - (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr; 452 | 453 | #pragma mark Multicast 454 | 455 | /** 456 | * Join multicast group. 457 | * Group should be an IP address (eg @"225.228.0.1"). 458 | * 459 | * On success, returns YES. 460 | * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr. 461 | **/ 462 | - (BOOL)joinMulticastGroup:(NSString *)group error:(NSError **)errPtr; 463 | 464 | /** 465 | * Join multicast group. 466 | * Group should be an IP address (eg @"225.228.0.1"). 467 | * The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35"). 468 | * 469 | * On success, returns YES. 470 | * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr. 471 | **/ 472 | - (BOOL)joinMulticastGroup:(NSString *)group onInterface:(NSString *)interface error:(NSError **)errPtr; 473 | 474 | - (BOOL)leaveMulticastGroup:(NSString *)group error:(NSError **)errPtr; 475 | - (BOOL)leaveMulticastGroup:(NSString *)group onInterface:(NSString *)interface error:(NSError **)errPtr; 476 | 477 | #pragma mark Reuse Port 478 | 479 | /** 480 | * By default, only one socket can be bound to a given IP address + port at a time. 481 | * To enable multiple processes to simultaneously bind to the same address+port, 482 | * you need to enable this functionality in the socket. All processes that wish to 483 | * use the address+port simultaneously must all enable reuse port on the socket 484 | * bound to that port. 485 | **/ 486 | - (BOOL)enableReusePort:(BOOL)flag error:(NSError **)errPtr; 487 | 488 | #pragma mark Broadcast 489 | 490 | /** 491 | * By default, the underlying socket in the OS will not allow you to send broadcast messages. 492 | * In order to send broadcast messages, you need to enable this functionality in the socket. 493 | * 494 | * A broadcast is a UDP message to addresses like "192.168.255.255" or "255.255.255.255" that is 495 | * delivered to every host on the network. 496 | * The reason this is generally disabled by default (by the OS) is to prevent 497 | * accidental broadcast messages from flooding the network. 498 | **/ 499 | - (BOOL)enableBroadcast:(BOOL)flag error:(NSError **)errPtr; 500 | 501 | #pragma mark Sending 502 | 503 | /** 504 | * Asynchronously sends the given data, with the given timeout and tag. 505 | * 506 | * This method may only be used with a connected socket. 507 | * Recall that connecting is optional for a UDP socket. 508 | * For connected sockets, data can only be sent to the connected address. 509 | * For non-connected sockets, the remote destination is specified for each packet. 510 | * For more information about optionally connecting udp sockets, see the documentation for the connect methods above. 511 | * 512 | * @param data 513 | * The data to send. 514 | * If data is nil or zero-length, this method does nothing. 515 | * If passing NSMutableData, please read the thread-safety notice below. 516 | * 517 | * @param timeout 518 | * The timeout for the send opeartion. 519 | * If the timeout value is negative, the send operation will not use a timeout. 520 | * 521 | * @param tag 522 | * The tag is for your convenience. 523 | * It is not sent or received over the socket in any manner what-so-ever. 524 | * It is reported back as a parameter in the udpSocket:didSendDataWithTag: 525 | * or udpSocket:didNotSendDataWithTag:dueToError: methods. 526 | * You can use it as an array index, state id, type constant, etc. 527 | * 528 | * 529 | * Thread-Safety Note: 530 | * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while 531 | * the socket is sending it. In other words, it's not safe to alter the data until after the delegate method 532 | * udpSocket:didSendDataWithTag: or udpSocket:didNotSendDataWithTag:dueToError: is invoked signifying 533 | * that this particular send operation has completed. 534 | * This is due to the fact that GCDAsyncUdpSocket does NOT copy the data. 535 | * It simply retains it for performance reasons. 536 | * Often times, if NSMutableData is passed, it is because a request/response was built up in memory. 537 | * Copying this data adds an unwanted/unneeded overhead. 538 | * If you need to write data from an immutable buffer, and you need to alter the buffer before the socket 539 | * completes sending the bytes (which is NOT immediately after this method returns, but rather at a later time 540 | * when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method. 541 | **/ 542 | - (void)sendData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; 543 | 544 | /** 545 | * Asynchronously sends the given data, with the given timeout and tag, to the given host and port. 546 | * 547 | * This method cannot be used with a connected socket. 548 | * Recall that connecting is optional for a UDP socket. 549 | * For connected sockets, data can only be sent to the connected address. 550 | * For non-connected sockets, the remote destination is specified for each packet. 551 | * For more information about optionally connecting udp sockets, see the documentation for the connect methods above. 552 | * 553 | * @param data 554 | * The data to send. 555 | * If data is nil or zero-length, this method does nothing. 556 | * If passing NSMutableData, please read the thread-safety notice below. 557 | * 558 | * @param host 559 | * The destination to send the udp packet to. 560 | * May be specified as a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2"). 561 | * You may also use the convenience strings of "loopback" or "localhost". 562 | * 563 | * @param port 564 | * The port of the host to send to. 565 | * 566 | * @param timeout 567 | * The timeout for the send opeartion. 568 | * If the timeout value is negative, the send operation will not use a timeout. 569 | * 570 | * @param tag 571 | * The tag is for your convenience. 572 | * It is not sent or received over the socket in any manner what-so-ever. 573 | * It is reported back as a parameter in the udpSocket:didSendDataWithTag: 574 | * or udpSocket:didNotSendDataWithTag:dueToError: methods. 575 | * You can use it as an array index, state id, type constant, etc. 576 | * 577 | * 578 | * Thread-Safety Note: 579 | * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while 580 | * the socket is sending it. In other words, it's not safe to alter the data until after the delegate method 581 | * udpSocket:didSendDataWithTag: or udpSocket:didNotSendDataWithTag:dueToError: is invoked signifying 582 | * that this particular send operation has completed. 583 | * This is due to the fact that GCDAsyncUdpSocket does NOT copy the data. 584 | * It simply retains it for performance reasons. 585 | * Often times, if NSMutableData is passed, it is because a request/response was built up in memory. 586 | * Copying this data adds an unwanted/unneeded overhead. 587 | * If you need to write data from an immutable buffer, and you need to alter the buffer before the socket 588 | * completes sending the bytes (which is NOT immediately after this method returns, but rather at a later time 589 | * when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method. 590 | **/ 591 | - (void)sendData:(NSData *)data 592 | toHost:(NSString *)host 593 | port:(uint16_t)port 594 | withTimeout:(NSTimeInterval)timeout 595 | tag:(long)tag; 596 | 597 | /** 598 | * Asynchronously sends the given data, with the given timeout and tag, to the given address. 599 | * 600 | * This method cannot be used with a connected socket. 601 | * Recall that connecting is optional for a UDP socket. 602 | * For connected sockets, data can only be sent to the connected address. 603 | * For non-connected sockets, the remote destination is specified for each packet. 604 | * For more information about optionally connecting udp sockets, see the documentation for the connect methods above. 605 | * 606 | * @param data 607 | * The data to send. 608 | * If data is nil or zero-length, this method does nothing. 609 | * If passing NSMutableData, please read the thread-safety notice below. 610 | * 611 | * @param remoteAddr 612 | * The address to send the data to (specified as a sockaddr structure wrapped in a NSData object). 613 | * 614 | * @param timeout 615 | * The timeout for the send opeartion. 616 | * If the timeout value is negative, the send operation will not use a timeout. 617 | * 618 | * @param tag 619 | * The tag is for your convenience. 620 | * It is not sent or received over the socket in any manner what-so-ever. 621 | * It is reported back as a parameter in the udpSocket:didSendDataWithTag: 622 | * or udpSocket:didNotSendDataWithTag:dueToError: methods. 623 | * You can use it as an array index, state id, type constant, etc. 624 | * 625 | * 626 | * Thread-Safety Note: 627 | * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while 628 | * the socket is sending it. In other words, it's not safe to alter the data until after the delegate method 629 | * udpSocket:didSendDataWithTag: or udpSocket:didNotSendDataWithTag:dueToError: is invoked signifying 630 | * that this particular send operation has completed. 631 | * This is due to the fact that GCDAsyncUdpSocket does NOT copy the data. 632 | * It simply retains it for performance reasons. 633 | * Often times, if NSMutableData is passed, it is because a request/response was built up in memory. 634 | * Copying this data adds an unwanted/unneeded overhead. 635 | * If you need to write data from an immutable buffer, and you need to alter the buffer before the socket 636 | * completes sending the bytes (which is NOT immediately after this method returns, but rather at a later time 637 | * when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method. 638 | **/ 639 | - (void)sendData:(NSData *)data toAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout tag:(long)tag; 640 | 641 | /** 642 | * You may optionally set a send filter for the socket. 643 | * A filter can provide several interesting possibilities: 644 | * 645 | * 1. Optional caching of resolved addresses for domain names. 646 | * The cache could later be consulted, resulting in fewer system calls to getaddrinfo. 647 | * 648 | * 2. Reusable modules of code for bandwidth monitoring. 649 | * 650 | * 3. Sometimes traffic shapers are needed to simulate real world environments. 651 | * A filter allows you to write custom code to simulate such environments. 652 | * The ability to code this yourself is especially helpful when your simulated environment 653 | * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), 654 | * or the system tools to handle this aren't available (e.g. on a mobile device). 655 | * 656 | * For more information about GCDAsyncUdpSocketSendFilterBlock, see the documentation for its typedef. 657 | * To remove a previously set filter, invoke this method and pass a nil filterBlock and NULL filterQueue. 658 | * 659 | * Note: This method invokes setSendFilter:withQueue:isAsynchronous: (documented below), 660 | * passing YES for the isAsynchronous parameter. 661 | **/ 662 | - (void)setSendFilter:(GCDAsyncUdpSocketSendFilterBlock)filterBlock withQueue:(dispatch_queue_t)filterQueue; 663 | 664 | /** 665 | * The receive filter can be run via dispatch_async or dispatch_sync. 666 | * Most typical situations call for asynchronous operation. 667 | * 668 | * However, there are a few situations in which synchronous operation is preferred. 669 | * Such is the case when the filter is extremely minimal and fast. 670 | * This is because dispatch_sync is faster than dispatch_async. 671 | * 672 | * If you choose synchronous operation, be aware of possible deadlock conditions. 673 | * Since the socket queue is executing your block via dispatch_sync, 674 | * then you cannot perform any tasks which may invoke dispatch_sync on the socket queue. 675 | * For example, you can't query properties on the socket. 676 | **/ 677 | - (void)setSendFilter:(GCDAsyncUdpSocketSendFilterBlock)filterBlock 678 | withQueue:(dispatch_queue_t)filterQueue 679 | isAsynchronous:(BOOL)isAsynchronous; 680 | 681 | #pragma mark Receiving 682 | 683 | /** 684 | * There are two modes of operation for receiving packets: one-at-a-time & continuous. 685 | * 686 | * In one-at-a-time mode, you call receiveOnce everytime your delegate is ready to process an incoming udp packet. 687 | * Receiving packets one-at-a-time may be better suited for implementing certain state machine code, 688 | * where your state machine may not always be ready to process incoming packets. 689 | * 690 | * In continuous mode, the delegate is invoked immediately everytime incoming udp packets are received. 691 | * Receiving packets continuously is better suited to real-time streaming applications. 692 | * 693 | * You may switch back and forth between one-at-a-time mode and continuous mode. 694 | * If the socket is currently in continuous mode, calling this method will switch it to one-at-a-time mode. 695 | * 696 | * When a packet is received (and not filtered by the optional receive filter), 697 | * the delegate method (udpSocket:didReceiveData:fromAddress:withFilterContext:) is invoked. 698 | * 699 | * If the socket is able to begin receiving packets, this method returns YES. 700 | * Otherwise it returns NO, and sets the errPtr with appropriate error information. 701 | * 702 | * An example error: 703 | * You created a udp socket to act as a server, and immediately called receive. 704 | * You forgot to first bind the socket to a port number, and received a error with a message like: 705 | * "Must bind socket before you can receive data." 706 | **/ 707 | - (BOOL)receiveOnce:(NSError **)errPtr; 708 | 709 | /** 710 | * There are two modes of operation for receiving packets: one-at-a-time & continuous. 711 | * 712 | * In one-at-a-time mode, you call receiveOnce everytime your delegate is ready to process an incoming udp packet. 713 | * Receiving packets one-at-a-time may be better suited for implementing certain state machine code, 714 | * where your state machine may not always be ready to process incoming packets. 715 | * 716 | * In continuous mode, the delegate is invoked immediately everytime incoming udp packets are received. 717 | * Receiving packets continuously is better suited to real-time streaming applications. 718 | * 719 | * You may switch back and forth between one-at-a-time mode and continuous mode. 720 | * If the socket is currently in one-at-a-time mode, calling this method will switch it to continuous mode. 721 | * 722 | * For every received packet (not filtered by the optional receive filter), 723 | * the delegate method (udpSocket:didReceiveData:fromAddress:withFilterContext:) is invoked. 724 | * 725 | * If the socket is able to begin receiving packets, this method returns YES. 726 | * Otherwise it returns NO, and sets the errPtr with appropriate error information. 727 | * 728 | * An example error: 729 | * You created a udp socket to act as a server, and immediately called receive. 730 | * You forgot to first bind the socket to a port number, and received a error with a message like: 731 | * "Must bind socket before you can receive data." 732 | **/ 733 | - (BOOL)beginReceiving:(NSError **)errPtr; 734 | 735 | /** 736 | * If the socket is currently receiving (beginReceiving has been called), this method pauses the receiving. 737 | * That is, it won't read any more packets from the underlying OS socket until beginReceiving is called again. 738 | * 739 | * Important Note: 740 | * GCDAsyncUdpSocket may be running in parallel with your code. 741 | * That is, your delegate is likely running on a separate thread/dispatch_queue. 742 | * When you invoke this method, GCDAsyncUdpSocket may have already dispatched delegate methods to be invoked. 743 | * Thus, if those delegate methods have already been dispatch_async'd, 744 | * your didReceive delegate method may still be invoked after this method has been called. 745 | * You should be aware of this, and program defensively. 746 | **/ 747 | - (void)pauseReceiving; 748 | 749 | /** 750 | * You may optionally set a receive filter for the socket. 751 | * This receive filter may be set to run in its own queue (independent of delegate queue). 752 | * 753 | * A filter can provide several useful features. 754 | * 755 | * 1. Many times udp packets need to be parsed. 756 | * Since the filter can run in its own independent queue, you can parallelize this parsing quite easily. 757 | * The end result is a parallel socket io, datagram parsing, and packet processing. 758 | * 759 | * 2. Many times udp packets are discarded because they are duplicate/unneeded/unsolicited. 760 | * The filter can prevent such packets from arriving at the delegate. 761 | * And because the filter can run in its own independent queue, this doesn't slow down the delegate. 762 | * 763 | * - Since the udp protocol does not guarantee delivery, udp packets may be lost. 764 | * Many protocols built atop udp thus provide various resend/re-request algorithms. 765 | * This sometimes results in duplicate packets arriving. 766 | * A filter may allow you to architect the duplicate detection code to run in parallel to normal processing. 767 | * 768 | * - Since the udp socket may be connectionless, its possible for unsolicited packets to arrive. 769 | * Such packets need to be ignored. 770 | * 771 | * 3. Sometimes traffic shapers are needed to simulate real world environments. 772 | * A filter allows you to write custom code to simulate such environments. 773 | * The ability to code this yourself is especially helpful when your simulated environment 774 | * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), 775 | * or the system tools to handle this aren't available (e.g. on a mobile device). 776 | * 777 | * Example: 778 | * 779 | * GCDAsyncUdpSocketReceiveFilterBlock filter = ^BOOL (NSData *data, NSData *address, id *context) { 780 | * 781 | * MyProtocolMessage *msg = [MyProtocol parseMessage:data]; 782 | * 783 | * *context = response; 784 | * return (response != nil); 785 | * }; 786 | * [udpSocket setReceiveFilter:filter withQueue:myParsingQueue]; 787 | * 788 | * For more information about GCDAsyncUdpSocketReceiveFilterBlock, see the documentation for its typedef. 789 | * To remove a previously set filter, invoke this method and pass a nil filterBlock and NULL filterQueue. 790 | * 791 | * Note: This method invokes setReceiveFilter:withQueue:isAsynchronous: (documented below), 792 | * passing YES for the isAsynchronous parameter. 793 | **/ 794 | - (void)setReceiveFilter:(GCDAsyncUdpSocketReceiveFilterBlock)filterBlock withQueue:(dispatch_queue_t)filterQueue; 795 | 796 | /** 797 | * The receive filter can be run via dispatch_async or dispatch_sync. 798 | * Most typical situations call for asynchronous operation. 799 | * 800 | * However, there are a few situations in which synchronous operation is preferred. 801 | * Such is the case when the filter is extremely minimal and fast. 802 | * This is because dispatch_sync is faster than dispatch_async. 803 | * 804 | * If you choose synchronous operation, be aware of possible deadlock conditions. 805 | * Since the socket queue is executing your block via dispatch_sync, 806 | * then you cannot perform any tasks which may invoke dispatch_sync on the socket queue. 807 | * For example, you can't query properties on the socket. 808 | **/ 809 | - (void)setReceiveFilter:(GCDAsyncUdpSocketReceiveFilterBlock)filterBlock 810 | withQueue:(dispatch_queue_t)filterQueue 811 | isAsynchronous:(BOOL)isAsynchronous; 812 | 813 | #pragma mark Closing 814 | 815 | /** 816 | * Immediately closes the underlying socket. 817 | * Any pending send operations are discarded. 818 | * 819 | * The GCDAsyncUdpSocket instance may optionally be used again. 820 | * (it will setup/configure/use another unnderlying BSD socket). 821 | **/ 822 | - (void)close; 823 | 824 | /** 825 | * Closes the underlying socket after all pending send operations have been sent. 826 | * 827 | * The GCDAsyncUdpSocket instance may optionally be used again. 828 | * (it will setup/configure/use another unnderlying BSD socket). 829 | **/ 830 | - (void)closeAfterSending; 831 | 832 | #pragma mark Advanced 833 | /** 834 | * GCDAsyncSocket maintains thread safety by using an internal serial dispatch_queue. 835 | * In most cases, the instance creates this queue itself. 836 | * However, to allow for maximum flexibility, the internal queue may be passed in the init method. 837 | * This allows for some advanced options such as controlling socket priority via target queues. 838 | * However, when one begins to use target queues like this, they open the door to some specific deadlock issues. 839 | * 840 | * For example, imagine there are 2 queues: 841 | * dispatch_queue_t socketQueue; 842 | * dispatch_queue_t socketTargetQueue; 843 | * 844 | * If you do this (pseudo-code): 845 | * socketQueue.targetQueue = socketTargetQueue; 846 | * 847 | * Then all socketQueue operations will actually get run on the given socketTargetQueue. 848 | * This is fine and works great in most situations. 849 | * But if you run code directly from within the socketTargetQueue that accesses the socket, 850 | * you could potentially get deadlock. Imagine the following code: 851 | * 852 | * - (BOOL)socketHasSomething 853 | * { 854 | * __block BOOL result = NO; 855 | * dispatch_block_t block = ^{ 856 | * result = [self someInternalMethodToBeRunOnlyOnSocketQueue]; 857 | * } 858 | * if (is_executing_on_queue(socketQueue)) 859 | * block(); 860 | * else 861 | * dispatch_sync(socketQueue, block); 862 | * 863 | * return result; 864 | * } 865 | * 866 | * What happens if you call this method from the socketTargetQueue? The result is deadlock. 867 | * This is because the GCD API offers no mechanism to discover a queue's targetQueue. 868 | * Thus we have no idea if our socketQueue is configured with a targetQueue. 869 | * If we had this information, we could easily avoid deadlock. 870 | * But, since these API's are missing or unfeasible, you'll have to explicitly set it. 871 | * 872 | * IF you pass a socketQueue via the init method, 873 | * AND you've configured the passed socketQueue with a targetQueue, 874 | * THEN you should pass the end queue in the target hierarchy. 875 | * 876 | * For example, consider the following queue hierarchy: 877 | * socketQueue -> ipQueue -> moduleQueue 878 | * 879 | * This example demonstrates priority shaping within some server. 880 | * All incoming client connections from the same IP address are executed on the same target queue. 881 | * And all connections for a particular module are executed on the same target queue. 882 | * Thus, the priority of all networking for the entire module can be changed on the fly. 883 | * Additionally, networking traffic from a single IP cannot monopolize the module. 884 | * 885 | * Here's how you would accomplish something like that: 886 | * - (dispatch_queue_t)newSocketQueueForConnectionFromAddress:(NSData *)address onSocket:(GCDAsyncSocket *)sock 887 | * { 888 | * dispatch_queue_t socketQueue = dispatch_queue_create("", NULL); 889 | * dispatch_queue_t ipQueue = [self ipQueueForAddress:address]; 890 | * 891 | * dispatch_set_target_queue(socketQueue, ipQueue); 892 | * dispatch_set_target_queue(iqQueue, moduleQueue); 893 | * 894 | * return socketQueue; 895 | * } 896 | * - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket 897 | * { 898 | * [clientConnections addObject:newSocket]; 899 | * [newSocket markSocketQueueTargetQueue:moduleQueue]; 900 | * } 901 | * 902 | * Note: This workaround is ONLY needed if you intend to execute code directly on the ipQueue or moduleQueue. 903 | * This is often NOT the case, as such queues are used solely for execution shaping. 904 | **/ 905 | - (void)markSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreConfiguredTargetQueue; 906 | - (void)unmarkSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreviouslyConfiguredTargetQueue; 907 | 908 | /** 909 | * It's not thread-safe to access certain variables from outside the socket's internal queue. 910 | * 911 | * For example, the socket file descriptor. 912 | * File descriptors are simply integers which reference an index in the per-process file table. 913 | * However, when one requests a new file descriptor (by opening a file or socket), 914 | * the file descriptor returned is guaranteed to be the lowest numbered unused descriptor. 915 | * So if we're not careful, the following could be possible: 916 | * 917 | * - Thread A invokes a method which returns the socket's file descriptor. 918 | * - The socket is closed via the socket's internal queue on thread B. 919 | * - Thread C opens a file, and subsequently receives the file descriptor that was previously the socket's FD. 920 | * - Thread A is now accessing/altering the file instead of the socket. 921 | * 922 | * In addition to this, other variables are not actually objects, 923 | * and thus cannot be retained/released or even autoreleased. 924 | * An example is the sslContext, of type SSLContextRef, which is actually a malloc'd struct. 925 | * 926 | * Although there are internal variables that make it difficult to maintain thread-safety, 927 | * it is important to provide access to these variables 928 | * to ensure this class can be used in a wide array of environments. 929 | * This method helps to accomplish this by invoking the current block on the socket's internal queue. 930 | * The methods below can be invoked from within the block to access 931 | * those generally thread-unsafe internal variables in a thread-safe manner. 932 | * The given block will be invoked synchronously on the socket's internal queue. 933 | * 934 | * If you save references to any protected variables and use them outside the block, you do so at your own peril. 935 | **/ 936 | - (void)performBlock:(dispatch_block_t)block; 937 | 938 | /** 939 | * These methods are only available from within the context of a performBlock: invocation. 940 | * See the documentation for the performBlock: method above. 941 | * 942 | * Provides access to the socket's file descriptor(s). 943 | * If the socket isn't connected, or explicity bound to a particular interface, 944 | * it might actually have multiple internal socket file descriptors - one for IPv4 and one for IPv6. 945 | **/ 946 | - (int)socketFD; 947 | - (int)socket4FD; 948 | - (int)socket6FD; 949 | 950 | #if TARGET_OS_IPHONE 951 | 952 | /** 953 | * These methods are only available from within the context of a performBlock: invocation. 954 | * See the documentation for the performBlock: method above. 955 | * 956 | * Returns (creating if necessary) a CFReadStream/CFWriteStream for the internal socket. 957 | * 958 | * Generally GCDAsyncUdpSocket doesn't use CFStream. (It uses the faster GCD API's.) 959 | * However, if you need one for any reason, 960 | * these methods are a convenient way to get access to a safe instance of one. 961 | **/ 962 | - (CFReadStreamRef)readStream; 963 | - (CFWriteStreamRef)writeStream; 964 | 965 | /** 966 | * This method is only available from within the context of a performBlock: invocation. 967 | * See the documentation for the performBlock: method above. 968 | * 969 | * Configures the socket to allow it to operate when the iOS application has been backgrounded. 970 | * In other words, this method creates a read & write stream, and invokes: 971 | * 972 | * CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); 973 | * CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); 974 | * 975 | * Returns YES if successful, NO otherwise. 976 | * 977 | * Example usage: 978 | * 979 | * [asyncUdpSocket performBlock:^{ 980 | * [asyncUdpSocket enableBackgroundingOnSocket]; 981 | * }]; 982 | * 983 | * 984 | * NOTE : Apple doesn't currently support backgrounding UDP sockets. (Only TCP for now). 985 | **/ 986 | //- (BOOL)enableBackgroundingOnSockets; 987 | 988 | #endif 989 | 990 | #pragma mark Utilities 991 | 992 | /** 993 | * Extracting host/port/family information from raw address data. 994 | **/ 995 | 996 | + (NSString *)hostFromAddress:(NSData *)address; 997 | + (uint16_t)portFromAddress:(NSData *)address; 998 | + (int)familyFromAddress:(NSData *)address; 999 | 1000 | + (BOOL)isIPv4Address:(NSData *)address; 1001 | + (BOOL)isIPv6Address:(NSData *)address; 1002 | 1003 | + (BOOL)getHost:(NSString **)hostPtr port:(uint16_t *)portPtr fromAddress:(NSData *)address; 1004 | + (BOOL)getHost:(NSString **)hostPtr port:(uint16_t *)portPtr family:(int *)afPtr fromAddress:(NSData *)address; 1005 | 1006 | @end 1007 | 1008 | -------------------------------------------------------------------------------- /ServiceApp/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright © 2017年 joymake. All rights reserved. 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /ServiceApp/LifeWork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joymakee/macSocketServer/0ecb59d9f8fafcbd22f6f43fe39f79b908dc031e/ServiceApp/LifeWork.png -------------------------------------------------------------------------------- /ServiceApp/MainWindow.h: -------------------------------------------------------------------------------- 1 | // 2 | // MainWindow.h 3 | // ServiceApp 4 | // 5 | // Created by wangguopeng on 2017/2/17. 6 | // Copyright © 2017年 joymake. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MainWindow : NSWindow 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ServiceApp/MainWindow.m: -------------------------------------------------------------------------------- 1 | // 2 | // MainWindow.m 3 | // ServiceApp 4 | // 5 | // Created by wangguopeng on 2017/2/17. 6 | // Copyright © 2017年 joymake. All rights reserved. 7 | // 8 | 9 | #import "MainWindow.h" 10 | #include 11 | #include 12 | #include 13 | #import "SerViceAPP.h" 14 | #import "GCDAsyncSocket.h" 15 | #import 16 | 17 | #define IOS_CELLULAR @"pdp_ip0" 18 | #define IOS_WIFI @"en0" 19 | #define IP_ADDR_IPv4 @"ipv4" 20 | #define IP_ADDR_IPv6 @"ipv6" 21 | 22 | @interface MainWindow () 23 | @property (weak) IBOutlet NSTextField *addressTextField; 24 | @property (weak) IBOutlet NSTextField *portTextField; 25 | @property (weak) IBOutlet NSTextField *userNumberTextField; 26 | @property (weak) IBOutlet NSTextField *clientAddressLabel; 27 | @property (weak) IBOutlet NSTextField *clientData; 28 | @property (weak) IBOutlet NSImageView *qrCodeImageView; 29 | @end 30 | 31 | @implementation MainWindow 32 | 33 | -(void)awakeFromNib{ 34 | [super awakeFromNib]; 35 | [self.addressTextField setStringValue:[self getIPAddress:true]]; 36 | self.clientData.maximumNumberOfLines = 10; 37 | [self generateQrCodeImage]; 38 | } 39 | - (IBAction)touchAction:(NSButton *)sender { 40 | [[SerViceAPP shareInstance] openSerVice]; 41 | sender.layer.backgroundColor = [NSColor colorWithRed:0.1 green:0.9 blue:0.2 alpha:0.7].CGColor; 42 | sender.layer.borderWidth =1; 43 | sender.layer.masksToBounds = YES; 44 | sender.layer.cornerRadius = 3; 45 | __weak typeof (&*self)weakSelf = self; 46 | [SerViceAPP shareInstance].messageBlock=^(GCDAsyncSocket *client,NSString *message){ 47 | dispatch_async(dispatch_get_main_queue(), ^{ 48 | weakSelf.clientAddressLabel.stringValue = [NSString stringWithFormat:@"地址:%@\t端口:%hu",client.connectedHost,client.connectedPort]; 49 | weakSelf.clientData.stringValue = message; 50 | 51 | }); 52 | }; 53 | 54 | [SerViceAPP shareInstance].userNumberBlock = ^(NSInteger number) { 55 | dispatch_async(dispatch_get_main_queue(), ^{ 56 | weakSelf.userNumberTextField.stringValue = [@(number) stringValue]; 57 | }); 58 | }; 59 | } 60 | 61 | - (void)generateQrCodeImage{ 62 | //1.实例化二维码滤镜 63 | CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"]; 64 | // CIFilter用来表示CoreImage提供的各种滤镜。滤镜使用键-值来设置输入值,这些值设置好之后,CIFilter就可以用来生成新的CIImage输出图像。这里的输出的图像不会进行实际的图像渲染。 65 | 66 | //2.恢复滤镜的默认属性(因为滤镜有可能保存上一次的属性) 67 | [filter setDefaults]; 68 | //3.经字符串转化成NSData 69 | NSData *data = [self.addressTextField.stringValue dataUsingEncoding:NSUTF8StringEncoding]; 70 | //4.通过KVC设置滤镜,传入data,将来滤镜就知道要通过传入的数据生成二维码 71 | [filter setValue:data forKey:@"inputMessage"]; 72 | //5.生成二维码 73 | CIImage *ciImage = [filter outputImage]; 74 | //CIImage是CoreImage框架中最基本代表图像的对象,他不仅包含元图像数据,还包含作用在原图像上的滤镜链。 75 | 76 | NSImage *nsImage = [self createNonInterpolatedUIImageFormCIImage:ciImage withSize:self.qrCodeImageView.bounds.size.width]; 77 | //6.设置生成好的二维码到imageVIew上 78 | self.qrCodeImageView.image = nsImage; 79 | } 80 | 81 | - (NSImage *)createNonInterpolatedUIImageFormCIImage:(CIImage *)image withSize:(CGFloat) size { 82 | CGRect extent = CGRectIntegral(image.extent); 83 | //设置比例 84 | CGFloat scale = MIN(size/CGRectGetWidth(extent), size/CGRectGetHeight(extent)); 85 | // 创建bitmap(位图); 86 | size_t width = CGRectGetWidth(extent) * scale; 87 | size_t height = CGRectGetHeight(extent) * scale; 88 | CGColorSpaceRef cs = CGColorSpaceCreateDeviceGray(); 89 | CGContextRef bitmapRef = CGBitmapContextCreate(nil, width, height, 8, 0, cs, (CGBitmapInfo)kCGImageAlphaNone); 90 | CIContext *context = [CIContext contextWithOptions:nil]; 91 | CGImageRef bitmapImage = [context createCGImage:image fromRect:extent]; 92 | CGContextSetInterpolationQuality(bitmapRef, kCGInterpolationNone); 93 | CGContextScaleCTM(bitmapRef, scale, scale); 94 | CGContextDrawImage(bitmapRef, extent, bitmapImage); 95 | // 保存bitmap到图片 96 | CGImageRef scaledImage = CGBitmapContextCreateImage(bitmapRef); 97 | CGContextRelease(bitmapRef); 98 | CGImageRelease(bitmapImage); 99 | return [self imageFromCGImageRef:scaledImage]; 100 | } 101 | 102 | //将CGImageRef转换为NSImage * 103 | 104 | - (NSImage*) imageFromCGImageRef:(CGImageRef)image 105 | 106 | { 107 | 108 | NSRect imageRect = NSMakeRect(0.0, 0.0, 0.0, 0.0); 109 | 110 | CGContextRef imageContext = nil; 111 | 112 | NSImage* newImage = nil; 113 | 114 | // Get the image dimensions. 115 | 116 | imageRect.size.height = CGImageGetHeight(image); 117 | 118 | imageRect.size.width = CGImageGetWidth(image); 119 | 120 | // Create a new image to receive the Quartz image data. 121 | 122 | newImage = [[NSImage alloc] initWithSize:imageRect.size]; 123 | 124 | [newImage lockFocus]; 125 | 126 | // Get the Quartz context and draw. 127 | 128 | imageContext = (CGContextRef)[[NSGraphicsContext currentContext] 129 | 130 | graphicsPort]; 131 | 132 | CGContextDrawImage(imageContext, *(CGRect*)&imageRect, image); 133 | 134 | [newImage unlockFocus]; 135 | 136 | return newImage; 137 | 138 | } 139 | 140 | //获取设备当前网络IP地址 141 | - (NSString *)getIPAddress:(BOOL)preferIPv4 142 | { 143 | NSArray *searchArray = preferIPv4 ? 144 | @[ /*IOS_VPN @"/" IP_ADDR_IPv4, IOS_VPN @"/" IP_ADDR_IPv6,*/ IOS_WIFI @"/" IP_ADDR_IPv4, IOS_WIFI @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6 ] : 145 | @[ /*IOS_VPN @"/" IP_ADDR_IPv6, IOS_VPN @"/" IP_ADDR_IPv4,*/ IOS_WIFI @"/" IP_ADDR_IPv6, IOS_WIFI @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4 ] ; 146 | 147 | NSDictionary *addresses = [self getIPAddresses]; 148 | NSLog(@"addresses: %@", addresses); 149 | 150 | __block NSString *address; 151 | [searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop) 152 | { 153 | address = addresses[key]; 154 | if(address) *stop = YES; 155 | } ]; 156 | return address ? :[addresses objectForKey:@"en18/ipv4"]? :@"0.0.0.0"; 157 | } 158 | 159 | //获取所有相关IP信息 160 | - (NSDictionary *)getIPAddresses 161 | { 162 | NSMutableDictionary *addresses = [NSMutableDictionary dictionaryWithCapacity:8]; 163 | 164 | // retrieve the current interfaces - returns 0 on success 165 | struct ifaddrs *interfaces; 166 | if(!getifaddrs(&interfaces)) { 167 | // Loop through linked list of interfaces 168 | struct ifaddrs *interface; 169 | for(interface=interfaces; interface; interface=interface->ifa_next) { 170 | if(!(interface->ifa_flags & IFF_UP) /* || (interface->ifa_flags & IFF_LOOPBACK) */ ) { 171 | continue; // deeply nested code harder to read 172 | } 173 | const struct sockaddr_in *addr = (const struct sockaddr_in*)interface->ifa_addr; 174 | char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ]; 175 | if(addr && (addr->sin_family==AF_INET || addr->sin_family==AF_INET6)) { 176 | NSString *name = [NSString stringWithUTF8String:interface->ifa_name]; 177 | NSString *type; 178 | if(addr->sin_family == AF_INET) { 179 | if(inet_ntop(AF_INET, &addr->sin_addr, addrBuf, INET_ADDRSTRLEN)) { 180 | type = IP_ADDR_IPv4; 181 | } 182 | } else { 183 | const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)interface->ifa_addr; 184 | if(inet_ntop(AF_INET6, &addr6->sin6_addr, addrBuf, INET6_ADDRSTRLEN)) { 185 | type = IP_ADDR_IPv6; 186 | } 187 | } 188 | if(type) { 189 | NSString *key = [NSString stringWithFormat:@"%@/%@", name, type]; 190 | addresses[key] = [NSString stringWithUTF8String:addrBuf]; 191 | } 192 | } 193 | } 194 | freeifaddrs(interfaces); 195 | } 196 | return [addresses count] ? addresses : nil; 197 | } 198 | 199 | @end 200 | -------------------------------------------------------------------------------- /ServiceApp/SerViceAPP.h: -------------------------------------------------------------------------------- 1 | // 2 | // SerViceAPP.h 3 | // SerVe 4 | // 5 | // Created by qianhaifeng on 16/5/5. 6 | // Copyright © 2016年 qianhaifeng. All rights reserved. 7 | // 8 | 9 | #import 10 | @class GCDAsyncSocket; 11 | typedef void (^SocketBlock)(GCDAsyncSocket *client,NSString *message); 12 | typedef void (^SocketUserBlock)(NSInteger number); 13 | @interface SerViceAPP : NSObject 14 | 15 | + (instancetype)shareInstance; 16 | 17 | -(void)openSerVice; 18 | 19 | - (void)closeService; 20 | 21 | @property (nonatomic,copy) SocketBlock messageBlock; 22 | 23 | @property (nonatomic,copy) SocketUserBlock userNumberBlock; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /ServiceApp/SerViceAPP.m: -------------------------------------------------------------------------------- 1 | // 2 | // SerViceAPP.m 3 | // SerVe 4 | // 5 | // Created by qianhaifeng on 16/5/5. 6 | // Copyright © 2016年 qianhaifeng. All rights reserved. 7 | // 8 | 9 | #import "SerViceAPP.h" 10 | #import "GCDAsyncSocket.h" 11 | #import "GCDAsyncSocket+category.h" 12 | 13 | @interface SerViceAPP() 14 | 15 | @property(nonatomic, strong)GCDAsyncSocket *serve; 16 | @property(nonatomic, strong)NSMutableArray *socketConnectsM; 17 | @property(nonatomic, strong)NSThread *checkThread; 18 | @end 19 | 20 | static SerViceAPP *instance; 21 | @implementation SerViceAPP 22 | 23 | + (instancetype)shareInstance{ 24 | static dispatch_once_t onceToken; 25 | dispatch_once(&onceToken, ^{ 26 | instance = [[super alloc] init]; 27 | }); 28 | return instance; 29 | } 30 | 31 | -(NSThread *)checkThread{ 32 | return _checkThread = _checkThread?:[[NSThread alloc]initWithTarget:self selector:@selector(checkClientOnline) object:nil]; 33 | } 34 | 35 | -(GCDAsyncSocket *)serve{ 36 | if (!_serve) { 37 | _serve = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)]; 38 | _serve.delegate = self; 39 | } 40 | return _serve; 41 | } 42 | 43 | -(NSMutableArray *)socketConnectsM{ 44 | return _socketConnectsM= _socketConnectsM?:[NSMutableArray array]; 45 | } 46 | 47 | -(void)openSerVice{ 48 | NSError *error; 49 | if (self.serve.isDisconnected) { 50 | [self.checkThread start]; 51 | BOOL sucess = [self.serve acceptOnPort:8088 error:&error]; 52 | NSLog(sucess?@"端口开启成功,并监听客户端请求连接...":@"端口开启失..."); 53 | } 54 | } 55 | 56 | - (void)closeService{ 57 | if (!self.checkThread.isCancelled) { 58 | [self.checkThread cancel]; 59 | } 60 | if (self.serve.isConnected) { 61 | @synchronized (_serve) { 62 | [self.serve disconnect]; 63 | } 64 | } 65 | } 66 | #pragma delegate 67 | 68 | - (void)socket:(GCDAsyncSocket *)serveSock didAcceptNewSocket:(GCDAsyncSocket *)clientSocket{ 69 | NSLog(@"%@ IP: %@: %zd 客户端请求连接...",clientSocket,clientSocket.connectedHost,clientSocket.connectedPort); 70 | // 1.将客户端socket保存起来 71 | clientSocket.timeNew = [NSDate date]; 72 | [self.socketConnectsM addObject:clientSocket]; 73 | self.userNumberBlock?self.userNumberBlock(self.socketConnectsM.count):nil; 74 | [clientSocket readDataWithTimeout:-1 tag:0]; 75 | } 76 | 77 | - (void)socket:(GCDAsyncSocket *)clientSocket didReadData:(NSData *)data withTag:(long)tag { 78 | NSString *clientStr = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; 79 | ![clientStr isEqualToString:@"heart"] && clientStr.length!=0 &&self.messageBlock?self.messageBlock(clientSocket,clientStr):nil; 80 | for (GCDAsyncSocket *socket in self.socketConnectsM) { 81 | if (![clientSocket isEqual:socket]) { 82 | //群聊 发送给其他客户端 83 | if(![clientStr isEqualToString:@"heart"] && clientStr.length!=0) 84 | { 85 | [self writeDataWithSocket:socket str:clientStr]; 86 | } 87 | } 88 | else{socket.timeNew = [NSDate date];} 89 | } 90 | [clientSocket readDataWithTimeout:-1 tag:0]; 91 | } 92 | 93 | - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err{ 94 | NSLog(@"又下线"); 95 | [self.socketConnectsM enumerateObjectsUsingBlock:^(GCDAsyncSocket *client, NSUInteger idx, BOOL * _Nonnull stop) { 96 | if([client isEqual:sock]){ 97 | [self.socketConnectsM removeObject:client]; 98 | *stop = YES; 99 | } 100 | }]; 101 | } 102 | 103 | -(void)exitWithSocket:(GCDAsyncSocket *)clientSocket{ 104 | [self writeDataWithSocket:clientSocket str:@"成功退出\n"]; 105 | [self.socketConnectsM enumerateObjectsUsingBlock:^(GCDAsyncSocket *client, NSUInteger idx, BOOL * _Nonnull stop) { 106 | if([client isEqual:clientSocket]){ 107 | [self.socketConnectsM removeObject:client]; 108 | *stop = YES; 109 | } 110 | }]; 111 | NSLog(@"当前在线用户个数:%ld",self.socketConnectsM.count); 112 | self.userNumberBlock?self.userNumberBlock(self.socketConnectsM.count):nil; 113 | } 114 | 115 | - (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{ 116 | NSLog(@"数据发送成功.."); 117 | } 118 | 119 | - (void)writeDataWithSocket:(GCDAsyncSocket*)clientSocket str:(NSString*)str{ 120 | [clientSocket writeData:[str dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0]; 121 | } 122 | 123 | #pragma checkTimeThread 124 | 125 | //开启线程 启动runloop 循环检测客户端socket最新time 126 | - (void)checkClientOnline{ 127 | @autoreleasepool { 128 | [NSTimer scheduledTimerWithTimeInterval:35 target:self selector:@selector(repeatCheckClinetOnline) userInfo:nil repeats:YES]; 129 | [[NSRunLoop currentRunLoop]run]; 130 | } 131 | } 132 | 133 | //移除 超过心跳的 client 134 | - (void)repeatCheckClinetOnline{ 135 | if (self.socketConnectsM.count == 0) { 136 | return; 137 | } 138 | NSDate *date = [NSDate date]; 139 | NSMutableArray *arrayNew = [NSMutableArray array]; 140 | for (GCDAsyncSocket *socket in self.socketConnectsM ) { 141 | if ([date timeIntervalSinceDate:socket.timeNew]>30) { 142 | continue; 143 | } 144 | [arrayNew addObject:socket ]; 145 | } 146 | self.socketConnectsM = arrayNew; 147 | self.userNumberBlock?self.userNumberBlock(self.socketConnectsM.count):nil; 148 | } 149 | @end 150 | -------------------------------------------------------------------------------- /ServiceApp/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ServiceApp 4 | // 5 | // Created by wangguopeng on 2017/2/17. 6 | // Copyright © 2017年 joymake. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, const char * argv[]) { 12 | return NSApplicationMain(argc, argv); 13 | } 14 | -------------------------------------------------------------------------------- /ServiceAppTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /ServiceAppTests/ServiceAppTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ServiceAppTests.m 3 | // ServiceAppTests 4 | // 5 | // Created by wangguopeng on 2017/2/17. 6 | // Copyright © 2017年 joymake. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ServiceAppTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation ServiceAppTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /ServiceAppUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /ServiceAppUITests/ServiceAppUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ServiceAppUITests.m 3 | // ServiceAppUITests 4 | // 5 | // Created by wangguopeng on 2017/2/17. 6 | // Copyright © 2017年 joymake. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ServiceAppUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation ServiceAppUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | --------------------------------------------------------------------------------