├── FLSocketDemo ├── .DS_Store ├── FLSocketDemo.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ │ └── clarence.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── clarence.xcuserdatad │ │ └── xcschemes │ │ ├── FLSocketDemo.xcscheme │ │ └── xcschememanagement.plist ├── FLSocketDemo.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── clarence.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── FLSocketDemo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── FLSocketManager.h │ ├── FLSocketManager.m │ ├── Info.plist │ ├── ViewController.h │ ├── ViewController.m │ └── main.m ├── FLSocketDemoTests │ ├── FLSocketDemoTests.m │ └── Info.plist ├── FLSocketDemoUITests │ ├── FLSocketDemoUITests.m │ └── Info.plist ├── Podfile ├── Podfile.lock └── Pods │ ├── Headers │ ├── Private │ │ └── SocketRocket │ │ │ ├── SRWebSocket.h │ │ │ └── SocketRocket.h │ └── Public │ │ └── SocketRocket │ │ ├── SRWebSocket.h │ │ └── SocketRocket.h │ ├── Manifest.lock │ ├── Pods.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ │ └── clarence.xcuserdatad │ │ └── xcschemes │ │ ├── Pods-FLSocketDemo.xcscheme │ │ ├── SocketRocket.xcscheme │ │ └── xcschememanagement.plist │ ├── SocketRocket │ ├── LICENSE │ ├── README.rst │ └── SocketRocket │ │ ├── SRWebSocket.h │ │ ├── SRWebSocket.m │ │ └── SocketRocket.h │ └── Target Support Files │ ├── Pods-FLSocketDemo │ ├── Pods-FLSocketDemo-acknowledgements.markdown │ ├── Pods-FLSocketDemo-acknowledgements.plist │ ├── Pods-FLSocketDemo-dummy.m │ ├── Pods-FLSocketDemo-frameworks.sh │ ├── Pods-FLSocketDemo-resources.sh │ ├── Pods-FLSocketDemo.debug.xcconfig │ └── Pods-FLSocketDemo.release.xcconfig │ └── SocketRocket │ ├── SocketRocket-dummy.m │ ├── SocketRocket-prefix.pch │ └── SocketRocket.xcconfig ├── README.md └── 看我.md /FLSocketDemo/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitkong/FLSocketManager/b9d0b40eaf24fd989d68f74a7a854f63e4141e81/FLSocketDemo/.DS_Store -------------------------------------------------------------------------------- /FLSocketDemo/FLSocketDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 11FFA9A01D95D31E00B89E70 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 11FFA99F1D95D31E00B89E70 /* main.m */; }; 11 | 11FFA9A31D95D31E00B89E70 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 11FFA9A21D95D31E00B89E70 /* AppDelegate.m */; }; 12 | 11FFA9A61D95D31E00B89E70 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 11FFA9A51D95D31E00B89E70 /* ViewController.m */; }; 13 | 11FFA9A91D95D31E00B89E70 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 11FFA9A71D95D31E00B89E70 /* Main.storyboard */; }; 14 | 11FFA9AB1D95D31E00B89E70 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 11FFA9AA1D95D31E00B89E70 /* Assets.xcassets */; }; 15 | 11FFA9AE1D95D31E00B89E70 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 11FFA9AC1D95D31E00B89E70 /* LaunchScreen.storyboard */; }; 16 | 11FFA9B91D95D31F00B89E70 /* FLSocketDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 11FFA9B81D95D31F00B89E70 /* FLSocketDemoTests.m */; }; 17 | 11FFA9C41D95D31F00B89E70 /* FLSocketDemoUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 11FFA9C31D95D31F00B89E70 /* FLSocketDemoUITests.m */; }; 18 | 11FFA9D31D95D33600B89E70 /* FLSocketManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 11FFA9D21D95D33600B89E70 /* FLSocketManager.m */; }; 19 | 69BEC1970BB22257B7369E56 /* libPods-FLSocketDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9468C124D8339895E8EF0043 /* libPods-FLSocketDemo.a */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXContainerItemProxy section */ 23 | 11FFA9B51D95D31F00B89E70 /* PBXContainerItemProxy */ = { 24 | isa = PBXContainerItemProxy; 25 | containerPortal = 11FFA9931D95D31E00B89E70 /* Project object */; 26 | proxyType = 1; 27 | remoteGlobalIDString = 11FFA99A1D95D31E00B89E70; 28 | remoteInfo = FLSocketDemo; 29 | }; 30 | 11FFA9C01D95D31F00B89E70 /* PBXContainerItemProxy */ = { 31 | isa = PBXContainerItemProxy; 32 | containerPortal = 11FFA9931D95D31E00B89E70 /* Project object */; 33 | proxyType = 1; 34 | remoteGlobalIDString = 11FFA99A1D95D31E00B89E70; 35 | remoteInfo = FLSocketDemo; 36 | }; 37 | /* End PBXContainerItemProxy section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 11FFA99B1D95D31E00B89E70 /* FLSocketDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FLSocketDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 11FFA99F1D95D31E00B89E70 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 42 | 11FFA9A11D95D31E00B89E70 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 43 | 11FFA9A21D95D31E00B89E70 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 44 | 11FFA9A41D95D31E00B89E70 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 45 | 11FFA9A51D95D31E00B89E70 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 46 | 11FFA9A81D95D31E00B89E70 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 47 | 11FFA9AA1D95D31E00B89E70 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 11FFA9AD1D95D31E00B89E70 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 49 | 11FFA9AF1D95D31E00B89E70 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | 11FFA9B41D95D31F00B89E70 /* FLSocketDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FLSocketDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | 11FFA9B81D95D31F00B89E70 /* FLSocketDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLSocketDemoTests.m; sourceTree = ""; }; 52 | 11FFA9BA1D95D31F00B89E70 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 53 | 11FFA9BF1D95D31F00B89E70 /* FLSocketDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FLSocketDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | 11FFA9C31D95D31F00B89E70 /* FLSocketDemoUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLSocketDemoUITests.m; sourceTree = ""; }; 55 | 11FFA9C51D95D31F00B89E70 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 56 | 11FFA9D11D95D33600B89E70 /* FLSocketManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLSocketManager.h; sourceTree = ""; }; 57 | 11FFA9D21D95D33600B89E70 /* FLSocketManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLSocketManager.m; sourceTree = ""; }; 58 | 9468C124D8339895E8EF0043 /* libPods-FLSocketDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-FLSocketDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | E128A12766C3C87B3E42E661 /* Pods-FLSocketDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FLSocketDemo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-FLSocketDemo/Pods-FLSocketDemo.debug.xcconfig"; sourceTree = ""; }; 60 | FE7FAB067525FE383B5E3DC2 /* Pods-FLSocketDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FLSocketDemo.release.xcconfig"; path = "Pods/Target Support Files/Pods-FLSocketDemo/Pods-FLSocketDemo.release.xcconfig"; sourceTree = ""; }; 61 | /* End PBXFileReference section */ 62 | 63 | /* Begin PBXFrameworksBuildPhase section */ 64 | 11FFA9981D95D31E00B89E70 /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | 69BEC1970BB22257B7369E56 /* libPods-FLSocketDemo.a in Frameworks */, 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | 11FFA9B11D95D31F00B89E70 /* Frameworks */ = { 73 | isa = PBXFrameworksBuildPhase; 74 | buildActionMask = 2147483647; 75 | files = ( 76 | ); 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | 11FFA9BC1D95D31F00B89E70 /* Frameworks */ = { 80 | isa = PBXFrameworksBuildPhase; 81 | buildActionMask = 2147483647; 82 | files = ( 83 | ); 84 | runOnlyForDeploymentPostprocessing = 0; 85 | }; 86 | /* End PBXFrameworksBuildPhase section */ 87 | 88 | /* Begin PBXGroup section */ 89 | 11FFA9921D95D31E00B89E70 = { 90 | isa = PBXGroup; 91 | children = ( 92 | 11FFA99D1D95D31E00B89E70 /* FLSocketDemo */, 93 | 11FFA9B71D95D31F00B89E70 /* FLSocketDemoTests */, 94 | 11FFA9C21D95D31F00B89E70 /* FLSocketDemoUITests */, 95 | 11FFA99C1D95D31E00B89E70 /* Products */, 96 | 984B76222911BD85717967EC /* Pods */, 97 | 423C759AC575A009C7191042 /* Frameworks */, 98 | ); 99 | sourceTree = ""; 100 | }; 101 | 11FFA99C1D95D31E00B89E70 /* Products */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 11FFA99B1D95D31E00B89E70 /* FLSocketDemo.app */, 105 | 11FFA9B41D95D31F00B89E70 /* FLSocketDemoTests.xctest */, 106 | 11FFA9BF1D95D31F00B89E70 /* FLSocketDemoUITests.xctest */, 107 | ); 108 | name = Products; 109 | sourceTree = ""; 110 | }; 111 | 11FFA99D1D95D31E00B89E70 /* FLSocketDemo */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 11FFA9D11D95D33600B89E70 /* FLSocketManager.h */, 115 | 11FFA9D21D95D33600B89E70 /* FLSocketManager.m */, 116 | 11FFA9A11D95D31E00B89E70 /* AppDelegate.h */, 117 | 11FFA9A21D95D31E00B89E70 /* AppDelegate.m */, 118 | 11FFA9A41D95D31E00B89E70 /* ViewController.h */, 119 | 11FFA9A51D95D31E00B89E70 /* ViewController.m */, 120 | 11FFA9A71D95D31E00B89E70 /* Main.storyboard */, 121 | 11FFA9AA1D95D31E00B89E70 /* Assets.xcassets */, 122 | 11FFA9AC1D95D31E00B89E70 /* LaunchScreen.storyboard */, 123 | 11FFA9AF1D95D31E00B89E70 /* Info.plist */, 124 | 11FFA99E1D95D31E00B89E70 /* Supporting Files */, 125 | ); 126 | path = FLSocketDemo; 127 | sourceTree = ""; 128 | }; 129 | 11FFA99E1D95D31E00B89E70 /* Supporting Files */ = { 130 | isa = PBXGroup; 131 | children = ( 132 | 11FFA99F1D95D31E00B89E70 /* main.m */, 133 | ); 134 | name = "Supporting Files"; 135 | sourceTree = ""; 136 | }; 137 | 11FFA9B71D95D31F00B89E70 /* FLSocketDemoTests */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | 11FFA9B81D95D31F00B89E70 /* FLSocketDemoTests.m */, 141 | 11FFA9BA1D95D31F00B89E70 /* Info.plist */, 142 | ); 143 | path = FLSocketDemoTests; 144 | sourceTree = ""; 145 | }; 146 | 11FFA9C21D95D31F00B89E70 /* FLSocketDemoUITests */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | 11FFA9C31D95D31F00B89E70 /* FLSocketDemoUITests.m */, 150 | 11FFA9C51D95D31F00B89E70 /* Info.plist */, 151 | ); 152 | path = FLSocketDemoUITests; 153 | sourceTree = ""; 154 | }; 155 | 423C759AC575A009C7191042 /* Frameworks */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | 9468C124D8339895E8EF0043 /* libPods-FLSocketDemo.a */, 159 | ); 160 | name = Frameworks; 161 | sourceTree = ""; 162 | }; 163 | 984B76222911BD85717967EC /* Pods */ = { 164 | isa = PBXGroup; 165 | children = ( 166 | E128A12766C3C87B3E42E661 /* Pods-FLSocketDemo.debug.xcconfig */, 167 | FE7FAB067525FE383B5E3DC2 /* Pods-FLSocketDemo.release.xcconfig */, 168 | ); 169 | name = Pods; 170 | sourceTree = ""; 171 | }; 172 | /* End PBXGroup section */ 173 | 174 | /* Begin PBXNativeTarget section */ 175 | 11FFA99A1D95D31E00B89E70 /* FLSocketDemo */ = { 176 | isa = PBXNativeTarget; 177 | buildConfigurationList = 11FFA9C81D95D31F00B89E70 /* Build configuration list for PBXNativeTarget "FLSocketDemo" */; 178 | buildPhases = ( 179 | C407D2D2C0C70006FA6959D6 /* [CP] Check Pods Manifest.lock */, 180 | 11FFA9971D95D31E00B89E70 /* Sources */, 181 | 11FFA9981D95D31E00B89E70 /* Frameworks */, 182 | 11FFA9991D95D31E00B89E70 /* Resources */, 183 | 88A57319F8463B8E57D089B0 /* [CP] Embed Pods Frameworks */, 184 | F49AC1D937208ED4B9ADC847 /* [CP] Copy Pods Resources */, 185 | ); 186 | buildRules = ( 187 | ); 188 | dependencies = ( 189 | ); 190 | name = FLSocketDemo; 191 | productName = FLSocketDemo; 192 | productReference = 11FFA99B1D95D31E00B89E70 /* FLSocketDemo.app */; 193 | productType = "com.apple.product-type.application"; 194 | }; 195 | 11FFA9B31D95D31F00B89E70 /* FLSocketDemoTests */ = { 196 | isa = PBXNativeTarget; 197 | buildConfigurationList = 11FFA9CB1D95D31F00B89E70 /* Build configuration list for PBXNativeTarget "FLSocketDemoTests" */; 198 | buildPhases = ( 199 | 11FFA9B01D95D31F00B89E70 /* Sources */, 200 | 11FFA9B11D95D31F00B89E70 /* Frameworks */, 201 | 11FFA9B21D95D31F00B89E70 /* Resources */, 202 | ); 203 | buildRules = ( 204 | ); 205 | dependencies = ( 206 | 11FFA9B61D95D31F00B89E70 /* PBXTargetDependency */, 207 | ); 208 | name = FLSocketDemoTests; 209 | productName = FLSocketDemoTests; 210 | productReference = 11FFA9B41D95D31F00B89E70 /* FLSocketDemoTests.xctest */; 211 | productType = "com.apple.product-type.bundle.unit-test"; 212 | }; 213 | 11FFA9BE1D95D31F00B89E70 /* FLSocketDemoUITests */ = { 214 | isa = PBXNativeTarget; 215 | buildConfigurationList = 11FFA9CE1D95D31F00B89E70 /* Build configuration list for PBXNativeTarget "FLSocketDemoUITests" */; 216 | buildPhases = ( 217 | 11FFA9BB1D95D31F00B89E70 /* Sources */, 218 | 11FFA9BC1D95D31F00B89E70 /* Frameworks */, 219 | 11FFA9BD1D95D31F00B89E70 /* Resources */, 220 | ); 221 | buildRules = ( 222 | ); 223 | dependencies = ( 224 | 11FFA9C11D95D31F00B89E70 /* PBXTargetDependency */, 225 | ); 226 | name = FLSocketDemoUITests; 227 | productName = FLSocketDemoUITests; 228 | productReference = 11FFA9BF1D95D31F00B89E70 /* FLSocketDemoUITests.xctest */; 229 | productType = "com.apple.product-type.bundle.ui-testing"; 230 | }; 231 | /* End PBXNativeTarget section */ 232 | 233 | /* Begin PBXProject section */ 234 | 11FFA9931D95D31E00B89E70 /* Project object */ = { 235 | isa = PBXProject; 236 | attributes = { 237 | LastUpgradeCheck = 0800; 238 | ORGANIZATIONNAME = clarence; 239 | TargetAttributes = { 240 | 11FFA99A1D95D31E00B89E70 = { 241 | CreatedOnToolsVersion = 8.0; 242 | ProvisioningStyle = Automatic; 243 | }; 244 | 11FFA9B31D95D31F00B89E70 = { 245 | CreatedOnToolsVersion = 8.0; 246 | ProvisioningStyle = Automatic; 247 | TestTargetID = 11FFA99A1D95D31E00B89E70; 248 | }; 249 | 11FFA9BE1D95D31F00B89E70 = { 250 | CreatedOnToolsVersion = 8.0; 251 | ProvisioningStyle = Automatic; 252 | TestTargetID = 11FFA99A1D95D31E00B89E70; 253 | }; 254 | }; 255 | }; 256 | buildConfigurationList = 11FFA9961D95D31E00B89E70 /* Build configuration list for PBXProject "FLSocketDemo" */; 257 | compatibilityVersion = "Xcode 3.2"; 258 | developmentRegion = English; 259 | hasScannedForEncodings = 0; 260 | knownRegions = ( 261 | en, 262 | Base, 263 | ); 264 | mainGroup = 11FFA9921D95D31E00B89E70; 265 | productRefGroup = 11FFA99C1D95D31E00B89E70 /* Products */; 266 | projectDirPath = ""; 267 | projectRoot = ""; 268 | targets = ( 269 | 11FFA99A1D95D31E00B89E70 /* FLSocketDemo */, 270 | 11FFA9B31D95D31F00B89E70 /* FLSocketDemoTests */, 271 | 11FFA9BE1D95D31F00B89E70 /* FLSocketDemoUITests */, 272 | ); 273 | }; 274 | /* End PBXProject section */ 275 | 276 | /* Begin PBXResourcesBuildPhase section */ 277 | 11FFA9991D95D31E00B89E70 /* Resources */ = { 278 | isa = PBXResourcesBuildPhase; 279 | buildActionMask = 2147483647; 280 | files = ( 281 | 11FFA9AE1D95D31E00B89E70 /* LaunchScreen.storyboard in Resources */, 282 | 11FFA9AB1D95D31E00B89E70 /* Assets.xcassets in Resources */, 283 | 11FFA9A91D95D31E00B89E70 /* Main.storyboard in Resources */, 284 | ); 285 | runOnlyForDeploymentPostprocessing = 0; 286 | }; 287 | 11FFA9B21D95D31F00B89E70 /* Resources */ = { 288 | isa = PBXResourcesBuildPhase; 289 | buildActionMask = 2147483647; 290 | files = ( 291 | ); 292 | runOnlyForDeploymentPostprocessing = 0; 293 | }; 294 | 11FFA9BD1D95D31F00B89E70 /* Resources */ = { 295 | isa = PBXResourcesBuildPhase; 296 | buildActionMask = 2147483647; 297 | files = ( 298 | ); 299 | runOnlyForDeploymentPostprocessing = 0; 300 | }; 301 | /* End PBXResourcesBuildPhase section */ 302 | 303 | /* Begin PBXShellScriptBuildPhase section */ 304 | 88A57319F8463B8E57D089B0 /* [CP] Embed Pods Frameworks */ = { 305 | isa = PBXShellScriptBuildPhase; 306 | buildActionMask = 2147483647; 307 | files = ( 308 | ); 309 | inputPaths = ( 310 | ); 311 | name = "[CP] Embed Pods Frameworks"; 312 | outputPaths = ( 313 | ); 314 | runOnlyForDeploymentPostprocessing = 0; 315 | shellPath = /bin/sh; 316 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-FLSocketDemo/Pods-FLSocketDemo-frameworks.sh\"\n"; 317 | showEnvVarsInLog = 0; 318 | }; 319 | C407D2D2C0C70006FA6959D6 /* [CP] Check Pods Manifest.lock */ = { 320 | isa = PBXShellScriptBuildPhase; 321 | buildActionMask = 2147483647; 322 | files = ( 323 | ); 324 | inputPaths = ( 325 | ); 326 | name = "[CP] Check Pods Manifest.lock"; 327 | outputPaths = ( 328 | ); 329 | runOnlyForDeploymentPostprocessing = 0; 330 | shellPath = /bin/sh; 331 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 332 | showEnvVarsInLog = 0; 333 | }; 334 | F49AC1D937208ED4B9ADC847 /* [CP] Copy Pods Resources */ = { 335 | isa = PBXShellScriptBuildPhase; 336 | buildActionMask = 2147483647; 337 | files = ( 338 | ); 339 | inputPaths = ( 340 | ); 341 | name = "[CP] Copy Pods Resources"; 342 | outputPaths = ( 343 | ); 344 | runOnlyForDeploymentPostprocessing = 0; 345 | shellPath = /bin/sh; 346 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-FLSocketDemo/Pods-FLSocketDemo-resources.sh\"\n"; 347 | showEnvVarsInLog = 0; 348 | }; 349 | /* End PBXShellScriptBuildPhase section */ 350 | 351 | /* Begin PBXSourcesBuildPhase section */ 352 | 11FFA9971D95D31E00B89E70 /* Sources */ = { 353 | isa = PBXSourcesBuildPhase; 354 | buildActionMask = 2147483647; 355 | files = ( 356 | 11FFA9D31D95D33600B89E70 /* FLSocketManager.m in Sources */, 357 | 11FFA9A61D95D31E00B89E70 /* ViewController.m in Sources */, 358 | 11FFA9A31D95D31E00B89E70 /* AppDelegate.m in Sources */, 359 | 11FFA9A01D95D31E00B89E70 /* main.m in Sources */, 360 | ); 361 | runOnlyForDeploymentPostprocessing = 0; 362 | }; 363 | 11FFA9B01D95D31F00B89E70 /* Sources */ = { 364 | isa = PBXSourcesBuildPhase; 365 | buildActionMask = 2147483647; 366 | files = ( 367 | 11FFA9B91D95D31F00B89E70 /* FLSocketDemoTests.m in Sources */, 368 | ); 369 | runOnlyForDeploymentPostprocessing = 0; 370 | }; 371 | 11FFA9BB1D95D31F00B89E70 /* Sources */ = { 372 | isa = PBXSourcesBuildPhase; 373 | buildActionMask = 2147483647; 374 | files = ( 375 | 11FFA9C41D95D31F00B89E70 /* FLSocketDemoUITests.m in Sources */, 376 | ); 377 | runOnlyForDeploymentPostprocessing = 0; 378 | }; 379 | /* End PBXSourcesBuildPhase section */ 380 | 381 | /* Begin PBXTargetDependency section */ 382 | 11FFA9B61D95D31F00B89E70 /* PBXTargetDependency */ = { 383 | isa = PBXTargetDependency; 384 | target = 11FFA99A1D95D31E00B89E70 /* FLSocketDemo */; 385 | targetProxy = 11FFA9B51D95D31F00B89E70 /* PBXContainerItemProxy */; 386 | }; 387 | 11FFA9C11D95D31F00B89E70 /* PBXTargetDependency */ = { 388 | isa = PBXTargetDependency; 389 | target = 11FFA99A1D95D31E00B89E70 /* FLSocketDemo */; 390 | targetProxy = 11FFA9C01D95D31F00B89E70 /* PBXContainerItemProxy */; 391 | }; 392 | /* End PBXTargetDependency section */ 393 | 394 | /* Begin PBXVariantGroup section */ 395 | 11FFA9A71D95D31E00B89E70 /* Main.storyboard */ = { 396 | isa = PBXVariantGroup; 397 | children = ( 398 | 11FFA9A81D95D31E00B89E70 /* Base */, 399 | ); 400 | name = Main.storyboard; 401 | sourceTree = ""; 402 | }; 403 | 11FFA9AC1D95D31E00B89E70 /* LaunchScreen.storyboard */ = { 404 | isa = PBXVariantGroup; 405 | children = ( 406 | 11FFA9AD1D95D31E00B89E70 /* Base */, 407 | ); 408 | name = LaunchScreen.storyboard; 409 | sourceTree = ""; 410 | }; 411 | /* End PBXVariantGroup section */ 412 | 413 | /* Begin XCBuildConfiguration section */ 414 | 11FFA9C61D95D31F00B89E70 /* Debug */ = { 415 | isa = XCBuildConfiguration; 416 | buildSettings = { 417 | ALWAYS_SEARCH_USER_PATHS = NO; 418 | CLANG_ANALYZER_NONNULL = YES; 419 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 420 | CLANG_CXX_LIBRARY = "libc++"; 421 | CLANG_ENABLE_MODULES = YES; 422 | CLANG_ENABLE_OBJC_ARC = YES; 423 | CLANG_WARN_BOOL_CONVERSION = YES; 424 | CLANG_WARN_CONSTANT_CONVERSION = YES; 425 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 426 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 427 | CLANG_WARN_EMPTY_BODY = YES; 428 | CLANG_WARN_ENUM_CONVERSION = YES; 429 | CLANG_WARN_INFINITE_RECURSION = YES; 430 | CLANG_WARN_INT_CONVERSION = YES; 431 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 432 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 433 | CLANG_WARN_UNREACHABLE_CODE = YES; 434 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 435 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 436 | COPY_PHASE_STRIP = NO; 437 | DEBUG_INFORMATION_FORMAT = dwarf; 438 | ENABLE_STRICT_OBJC_MSGSEND = YES; 439 | ENABLE_TESTABILITY = YES; 440 | GCC_C_LANGUAGE_STANDARD = gnu99; 441 | GCC_DYNAMIC_NO_PIC = NO; 442 | GCC_NO_COMMON_BLOCKS = YES; 443 | GCC_OPTIMIZATION_LEVEL = 0; 444 | GCC_PREPROCESSOR_DEFINITIONS = ( 445 | "DEBUG=1", 446 | "$(inherited)", 447 | ); 448 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 449 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 450 | GCC_WARN_UNDECLARED_SELECTOR = YES; 451 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 452 | GCC_WARN_UNUSED_FUNCTION = YES; 453 | GCC_WARN_UNUSED_VARIABLE = YES; 454 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 455 | MTL_ENABLE_DEBUG_INFO = YES; 456 | ONLY_ACTIVE_ARCH = YES; 457 | SDKROOT = iphoneos; 458 | }; 459 | name = Debug; 460 | }; 461 | 11FFA9C71D95D31F00B89E70 /* Release */ = { 462 | isa = XCBuildConfiguration; 463 | buildSettings = { 464 | ALWAYS_SEARCH_USER_PATHS = NO; 465 | CLANG_ANALYZER_NONNULL = YES; 466 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 467 | CLANG_CXX_LIBRARY = "libc++"; 468 | CLANG_ENABLE_MODULES = YES; 469 | CLANG_ENABLE_OBJC_ARC = YES; 470 | CLANG_WARN_BOOL_CONVERSION = YES; 471 | CLANG_WARN_CONSTANT_CONVERSION = YES; 472 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 473 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 474 | CLANG_WARN_EMPTY_BODY = YES; 475 | CLANG_WARN_ENUM_CONVERSION = YES; 476 | CLANG_WARN_INFINITE_RECURSION = YES; 477 | CLANG_WARN_INT_CONVERSION = YES; 478 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 479 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 480 | CLANG_WARN_UNREACHABLE_CODE = YES; 481 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 482 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 483 | COPY_PHASE_STRIP = NO; 484 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 485 | ENABLE_NS_ASSERTIONS = NO; 486 | ENABLE_STRICT_OBJC_MSGSEND = YES; 487 | GCC_C_LANGUAGE_STANDARD = gnu99; 488 | GCC_NO_COMMON_BLOCKS = YES; 489 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 490 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 491 | GCC_WARN_UNDECLARED_SELECTOR = YES; 492 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 493 | GCC_WARN_UNUSED_FUNCTION = YES; 494 | GCC_WARN_UNUSED_VARIABLE = YES; 495 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 496 | MTL_ENABLE_DEBUG_INFO = NO; 497 | SDKROOT = iphoneos; 498 | VALIDATE_PRODUCT = YES; 499 | }; 500 | name = Release; 501 | }; 502 | 11FFA9C91D95D31F00B89E70 /* Debug */ = { 503 | isa = XCBuildConfiguration; 504 | baseConfigurationReference = E128A12766C3C87B3E42E661 /* Pods-FLSocketDemo.debug.xcconfig */; 505 | buildSettings = { 506 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 507 | INFOPLIST_FILE = FLSocketDemo/Info.plist; 508 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 509 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 510 | PRODUCT_BUNDLE_IDENTIFIER = clarence.FLSocketDemo; 511 | PRODUCT_NAME = "$(TARGET_NAME)"; 512 | }; 513 | name = Debug; 514 | }; 515 | 11FFA9CA1D95D31F00B89E70 /* Release */ = { 516 | isa = XCBuildConfiguration; 517 | baseConfigurationReference = FE7FAB067525FE383B5E3DC2 /* Pods-FLSocketDemo.release.xcconfig */; 518 | buildSettings = { 519 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 520 | INFOPLIST_FILE = FLSocketDemo/Info.plist; 521 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 522 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 523 | PRODUCT_BUNDLE_IDENTIFIER = clarence.FLSocketDemo; 524 | PRODUCT_NAME = "$(TARGET_NAME)"; 525 | }; 526 | name = Release; 527 | }; 528 | 11FFA9CC1D95D31F00B89E70 /* Debug */ = { 529 | isa = XCBuildConfiguration; 530 | buildSettings = { 531 | BUNDLE_LOADER = "$(TEST_HOST)"; 532 | INFOPLIST_FILE = FLSocketDemoTests/Info.plist; 533 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 534 | PRODUCT_BUNDLE_IDENTIFIER = clarence.FLSocketDemoTests; 535 | PRODUCT_NAME = "$(TARGET_NAME)"; 536 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FLSocketDemo.app/FLSocketDemo"; 537 | }; 538 | name = Debug; 539 | }; 540 | 11FFA9CD1D95D31F00B89E70 /* Release */ = { 541 | isa = XCBuildConfiguration; 542 | buildSettings = { 543 | BUNDLE_LOADER = "$(TEST_HOST)"; 544 | INFOPLIST_FILE = FLSocketDemoTests/Info.plist; 545 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 546 | PRODUCT_BUNDLE_IDENTIFIER = clarence.FLSocketDemoTests; 547 | PRODUCT_NAME = "$(TARGET_NAME)"; 548 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FLSocketDemo.app/FLSocketDemo"; 549 | }; 550 | name = Release; 551 | }; 552 | 11FFA9CF1D95D31F00B89E70 /* Debug */ = { 553 | isa = XCBuildConfiguration; 554 | buildSettings = { 555 | INFOPLIST_FILE = FLSocketDemoUITests/Info.plist; 556 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 557 | PRODUCT_BUNDLE_IDENTIFIER = clarence.FLSocketDemoUITests; 558 | PRODUCT_NAME = "$(TARGET_NAME)"; 559 | TEST_TARGET_NAME = FLSocketDemo; 560 | }; 561 | name = Debug; 562 | }; 563 | 11FFA9D01D95D31F00B89E70 /* Release */ = { 564 | isa = XCBuildConfiguration; 565 | buildSettings = { 566 | INFOPLIST_FILE = FLSocketDemoUITests/Info.plist; 567 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 568 | PRODUCT_BUNDLE_IDENTIFIER = clarence.FLSocketDemoUITests; 569 | PRODUCT_NAME = "$(TARGET_NAME)"; 570 | TEST_TARGET_NAME = FLSocketDemo; 571 | }; 572 | name = Release; 573 | }; 574 | /* End XCBuildConfiguration section */ 575 | 576 | /* Begin XCConfigurationList section */ 577 | 11FFA9961D95D31E00B89E70 /* Build configuration list for PBXProject "FLSocketDemo" */ = { 578 | isa = XCConfigurationList; 579 | buildConfigurations = ( 580 | 11FFA9C61D95D31F00B89E70 /* Debug */, 581 | 11FFA9C71D95D31F00B89E70 /* Release */, 582 | ); 583 | defaultConfigurationIsVisible = 0; 584 | defaultConfigurationName = Release; 585 | }; 586 | 11FFA9C81D95D31F00B89E70 /* Build configuration list for PBXNativeTarget "FLSocketDemo" */ = { 587 | isa = XCConfigurationList; 588 | buildConfigurations = ( 589 | 11FFA9C91D95D31F00B89E70 /* Debug */, 590 | 11FFA9CA1D95D31F00B89E70 /* Release */, 591 | ); 592 | defaultConfigurationIsVisible = 0; 593 | defaultConfigurationName = Release; 594 | }; 595 | 11FFA9CB1D95D31F00B89E70 /* Build configuration list for PBXNativeTarget "FLSocketDemoTests" */ = { 596 | isa = XCConfigurationList; 597 | buildConfigurations = ( 598 | 11FFA9CC1D95D31F00B89E70 /* Debug */, 599 | 11FFA9CD1D95D31F00B89E70 /* Release */, 600 | ); 601 | defaultConfigurationIsVisible = 0; 602 | defaultConfigurationName = Release; 603 | }; 604 | 11FFA9CE1D95D31F00B89E70 /* Build configuration list for PBXNativeTarget "FLSocketDemoUITests" */ = { 605 | isa = XCConfigurationList; 606 | buildConfigurations = ( 607 | 11FFA9CF1D95D31F00B89E70 /* Debug */, 608 | 11FFA9D01D95D31F00B89E70 /* Release */, 609 | ); 610 | defaultConfigurationIsVisible = 0; 611 | defaultConfigurationName = Release; 612 | }; 613 | /* End XCConfigurationList section */ 614 | }; 615 | rootObject = 11FFA9931D95D31E00B89E70 /* Project object */; 616 | } 617 | -------------------------------------------------------------------------------- /FLSocketDemo/FLSocketDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /FLSocketDemo/FLSocketDemo.xcodeproj/project.xcworkspace/xcuserdata/clarence.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitkong/FLSocketManager/b9d0b40eaf24fd989d68f74a7a854f63e4141e81/FLSocketDemo/FLSocketDemo.xcodeproj/project.xcworkspace/xcuserdata/clarence.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /FLSocketDemo/FLSocketDemo.xcodeproj/xcuserdata/clarence.xcuserdatad/xcschemes/FLSocketDemo.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 | -------------------------------------------------------------------------------- /FLSocketDemo/FLSocketDemo.xcodeproj/xcuserdata/clarence.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | FLSocketDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 11FFA99A1D95D31E00B89E70 16 | 17 | primary 18 | 19 | 20 | 11FFA9B31D95D31F00B89E70 21 | 22 | primary 23 | 24 | 25 | 11FFA9BE1D95D31F00B89E70 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /FLSocketDemo/FLSocketDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /FLSocketDemo/FLSocketDemo.xcworkspace/xcuserdata/clarence.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitkong/FLSocketManager/b9d0b40eaf24fd989d68f74a7a854f63e4141e81/FLSocketDemo/FLSocketDemo.xcworkspace/xcuserdata/clarence.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /FLSocketDemo/FLSocketDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // FLSocketDemo 4 | // 5 | // Created by clarence on 16/9/24. 6 | // Copyright © 2016年 clarence. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /FLSocketDemo/FLSocketDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // FLSocketDemo 4 | // 5 | // Created by clarence on 16/9/24. 6 | // Copyright © 2016年 clarence. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /FLSocketDemo/FLSocketDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /FLSocketDemo/FLSocketDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /FLSocketDemo/FLSocketDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /FLSocketDemo/FLSocketDemo/FLSocketManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * author 孔凡列 3 | * 4 | * gitHub https://github.com/gitkong 5 | * cocoaChina http://code.cocoachina.com/user/ 6 | * 简书 http://www.jianshu.com/users/fe5700cfb223/latest_articles 7 | * QQ 279761135 8 | * 喜欢就给个like 和 star 喔~ 9 | */ 10 | 11 | #import 12 | 13 | /** 14 | * @author 孔凡列 15 | * 16 | * socket状态 17 | */ 18 | typedef NS_ENUM(NSInteger,FLSocketStatus){ 19 | FLSocketStatusConnected,// 已连接 20 | FLSocketStatusFailed,// 失败 21 | FLSocketStatusClosedByServer,// 系统关闭 22 | FLSocketStatusClosedByUser,// 用户关闭 23 | FLSocketStatusReceived// 接收消息 24 | }; 25 | /** 26 | * @author 孔凡列 27 | * 28 | * 消息类型 29 | */ 30 | typedef NS_ENUM(NSInteger,FLSocketReceiveType){ 31 | FLSocketReceiveTypeForMessage, 32 | FLSocketReceiveTypeForPong 33 | }; 34 | /** 35 | * @author 孔凡列 36 | * 37 | * 连接成功回调 38 | */ 39 | typedef void(^FLSocketDidConnectBlock)(); 40 | /** 41 | * @author 孔凡列 42 | * 43 | * 失败回调 44 | */ 45 | typedef void(^FLSocketDidFailBlock)(NSError *error); 46 | /** 47 | * @author 孔凡列 48 | * 49 | * 关闭回调 50 | */ 51 | typedef void(^FLSocketDidCloseBlock)(NSInteger code,NSString *reason,BOOL wasClean); 52 | /** 53 | * @author 孔凡列 54 | * 55 | * 消息接收回调 56 | */ 57 | typedef void(^FLSocketDidReceiveBlock)(id message ,FLSocketReceiveType type); 58 | 59 | @interface FLSocketManager : NSObject 60 | /** 61 | * @author 孔凡列 62 | * 63 | * 连接回调 64 | */ 65 | @property (nonatomic,copy)FLSocketDidConnectBlock connect; 66 | /** 67 | * @author 孔凡列 68 | * 69 | * 接收消息回调 70 | */ 71 | @property (nonatomic,copy)FLSocketDidReceiveBlock receive; 72 | /** 73 | * @author 孔凡列 74 | * 75 | * 失败回调 76 | */ 77 | @property (nonatomic,copy)FLSocketDidFailBlock failure; 78 | /** 79 | * @author 孔凡列 80 | * 81 | * 关闭回调 82 | */ 83 | @property (nonatomic,copy)FLSocketDidCloseBlock close; 84 | /** 85 | * @author 孔凡列 86 | * 87 | * 当前的socket状态 88 | */ 89 | @property (nonatomic,assign,readonly)FLSocketStatus fl_socketStatus; 90 | /** 91 | * @author 孔凡列 92 | * 93 | * 超时重连时间,默认1秒 94 | */ 95 | @property (nonatomic,assign)NSTimeInterval overtime; 96 | /** 97 | * @author Clarence 98 | * 99 | * 重连次数,默认5次 100 | */ 101 | @property (nonatomic, assign)NSUInteger reconnectCount; 102 | /** 103 | * @author 孔凡列 104 | * 105 | * 单例调用 106 | */ 107 | + (instancetype)shareManager; 108 | /** 109 | * @author 孔凡列 110 | * 111 | * 开启socket 112 | * 113 | * @param urlStr 服务器地址 114 | * @param connect 连接成功回调 115 | * @param receive 接收消息回调 116 | * @param failure 失败回调 117 | */ 118 | - (void)fl_open:(NSString *)urlStr connect:(FLSocketDidConnectBlock)connect receive:(FLSocketDidReceiveBlock)receive failure:(FLSocketDidFailBlock)failure; 119 | /** 120 | * @author 孔凡列 121 | * 122 | * 关闭socket 123 | * 124 | * @param close 关闭回调 125 | */ 126 | - (void)fl_close:(FLSocketDidCloseBlock)close; 127 | /** 128 | * @author 孔凡列 129 | * 130 | * 发送消息,NSString 或者 NSData 131 | * 132 | * @param data Send a UTF8 String or Data. 133 | */ 134 | - (void)fl_send:(id)data; 135 | 136 | @end 137 | -------------------------------------------------------------------------------- /FLSocketDemo/FLSocketDemo/FLSocketManager.m: -------------------------------------------------------------------------------- 1 | /* 2 | * author 孔凡列 3 | * 4 | * gitHub https://github.com/gitkong 5 | * cocoaChina http://code.cocoachina.com/user/ 6 | * 简书 http://www.jianshu.com/users/fe5700cfb223/latest_articles 7 | * QQ 279761135 8 | * 喜欢就给个like 和 star 喔~ 9 | */ 10 | 11 | #import "FLSocketManager.h" 12 | #import "SRWebSocket.h" 13 | @interface FLSocketManager () 14 | @property (nonatomic,strong)SRWebSocket *webSocket; 15 | 16 | @property (nonatomic,assign)FLSocketStatus fl_socketStatus; 17 | 18 | @property (nonatomic,weak)NSTimer *timer; 19 | 20 | @property (nonatomic,copy)NSString *urlString; 21 | 22 | @end 23 | 24 | @implementation FLSocketManager{ 25 | NSInteger _reconnectCounter; 26 | } 27 | 28 | 29 | + (instancetype)shareManager{ 30 | static FLSocketManager *instance; 31 | static dispatch_once_t onceToken; 32 | dispatch_once(&onceToken, ^{ 33 | instance = [[self alloc] init]; 34 | instance.overtime = 1; 35 | instance.reconnectCount = 5; 36 | }); 37 | return instance; 38 | } 39 | 40 | - (void)fl_open:(NSString *)urlStr connect:(FLSocketDidConnectBlock)connect receive:(FLSocketDidReceiveBlock)receive failure:(FLSocketDidFailBlock)failure{ 41 | [FLSocketManager shareManager].connect = connect; 42 | [FLSocketManager shareManager].receive = receive; 43 | [FLSocketManager shareManager].failure = failure; 44 | [self fl_open:urlStr]; 45 | } 46 | 47 | - (void)fl_close:(FLSocketDidCloseBlock)close{ 48 | [FLSocketManager shareManager].close = close; 49 | [self fl_close]; 50 | } 51 | 52 | // Send a UTF8 String or Data. 53 | - (void)fl_send:(id)data{ 54 | switch ([FLSocketManager shareManager].fl_socketStatus) { 55 | case FLSocketStatusConnected: 56 | case FLSocketStatusReceived:{ 57 | NSLog(@"发送中。。。"); 58 | [self.webSocket send:data]; 59 | break; 60 | } 61 | case FLSocketStatusFailed: 62 | NSLog(@"发送失败"); 63 | break; 64 | case FLSocketStatusClosedByServer: 65 | NSLog(@"已经关闭"); 66 | break; 67 | case FLSocketStatusClosedByUser: 68 | NSLog(@"已经关闭"); 69 | break; 70 | } 71 | 72 | } 73 | 74 | #pragma mark -- private method 75 | - (void)fl_open:(id)params{ 76 | // NSLog(@"params = %@",params); 77 | NSString *urlStr = nil; 78 | if ([params isKindOfClass:[NSString class]]) { 79 | urlStr = (NSString *)params; 80 | } 81 | else if([params isKindOfClass:[NSTimer class]]){ 82 | NSTimer *timer = (NSTimer *)params; 83 | urlStr = [timer userInfo]; 84 | } 85 | [FLSocketManager shareManager].urlString = urlStr; 86 | [self.webSocket close]; 87 | self.webSocket.delegate = nil; 88 | 89 | self.webSocket = [[SRWebSocket alloc] initWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlStr]]]; 90 | self.webSocket.delegate = self; 91 | 92 | [self.webSocket open]; 93 | } 94 | 95 | - (void)fl_close{ 96 | 97 | [self.webSocket close]; 98 | self.webSocket = nil; 99 | [self.timer invalidate]; 100 | self.timer = nil; 101 | } 102 | 103 | - (void)fl_reconnect{ 104 | // 计数+1 105 | if (_reconnectCounter < self.reconnectCount - 1) { 106 | _reconnectCounter ++; 107 | // 开启定时器 108 | NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:self.overtime target:self selector:@selector(fl_open:) userInfo:self.urlString repeats:NO]; 109 | [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; 110 | self.timer = timer; 111 | } 112 | else{ 113 | NSLog(@"Websocket Reconnected Outnumber ReconnectCount"); 114 | if (self.timer) { 115 | [self.timer invalidate]; 116 | self.timer = nil; 117 | } 118 | return; 119 | } 120 | 121 | } 122 | 123 | #pragma mark -- SRWebSocketDelegate 124 | - (void)webSocketDidOpen:(SRWebSocket *)webSocket{ 125 | NSLog(@"Websocket Connected"); 126 | 127 | [FLSocketManager shareManager].connect ? [FLSocketManager shareManager].connect() : nil; 128 | [FLSocketManager shareManager].fl_socketStatus = FLSocketStatusConnected; 129 | // 开启成功后重置重连计数器 130 | _reconnectCounter = 0; 131 | } 132 | 133 | - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error{ 134 | NSLog(@":( Websocket Failed With Error %@", error); 135 | [FLSocketManager shareManager].fl_socketStatus = FLSocketStatusFailed; 136 | [FLSocketManager shareManager].failure ? [FLSocketManager shareManager].failure(error) : nil; 137 | // 重连 138 | [self fl_reconnect]; 139 | } 140 | 141 | - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message{ 142 | NSLog(@":( Websocket Receive With message %@", message); 143 | [FLSocketManager shareManager].fl_socketStatus = FLSocketStatusReceived; 144 | [FLSocketManager shareManager].receive ? [FLSocketManager shareManager].receive(message,FLSocketReceiveTypeForMessage) : nil; 145 | } 146 | 147 | - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean{ 148 | NSLog(@"Closed Reason:%@ code = %zd",reason,code); 149 | if (reason) { 150 | [FLSocketManager shareManager].fl_socketStatus = FLSocketStatusClosedByServer; 151 | // 重连 152 | [self fl_reconnect]; 153 | } 154 | else{ 155 | [FLSocketManager shareManager].fl_socketStatus = FLSocketStatusClosedByUser; 156 | } 157 | [FLSocketManager shareManager].close ? [FLSocketManager shareManager].close(code,reason,wasClean) : nil; 158 | self.webSocket = nil; 159 | } 160 | 161 | - (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload{ 162 | [FLSocketManager shareManager].receive ? [FLSocketManager shareManager].receive(pongPayload,FLSocketReceiveTypeForPong) : nil; 163 | } 164 | 165 | - (void)dealloc{ 166 | // Close WebSocket 167 | [self fl_close]; 168 | } 169 | 170 | @end 171 | -------------------------------------------------------------------------------- /FLSocketDemo/FLSocketDemo/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /FLSocketDemo/FLSocketDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // FLSocketDemo 4 | // 5 | // Created by clarence on 16/9/24. 6 | // Copyright © 2016年 clarence. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /FLSocketDemo/FLSocketDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // FLSocketDemo 4 | // 5 | // Created by clarence on 16/9/24. 6 | // Copyright © 2016年 clarence. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "FLSocketManager.h" 11 | @interface ViewController () 12 | 13 | @end 14 | 15 | @implementation ViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | /** 20 | * @author 孔凡列, 16-09-21 08:09:06 21 | * 22 | * 开启连接 23 | */ 24 | NSString *url = @"服务器给你的地址"; 25 | [[FLSocketManager shareManager] fl_open:url connect:^{ 26 | NSLog(@"成功连接"); 27 | } receive:^(id message, FLSocketReceiveType type) { 28 | if (type == FLSocketReceiveTypeForMessage) { 29 | NSLog(@"接收 类型1--%@",message); 30 | } 31 | else if (type == FLSocketReceiveTypeForPong){ 32 | NSLog(@"接收 类型2--%@",message); 33 | } 34 | } failure:^(NSError *error) { 35 | NSLog(@"连接失败"); 36 | }]; 37 | } 38 | 39 | - (void)viewWillDisappear:(BOOL)animated{ 40 | [super viewWillDisappear:animated]; 41 | [[FLSocketManager shareManager] fl_close:^(NSInteger code, NSString *reason, BOOL wasClean) { 42 | NSLog(@"code = %zd,reason = %@",code,reason); 43 | }]; 44 | } 45 | 46 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 47 | [[FLSocketManager shareManager] fl_send:@"hello world"]; 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /FLSocketDemo/FLSocketDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // FLSocketDemo 4 | // 5 | // Created by clarence on 16/9/24. 6 | // Copyright © 2016年 clarence. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /FLSocketDemo/FLSocketDemoTests/FLSocketDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // FLSocketDemoTests.m 3 | // FLSocketDemoTests 4 | // 5 | // Created by clarence on 16/9/24. 6 | // Copyright © 2016年 clarence. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FLSocketDemoTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation FLSocketDemoTests 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 | -------------------------------------------------------------------------------- /FLSocketDemo/FLSocketDemoTests/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 | -------------------------------------------------------------------------------- /FLSocketDemo/FLSocketDemoUITests/FLSocketDemoUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // FLSocketDemoUITests.m 3 | // FLSocketDemoUITests 4 | // 5 | // Created by clarence on 16/9/24. 6 | // Copyright © 2016年 clarence. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FLSocketDemoUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation FLSocketDemoUITests 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 | -------------------------------------------------------------------------------- /FLSocketDemo/FLSocketDemoUITests/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 | -------------------------------------------------------------------------------- /FLSocketDemo/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'FLSocketDemo' do 5 | # Uncomment this line if you're using Swift or would like to use dynamic frameworks 6 | # use_frameworks! 7 | 8 | # Pods for FLSocketDemo 9 | 10 | pod 'SocketRocket' 11 | 12 | end 13 | -------------------------------------------------------------------------------- /FLSocketDemo/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - SocketRocket (0.5.1) 3 | 4 | DEPENDENCIES: 5 | - SocketRocket 6 | 7 | SPEC CHECKSUMS: 8 | SocketRocket: d57c7159b83c3c6655745cd15302aa24b6bae531 9 | 10 | PODFILE CHECKSUM: a1ac64ab5943a94be907b47c7a7a81b783393884 11 | 12 | COCOAPODS: 1.0.1 13 | -------------------------------------------------------------------------------- /FLSocketDemo/Pods/Headers/Private/SocketRocket/SRWebSocket.h: -------------------------------------------------------------------------------- 1 | ../../../SocketRocket/SocketRocket/SRWebSocket.h -------------------------------------------------------------------------------- /FLSocketDemo/Pods/Headers/Private/SocketRocket/SocketRocket.h: -------------------------------------------------------------------------------- 1 | ../../../SocketRocket/SocketRocket/SocketRocket.h -------------------------------------------------------------------------------- /FLSocketDemo/Pods/Headers/Public/SocketRocket/SRWebSocket.h: -------------------------------------------------------------------------------- 1 | ../../../SocketRocket/SocketRocket/SRWebSocket.h -------------------------------------------------------------------------------- /FLSocketDemo/Pods/Headers/Public/SocketRocket/SocketRocket.h: -------------------------------------------------------------------------------- 1 | ../../../SocketRocket/SocketRocket/SocketRocket.h -------------------------------------------------------------------------------- /FLSocketDemo/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - SocketRocket (0.5.1) 3 | 4 | DEPENDENCIES: 5 | - SocketRocket 6 | 7 | SPEC CHECKSUMS: 8 | SocketRocket: d57c7159b83c3c6655745cd15302aa24b6bae531 9 | 10 | PODFILE CHECKSUM: a1ac64ab5943a94be907b47c7a7a81b783393884 11 | 12 | COCOAPODS: 1.0.1 13 | -------------------------------------------------------------------------------- /FLSocketDemo/Pods/Pods.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 57DEBE183E4795F21E74DDB88CD493AC /* SocketRocket.h in Headers */ = {isa = PBXBuildFile; fileRef = E3AD439A80B2F1ABD299002CAA341262 /* SocketRocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 5C4F2FEC9E9686E30372B3126918E593 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EABC45C2732A4ED943A7E8ECF24F14F7 /* Foundation.framework */; }; 12 | 60BA8990D27D88BE85A615FE6A6314EB /* Pods-FLSocketDemo-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 520E21632A87312E48E5DB054A5D7548 /* Pods-FLSocketDemo-dummy.m */; }; 13 | 8AD888239ED3CAD1602B29584A956AA0 /* SocketRocket-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 1AD643BBD53C0BC9592126E22C82B77A /* SocketRocket-dummy.m */; }; 14 | 8EC538C131F0736700BE8495078608AE /* SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 5AF4E5EED2BE7953295FCE1F8F29887B /* SRWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; 15 | B3EE357979CAB47DE0F2600398D36152 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5135EC14FA83C331E94D40C19DAB0764 /* CFNetwork.framework */; }; 16 | C7A0E4B83D9574CF9E168CB43B02320F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EABC45C2732A4ED943A7E8ECF24F14F7 /* Foundation.framework */; }; 17 | D1C88B148B029BFEC0C95ADD97A7A1F2 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7CAF4AB1ECD042E4FED766B8370F6530 /* Security.framework */; }; 18 | D2B17059DB77F413753368FE4ED91A78 /* SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 1095759AC714C71F66EB69CA0E335A64 /* SRWebSocket.m */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXContainerItemProxy section */ 22 | 9869A2E87029AD68F74960D6C5D064C9 /* PBXContainerItemProxy */ = { 23 | isa = PBXContainerItemProxy; 24 | containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; 25 | proxyType = 1; 26 | remoteGlobalIDString = 530EFE8D1C66341F6344F5BD0DF76787; 27 | remoteInfo = SocketRocket; 28 | }; 29 | /* End PBXContainerItemProxy section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 1095759AC714C71F66EB69CA0E335A64 /* SRWebSocket.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SRWebSocket.m; path = SocketRocket/SRWebSocket.m; sourceTree = ""; }; 33 | 12312D37825868342EAC853D6FDFD09B /* Pods-FLSocketDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-FLSocketDemo.debug.xcconfig"; sourceTree = ""; }; 34 | 1AD643BBD53C0BC9592126E22C82B77A /* SocketRocket-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SocketRocket-dummy.m"; sourceTree = ""; }; 35 | 212966F0D6890CDB0674CCBE89899ADE /* Pods-FLSocketDemo-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-FLSocketDemo-acknowledgements.markdown"; sourceTree = ""; }; 36 | 3877F1923DC7842806D8BBC0AFFCE509 /* Pods-FLSocketDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-FLSocketDemo.release.xcconfig"; sourceTree = ""; }; 37 | 4F022DEFB327581CBAD1A5167F20DA0B /* libPods-FLSocketDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-FLSocketDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | 5135EC14FA83C331E94D40C19DAB0764 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; }; 39 | 520E21632A87312E48E5DB054A5D7548 /* Pods-FLSocketDemo-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-FLSocketDemo-dummy.m"; sourceTree = ""; }; 40 | 5AF4E5EED2BE7953295FCE1F8F29887B /* SRWebSocket.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SRWebSocket.h; path = SocketRocket/SRWebSocket.h; sourceTree = ""; }; 41 | 5D6D52EF3528C9767840102678D57A4A /* libSocketRocket.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libSocketRocket.a; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | 7CAF4AB1ECD042E4FED766B8370F6530 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; }; 43 | 81ED5606DF56484C11C9E852E586A23C /* SocketRocket-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SocketRocket-prefix.pch"; sourceTree = ""; }; 44 | 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 45 | 9840C5694E826EE8B47BB9E78783D51C /* Pods-FLSocketDemo-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-FLSocketDemo-frameworks.sh"; sourceTree = ""; }; 46 | D271E1ACC894BAE9E6FC8AAAF017C9F4 /* SocketRocket.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SocketRocket.xcconfig; sourceTree = ""; }; 47 | E3AD439A80B2F1ABD299002CAA341262 /* SocketRocket.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SocketRocket.h; path = SocketRocket/SocketRocket.h; sourceTree = ""; }; 48 | EABC45C2732A4ED943A7E8ECF24F14F7 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 49 | EFCF74D7DEB060AD55F408196F371F42 /* Pods-FLSocketDemo-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-FLSocketDemo-acknowledgements.plist"; sourceTree = ""; }; 50 | FF406736AD1BB9AB3E40FD8FE09D0B7D /* Pods-FLSocketDemo-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-FLSocketDemo-resources.sh"; sourceTree = ""; }; 51 | /* End PBXFileReference section */ 52 | 53 | /* Begin PBXFrameworksBuildPhase section */ 54 | 2BE1742871117045573AEB0735705601 /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | B3EE357979CAB47DE0F2600398D36152 /* CFNetwork.framework in Frameworks */, 59 | C7A0E4B83D9574CF9E168CB43B02320F /* Foundation.framework in Frameworks */, 60 | D1C88B148B029BFEC0C95ADD97A7A1F2 /* Security.framework in Frameworks */, 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | B03579455A6C3FBDA01D0518366CD392 /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | 5C4F2FEC9E9686E30372B3126918E593 /* Foundation.framework in Frameworks */, 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | /* End PBXFrameworksBuildPhase section */ 73 | 74 | /* Begin PBXGroup section */ 75 | 04310F8822AFFD42107F8DA169E0A47A /* Support Files */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | D271E1ACC894BAE9E6FC8AAAF017C9F4 /* SocketRocket.xcconfig */, 79 | 1AD643BBD53C0BC9592126E22C82B77A /* SocketRocket-dummy.m */, 80 | 81ED5606DF56484C11C9E852E586A23C /* SocketRocket-prefix.pch */, 81 | ); 82 | name = "Support Files"; 83 | path = "../Target Support Files/SocketRocket"; 84 | sourceTree = ""; 85 | }; 86 | 1178F420F04662BB9B205EF659BBA2AB /* iOS */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 5135EC14FA83C331E94D40C19DAB0764 /* CFNetwork.framework */, 90 | EABC45C2732A4ED943A7E8ECF24F14F7 /* Foundation.framework */, 91 | 7CAF4AB1ECD042E4FED766B8370F6530 /* Security.framework */, 92 | ); 93 | name = iOS; 94 | sourceTree = ""; 95 | }; 96 | 122DA2E5084A4393C29BE363C764795C /* Frameworks */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 1178F420F04662BB9B205EF659BBA2AB /* iOS */, 100 | ); 101 | name = Frameworks; 102 | sourceTree = ""; 103 | }; 104 | 2D1DB29F316B9CB9254775AE74A7EB02 /* Targets Support Files */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | AE6D04795F5DE55C09965E13233D76F7 /* Pods-FLSocketDemo */, 108 | ); 109 | name = "Targets Support Files"; 110 | sourceTree = ""; 111 | }; 112 | 442A33B8C6B36F3798A98B5F03FC1762 /* Products */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | 4F022DEFB327581CBAD1A5167F20DA0B /* libPods-FLSocketDemo.a */, 116 | 5D6D52EF3528C9767840102678D57A4A /* libSocketRocket.a */, 117 | ); 118 | name = Products; 119 | sourceTree = ""; 120 | }; 121 | 4FF663BB937700D1580BDAB7F3EF0D97 /* Pods */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 702DD509C6164C2492FCB546BD72BB07 /* SocketRocket */, 125 | ); 126 | name = Pods; 127 | sourceTree = ""; 128 | }; 129 | 702DD509C6164C2492FCB546BD72BB07 /* SocketRocket */ = { 130 | isa = PBXGroup; 131 | children = ( 132 | E3AD439A80B2F1ABD299002CAA341262 /* SocketRocket.h */, 133 | 5AF4E5EED2BE7953295FCE1F8F29887B /* SRWebSocket.h */, 134 | 1095759AC714C71F66EB69CA0E335A64 /* SRWebSocket.m */, 135 | 04310F8822AFFD42107F8DA169E0A47A /* Support Files */, 136 | ); 137 | path = SocketRocket; 138 | sourceTree = ""; 139 | }; 140 | 7DB346D0F39D3F0E887471402A8071AB = { 141 | isa = PBXGroup; 142 | children = ( 143 | 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */, 144 | 122DA2E5084A4393C29BE363C764795C /* Frameworks */, 145 | 4FF663BB937700D1580BDAB7F3EF0D97 /* Pods */, 146 | 442A33B8C6B36F3798A98B5F03FC1762 /* Products */, 147 | 2D1DB29F316B9CB9254775AE74A7EB02 /* Targets Support Files */, 148 | ); 149 | sourceTree = ""; 150 | }; 151 | AE6D04795F5DE55C09965E13233D76F7 /* Pods-FLSocketDemo */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 212966F0D6890CDB0674CCBE89899ADE /* Pods-FLSocketDemo-acknowledgements.markdown */, 155 | EFCF74D7DEB060AD55F408196F371F42 /* Pods-FLSocketDemo-acknowledgements.plist */, 156 | 520E21632A87312E48E5DB054A5D7548 /* Pods-FLSocketDemo-dummy.m */, 157 | 9840C5694E826EE8B47BB9E78783D51C /* Pods-FLSocketDemo-frameworks.sh */, 158 | FF406736AD1BB9AB3E40FD8FE09D0B7D /* Pods-FLSocketDemo-resources.sh */, 159 | 12312D37825868342EAC853D6FDFD09B /* Pods-FLSocketDemo.debug.xcconfig */, 160 | 3877F1923DC7842806D8BBC0AFFCE509 /* Pods-FLSocketDemo.release.xcconfig */, 161 | ); 162 | name = "Pods-FLSocketDemo"; 163 | path = "Target Support Files/Pods-FLSocketDemo"; 164 | sourceTree = ""; 165 | }; 166 | /* End PBXGroup section */ 167 | 168 | /* Begin PBXHeadersBuildPhase section */ 169 | 4ECDAADD47F79C0C88C157D4B3CF8C98 /* Headers */ = { 170 | isa = PBXHeadersBuildPhase; 171 | buildActionMask = 2147483647; 172 | files = ( 173 | 57DEBE183E4795F21E74DDB88CD493AC /* SocketRocket.h in Headers */, 174 | 8EC538C131F0736700BE8495078608AE /* SRWebSocket.h in Headers */, 175 | ); 176 | runOnlyForDeploymentPostprocessing = 0; 177 | }; 178 | /* End PBXHeadersBuildPhase section */ 179 | 180 | /* Begin PBXNativeTarget section */ 181 | 29EC20E78DB7BEF4C8F0F700546F0467 /* Pods-FLSocketDemo */ = { 182 | isa = PBXNativeTarget; 183 | buildConfigurationList = 567CCEB0549CA43E6D8A4CF7585733A5 /* Build configuration list for PBXNativeTarget "Pods-FLSocketDemo" */; 184 | buildPhases = ( 185 | E6ED0FF8B1BC2BE23A3CDC2304E5B582 /* Sources */, 186 | B03579455A6C3FBDA01D0518366CD392 /* Frameworks */, 187 | ); 188 | buildRules = ( 189 | ); 190 | dependencies = ( 191 | A034A84DFB64F1A87C5CEAB0E6C56877 /* PBXTargetDependency */, 192 | ); 193 | name = "Pods-FLSocketDemo"; 194 | productName = "Pods-FLSocketDemo"; 195 | productReference = 4F022DEFB327581CBAD1A5167F20DA0B /* libPods-FLSocketDemo.a */; 196 | productType = "com.apple.product-type.library.static"; 197 | }; 198 | 530EFE8D1C66341F6344F5BD0DF76787 /* SocketRocket */ = { 199 | isa = PBXNativeTarget; 200 | buildConfigurationList = E8D3C8A6C0BBB732A7386F72AB9D15FE /* Build configuration list for PBXNativeTarget "SocketRocket" */; 201 | buildPhases = ( 202 | 7369B5D3374AE28D507ABDA20138505A /* Sources */, 203 | 2BE1742871117045573AEB0735705601 /* Frameworks */, 204 | 4ECDAADD47F79C0C88C157D4B3CF8C98 /* Headers */, 205 | ); 206 | buildRules = ( 207 | ); 208 | dependencies = ( 209 | ); 210 | name = SocketRocket; 211 | productName = SocketRocket; 212 | productReference = 5D6D52EF3528C9767840102678D57A4A /* libSocketRocket.a */; 213 | productType = "com.apple.product-type.library.static"; 214 | }; 215 | /* End PBXNativeTarget section */ 216 | 217 | /* Begin PBXProject section */ 218 | D41D8CD98F00B204E9800998ECF8427E /* Project object */ = { 219 | isa = PBXProject; 220 | attributes = { 221 | LastSwiftUpdateCheck = 0730; 222 | LastUpgradeCheck = 0700; 223 | }; 224 | buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */; 225 | compatibilityVersion = "Xcode 3.2"; 226 | developmentRegion = English; 227 | hasScannedForEncodings = 0; 228 | knownRegions = ( 229 | en, 230 | ); 231 | mainGroup = 7DB346D0F39D3F0E887471402A8071AB; 232 | productRefGroup = 442A33B8C6B36F3798A98B5F03FC1762 /* Products */; 233 | projectDirPath = ""; 234 | projectRoot = ""; 235 | targets = ( 236 | 29EC20E78DB7BEF4C8F0F700546F0467 /* Pods-FLSocketDemo */, 237 | 530EFE8D1C66341F6344F5BD0DF76787 /* SocketRocket */, 238 | ); 239 | }; 240 | /* End PBXProject section */ 241 | 242 | /* Begin PBXSourcesBuildPhase section */ 243 | 7369B5D3374AE28D507ABDA20138505A /* Sources */ = { 244 | isa = PBXSourcesBuildPhase; 245 | buildActionMask = 2147483647; 246 | files = ( 247 | 8AD888239ED3CAD1602B29584A956AA0 /* SocketRocket-dummy.m in Sources */, 248 | D2B17059DB77F413753368FE4ED91A78 /* SRWebSocket.m in Sources */, 249 | ); 250 | runOnlyForDeploymentPostprocessing = 0; 251 | }; 252 | E6ED0FF8B1BC2BE23A3CDC2304E5B582 /* Sources */ = { 253 | isa = PBXSourcesBuildPhase; 254 | buildActionMask = 2147483647; 255 | files = ( 256 | 60BA8990D27D88BE85A615FE6A6314EB /* Pods-FLSocketDemo-dummy.m in Sources */, 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | }; 260 | /* End PBXSourcesBuildPhase section */ 261 | 262 | /* Begin PBXTargetDependency section */ 263 | A034A84DFB64F1A87C5CEAB0E6C56877 /* PBXTargetDependency */ = { 264 | isa = PBXTargetDependency; 265 | name = SocketRocket; 266 | target = 530EFE8D1C66341F6344F5BD0DF76787 /* SocketRocket */; 267 | targetProxy = 9869A2E87029AD68F74960D6C5D064C9 /* PBXContainerItemProxy */; 268 | }; 269 | /* End PBXTargetDependency section */ 270 | 271 | /* Begin XCBuildConfiguration section */ 272 | 18D5267166170121BAFD7B21946CC46F /* Release */ = { 273 | isa = XCBuildConfiguration; 274 | baseConfigurationReference = 3877F1923DC7842806D8BBC0AFFCE509 /* Pods-FLSocketDemo.release.xcconfig */; 275 | buildSettings = { 276 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 277 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 278 | ENABLE_STRICT_OBJC_MSGSEND = YES; 279 | GCC_NO_COMMON_BLOCKS = YES; 280 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 281 | MACH_O_TYPE = staticlib; 282 | MTL_ENABLE_DEBUG_INFO = NO; 283 | OTHER_LDFLAGS = ""; 284 | OTHER_LIBTOOLFLAGS = ""; 285 | PODS_ROOT = "$(SRCROOT)"; 286 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; 287 | PRODUCT_NAME = "$(TARGET_NAME)"; 288 | SDKROOT = iphoneos; 289 | SKIP_INSTALL = YES; 290 | }; 291 | name = Release; 292 | }; 293 | 47BEF9D903506B003EA5C2B249729489 /* Debug */ = { 294 | isa = XCBuildConfiguration; 295 | buildSettings = { 296 | ALWAYS_SEARCH_USER_PATHS = NO; 297 | CLANG_ANALYZER_NONNULL = YES; 298 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 299 | CLANG_CXX_LIBRARY = "libc++"; 300 | CLANG_ENABLE_MODULES = YES; 301 | CLANG_ENABLE_OBJC_ARC = YES; 302 | CLANG_WARN_BOOL_CONVERSION = YES; 303 | CLANG_WARN_CONSTANT_CONVERSION = YES; 304 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; 305 | CLANG_WARN_EMPTY_BODY = YES; 306 | CLANG_WARN_ENUM_CONVERSION = YES; 307 | CLANG_WARN_INT_CONVERSION = YES; 308 | CLANG_WARN_OBJC_ROOT_CLASS = YES; 309 | CLANG_WARN_UNREACHABLE_CODE = YES; 310 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 311 | COPY_PHASE_STRIP = NO; 312 | ENABLE_TESTABILITY = YES; 313 | GCC_C_LANGUAGE_STANDARD = gnu99; 314 | GCC_DYNAMIC_NO_PIC = NO; 315 | GCC_OPTIMIZATION_LEVEL = 0; 316 | GCC_PREPROCESSOR_DEFINITIONS = ( 317 | "POD_CONFIGURATION_DEBUG=1", 318 | "DEBUG=1", 319 | "$(inherited)", 320 | ); 321 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 322 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 323 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 324 | GCC_WARN_UNDECLARED_SELECTOR = YES; 325 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 326 | GCC_WARN_UNUSED_FUNCTION = YES; 327 | GCC_WARN_UNUSED_VARIABLE = YES; 328 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 329 | ONLY_ACTIVE_ARCH = YES; 330 | STRIP_INSTALLED_PRODUCT = NO; 331 | SYMROOT = "${SRCROOT}/../build"; 332 | }; 333 | name = Debug; 334 | }; 335 | 76C8EC77CD2FDA183A4764EC730D6A91 /* Release */ = { 336 | isa = XCBuildConfiguration; 337 | baseConfigurationReference = D271E1ACC894BAE9E6FC8AAAF017C9F4 /* SocketRocket.xcconfig */; 338 | buildSettings = { 339 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 340 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 341 | ENABLE_STRICT_OBJC_MSGSEND = YES; 342 | GCC_NO_COMMON_BLOCKS = YES; 343 | GCC_PREFIX_HEADER = "Target Support Files/SocketRocket/SocketRocket-prefix.pch"; 344 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 345 | MTL_ENABLE_DEBUG_INFO = NO; 346 | OTHER_LDFLAGS = ""; 347 | OTHER_LIBTOOLFLAGS = ""; 348 | PRIVATE_HEADERS_FOLDER_PATH = ""; 349 | PRODUCT_NAME = "$(TARGET_NAME)"; 350 | PUBLIC_HEADERS_FOLDER_PATH = ""; 351 | SDKROOT = iphoneos; 352 | SKIP_INSTALL = YES; 353 | }; 354 | name = Release; 355 | }; 356 | 8F74DCC8C1C30A5BCB76EFF368789B1B /* Debug */ = { 357 | isa = XCBuildConfiguration; 358 | baseConfigurationReference = D271E1ACC894BAE9E6FC8AAAF017C9F4 /* SocketRocket.xcconfig */; 359 | buildSettings = { 360 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 361 | DEBUG_INFORMATION_FORMAT = dwarf; 362 | ENABLE_STRICT_OBJC_MSGSEND = YES; 363 | GCC_NO_COMMON_BLOCKS = YES; 364 | GCC_PREFIX_HEADER = "Target Support Files/SocketRocket/SocketRocket-prefix.pch"; 365 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 366 | MTL_ENABLE_DEBUG_INFO = YES; 367 | OTHER_LDFLAGS = ""; 368 | OTHER_LIBTOOLFLAGS = ""; 369 | PRIVATE_HEADERS_FOLDER_PATH = ""; 370 | PRODUCT_NAME = "$(TARGET_NAME)"; 371 | PUBLIC_HEADERS_FOLDER_PATH = ""; 372 | SDKROOT = iphoneos; 373 | SKIP_INSTALL = YES; 374 | }; 375 | name = Debug; 376 | }; 377 | 98F1453C50494C5D73D0378ADBC05D46 /* Debug */ = { 378 | isa = XCBuildConfiguration; 379 | baseConfigurationReference = 12312D37825868342EAC853D6FDFD09B /* Pods-FLSocketDemo.debug.xcconfig */; 380 | buildSettings = { 381 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 382 | DEBUG_INFORMATION_FORMAT = dwarf; 383 | ENABLE_STRICT_OBJC_MSGSEND = YES; 384 | GCC_NO_COMMON_BLOCKS = YES; 385 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 386 | MACH_O_TYPE = staticlib; 387 | MTL_ENABLE_DEBUG_INFO = YES; 388 | OTHER_LDFLAGS = ""; 389 | OTHER_LIBTOOLFLAGS = ""; 390 | PODS_ROOT = "$(SRCROOT)"; 391 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; 392 | PRODUCT_NAME = "$(TARGET_NAME)"; 393 | SDKROOT = iphoneos; 394 | SKIP_INSTALL = YES; 395 | }; 396 | name = Debug; 397 | }; 398 | AAF678CED40D3499169D10F63CA0719E /* Release */ = { 399 | isa = XCBuildConfiguration; 400 | buildSettings = { 401 | ALWAYS_SEARCH_USER_PATHS = NO; 402 | CLANG_ANALYZER_NONNULL = YES; 403 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 404 | CLANG_CXX_LIBRARY = "libc++"; 405 | CLANG_ENABLE_MODULES = YES; 406 | CLANG_ENABLE_OBJC_ARC = YES; 407 | CLANG_WARN_BOOL_CONVERSION = YES; 408 | CLANG_WARN_CONSTANT_CONVERSION = YES; 409 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; 410 | CLANG_WARN_EMPTY_BODY = YES; 411 | CLANG_WARN_ENUM_CONVERSION = YES; 412 | CLANG_WARN_INT_CONVERSION = YES; 413 | CLANG_WARN_OBJC_ROOT_CLASS = YES; 414 | CLANG_WARN_UNREACHABLE_CODE = YES; 415 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 416 | COPY_PHASE_STRIP = YES; 417 | ENABLE_NS_ASSERTIONS = NO; 418 | GCC_C_LANGUAGE_STANDARD = gnu99; 419 | GCC_PREPROCESSOR_DEFINITIONS = ( 420 | "POD_CONFIGURATION_RELEASE=1", 421 | "$(inherited)", 422 | ); 423 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 424 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 425 | GCC_WARN_UNDECLARED_SELECTOR = YES; 426 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 427 | GCC_WARN_UNUSED_FUNCTION = YES; 428 | GCC_WARN_UNUSED_VARIABLE = YES; 429 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 430 | STRIP_INSTALLED_PRODUCT = NO; 431 | SYMROOT = "${SRCROOT}/../build"; 432 | VALIDATE_PRODUCT = YES; 433 | }; 434 | name = Release; 435 | }; 436 | /* End XCBuildConfiguration section */ 437 | 438 | /* Begin XCConfigurationList section */ 439 | 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */ = { 440 | isa = XCConfigurationList; 441 | buildConfigurations = ( 442 | 47BEF9D903506B003EA5C2B249729489 /* Debug */, 443 | AAF678CED40D3499169D10F63CA0719E /* Release */, 444 | ); 445 | defaultConfigurationIsVisible = 0; 446 | defaultConfigurationName = Release; 447 | }; 448 | 567CCEB0549CA43E6D8A4CF7585733A5 /* Build configuration list for PBXNativeTarget "Pods-FLSocketDemo" */ = { 449 | isa = XCConfigurationList; 450 | buildConfigurations = ( 451 | 98F1453C50494C5D73D0378ADBC05D46 /* Debug */, 452 | 18D5267166170121BAFD7B21946CC46F /* Release */, 453 | ); 454 | defaultConfigurationIsVisible = 0; 455 | defaultConfigurationName = Release; 456 | }; 457 | E8D3C8A6C0BBB732A7386F72AB9D15FE /* Build configuration list for PBXNativeTarget "SocketRocket" */ = { 458 | isa = XCConfigurationList; 459 | buildConfigurations = ( 460 | 8F74DCC8C1C30A5BCB76EFF368789B1B /* Debug */, 461 | 76C8EC77CD2FDA183A4764EC730D6A91 /* Release */, 462 | ); 463 | defaultConfigurationIsVisible = 0; 464 | defaultConfigurationName = Release; 465 | }; 466 | /* End XCConfigurationList section */ 467 | }; 468 | rootObject = D41D8CD98F00B204E9800998ECF8427E /* Project object */; 469 | } 470 | -------------------------------------------------------------------------------- /FLSocketDemo/Pods/Pods.xcodeproj/xcuserdata/clarence.xcuserdatad/xcschemes/Pods-FLSocketDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /FLSocketDemo/Pods/Pods.xcodeproj/xcuserdata/clarence.xcuserdatad/xcschemes/SocketRocket.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /FLSocketDemo/Pods/Pods.xcodeproj/xcuserdata/clarence.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Pods-FLSocketDemo.xcscheme 8 | 9 | isShown 10 | 11 | 12 | SocketRocket.xcscheme 13 | 14 | isShown 15 | 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 29EC20E78DB7BEF4C8F0F700546F0467 21 | 22 | primary 23 | 24 | 25 | 530EFE8D1C66341F6344F5BD0DF76787 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /FLSocketDemo/Pods/SocketRocket/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright 2012 Square Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | -------------------------------------------------------------------------------- /FLSocketDemo/Pods/SocketRocket/README.rst: -------------------------------------------------------------------------------- 1 | SocketRocket Objective-C WebSocket Client (beta) 2 | ================================================ 3 | A conforming WebSocket (`RFC 6455 `_) 4 | client library. 5 | 6 | `Test results for SocketRocket here `_. 7 | You can compare to what `modern browsers look like here 8 | `_. 9 | 10 | SocketRocket currently conforms to all ~300 of `Autobahn 11 | `_'s fuzzing tests (aside from 12 | two UTF-8 ones where it is merely *non-strict*. tests 6.4.2 and 6.4.4) 13 | 14 | Features/Design 15 | --------------- 16 | - TLS (wss) support. It uses CFStream so we get this for *free* 17 | - Uses NSStream/CFNetworking. Earlier implementations used ``dispatch_io``, 18 | however, this proved to be make TLS nearly impossible. Also I wanted this to 19 | work in iOS 4.x. (SocketRocket only supports 5.0 and above now) 20 | - Uses ARC. It uses the 4.0 compatible subset (no weak refs). 21 | - Seems to perform quite well 22 | - Parallel architecture. Most of the work is done in background worker queues. 23 | - Delegate-based. Had older versions that could use blocks too, but I felt it 24 | didn't blend well with retain cycles and just objective C in general. 25 | 26 | Changes 27 | ------- 28 | 29 | v0.3.1-beta2 - 2013-01-12 30 | ````````````````````````` 31 | 32 | - Stability fix for ``closeWithCode:reason:`` (Thanks @michaelpetrov!) 33 | - Actually clean up the NSStreams and remove them from their runloops 34 | - ``_SRRunLoopThread``'s ``main`` wasn't correctly wrapped with 35 | ``@autoreleasepool`` 36 | 37 | v0.3.1-beta1 - 2013-01-12 38 | ````````````````````````` 39 | 40 | - Cleaned up GCD so OS_OBJECT_USE_OBJC_RETAIN_RELEASE is optional 41 | - Removed deprecated ``dispatch_get_current_queue`` in favor of ``dispatch_queue_set_specific`` and ``dispatch_get_specific`` 42 | - Dropping support for iOS 4.0 (it may still work) 43 | 44 | 45 | Installing (iOS) 46 | ---------------- 47 | There's a few options. Choose one, or just figure it out 48 | 49 | - You can copy all the files in the SocketRocket group into your app. 50 | - Include SocketRocket as a subproject and use libSocketRocket 51 | 52 | If you do this, you must add -ObjC to your "other linker flags" option 53 | 54 | - For OS X you will have to repackage make a .framework target. I will take 55 | contributions. Message me if you are interested. 56 | 57 | 58 | Depending on how you configure your project you may need to ``#import`` either 59 | ```` or ``"SRWebSocket.h"`` 60 | 61 | Framework Dependencies 62 | `````````````````````` 63 | Your .app must be linked against the following frameworks/dylibs 64 | 65 | - libicucore.dylib 66 | - CFNetwork.framework 67 | - Security.framework 68 | - Foundation.framework 69 | 70 | Installing (OS X) 71 | ----------------- 72 | SocketRocket now has (64-bit only) OS X support. ``SocketRocket.framework`` 73 | inside Xcode project is for OS X only. It should be identical in function aside 74 | from the unicode validation. ICU isn't shipped with OS X which is what the 75 | original implementation used for unicode validation. The workaround is much 76 | more rudimentary and less robust. 77 | 78 | 1. Add SocketRocket.xcodeproj as either a subproject of your app or in your workspace. 79 | 2. Add ``SocketRocket.framework`` to the link libraries 80 | 3. If you don't have a "copy files" step for ``Framework``, create one 81 | 4. Add ``SocketRocket.framework`` to the "copy files" step. 82 | 83 | API 84 | --- 85 | The classes 86 | 87 | ``SRWebSocket`` 88 | ``````````````` 89 | The Web Socket. 90 | 91 | .. note:: ``SRWebSocket`` will retain itself between ``-(void)open`` and when it 92 | closes, errors, or fails. This is similar to how ``NSURLConnection`` behaves. 93 | (unlike ``NSURLConnection``, ``SRWebSocket`` won't retain the delegate) 94 | 95 | What you need to know 96 | 97 | .. code-block:: objective-c 98 | 99 | @interface SRWebSocket : NSObject 100 | 101 | // Make it with this 102 | - (id)initWithURLRequest:(NSURLRequest *)request; 103 | 104 | // Set this before opening 105 | @property (nonatomic, assign) id delegate; 106 | 107 | - (void)open; 108 | 109 | // Close it with this 110 | - (void)close; 111 | 112 | // Send a UTF8 String or Data 113 | - (void)send:(id)data; 114 | 115 | @end 116 | 117 | ``SRWebSocketDelegate`` 118 | ``````````````````````` 119 | You implement this 120 | 121 | .. code-block:: objective-c 122 | 123 | @protocol SRWebSocketDelegate 124 | 125 | - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message; 126 | 127 | @optional 128 | 129 | - (void)webSocketDidOpen:(SRWebSocket *)webSocket; 130 | - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error; 131 | - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean; 132 | 133 | @end 134 | 135 | Known Issues/Server Todo's 136 | -------------------------- 137 | - Needs auth delegates (like in NSURLConnection) 138 | - Move the streams off the main runloop (most of the work is backgrounded uses 139 | GCD, but I just haven't gotten around to moving it off the main loop since I 140 | converted it from dispatch_io) 141 | - Re-implement server. I removed an existing implementation as well because it 142 | wasn't being used and I wasn't super happy with the interface. Will revisit 143 | this. 144 | - Separate framer and client logic. This will make it nicer when having a 145 | server. 146 | 147 | Testing 148 | ------- 149 | Included are setup scripts for the python testing environment. It comes 150 | packaged with vitualenv so all the dependencies are installed in userland. 151 | 152 | To run the short test from the command line, run:: 153 | 154 | make test 155 | 156 | To run all the tests, run:: 157 | 158 | make test_all 159 | 160 | The short tests don't include the performance tests. (the test harness is 161 | actually the bottleneck, not SocketRocket). 162 | 163 | The first time this is run, it may take a while to install the dependencies. It 164 | will be smooth sailing after that. After the test runs the makefile will open 165 | the results page in your browser. If nothing comes up, you failed. Working on 166 | making this interface a bit nicer. 167 | 168 | To run from the app, choose the ``SocketRocket`` target and run the test action 169 | (``cmd+u``). It runs the same thing, but makes it easier to debug. There is 170 | some serious pre/post hooks in the Test action. You can edit it to customize 171 | behavior. 172 | 173 | .. note:: Xcode only up to version 4.4 is currently supported for the test 174 | harness 175 | 176 | TestChat Demo Application 177 | ------------------------- 178 | SocketRocket includes a demo app, TestChat. It will "chat" with a listening 179 | websocket on port 9900. 180 | 181 | It's a simple project. Uses storyboard. Storyboard is sweet. 182 | 183 | 184 | TestChat Server 185 | ``````````````` 186 | We've included a small server for the chat app. It has a simple function. 187 | It will take a message and broadcast it to all other connected clients. 188 | 189 | We have to get some dependencies. We also want to reuse the virtualenv we made 190 | when we ran the tests. If you haven't run the tests yet, go into the 191 | SocketRocket root directory and type:: 192 | 193 | make test 194 | 195 | This will set up your `virtualenv `_. 196 | Now, in your terminal:: 197 | 198 | source .env/bin/activate 199 | pip install git+https://github.com/tornadoweb/tornado.git 200 | 201 | In the same terminal session, start the chatroom server:: 202 | 203 | python TestChatServer/py/chatroom.py 204 | 205 | There's also a Go implementation (with the latest weekly) where you can:: 206 | 207 | cd TestChatServer/go 208 | go run chatroom.go 209 | 210 | Chatting 211 | ```````` 212 | Now, start TestChat.app (just run the target in the Xcode project). If you had 213 | it started already you can hit the refresh button to reconnect. It should say 214 | "Connected!" on top. 215 | 216 | To talk with the app, open up your browser to `http://localhost:9000 `_ and 217 | start chatting. 218 | 219 | 220 | WebSocket Server Implementation Recommendations 221 | ----------------------------------------------- 222 | SocketRocket has been used with the following libraries: 223 | 224 | - `Tornado `_ 225 | - Go's `WebSocket package `_ or Gorilla's `version `_ 226 | - `Autobahn `_ (using its fuzzing 227 | client) 228 | 229 | The Tornado one is dirt simple and works like a charm. (`IPython notebook 230 | `_ uses it 231 | too). It's much easier to configure handlers and routes than in 232 | Autobahn/twisted. 233 | 234 | As far as Go's goes, it works in my limited testing. I much prefer go's 235 | concurrency model as well. Try it! You may like it. 236 | It could use some more control over things such as pings, etc., but I 237 | am sure it will come in time. 238 | 239 | Autobahn is a great test suite. The Python server code is good, and conforms 240 | well (obviously). However for me, twisted would be a deal-breaker for writing 241 | something new. I find it a bit too complex and heavy for a simple service. If 242 | you are already using twisted though, Autobahn is probably for you. 243 | 244 | Contributing 245 | ------------ 246 | We’re glad you’re interested in SocketRocket, and we’d love to see where you take it. Please read our `contributing guidelines `_ prior to submitting a Pull Request. -------------------------------------------------------------------------------- /FLSocketDemo/Pods/SocketRocket/SocketRocket/SRWebSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2012 Square Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #import 18 | #import 19 | 20 | typedef NS_ENUM(NSInteger, SRReadyState) { 21 | SR_CONNECTING = 0, 22 | SR_OPEN = 1, 23 | SR_CLOSING = 2, 24 | SR_CLOSED = 3, 25 | }; 26 | 27 | typedef enum SRStatusCode : NSInteger { 28 | // 0–999: Reserved and not used. 29 | SRStatusCodeNormal = 1000, 30 | SRStatusCodeGoingAway = 1001, 31 | SRStatusCodeProtocolError = 1002, 32 | SRStatusCodeUnhandledType = 1003, 33 | // 1004 reserved. 34 | SRStatusNoStatusReceived = 1005, 35 | SRStatusCodeAbnormal = 1006, 36 | SRStatusCodeInvalidUTF8 = 1007, 37 | SRStatusCodePolicyViolated = 1008, 38 | SRStatusCodeMessageTooBig = 1009, 39 | SRStatusCodeMissingExtension = 1010, 40 | SRStatusCodeInternalError = 1011, 41 | SRStatusCodeServiceRestart = 1012, 42 | SRStatusCodeTryAgainLater = 1013, 43 | // 1014: Reserved for future use by the WebSocket standard. 44 | SRStatusCodeTLSHandshake = 1015, 45 | // 1016–1999: Reserved for future use by the WebSocket standard. 46 | // 2000–2999: Reserved for use by WebSocket extensions. 47 | // 3000–3999: Available for use by libraries and frameworks. May not be used by applications. Available for registration at the IANA via first-come, first-serve. 48 | // 4000–4999: Available for use by applications. 49 | } SRStatusCode; 50 | 51 | @class SRWebSocket; 52 | 53 | extern NSString *const SRWebSocketErrorDomain; 54 | extern NSString *const SRHTTPResponseErrorKey; 55 | 56 | #pragma mark - SRWebSocketDelegate 57 | 58 | @protocol SRWebSocketDelegate; 59 | 60 | #pragma mark - SRWebSocket 61 | 62 | @interface SRWebSocket : NSObject 63 | 64 | @property (nonatomic, weak) id delegate; 65 | 66 | @property (nonatomic, readonly) SRReadyState readyState; 67 | @property (nonatomic, readonly, retain) NSURL *url; 68 | 69 | 70 | @property (nonatomic, readonly) CFHTTPMessageRef receivedHTTPHeaders; 71 | 72 | // Optional array of cookies (NSHTTPCookie objects) to apply to the connections 73 | @property (nonatomic, readwrite) NSArray * requestCookies; 74 | 75 | // This returns the negotiated protocol. 76 | // It will be nil until after the handshake completes. 77 | @property (nonatomic, readonly, copy) NSString *protocol; 78 | 79 | // Protocols should be an array of strings that turn into Sec-WebSocket-Protocol. 80 | - (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates; 81 | - (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols; 82 | - (id)initWithURLRequest:(NSURLRequest *)request; 83 | 84 | // Some helper constructors. 85 | - (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates; 86 | - (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols; 87 | - (id)initWithURL:(NSURL *)url; 88 | 89 | // Delegate queue will be dispatch_main_queue by default. 90 | // You cannot set both OperationQueue and dispatch_queue. 91 | - (void)setDelegateOperationQueue:(NSOperationQueue*) queue; 92 | - (void)setDelegateDispatchQueue:(dispatch_queue_t) queue; 93 | 94 | // By default, it will schedule itself on +[NSRunLoop SR_networkRunLoop] using defaultModes. 95 | - (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; 96 | - (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; 97 | 98 | // SRWebSockets are intended for one-time-use only. Open should be called once and only once. 99 | - (void)open; 100 | 101 | - (void)close; 102 | - (void)closeWithCode:(NSInteger)code reason:(NSString *)reason; 103 | 104 | // Send a UTF8 String or Data. 105 | - (void)send:(id)data; 106 | 107 | // Send Data (can be nil) in a ping message. 108 | - (void)sendPing:(NSData *)data; 109 | 110 | @end 111 | 112 | #pragma mark - SRWebSocketDelegate 113 | 114 | @protocol SRWebSocketDelegate 115 | 116 | // message will either be an NSString if the server is using text 117 | // or NSData if the server is using binary. 118 | - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message; 119 | 120 | @optional 121 | 122 | - (void)webSocketDidOpen:(SRWebSocket *)webSocket; 123 | - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error; 124 | - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean; 125 | - (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload; 126 | 127 | // Return YES to convert messages sent as Text to an NSString. Return NO to skip NSData -> NSString conversion for Text messages. Defaults to YES. 128 | - (BOOL)webSocketShouldConvertTextFrameToString:(SRWebSocket *)webSocket; 129 | 130 | @end 131 | 132 | #pragma mark - NSURLRequest (SRCertificateAdditions) 133 | 134 | @interface NSURLRequest (SRCertificateAdditions) 135 | 136 | @property (nonatomic, retain, readonly) NSArray *SR_SSLPinnedCertificates; 137 | 138 | @end 139 | 140 | #pragma mark - NSMutableURLRequest (SRCertificateAdditions) 141 | 142 | @interface NSMutableURLRequest (SRCertificateAdditions) 143 | 144 | @property (nonatomic, retain) NSArray *SR_SSLPinnedCertificates; 145 | 146 | @end 147 | 148 | #pragma mark - NSRunLoop (SRWebSocket) 149 | 150 | @interface NSRunLoop (SRWebSocket) 151 | 152 | + (NSRunLoop *)SR_networkRunLoop; 153 | 154 | @end 155 | -------------------------------------------------------------------------------- /FLSocketDemo/Pods/SocketRocket/SocketRocket/SRWebSocket.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2012 Square Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | 18 | #import "SRWebSocket.h" 19 | 20 | #if TARGET_OS_IPHONE 21 | #define HAS_ICU 22 | #endif 23 | 24 | #ifdef HAS_ICU 25 | #import 26 | #endif 27 | 28 | #if TARGET_OS_IPHONE 29 | #import 30 | #else 31 | #import 32 | #endif 33 | 34 | #import 35 | #import 36 | 37 | #if OS_OBJECT_USE_OBJC_RETAIN_RELEASE 38 | #define sr_dispatch_retain(x) 39 | #define sr_dispatch_release(x) 40 | #define maybe_bridge(x) ((__bridge void *) x) 41 | #else 42 | #define sr_dispatch_retain(x) dispatch_retain(x) 43 | #define sr_dispatch_release(x) dispatch_release(x) 44 | #define maybe_bridge(x) (x) 45 | #endif 46 | 47 | #if !__has_feature(objc_arc) 48 | #error SocketRocket must be compiled with ARC enabled 49 | #endif 50 | 51 | 52 | typedef enum { 53 | SROpCodeTextFrame = 0x1, 54 | SROpCodeBinaryFrame = 0x2, 55 | // 3-7 reserved. 56 | SROpCodeConnectionClose = 0x8, 57 | SROpCodePing = 0x9, 58 | SROpCodePong = 0xA, 59 | // B-F reserved. 60 | } SROpCode; 61 | 62 | typedef struct { 63 | BOOL fin; 64 | // BOOL rsv1; 65 | // BOOL rsv2; 66 | // BOOL rsv3; 67 | uint8_t opcode; 68 | BOOL masked; 69 | uint64_t payload_length; 70 | } frame_header; 71 | 72 | static NSString *const SRWebSocketAppendToSecKeyString = @"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 73 | 74 | static inline int32_t validate_dispatch_data_partial_string(NSData *data); 75 | static inline void SRFastLog(NSString *format, ...); 76 | 77 | @interface NSData (SRWebSocket) 78 | 79 | - (NSString *)stringBySHA1ThenBase64Encoding; 80 | 81 | @end 82 | 83 | 84 | @interface NSString (SRWebSocket) 85 | 86 | - (NSString *)stringBySHA1ThenBase64Encoding; 87 | 88 | @end 89 | 90 | 91 | @interface NSURL (SRWebSocket) 92 | 93 | // The origin isn't really applicable for a native application. 94 | // So instead, just map ws -> http and wss -> https. 95 | - (NSString *)SR_origin; 96 | 97 | @end 98 | 99 | 100 | @interface _SRRunLoopThread : NSThread 101 | 102 | @property (nonatomic, readonly) NSRunLoop *runLoop; 103 | 104 | @end 105 | 106 | 107 | static NSString *newSHA1String(const char *bytes, size_t length) { 108 | uint8_t md[CC_SHA1_DIGEST_LENGTH]; 109 | 110 | assert(length >= 0); 111 | assert(length <= UINT32_MAX); 112 | CC_SHA1(bytes, (CC_LONG)length, md); 113 | 114 | NSData *data = [NSData dataWithBytes:md length:CC_SHA1_DIGEST_LENGTH]; 115 | 116 | if ([data respondsToSelector:@selector(base64EncodedStringWithOptions:)]) { 117 | return [data base64EncodedStringWithOptions:0]; 118 | } 119 | 120 | #pragma clang diagnostic push 121 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 122 | return [data base64Encoding]; 123 | #pragma clang diagnostic pop 124 | } 125 | 126 | @implementation NSData (SRWebSocket) 127 | 128 | - (NSString *)stringBySHA1ThenBase64Encoding; 129 | { 130 | return newSHA1String(self.bytes, self.length); 131 | } 132 | 133 | @end 134 | 135 | 136 | @implementation NSString (SRWebSocket) 137 | 138 | - (NSString *)stringBySHA1ThenBase64Encoding; 139 | { 140 | return newSHA1String(self.UTF8String, self.length); 141 | } 142 | 143 | @end 144 | 145 | NSString *const SRWebSocketErrorDomain = @"SRWebSocketErrorDomain"; 146 | NSString *const SRHTTPResponseErrorKey = @"HTTPResponseStatusCode"; 147 | 148 | // Returns number of bytes consumed. Returning 0 means you didn't match. 149 | // Sends bytes to callback handler; 150 | typedef size_t (^stream_scanner)(NSData *collected_data); 151 | 152 | typedef void (^data_callback)(SRWebSocket *webSocket, NSData *data); 153 | 154 | @interface SRIOConsumer : NSObject { 155 | stream_scanner _scanner; 156 | data_callback _handler; 157 | size_t _bytesNeeded; 158 | BOOL _readToCurrentFrame; 159 | BOOL _unmaskBytes; 160 | } 161 | @property (nonatomic, copy, readonly) stream_scanner consumer; 162 | @property (nonatomic, copy, readonly) data_callback handler; 163 | @property (nonatomic, assign) size_t bytesNeeded; 164 | @property (nonatomic, assign, readonly) BOOL readToCurrentFrame; 165 | @property (nonatomic, assign, readonly) BOOL unmaskBytes; 166 | 167 | @end 168 | 169 | // This class is not thread-safe, and is expected to always be run on the same queue. 170 | @interface SRIOConsumerPool : NSObject 171 | 172 | - (id)initWithBufferCapacity:(NSUInteger)poolSize; 173 | 174 | - (SRIOConsumer *)consumerWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; 175 | - (void)returnConsumer:(SRIOConsumer *)consumer; 176 | 177 | @end 178 | 179 | @interface SRWebSocket () 180 | 181 | @property (nonatomic) SRReadyState readyState; 182 | 183 | @property (nonatomic) NSOperationQueue *delegateOperationQueue; 184 | @property (nonatomic) dispatch_queue_t delegateDispatchQueue; 185 | 186 | // Specifies whether SSL trust chain should NOT be evaluated. 187 | // By default this flag is set to NO, meaning only secure SSL connections are allowed. 188 | // For DEBUG builds this flag is ignored, and SSL connections are allowed regardless 189 | // of the certificate trust configuration 190 | @property (nonatomic, readwrite) BOOL allowsUntrustedSSLCertificates; 191 | 192 | @end 193 | 194 | 195 | @implementation SRWebSocket { 196 | NSInteger _webSocketVersion; 197 | 198 | NSOperationQueue *_delegateOperationQueue; 199 | dispatch_queue_t _delegateDispatchQueue; 200 | 201 | dispatch_queue_t _workQueue; 202 | NSMutableArray *_consumers; 203 | 204 | NSInputStream *_inputStream; 205 | NSOutputStream *_outputStream; 206 | 207 | NSMutableData *_readBuffer; 208 | NSUInteger _readBufferOffset; 209 | 210 | NSMutableData *_outputBuffer; 211 | NSUInteger _outputBufferOffset; 212 | 213 | uint8_t _currentFrameOpcode; 214 | size_t _currentFrameCount; 215 | size_t _readOpCount; 216 | uint32_t _currentStringScanPosition; 217 | NSMutableData *_currentFrameData; 218 | 219 | NSString *_closeReason; 220 | 221 | NSString *_secKey; 222 | NSString *_basicAuthorizationString; 223 | 224 | BOOL _pinnedCertFound; 225 | 226 | uint8_t _currentReadMaskKey[4]; 227 | size_t _currentReadMaskOffset; 228 | 229 | BOOL _consumerStopped; 230 | 231 | BOOL _closeWhenFinishedWriting; 232 | BOOL _failed; 233 | 234 | BOOL _secure; 235 | NSURLRequest *_urlRequest; 236 | 237 | BOOL _sentClose; 238 | BOOL _didFail; 239 | BOOL _cleanupScheduled; 240 | int _closeCode; 241 | 242 | BOOL _isPumping; 243 | 244 | NSMutableSet *_scheduledRunloops; 245 | 246 | // We use this to retain ourselves. 247 | __strong SRWebSocket *_selfRetain; 248 | 249 | NSArray *_requestedProtocols; 250 | SRIOConsumerPool *_consumerPool; 251 | } 252 | 253 | @synthesize delegate = _delegate; 254 | @synthesize url = _url; 255 | @synthesize readyState = _readyState; 256 | @synthesize protocol = _protocol; 257 | 258 | static __strong NSData *CRLFCRLF; 259 | 260 | + (void)initialize; 261 | { 262 | CRLFCRLF = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4]; 263 | } 264 | 265 | - (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates; 266 | { 267 | self = [super init]; 268 | if (self) { 269 | assert(request.URL); 270 | _url = request.URL; 271 | _urlRequest = request; 272 | _allowsUntrustedSSLCertificates = allowsUntrustedSSLCertificates; 273 | 274 | _requestedProtocols = [protocols copy]; 275 | 276 | [self _SR_commonInit]; 277 | } 278 | 279 | return self; 280 | } 281 | 282 | - (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols; 283 | { 284 | return [self initWithURLRequest:request protocols:protocols allowsUntrustedSSLCertificates:NO]; 285 | } 286 | 287 | - (id)initWithURLRequest:(NSURLRequest *)request; 288 | { 289 | return [self initWithURLRequest:request protocols:nil]; 290 | } 291 | 292 | - (id)initWithURL:(NSURL *)url; 293 | { 294 | return [self initWithURL:url protocols:nil]; 295 | } 296 | 297 | - (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols; 298 | { 299 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; 300 | return [self initWithURLRequest:request protocols:protocols]; 301 | } 302 | 303 | - (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates; 304 | { 305 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; 306 | return [self initWithURLRequest:request protocols:protocols allowsUntrustedSSLCertificates:allowsUntrustedSSLCertificates]; 307 | } 308 | 309 | - (void)_SR_commonInit; 310 | { 311 | NSString *scheme = _url.scheme.lowercaseString; 312 | assert([scheme isEqualToString:@"ws"] || [scheme isEqualToString:@"http"] || [scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]); 313 | 314 | if ([scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]) { 315 | _secure = YES; 316 | } 317 | 318 | _readyState = SR_CONNECTING; 319 | _consumerStopped = YES; 320 | _webSocketVersion = 13; 321 | 322 | _workQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); 323 | 324 | // Going to set a specific on the queue so we can validate we're on the work queue 325 | dispatch_queue_set_specific(_workQueue, (__bridge void *)self, maybe_bridge(_workQueue), NULL); 326 | 327 | _delegateDispatchQueue = dispatch_get_main_queue(); 328 | sr_dispatch_retain(_delegateDispatchQueue); 329 | 330 | _readBuffer = [[NSMutableData alloc] init]; 331 | _outputBuffer = [[NSMutableData alloc] init]; 332 | 333 | _currentFrameData = [[NSMutableData alloc] init]; 334 | 335 | _consumers = [[NSMutableArray alloc] init]; 336 | 337 | _consumerPool = [[SRIOConsumerPool alloc] init]; 338 | 339 | _scheduledRunloops = [[NSMutableSet alloc] init]; 340 | 341 | [self _initializeStreams]; 342 | 343 | // default handlers 344 | } 345 | 346 | - (void)assertOnWorkQueue; 347 | { 348 | assert(dispatch_get_specific((__bridge void *)self) == maybe_bridge(_workQueue)); 349 | } 350 | 351 | - (void)dealloc 352 | { 353 | _inputStream.delegate = nil; 354 | _outputStream.delegate = nil; 355 | 356 | [_inputStream close]; 357 | [_outputStream close]; 358 | 359 | if (_workQueue) { 360 | sr_dispatch_release(_workQueue); 361 | _workQueue = NULL; 362 | } 363 | 364 | if (_receivedHTTPHeaders) { 365 | CFRelease(_receivedHTTPHeaders); 366 | _receivedHTTPHeaders = NULL; 367 | } 368 | 369 | if (_delegateDispatchQueue) { 370 | sr_dispatch_release(_delegateDispatchQueue); 371 | _delegateDispatchQueue = NULL; 372 | } 373 | } 374 | 375 | #ifndef NDEBUG 376 | 377 | - (void)setReadyState:(SRReadyState)aReadyState; 378 | { 379 | assert(aReadyState > _readyState); 380 | _readyState = aReadyState; 381 | } 382 | 383 | #endif 384 | 385 | - (void)open; 386 | { 387 | assert(_url); 388 | NSAssert(_readyState == SR_CONNECTING, @"Cannot call -(void)open on SRWebSocket more than once"); 389 | 390 | _selfRetain = self; 391 | 392 | if (_urlRequest.timeoutInterval > 0) 393 | { 394 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, _urlRequest.timeoutInterval * NSEC_PER_SEC); 395 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 396 | if (self.readyState == SR_CONNECTING) 397 | [self _failWithError:[NSError errorWithDomain:@"com.squareup.SocketRocket" code:504 userInfo:@{NSLocalizedDescriptionKey: @"Timeout Connecting to Server"}]]; 398 | }); 399 | } 400 | 401 | [self openConnection]; 402 | } 403 | 404 | // Calls block on delegate queue 405 | - (void)_performDelegateBlock:(dispatch_block_t)block; 406 | { 407 | if (_delegateOperationQueue) { 408 | [_delegateOperationQueue addOperationWithBlock:block]; 409 | } else { 410 | assert(_delegateDispatchQueue); 411 | dispatch_async(_delegateDispatchQueue, block); 412 | } 413 | } 414 | 415 | - (void)setDelegateDispatchQueue:(dispatch_queue_t)queue; 416 | { 417 | if (queue) { 418 | sr_dispatch_retain(queue); 419 | } 420 | 421 | if (_delegateDispatchQueue) { 422 | sr_dispatch_release(_delegateDispatchQueue); 423 | } 424 | 425 | _delegateDispatchQueue = queue; 426 | } 427 | 428 | - (BOOL)_checkHandshake:(CFHTTPMessageRef)httpMessage; 429 | { 430 | NSString *acceptHeader = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(httpMessage, CFSTR("Sec-WebSocket-Accept"))); 431 | 432 | if (acceptHeader == nil) { 433 | return NO; 434 | } 435 | 436 | NSString *concattedString = [_secKey stringByAppendingString:SRWebSocketAppendToSecKeyString]; 437 | NSString *expectedAccept = [concattedString stringBySHA1ThenBase64Encoding]; 438 | 439 | return [acceptHeader isEqualToString:expectedAccept]; 440 | } 441 | 442 | - (void)_HTTPHeadersDidFinish; 443 | { 444 | NSInteger responseCode = CFHTTPMessageGetResponseStatusCode(_receivedHTTPHeaders); 445 | 446 | if (responseCode >= 400) { 447 | SRFastLog(@"Request failed with response code %d", responseCode); 448 | [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2132 userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"received bad response code from server %ld", (long)responseCode], SRHTTPResponseErrorKey:@(responseCode)}]]; 449 | return; 450 | } 451 | 452 | if(![self _checkHandshake:_receivedHTTPHeaders]) { 453 | [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2133 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Invalid Sec-WebSocket-Accept response"] forKey:NSLocalizedDescriptionKey]]]; 454 | return; 455 | } 456 | 457 | NSString *negotiatedProtocol = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(_receivedHTTPHeaders, CFSTR("Sec-WebSocket-Protocol"))); 458 | if (negotiatedProtocol) { 459 | // Make sure we requested the protocol 460 | if ([_requestedProtocols indexOfObject:negotiatedProtocol] == NSNotFound) { 461 | [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2133 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Server specified Sec-WebSocket-Protocol that wasn't requested"] forKey:NSLocalizedDescriptionKey]]]; 462 | return; 463 | } 464 | 465 | _protocol = negotiatedProtocol; 466 | } 467 | 468 | self.readyState = SR_OPEN; 469 | 470 | if (!_didFail) { 471 | [self _readFrameNew]; 472 | } 473 | 474 | [self _performDelegateBlock:^{ 475 | if ([self.delegate respondsToSelector:@selector(webSocketDidOpen:)]) { 476 | [self.delegate webSocketDidOpen:self]; 477 | }; 478 | }]; 479 | } 480 | 481 | 482 | - (void)_readHTTPHeader; 483 | { 484 | if (_receivedHTTPHeaders == NULL) { 485 | _receivedHTTPHeaders = CFHTTPMessageCreateEmpty(NULL, NO); 486 | } 487 | 488 | [self _readUntilHeaderCompleteWithCallback:^(SRWebSocket *self, NSData *data) { 489 | CFHTTPMessageAppendBytes(_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length); 490 | 491 | if (CFHTTPMessageIsHeaderComplete(_receivedHTTPHeaders)) { 492 | SRFastLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_receivedHTTPHeaders))); 493 | [self _HTTPHeadersDidFinish]; 494 | } else { 495 | [self _readHTTPHeader]; 496 | } 497 | }]; 498 | } 499 | 500 | - (void)didConnect; 501 | { 502 | SRFastLog(@"Connected"); 503 | CFHTTPMessageRef request = CFHTTPMessageCreateRequest(NULL, CFSTR("GET"), (__bridge CFURLRef)_url, kCFHTTPVersion1_1); 504 | 505 | // Set host first so it defaults 506 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Host"), (__bridge CFStringRef)(_url.port ? [NSString stringWithFormat:@"%@:%@", _url.host, _url.port] : _url.host)); 507 | 508 | NSMutableData *keyBytes = [[NSMutableData alloc] initWithLength:16]; 509 | SecRandomCopyBytes(kSecRandomDefault, keyBytes.length, keyBytes.mutableBytes); 510 | 511 | if ([keyBytes respondsToSelector:@selector(base64EncodedStringWithOptions:)]) { 512 | _secKey = [keyBytes base64EncodedStringWithOptions:0]; 513 | } else { 514 | #pragma clang diagnostic push 515 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 516 | _secKey = [keyBytes base64Encoding]; 517 | #pragma clang diagnostic pop 518 | } 519 | 520 | assert([_secKey length] == 24); 521 | 522 | // Apply cookies if any have been provided 523 | NSDictionary * cookies = [NSHTTPCookie requestHeaderFieldsWithCookies:[self requestCookies]]; 524 | for (NSString * cookieKey in cookies) { 525 | NSString * cookieValue = [cookies objectForKey:cookieKey]; 526 | if ([cookieKey length] && [cookieValue length]) { 527 | CFHTTPMessageSetHeaderFieldValue(request, (__bridge CFStringRef)cookieKey, (__bridge CFStringRef)cookieValue); 528 | } 529 | } 530 | 531 | // set header for http basic auth 532 | if (_url.user.length && _url.password.length) { 533 | NSData *userAndPassword = [[NSString stringWithFormat:@"%@:%@", _url.user, _url.password] dataUsingEncoding:NSUTF8StringEncoding]; 534 | NSString *userAndPasswordBase64Encoded; 535 | if ([keyBytes respondsToSelector:@selector(base64EncodedStringWithOptions:)]) { 536 | userAndPasswordBase64Encoded = [userAndPassword base64EncodedStringWithOptions:0]; 537 | } else { 538 | #pragma clang diagnostic push 539 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 540 | userAndPasswordBase64Encoded = [userAndPassword base64Encoding]; 541 | #pragma clang diagnostic pop 542 | } 543 | _basicAuthorizationString = [NSString stringWithFormat:@"Basic %@", userAndPasswordBase64Encoded]; 544 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Authorization"), (__bridge CFStringRef)_basicAuthorizationString); 545 | } 546 | 547 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Upgrade"), CFSTR("websocket")); 548 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Connection"), CFSTR("Upgrade")); 549 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Key"), (__bridge CFStringRef)_secKey); 550 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Version"), (__bridge CFStringRef)[NSString stringWithFormat:@"%ld", (long)_webSocketVersion]); 551 | 552 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Origin"), (__bridge CFStringRef)_url.SR_origin); 553 | 554 | if (_requestedProtocols) { 555 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Protocol"), (__bridge CFStringRef)[_requestedProtocols componentsJoinedByString:@", "]); 556 | } 557 | 558 | [_urlRequest.allHTTPHeaderFields enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 559 | CFHTTPMessageSetHeaderFieldValue(request, (__bridge CFStringRef)key, (__bridge CFStringRef)obj); 560 | }]; 561 | 562 | NSData *message = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(request)); 563 | 564 | CFRelease(request); 565 | 566 | [self _writeData:message]; 567 | [self _readHTTPHeader]; 568 | } 569 | 570 | - (void)_initializeStreams; 571 | { 572 | assert(_url.port.unsignedIntValue <= UINT32_MAX); 573 | uint32_t port = _url.port.unsignedIntValue; 574 | if (port == 0) { 575 | if (!_secure) { 576 | port = 80; 577 | } else { 578 | port = 443; 579 | } 580 | } 581 | NSString *host = _url.host; 582 | 583 | CFReadStreamRef readStream = NULL; 584 | CFWriteStreamRef writeStream = NULL; 585 | 586 | CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &readStream, &writeStream); 587 | 588 | _outputStream = CFBridgingRelease(writeStream); 589 | _inputStream = CFBridgingRelease(readStream); 590 | 591 | _inputStream.delegate = self; 592 | _outputStream.delegate = self; 593 | } 594 | 595 | - (void)_updateSecureStreamOptions; 596 | { 597 | if (_secure) { 598 | NSMutableDictionary *SSLOptions = [[NSMutableDictionary alloc] init]; 599 | 600 | [_outputStream setProperty:(__bridge id)kCFStreamSocketSecurityLevelNegotiatedSSL forKey:(__bridge id)kCFStreamPropertySocketSecurityLevel]; 601 | 602 | // If we're using pinned certs, don't validate the certificate chain 603 | if ([_urlRequest SR_SSLPinnedCertificates].count) { 604 | [SSLOptions setValue:@NO forKey:(__bridge id)kCFStreamSSLValidatesCertificateChain]; 605 | } 606 | 607 | #if DEBUG 608 | self.allowsUntrustedSSLCertificates = YES; 609 | #endif 610 | 611 | if (self.allowsUntrustedSSLCertificates) { 612 | [SSLOptions setValue:@NO forKey:(__bridge id)kCFStreamSSLValidatesCertificateChain]; 613 | SRFastLog(@"Allowing connection to any root cert"); 614 | } 615 | 616 | [_outputStream setProperty:SSLOptions 617 | forKey:(__bridge id)kCFStreamPropertySSLSettings]; 618 | } 619 | 620 | _inputStream.delegate = self; 621 | _outputStream.delegate = self; 622 | 623 | [self setupNetworkServiceType:_urlRequest.networkServiceType]; 624 | } 625 | 626 | - (void)setupNetworkServiceType:(NSURLRequestNetworkServiceType)requestNetworkServiceType 627 | { 628 | NSString *networkServiceType; 629 | switch (requestNetworkServiceType) { 630 | case NSURLNetworkServiceTypeDefault: 631 | break; 632 | case NSURLNetworkServiceTypeVoIP: { 633 | networkServiceType = NSStreamNetworkServiceTypeVoIP; 634 | #if TARGET_OS_IPHONE && __IPHONE_9_0 635 | if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_8_3) { 636 | static dispatch_once_t predicate; 637 | dispatch_once(&predicate, ^{ 638 | NSLog(@"SocketRocket: %@ - this service type is deprecated in favor of using PushKit for VoIP control", networkServiceType); 639 | }); 640 | } 641 | #endif 642 | break; 643 | } 644 | case NSURLNetworkServiceTypeVideo: 645 | networkServiceType = NSStreamNetworkServiceTypeVideo; 646 | break; 647 | case NSURLNetworkServiceTypeBackground: 648 | networkServiceType = NSStreamNetworkServiceTypeBackground; 649 | break; 650 | case NSURLNetworkServiceTypeVoice: 651 | networkServiceType = NSStreamNetworkServiceTypeVoice; 652 | break; 653 | } 654 | 655 | if (networkServiceType != nil) { 656 | [_inputStream setProperty:networkServiceType forKey:NSStreamNetworkServiceType]; 657 | [_outputStream setProperty:networkServiceType forKey:NSStreamNetworkServiceType]; 658 | } 659 | } 660 | 661 | - (void)openConnection; 662 | { 663 | [self _updateSecureStreamOptions]; 664 | 665 | if (!_scheduledRunloops.count) { 666 | [self scheduleInRunLoop:[NSRunLoop SR_networkRunLoop] forMode:NSDefaultRunLoopMode]; 667 | } 668 | 669 | 670 | [_outputStream open]; 671 | [_inputStream open]; 672 | } 673 | 674 | - (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; 675 | { 676 | [_outputStream scheduleInRunLoop:aRunLoop forMode:mode]; 677 | [_inputStream scheduleInRunLoop:aRunLoop forMode:mode]; 678 | 679 | [_scheduledRunloops addObject:@[aRunLoop, mode]]; 680 | } 681 | 682 | - (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; 683 | { 684 | [_outputStream removeFromRunLoop:aRunLoop forMode:mode]; 685 | [_inputStream removeFromRunLoop:aRunLoop forMode:mode]; 686 | 687 | [_scheduledRunloops removeObject:@[aRunLoop, mode]]; 688 | } 689 | 690 | - (void)close; 691 | { 692 | [self closeWithCode:SRStatusCodeNormal reason:nil]; 693 | } 694 | 695 | - (void)closeWithCode:(NSInteger)code reason:(NSString *)reason; 696 | { 697 | assert(code); 698 | dispatch_async(_workQueue, ^{ 699 | if (self.readyState == SR_CLOSING || self.readyState == SR_CLOSED) { 700 | return; 701 | } 702 | 703 | BOOL wasConnecting = self.readyState == SR_CONNECTING; 704 | 705 | self.readyState = SR_CLOSING; 706 | 707 | SRFastLog(@"Closing with code %d reason %@", code, reason); 708 | 709 | if (wasConnecting) { 710 | [self closeConnection]; 711 | return; 712 | } 713 | 714 | size_t maxMsgSize = [reason maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 715 | NSMutableData *mutablePayload = [[NSMutableData alloc] initWithLength:sizeof(uint16_t) + maxMsgSize]; 716 | NSData *payload = mutablePayload; 717 | 718 | ((uint16_t *)mutablePayload.mutableBytes)[0] = EndianU16_BtoN(code); 719 | 720 | if (reason) { 721 | NSRange remainingRange = {0}; 722 | 723 | NSUInteger usedLength = 0; 724 | 725 | BOOL success = [reason getBytes:(char *)mutablePayload.mutableBytes + sizeof(uint16_t) maxLength:payload.length - sizeof(uint16_t) usedLength:&usedLength encoding:NSUTF8StringEncoding options:NSStringEncodingConversionExternalRepresentation range:NSMakeRange(0, reason.length) remainingRange:&remainingRange]; 726 | #pragma unused (success) 727 | 728 | assert(success); 729 | assert(remainingRange.length == 0); 730 | 731 | if (usedLength != maxMsgSize) { 732 | payload = [payload subdataWithRange:NSMakeRange(0, usedLength + sizeof(uint16_t))]; 733 | } 734 | } 735 | 736 | 737 | [self _sendFrameWithOpcode:SROpCodeConnectionClose data:payload]; 738 | }); 739 | } 740 | 741 | - (void)_closeWithProtocolError:(NSString *)message; 742 | { 743 | // Need to shunt this on the _callbackQueue first to see if they received any messages 744 | [self _performDelegateBlock:^{ 745 | [self closeWithCode:SRStatusCodeProtocolError reason:message]; 746 | dispatch_async(_workQueue, ^{ 747 | [self closeConnection]; 748 | }); 749 | }]; 750 | } 751 | 752 | - (void)_failWithError:(NSError *)error; 753 | { 754 | dispatch_async(_workQueue, ^{ 755 | if (self.readyState != SR_CLOSED) { 756 | _failed = YES; 757 | [self _performDelegateBlock:^{ 758 | if ([self.delegate respondsToSelector:@selector(webSocket:didFailWithError:)]) { 759 | [self.delegate webSocket:self didFailWithError:error]; 760 | } 761 | }]; 762 | 763 | self.readyState = SR_CLOSED; 764 | 765 | SRFastLog(@"Failing with error %@", error.localizedDescription); 766 | 767 | [self closeConnection]; 768 | [self _scheduleCleanup]; 769 | } 770 | }); 771 | } 772 | 773 | - (void)_writeData:(NSData *)data; 774 | { 775 | [self assertOnWorkQueue]; 776 | 777 | if (_closeWhenFinishedWriting) { 778 | return; 779 | } 780 | [_outputBuffer appendData:data]; 781 | [self _pumpWriting]; 782 | } 783 | 784 | - (void)send:(id)data; 785 | { 786 | NSAssert(self.readyState != SR_CONNECTING, @"Invalid State: Cannot call send: until connection is open"); 787 | // TODO: maybe not copy this for performance 788 | data = [data copy]; 789 | dispatch_async(_workQueue, ^{ 790 | if ([data isKindOfClass:[NSString class]]) { 791 | [self _sendFrameWithOpcode:SROpCodeTextFrame data:[(NSString *)data dataUsingEncoding:NSUTF8StringEncoding]]; 792 | } else if ([data isKindOfClass:[NSData class]]) { 793 | [self _sendFrameWithOpcode:SROpCodeBinaryFrame data:data]; 794 | } else if (data == nil) { 795 | [self _sendFrameWithOpcode:SROpCodeTextFrame data:data]; 796 | } else { 797 | assert(NO); 798 | } 799 | }); 800 | } 801 | 802 | - (void)sendPing:(NSData *)data; 803 | { 804 | NSAssert(self.readyState == SR_OPEN, @"Invalid State: Cannot call send: until connection is open"); 805 | // TODO: maybe not copy this for performance 806 | data = [data copy] ?: [NSData data]; // It's okay for a ping to be empty 807 | dispatch_async(_workQueue, ^{ 808 | [self _sendFrameWithOpcode:SROpCodePing data:data]; 809 | }); 810 | } 811 | 812 | - (void)handlePing:(NSData *)pingData; 813 | { 814 | // Need to pingpong this off _callbackQueue first to make sure messages happen in order 815 | [self _performDelegateBlock:^{ 816 | dispatch_async(_workQueue, ^{ 817 | [self _sendFrameWithOpcode:SROpCodePong data:pingData]; 818 | }); 819 | }]; 820 | } 821 | 822 | - (void)handlePong:(NSData *)pongData; 823 | { 824 | SRFastLog(@"Received pong"); 825 | [self _performDelegateBlock:^{ 826 | if ([self.delegate respondsToSelector:@selector(webSocket:didReceivePong:)]) { 827 | [self.delegate webSocket:self didReceivePong:pongData]; 828 | } 829 | }]; 830 | } 831 | 832 | - (void)_handleMessage:(id)message 833 | { 834 | SRFastLog(@"Received message"); 835 | [self _performDelegateBlock:^{ 836 | [self.delegate webSocket:self didReceiveMessage:message]; 837 | }]; 838 | } 839 | 840 | 841 | static inline BOOL closeCodeIsValid(int closeCode) { 842 | if (closeCode < 1000) { 843 | return NO; 844 | } 845 | 846 | if (closeCode >= 1000 && closeCode <= 1011) { 847 | if (closeCode == 1004 || 848 | closeCode == 1005 || 849 | closeCode == 1006) { 850 | return NO; 851 | } 852 | return YES; 853 | } 854 | 855 | if (closeCode >= 3000 && closeCode <= 3999) { 856 | return YES; 857 | } 858 | 859 | if (closeCode >= 4000 && closeCode <= 4999) { 860 | return YES; 861 | } 862 | 863 | return NO; 864 | } 865 | 866 | // Note from RFC: 867 | // 868 | // If there is a body, the first two 869 | // bytes of the body MUST be a 2-byte unsigned integer (in network byte 870 | // order) representing a status code with value /code/ defined in 871 | // Section 7.4. Following the 2-byte integer the body MAY contain UTF-8 872 | // encoded data with value /reason/, the interpretation of which is not 873 | // defined by this specification. 874 | 875 | - (void)handleCloseWithData:(NSData *)data; 876 | { 877 | size_t dataSize = data.length; 878 | __block uint16_t closeCode = 0; 879 | 880 | SRFastLog(@"Received close frame"); 881 | 882 | if (dataSize == 1) { 883 | // TODO handle error 884 | [self _closeWithProtocolError:@"Payload for close must be larger than 2 bytes"]; 885 | return; 886 | } else if (dataSize >= 2) { 887 | [data getBytes:&closeCode length:sizeof(closeCode)]; 888 | _closeCode = EndianU16_BtoN(closeCode); 889 | if (!closeCodeIsValid(_closeCode)) { 890 | [self _closeWithProtocolError:[NSString stringWithFormat:@"Cannot have close code of %d", _closeCode]]; 891 | return; 892 | } 893 | if (dataSize > 2) { 894 | _closeReason = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(2, dataSize - 2)] encoding:NSUTF8StringEncoding]; 895 | if (!_closeReason) { 896 | [self _closeWithProtocolError:@"Close reason MUST be valid UTF-8"]; 897 | return; 898 | } 899 | } 900 | } else { 901 | _closeCode = SRStatusNoStatusReceived; 902 | } 903 | 904 | [self assertOnWorkQueue]; 905 | 906 | if (self.readyState == SR_OPEN) { 907 | [self closeWithCode:1000 reason:nil]; 908 | } 909 | dispatch_async(_workQueue, ^{ 910 | [self closeConnection]; 911 | }); 912 | } 913 | 914 | - (void)closeConnection; 915 | { 916 | [self assertOnWorkQueue]; 917 | SRFastLog(@"Trying to disconnect"); 918 | _closeWhenFinishedWriting = YES; 919 | [self _pumpWriting]; 920 | } 921 | 922 | - (void)_handleFrameWithData:(NSData *)frameData opCode:(NSInteger)opcode; 923 | { 924 | // Check that the current data is valid UTF8 925 | 926 | BOOL isControlFrame = (opcode == SROpCodePing || opcode == SROpCodePong || opcode == SROpCodeConnectionClose); 927 | if (!isControlFrame) { 928 | [self _readFrameNew]; 929 | } else { 930 | dispatch_async(_workQueue, ^{ 931 | [self _readFrameContinue]; 932 | }); 933 | } 934 | 935 | //frameData will be copied before passing to handlers 936 | //otherwise there can be misbehaviours when value at the pointer is changed 937 | switch (opcode) { 938 | case SROpCodeTextFrame: { 939 | if ([self.delegate respondsToSelector:@selector(webSocketShouldConvertTextFrameToString:)] && ![self.delegate webSocketShouldConvertTextFrameToString:self]) { 940 | [self _handleMessage:[frameData copy]]; 941 | } else { 942 | NSString *str = [[NSString alloc] initWithData:frameData encoding:NSUTF8StringEncoding]; 943 | if (str == nil && frameData) { 944 | [self closeWithCode:SRStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"]; 945 | dispatch_async(_workQueue, ^{ 946 | [self closeConnection]; 947 | }); 948 | return; 949 | } 950 | [self _handleMessage:str]; 951 | } 952 | break; 953 | } 954 | case SROpCodeBinaryFrame: 955 | [self _handleMessage:[frameData copy]]; 956 | break; 957 | case SROpCodeConnectionClose: 958 | [self handleCloseWithData:[frameData copy]]; 959 | break; 960 | case SROpCodePing: 961 | [self handlePing:[frameData copy]]; 962 | break; 963 | case SROpCodePong: 964 | [self handlePong:[frameData copy]]; 965 | break; 966 | default: 967 | [self _closeWithProtocolError:[NSString stringWithFormat:@"Unknown opcode %ld", (long)opcode]]; 968 | // TODO: Handle invalid opcode 969 | break; 970 | } 971 | } 972 | 973 | - (void)_handleFrameHeader:(frame_header)frame_header curData:(NSData *)curData; 974 | { 975 | assert(frame_header.opcode != 0); 976 | 977 | if (self.readyState == SR_CLOSED) { 978 | return; 979 | } 980 | 981 | 982 | BOOL isControlFrame = (frame_header.opcode == SROpCodePing || frame_header.opcode == SROpCodePong || frame_header.opcode == SROpCodeConnectionClose); 983 | 984 | if (isControlFrame && !frame_header.fin) { 985 | [self _closeWithProtocolError:@"Fragmented control frames not allowed"]; 986 | return; 987 | } 988 | 989 | if (isControlFrame && frame_header.payload_length >= 126) { 990 | [self _closeWithProtocolError:@"Control frames cannot have payloads larger than 126 bytes"]; 991 | return; 992 | } 993 | 994 | if (!isControlFrame) { 995 | _currentFrameOpcode = frame_header.opcode; 996 | _currentFrameCount += 1; 997 | } 998 | 999 | if (frame_header.payload_length == 0) { 1000 | if (isControlFrame) { 1001 | [self _handleFrameWithData:curData opCode:frame_header.opcode]; 1002 | } else { 1003 | if (frame_header.fin) { 1004 | [self _handleFrameWithData:_currentFrameData opCode:frame_header.opcode]; 1005 | } else { 1006 | // TODO add assert that opcode is not a control; 1007 | [self _readFrameContinue]; 1008 | } 1009 | } 1010 | } else { 1011 | assert(frame_header.payload_length <= SIZE_T_MAX); 1012 | [self _addConsumerWithDataLength:(size_t)frame_header.payload_length callback:^(SRWebSocket *self, NSData *newData) { 1013 | if (isControlFrame) { 1014 | [self _handleFrameWithData:newData opCode:frame_header.opcode]; 1015 | } else { 1016 | if (frame_header.fin) { 1017 | [self _handleFrameWithData:self->_currentFrameData opCode:frame_header.opcode]; 1018 | } else { 1019 | // TODO add assert that opcode is not a control; 1020 | [self _readFrameContinue]; 1021 | } 1022 | 1023 | } 1024 | } readToCurrentFrame:!isControlFrame unmaskBytes:frame_header.masked]; 1025 | } 1026 | } 1027 | 1028 | /* From RFC: 1029 | 1030 | 0 1 2 3 1031 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 1032 | +-+-+-+-+-------+-+-------------+-------------------------------+ 1033 | |F|R|R|R| opcode|M| Payload len | Extended payload length | 1034 | |I|S|S|S| (4) |A| (7) | (16/64) | 1035 | |N|V|V|V| |S| | (if payload len==126/127) | 1036 | | |1|2|3| |K| | | 1037 | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + 1038 | | Extended payload length continued, if payload len == 127 | 1039 | + - - - - - - - - - - - - - - - +-------------------------------+ 1040 | | |Masking-key, if MASK set to 1 | 1041 | +-------------------------------+-------------------------------+ 1042 | | Masking-key (continued) | Payload Data | 1043 | +-------------------------------- - - - - - - - - - - - - - - - + 1044 | : Payload Data continued ... : 1045 | + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 1046 | | Payload Data continued ... | 1047 | +---------------------------------------------------------------+ 1048 | */ 1049 | 1050 | static const uint8_t SRFinMask = 0x80; 1051 | static const uint8_t SROpCodeMask = 0x0F; 1052 | static const uint8_t SRRsvMask = 0x70; 1053 | static const uint8_t SRMaskMask = 0x80; 1054 | static const uint8_t SRPayloadLenMask = 0x7F; 1055 | 1056 | 1057 | - (void)_readFrameContinue; 1058 | { 1059 | assert((_currentFrameCount == 0 && _currentFrameOpcode == 0) || (_currentFrameCount > 0 && _currentFrameOpcode > 0)); 1060 | 1061 | [self _addConsumerWithDataLength:2 callback:^(SRWebSocket *self, NSData *data) { 1062 | __block frame_header header = {0}; 1063 | 1064 | const uint8_t *headerBuffer = data.bytes; 1065 | assert(data.length >= 2); 1066 | 1067 | if (headerBuffer[0] & SRRsvMask) { 1068 | [self _closeWithProtocolError:@"Server used RSV bits"]; 1069 | return; 1070 | } 1071 | 1072 | uint8_t receivedOpcode = (SROpCodeMask & headerBuffer[0]); 1073 | 1074 | BOOL isControlFrame = (receivedOpcode == SROpCodePing || receivedOpcode == SROpCodePong || receivedOpcode == SROpCodeConnectionClose); 1075 | 1076 | if (!isControlFrame && receivedOpcode != 0 && self->_currentFrameCount > 0) { 1077 | [self _closeWithProtocolError:@"all data frames after the initial data frame must have opcode 0"]; 1078 | return; 1079 | } 1080 | 1081 | if (receivedOpcode == 0 && self->_currentFrameCount == 0) { 1082 | [self _closeWithProtocolError:@"cannot continue a message"]; 1083 | return; 1084 | } 1085 | 1086 | header.opcode = receivedOpcode == 0 ? self->_currentFrameOpcode : receivedOpcode; 1087 | 1088 | header.fin = !!(SRFinMask & headerBuffer[0]); 1089 | 1090 | 1091 | header.masked = !!(SRMaskMask & headerBuffer[1]); 1092 | header.payload_length = SRPayloadLenMask & headerBuffer[1]; 1093 | 1094 | headerBuffer = NULL; 1095 | 1096 | if (header.masked) { 1097 | [self _closeWithProtocolError:@"Client must receive unmasked data"]; 1098 | } 1099 | 1100 | size_t extra_bytes_needed = header.masked ? sizeof(_currentReadMaskKey) : 0; 1101 | 1102 | if (header.payload_length == 126) { 1103 | extra_bytes_needed += sizeof(uint16_t); 1104 | } else if (header.payload_length == 127) { 1105 | extra_bytes_needed += sizeof(uint64_t); 1106 | } 1107 | 1108 | if (extra_bytes_needed == 0) { 1109 | [self _handleFrameHeader:header curData:self->_currentFrameData]; 1110 | } else { 1111 | [self _addConsumerWithDataLength:extra_bytes_needed callback:^(SRWebSocket *self, NSData *data) { 1112 | size_t mapped_size = data.length; 1113 | #pragma unused (mapped_size) 1114 | const void *mapped_buffer = data.bytes; 1115 | size_t offset = 0; 1116 | 1117 | if (header.payload_length == 126) { 1118 | assert(mapped_size >= sizeof(uint16_t)); 1119 | uint16_t newLen = EndianU16_BtoN(*(uint16_t *)(mapped_buffer)); 1120 | header.payload_length = newLen; 1121 | offset += sizeof(uint16_t); 1122 | } else if (header.payload_length == 127) { 1123 | assert(mapped_size >= sizeof(uint64_t)); 1124 | header.payload_length = EndianU64_BtoN(*(uint64_t *)(mapped_buffer)); 1125 | offset += sizeof(uint64_t); 1126 | } else { 1127 | assert(header.payload_length < 126 && header.payload_length >= 0); 1128 | } 1129 | 1130 | if (header.masked) { 1131 | assert(mapped_size >= sizeof(_currentReadMaskOffset) + offset); 1132 | memcpy(self->_currentReadMaskKey, ((uint8_t *)mapped_buffer) + offset, sizeof(self->_currentReadMaskKey)); 1133 | } 1134 | 1135 | [self _handleFrameHeader:header curData:self->_currentFrameData]; 1136 | } readToCurrentFrame:NO unmaskBytes:NO]; 1137 | } 1138 | } readToCurrentFrame:NO unmaskBytes:NO]; 1139 | } 1140 | 1141 | - (void)_readFrameNew; 1142 | { 1143 | dispatch_async(_workQueue, ^{ 1144 | [_currentFrameData setLength:0]; 1145 | 1146 | _currentFrameOpcode = 0; 1147 | _currentFrameCount = 0; 1148 | _readOpCount = 0; 1149 | _currentStringScanPosition = 0; 1150 | 1151 | [self _readFrameContinue]; 1152 | }); 1153 | } 1154 | 1155 | - (void)_pumpWriting; 1156 | { 1157 | [self assertOnWorkQueue]; 1158 | 1159 | NSUInteger dataLength = _outputBuffer.length; 1160 | if (dataLength - _outputBufferOffset > 0 && _outputStream.hasSpaceAvailable) { 1161 | NSInteger bytesWritten = [_outputStream write:_outputBuffer.bytes + _outputBufferOffset maxLength:dataLength - _outputBufferOffset]; 1162 | if (bytesWritten == -1) { 1163 | [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2145 userInfo:[NSDictionary dictionaryWithObject:@"Error writing to stream" forKey:NSLocalizedDescriptionKey]]]; 1164 | return; 1165 | } 1166 | 1167 | _outputBufferOffset += bytesWritten; 1168 | 1169 | if (_outputBufferOffset > 4096 && _outputBufferOffset > (_outputBuffer.length >> 1)) { 1170 | _outputBuffer = [[NSMutableData alloc] initWithBytes:(char *)_outputBuffer.bytes + _outputBufferOffset length:_outputBuffer.length - _outputBufferOffset]; 1171 | _outputBufferOffset = 0; 1172 | } 1173 | } 1174 | 1175 | if (_closeWhenFinishedWriting && 1176 | _outputBuffer.length - _outputBufferOffset == 0 && 1177 | (_inputStream.streamStatus != NSStreamStatusNotOpen && 1178 | _inputStream.streamStatus != NSStreamStatusClosed) && 1179 | !_sentClose) { 1180 | _sentClose = YES; 1181 | 1182 | @synchronized(self) { 1183 | [_outputStream close]; 1184 | [_inputStream close]; 1185 | 1186 | 1187 | for (NSArray *runLoop in [_scheduledRunloops copy]) { 1188 | [self unscheduleFromRunLoop:[runLoop objectAtIndex:0] forMode:[runLoop objectAtIndex:1]]; 1189 | } 1190 | } 1191 | 1192 | if (!_failed) { 1193 | [self _performDelegateBlock:^{ 1194 | if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) { 1195 | [self.delegate webSocket:self didCloseWithCode:_closeCode reason:_closeReason wasClean:YES]; 1196 | } 1197 | }]; 1198 | } 1199 | 1200 | [self _scheduleCleanup]; 1201 | } 1202 | } 1203 | 1204 | - (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback; 1205 | { 1206 | [self assertOnWorkQueue]; 1207 | [self _addConsumerWithScanner:consumer callback:callback dataLength:0]; 1208 | } 1209 | 1210 | - (void)_addConsumerWithDataLength:(size_t)dataLength callback:(data_callback)callback readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; 1211 | { 1212 | [self assertOnWorkQueue]; 1213 | assert(dataLength); 1214 | 1215 | [_consumers addObject:[_consumerPool consumerWithScanner:nil handler:callback bytesNeeded:dataLength readToCurrentFrame:readToCurrentFrame unmaskBytes:unmaskBytes]]; 1216 | [self _pumpScanner]; 1217 | } 1218 | 1219 | - (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback dataLength:(size_t)dataLength; 1220 | { 1221 | [self assertOnWorkQueue]; 1222 | [_consumers addObject:[_consumerPool consumerWithScanner:consumer handler:callback bytesNeeded:dataLength readToCurrentFrame:NO unmaskBytes:NO]]; 1223 | [self _pumpScanner]; 1224 | } 1225 | 1226 | 1227 | - (void)_scheduleCleanup 1228 | { 1229 | @synchronized(self) { 1230 | if (_cleanupScheduled) { 1231 | return; 1232 | } 1233 | 1234 | _cleanupScheduled = YES; 1235 | 1236 | // Cleanup NSStream delegate's in the same RunLoop used by the streams themselves: 1237 | // This way we'll prevent race conditions between handleEvent and SRWebsocket's dealloc 1238 | NSTimer *timer = [NSTimer timerWithTimeInterval:(0.0f) target:self selector:@selector(_cleanupSelfReference:) userInfo:nil repeats:NO]; 1239 | [[NSRunLoop SR_networkRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; 1240 | } 1241 | } 1242 | 1243 | - (void)_cleanupSelfReference:(NSTimer *)timer 1244 | { 1245 | @synchronized(self) { 1246 | // Nuke NSStream delegate's 1247 | _inputStream.delegate = nil; 1248 | _outputStream.delegate = nil; 1249 | 1250 | // Remove the streams, right now, from the networkRunLoop 1251 | [_inputStream close]; 1252 | [_outputStream close]; 1253 | } 1254 | 1255 | // Cleanup selfRetain in the same GCD queue as usual 1256 | dispatch_async(_workQueue, ^{ 1257 | _selfRetain = nil; 1258 | }); 1259 | } 1260 | 1261 | 1262 | static const char CRLFCRLFBytes[] = {'\r', '\n', '\r', '\n'}; 1263 | 1264 | - (void)_readUntilHeaderCompleteWithCallback:(data_callback)dataHandler; 1265 | { 1266 | [self _readUntilBytes:CRLFCRLFBytes length:sizeof(CRLFCRLFBytes) callback:dataHandler]; 1267 | } 1268 | 1269 | - (void)_readUntilBytes:(const void *)bytes length:(size_t)length callback:(data_callback)dataHandler; 1270 | { 1271 | // TODO optimize so this can continue from where we last searched 1272 | stream_scanner consumer = ^size_t(NSData *data) { 1273 | __block size_t found_size = 0; 1274 | __block size_t match_count = 0; 1275 | 1276 | size_t size = data.length; 1277 | const unsigned char *buffer = data.bytes; 1278 | for (size_t i = 0; i < size; i++ ) { 1279 | if (((const unsigned char *)buffer)[i] == ((const unsigned char *)bytes)[match_count]) { 1280 | match_count += 1; 1281 | if (match_count == length) { 1282 | found_size = i + 1; 1283 | break; 1284 | } 1285 | } else { 1286 | match_count = 0; 1287 | } 1288 | } 1289 | return found_size; 1290 | }; 1291 | [self _addConsumerWithScanner:consumer callback:dataHandler]; 1292 | } 1293 | 1294 | 1295 | // Returns true if did work 1296 | - (BOOL)_innerPumpScanner { 1297 | 1298 | BOOL didWork = NO; 1299 | 1300 | if (self.readyState >= SR_CLOSED) { 1301 | return didWork; 1302 | } 1303 | 1304 | if (!_consumers.count) { 1305 | return didWork; 1306 | } 1307 | 1308 | size_t curSize = _readBuffer.length - _readBufferOffset; 1309 | if (!curSize) { 1310 | return didWork; 1311 | } 1312 | 1313 | SRIOConsumer *consumer = [_consumers objectAtIndex:0]; 1314 | 1315 | size_t bytesNeeded = consumer.bytesNeeded; 1316 | 1317 | size_t foundSize = 0; 1318 | if (consumer.consumer) { 1319 | NSData *tempView = [NSData dataWithBytesNoCopy:(char *)_readBuffer.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset freeWhenDone:NO]; 1320 | foundSize = consumer.consumer(tempView); 1321 | } else { 1322 | assert(consumer.bytesNeeded); 1323 | if (curSize >= bytesNeeded) { 1324 | foundSize = bytesNeeded; 1325 | } else if (consumer.readToCurrentFrame) { 1326 | foundSize = curSize; 1327 | } 1328 | } 1329 | 1330 | NSData *slice = nil; 1331 | if (consumer.readToCurrentFrame || foundSize) { 1332 | NSRange sliceRange = NSMakeRange(_readBufferOffset, foundSize); 1333 | slice = [_readBuffer subdataWithRange:sliceRange]; 1334 | 1335 | _readBufferOffset += foundSize; 1336 | 1337 | if (_readBufferOffset > 4096 && _readBufferOffset > (_readBuffer.length >> 1)) { 1338 | _readBuffer = [[NSMutableData alloc] initWithBytes:(char *)_readBuffer.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset]; _readBufferOffset = 0; 1339 | } 1340 | 1341 | if (consumer.unmaskBytes) { 1342 | NSMutableData *mutableSlice = [slice mutableCopy]; 1343 | 1344 | NSUInteger len = mutableSlice.length; 1345 | uint8_t *bytes = mutableSlice.mutableBytes; 1346 | 1347 | for (NSUInteger i = 0; i < len; i++) { 1348 | bytes[i] = bytes[i] ^ _currentReadMaskKey[_currentReadMaskOffset % sizeof(_currentReadMaskKey)]; 1349 | _currentReadMaskOffset += 1; 1350 | } 1351 | 1352 | slice = mutableSlice; 1353 | } 1354 | 1355 | if (consumer.readToCurrentFrame) { 1356 | [_currentFrameData appendData:slice]; 1357 | 1358 | _readOpCount += 1; 1359 | 1360 | if (_currentFrameOpcode == SROpCodeTextFrame) { 1361 | // Validate UTF8 stuff. 1362 | size_t currentDataSize = _currentFrameData.length; 1363 | if (_currentFrameOpcode == SROpCodeTextFrame && currentDataSize > 0) { 1364 | // TODO: Optimize the crap out of this. Don't really have to copy all the data each time 1365 | 1366 | size_t scanSize = currentDataSize - _currentStringScanPosition; 1367 | 1368 | NSData *scan_data = [_currentFrameData subdataWithRange:NSMakeRange(_currentStringScanPosition, scanSize)]; 1369 | int32_t valid_utf8_size = validate_dispatch_data_partial_string(scan_data); 1370 | 1371 | if (valid_utf8_size == -1) { 1372 | [self closeWithCode:SRStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"]; 1373 | dispatch_async(_workQueue, ^{ 1374 | [self closeConnection]; 1375 | }); 1376 | return didWork; 1377 | } else { 1378 | _currentStringScanPosition += valid_utf8_size; 1379 | } 1380 | } 1381 | 1382 | } 1383 | 1384 | consumer.bytesNeeded -= foundSize; 1385 | 1386 | if (consumer.bytesNeeded == 0) { 1387 | [_consumers removeObjectAtIndex:0]; 1388 | consumer.handler(self, nil); 1389 | [_consumerPool returnConsumer:consumer]; 1390 | didWork = YES; 1391 | } 1392 | } else if (foundSize) { 1393 | [_consumers removeObjectAtIndex:0]; 1394 | consumer.handler(self, slice); 1395 | [_consumerPool returnConsumer:consumer]; 1396 | didWork = YES; 1397 | } 1398 | } 1399 | return didWork; 1400 | } 1401 | 1402 | -(void)_pumpScanner; 1403 | { 1404 | [self assertOnWorkQueue]; 1405 | 1406 | if (!_isPumping) { 1407 | _isPumping = YES; 1408 | } else { 1409 | return; 1410 | } 1411 | 1412 | while ([self _innerPumpScanner]) { 1413 | 1414 | } 1415 | 1416 | _isPumping = NO; 1417 | } 1418 | 1419 | //#define NOMASK 1420 | 1421 | static const size_t SRFrameHeaderOverhead = 32; 1422 | 1423 | - (void)_sendFrameWithOpcode:(SROpCode)opcode data:(id)data; 1424 | { 1425 | [self assertOnWorkQueue]; 1426 | 1427 | if (nil == data) { 1428 | return; 1429 | } 1430 | 1431 | NSAssert([data isKindOfClass:[NSData class]] || [data isKindOfClass:[NSString class]], @"NSString or NSData"); 1432 | 1433 | size_t payloadLength = [data isKindOfClass:[NSString class]] ? [(NSString *)data lengthOfBytesUsingEncoding:NSUTF8StringEncoding] : [data length]; 1434 | 1435 | NSMutableData *frame = [[NSMutableData alloc] initWithLength:payloadLength + SRFrameHeaderOverhead]; 1436 | if (!frame) { 1437 | [self closeWithCode:SRStatusCodeMessageTooBig reason:@"Message too big"]; 1438 | return; 1439 | } 1440 | uint8_t *frame_buffer = (uint8_t *)[frame mutableBytes]; 1441 | 1442 | // set fin 1443 | frame_buffer[0] = SRFinMask | opcode; 1444 | 1445 | BOOL useMask = YES; 1446 | #ifdef NOMASK 1447 | useMask = NO; 1448 | #endif 1449 | 1450 | if (useMask) { 1451 | // set the mask and header 1452 | frame_buffer[1] |= SRMaskMask; 1453 | } 1454 | 1455 | size_t frame_buffer_size = 2; 1456 | 1457 | const uint8_t *unmasked_payload = NULL; 1458 | if ([data isKindOfClass:[NSData class]]) { 1459 | unmasked_payload = (uint8_t *)[data bytes]; 1460 | } else if ([data isKindOfClass:[NSString class]]) { 1461 | unmasked_payload = (const uint8_t *)[data UTF8String]; 1462 | } else { 1463 | return; 1464 | } 1465 | 1466 | if (payloadLength < 126) { 1467 | frame_buffer[1] |= payloadLength; 1468 | } else if (payloadLength <= UINT16_MAX) { 1469 | frame_buffer[1] |= 126; 1470 | *((uint16_t *)(frame_buffer + frame_buffer_size)) = EndianU16_BtoN((uint16_t)payloadLength); 1471 | frame_buffer_size += sizeof(uint16_t); 1472 | } else { 1473 | frame_buffer[1] |= 127; 1474 | *((uint64_t *)(frame_buffer + frame_buffer_size)) = EndianU64_BtoN((uint64_t)payloadLength); 1475 | frame_buffer_size += sizeof(uint64_t); 1476 | } 1477 | 1478 | if (!useMask) { 1479 | for (size_t i = 0; i < payloadLength; i++) { 1480 | frame_buffer[frame_buffer_size] = unmasked_payload[i]; 1481 | frame_buffer_size += 1; 1482 | } 1483 | } else { 1484 | uint8_t *mask_key = frame_buffer + frame_buffer_size; 1485 | SecRandomCopyBytes(kSecRandomDefault, sizeof(uint32_t), (uint8_t *)mask_key); 1486 | frame_buffer_size += sizeof(uint32_t); 1487 | 1488 | // TODO: could probably optimize this with SIMD 1489 | for (size_t i = 0; i < payloadLength; i++) { 1490 | frame_buffer[frame_buffer_size] = unmasked_payload[i] ^ mask_key[i % sizeof(uint32_t)]; 1491 | frame_buffer_size += 1; 1492 | } 1493 | } 1494 | 1495 | assert(frame_buffer_size <= [frame length]); 1496 | frame.length = frame_buffer_size; 1497 | 1498 | [self _writeData:frame]; 1499 | } 1500 | 1501 | - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode; 1502 | { 1503 | __weak typeof(self) weakSelf = self; 1504 | 1505 | if (_secure && !_pinnedCertFound && (eventCode == NSStreamEventHasBytesAvailable || eventCode == NSStreamEventHasSpaceAvailable)) { 1506 | 1507 | NSArray *sslCerts = [_urlRequest SR_SSLPinnedCertificates]; 1508 | if (sslCerts) { 1509 | SecTrustRef secTrust = (__bridge SecTrustRef)[aStream propertyForKey:(__bridge id)kCFStreamPropertySSLPeerTrust]; 1510 | if (secTrust) { 1511 | NSInteger numCerts = SecTrustGetCertificateCount(secTrust); 1512 | for (NSInteger i = 0; i < numCerts && !_pinnedCertFound; i++) { 1513 | SecCertificateRef cert = SecTrustGetCertificateAtIndex(secTrust, i); 1514 | NSData *certData = CFBridgingRelease(SecCertificateCopyData(cert)); 1515 | 1516 | for (id ref in sslCerts) { 1517 | SecCertificateRef trustedCert = (__bridge SecCertificateRef)ref; 1518 | NSData *trustedCertData = CFBridgingRelease(SecCertificateCopyData(trustedCert)); 1519 | 1520 | if ([trustedCertData isEqualToData:certData]) { 1521 | _pinnedCertFound = YES; 1522 | break; 1523 | } 1524 | } 1525 | } 1526 | } 1527 | 1528 | if (!_pinnedCertFound) { 1529 | dispatch_async(_workQueue, ^{ 1530 | NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : @"Invalid server cert" }; 1531 | [weakSelf _failWithError:[NSError errorWithDomain:@"org.lolrus.SocketRocket" code:23556 userInfo:userInfo]]; 1532 | }); 1533 | return; 1534 | } else if (aStream == _outputStream) { 1535 | dispatch_async(_workQueue, ^{ 1536 | [self didConnect]; 1537 | }); 1538 | } 1539 | } 1540 | } 1541 | 1542 | dispatch_async(_workQueue, ^{ 1543 | [weakSelf safeHandleEvent:eventCode stream:aStream]; 1544 | }); 1545 | } 1546 | 1547 | - (void)safeHandleEvent:(NSStreamEvent)eventCode stream:(NSStream *)aStream 1548 | { 1549 | switch (eventCode) { 1550 | case NSStreamEventOpenCompleted: { 1551 | SRFastLog(@"NSStreamEventOpenCompleted %@", aStream); 1552 | if (self.readyState >= SR_CLOSING) { 1553 | return; 1554 | } 1555 | assert(_readBuffer); 1556 | 1557 | // didConnect fires after certificate verification if we're using pinned certificates. 1558 | BOOL usingPinnedCerts = [[_urlRequest SR_SSLPinnedCertificates] count] > 0; 1559 | if ((!_secure || !usingPinnedCerts) && self.readyState == SR_CONNECTING && aStream == _inputStream) { 1560 | [self didConnect]; 1561 | } 1562 | [self _pumpWriting]; 1563 | [self _pumpScanner]; 1564 | break; 1565 | } 1566 | 1567 | case NSStreamEventErrorOccurred: { 1568 | SRFastLog(@"NSStreamEventErrorOccurred %@ %@", aStream, [[aStream streamError] copy]); 1569 | /// TODO specify error better! 1570 | [self _failWithError:aStream.streamError]; 1571 | _readBufferOffset = 0; 1572 | [_readBuffer setLength:0]; 1573 | break; 1574 | 1575 | } 1576 | 1577 | case NSStreamEventEndEncountered: { 1578 | [self _pumpScanner]; 1579 | SRFastLog(@"NSStreamEventEndEncountered %@", aStream); 1580 | if (aStream.streamError) { 1581 | [self _failWithError:aStream.streamError]; 1582 | } else { 1583 | dispatch_async(_workQueue, ^{ 1584 | if (self.readyState != SR_CLOSED) { 1585 | self.readyState = SR_CLOSED; 1586 | [self _scheduleCleanup]; 1587 | } 1588 | 1589 | if (!_sentClose && !_failed) { 1590 | _sentClose = YES; 1591 | // If we get closed in this state it's probably not clean because we should be sending this when we send messages 1592 | [self _performDelegateBlock:^{ 1593 | if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) { 1594 | [self.delegate webSocket:self didCloseWithCode:SRStatusCodeGoingAway reason:@"Stream end encountered" wasClean:NO]; 1595 | } 1596 | }]; 1597 | } 1598 | }); 1599 | } 1600 | 1601 | break; 1602 | } 1603 | 1604 | case NSStreamEventHasBytesAvailable: { 1605 | SRFastLog(@"NSStreamEventHasBytesAvailable %@", aStream); 1606 | const int bufferSize = 2048; 1607 | uint8_t buffer[bufferSize]; 1608 | 1609 | while (_inputStream.hasBytesAvailable) { 1610 | NSInteger bytes_read = [_inputStream read:buffer maxLength:bufferSize]; 1611 | 1612 | if (bytes_read > 0) { 1613 | [_readBuffer appendBytes:buffer length:bytes_read]; 1614 | } else if (bytes_read < 0) { 1615 | [self _failWithError:_inputStream.streamError]; 1616 | } 1617 | 1618 | if (bytes_read != bufferSize) { 1619 | break; 1620 | } 1621 | }; 1622 | [self _pumpScanner]; 1623 | break; 1624 | } 1625 | 1626 | case NSStreamEventHasSpaceAvailable: { 1627 | SRFastLog(@"NSStreamEventHasSpaceAvailable %@", aStream); 1628 | [self _pumpWriting]; 1629 | break; 1630 | } 1631 | 1632 | default: 1633 | SRFastLog(@"(default) %@", aStream); 1634 | break; 1635 | } 1636 | } 1637 | 1638 | @end 1639 | 1640 | 1641 | @implementation SRIOConsumer 1642 | 1643 | @synthesize bytesNeeded = _bytesNeeded; 1644 | @synthesize consumer = _scanner; 1645 | @synthesize handler = _handler; 1646 | @synthesize readToCurrentFrame = _readToCurrentFrame; 1647 | @synthesize unmaskBytes = _unmaskBytes; 1648 | 1649 | - (void)setupWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; 1650 | { 1651 | _scanner = [scanner copy]; 1652 | _handler = [handler copy]; 1653 | _bytesNeeded = bytesNeeded; 1654 | _readToCurrentFrame = readToCurrentFrame; 1655 | _unmaskBytes = unmaskBytes; 1656 | assert(_scanner || _bytesNeeded); 1657 | } 1658 | 1659 | 1660 | @end 1661 | 1662 | 1663 | @implementation SRIOConsumerPool { 1664 | NSUInteger _poolSize; 1665 | NSMutableArray *_bufferedConsumers; 1666 | } 1667 | 1668 | - (id)initWithBufferCapacity:(NSUInteger)poolSize; 1669 | { 1670 | self = [super init]; 1671 | if (self) { 1672 | _poolSize = poolSize; 1673 | _bufferedConsumers = [[NSMutableArray alloc] initWithCapacity:poolSize]; 1674 | } 1675 | return self; 1676 | } 1677 | 1678 | - (id)init 1679 | { 1680 | return [self initWithBufferCapacity:8]; 1681 | } 1682 | 1683 | - (SRIOConsumer *)consumerWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; 1684 | { 1685 | SRIOConsumer *consumer = nil; 1686 | if (_bufferedConsumers.count) { 1687 | consumer = [_bufferedConsumers lastObject]; 1688 | [_bufferedConsumers removeLastObject]; 1689 | } else { 1690 | consumer = [[SRIOConsumer alloc] init]; 1691 | } 1692 | 1693 | [consumer setupWithScanner:scanner handler:handler bytesNeeded:bytesNeeded readToCurrentFrame:readToCurrentFrame unmaskBytes:unmaskBytes]; 1694 | 1695 | return consumer; 1696 | } 1697 | 1698 | - (void)returnConsumer:(SRIOConsumer *)consumer; 1699 | { 1700 | if (_bufferedConsumers.count < _poolSize) { 1701 | [_bufferedConsumers addObject:consumer]; 1702 | } 1703 | } 1704 | 1705 | @end 1706 | 1707 | 1708 | @implementation NSURLRequest (SRCertificateAdditions) 1709 | 1710 | - (NSArray *)SR_SSLPinnedCertificates; 1711 | { 1712 | return [NSURLProtocol propertyForKey:@"SR_SSLPinnedCertificates" inRequest:self]; 1713 | } 1714 | 1715 | @end 1716 | 1717 | @implementation NSMutableURLRequest (SRCertificateAdditions) 1718 | 1719 | - (NSArray *)SR_SSLPinnedCertificates; 1720 | { 1721 | return [NSURLProtocol propertyForKey:@"SR_SSLPinnedCertificates" inRequest:self]; 1722 | } 1723 | 1724 | - (void)setSR_SSLPinnedCertificates:(NSArray *)SR_SSLPinnedCertificates; 1725 | { 1726 | [NSURLProtocol setProperty:SR_SSLPinnedCertificates forKey:@"SR_SSLPinnedCertificates" inRequest:self]; 1727 | } 1728 | 1729 | @end 1730 | 1731 | @implementation NSURL (SRWebSocket) 1732 | 1733 | - (NSString *)SR_origin; 1734 | { 1735 | NSString *scheme = [self.scheme lowercaseString]; 1736 | 1737 | if ([scheme isEqualToString:@"wss"]) { 1738 | scheme = @"https"; 1739 | } else if ([scheme isEqualToString:@"ws"]) { 1740 | scheme = @"http"; 1741 | } 1742 | 1743 | BOOL portIsDefault = !self.port || 1744 | ([scheme isEqualToString:@"http"] && self.port.integerValue == 80) || 1745 | ([scheme isEqualToString:@"https"] && self.port.integerValue == 443); 1746 | 1747 | if (!portIsDefault) { 1748 | return [NSString stringWithFormat:@"%@://%@:%@", scheme, self.host, self.port]; 1749 | } else { 1750 | return [NSString stringWithFormat:@"%@://%@", scheme, self.host]; 1751 | } 1752 | } 1753 | 1754 | @end 1755 | 1756 | //#define SR_ENABLE_LOG 1757 | 1758 | static inline void SRFastLog(NSString *format, ...) { 1759 | #ifdef SR_ENABLE_LOG 1760 | __block va_list arg_list; 1761 | va_start (arg_list, format); 1762 | 1763 | NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list]; 1764 | 1765 | va_end(arg_list); 1766 | 1767 | NSLog(@"[SR] %@", formattedString); 1768 | #endif 1769 | } 1770 | 1771 | 1772 | #ifdef HAS_ICU 1773 | 1774 | static inline int32_t validate_dispatch_data_partial_string(NSData *data) { 1775 | if ([data length] > INT32_MAX) { 1776 | // INT32_MAX is the limit so long as this Framework is using 32 bit ints everywhere. 1777 | return -1; 1778 | } 1779 | 1780 | int32_t size = (int32_t)[data length]; 1781 | 1782 | const void * contents = [data bytes]; 1783 | const uint8_t *str = (const uint8_t *)contents; 1784 | 1785 | UChar32 codepoint = 1; 1786 | int32_t offset = 0; 1787 | int32_t lastOffset = 0; 1788 | while(offset < size && codepoint > 0) { 1789 | lastOffset = offset; 1790 | U8_NEXT(str, offset, size, codepoint); 1791 | } 1792 | 1793 | if (codepoint == -1) { 1794 | // Check to see if the last byte is valid or whether it was just continuing 1795 | if (!U8_IS_LEAD(str[lastOffset]) || U8_COUNT_TRAIL_BYTES(str[lastOffset]) + lastOffset < (int32_t)size) { 1796 | 1797 | size = -1; 1798 | } else { 1799 | uint8_t leadByte = str[lastOffset]; 1800 | U8_MASK_LEAD_BYTE(leadByte, U8_COUNT_TRAIL_BYTES(leadByte)); 1801 | 1802 | for (int i = lastOffset + 1; i < offset; i++) { 1803 | if (U8_IS_SINGLE(str[i]) || U8_IS_LEAD(str[i]) || !U8_IS_TRAIL(str[i])) { 1804 | size = -1; 1805 | } 1806 | } 1807 | 1808 | if (size != -1) { 1809 | size = lastOffset; 1810 | } 1811 | } 1812 | } 1813 | 1814 | if (size != -1 && ![[NSString alloc] initWithBytesNoCopy:(char *)[data bytes] length:size encoding:NSUTF8StringEncoding freeWhenDone:NO]) { 1815 | size = -1; 1816 | } 1817 | 1818 | return size; 1819 | } 1820 | 1821 | #else 1822 | 1823 | // This is a hack, and probably not optimal 1824 | static inline int32_t validate_dispatch_data_partial_string(NSData *data) { 1825 | static const int maxCodepointSize = 3; 1826 | 1827 | for (int i = 0; i < maxCodepointSize; i++) { 1828 | NSString *str = [[NSString alloc] initWithBytesNoCopy:(char *)data.bytes length:data.length - i encoding:NSUTF8StringEncoding freeWhenDone:NO]; 1829 | if (str) { 1830 | return (int32_t)data.length - i; 1831 | } 1832 | } 1833 | 1834 | return -1; 1835 | } 1836 | 1837 | #endif 1838 | 1839 | static _SRRunLoopThread *networkThread = nil; 1840 | static NSRunLoop *networkRunLoop = nil; 1841 | 1842 | @implementation NSRunLoop (SRWebSocket) 1843 | 1844 | + (NSRunLoop *)SR_networkRunLoop { 1845 | static dispatch_once_t onceToken; 1846 | dispatch_once(&onceToken, ^{ 1847 | networkThread = [[_SRRunLoopThread alloc] init]; 1848 | networkThread.name = @"com.squareup.SocketRocket.NetworkThread"; 1849 | [networkThread start]; 1850 | networkRunLoop = networkThread.runLoop; 1851 | }); 1852 | 1853 | return networkRunLoop; 1854 | } 1855 | 1856 | @end 1857 | 1858 | 1859 | @implementation _SRRunLoopThread { 1860 | dispatch_group_t _waitGroup; 1861 | } 1862 | 1863 | @synthesize runLoop = _runLoop; 1864 | 1865 | - (void)dealloc 1866 | { 1867 | sr_dispatch_release(_waitGroup); 1868 | } 1869 | 1870 | - (id)init 1871 | { 1872 | self = [super init]; 1873 | if (self) { 1874 | _waitGroup = dispatch_group_create(); 1875 | dispatch_group_enter(_waitGroup); 1876 | } 1877 | return self; 1878 | } 1879 | 1880 | - (void)main; 1881 | { 1882 | @autoreleasepool { 1883 | _runLoop = [NSRunLoop currentRunLoop]; 1884 | dispatch_group_leave(_waitGroup); 1885 | 1886 | // Add an empty run loop source to prevent runloop from spinning. 1887 | CFRunLoopSourceContext sourceCtx = { 1888 | .version = 0, 1889 | .info = NULL, 1890 | .retain = NULL, 1891 | .release = NULL, 1892 | .copyDescription = NULL, 1893 | .equal = NULL, 1894 | .hash = NULL, 1895 | .schedule = NULL, 1896 | .cancel = NULL, 1897 | .perform = NULL 1898 | }; 1899 | CFRunLoopSourceRef source = CFRunLoopSourceCreate(NULL, 0, &sourceCtx); 1900 | CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); 1901 | CFRelease(source); 1902 | 1903 | while ([_runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) { 1904 | 1905 | } 1906 | assert(NO); 1907 | } 1908 | } 1909 | 1910 | - (NSRunLoop *)runLoop; 1911 | { 1912 | dispatch_group_wait(_waitGroup, DISPATCH_TIME_FOREVER); 1913 | return _runLoop; 1914 | } 1915 | 1916 | @end 1917 | -------------------------------------------------------------------------------- /FLSocketDemo/Pods/SocketRocket/SocketRocket/SocketRocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2012 Square Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #import 18 | -------------------------------------------------------------------------------- /FLSocketDemo/Pods/Target Support Files/Pods-FLSocketDemo/Pods-FLSocketDemo-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## SocketRocket 5 | 6 | 7 | Copyright 2012 Square Inc. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | 22 | Generated by CocoaPods - https://cocoapods.org 23 | -------------------------------------------------------------------------------- /FLSocketDemo/Pods/Target Support Files/Pods-FLSocketDemo/Pods-FLSocketDemo-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | 18 | Copyright 2012 Square Inc. 19 | 20 | Licensed under the Apache License, Version 2.0 (the "License"); 21 | you may not use this file except in compliance with the License. 22 | You may obtain a copy of the License at 23 | 24 | http://www.apache.org/licenses/LICENSE-2.0 25 | 26 | Unless required by applicable law or agreed to in writing, software 27 | distributed under the License is distributed on an "AS IS" BASIS, 28 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 29 | See the License for the specific language governing permissions and 30 | limitations under the License. 31 | 32 | 33 | Title 34 | SocketRocket 35 | Type 36 | PSGroupSpecifier 37 | 38 | 39 | FooterText 40 | Generated by CocoaPods - https://cocoapods.org 41 | Title 42 | 43 | Type 44 | PSGroupSpecifier 45 | 46 | 47 | StringsTable 48 | Acknowledgements 49 | Title 50 | Acknowledgements 51 | 52 | 53 | -------------------------------------------------------------------------------- /FLSocketDemo/Pods/Target Support Files/Pods-FLSocketDemo/Pods-FLSocketDemo-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_FLSocketDemo : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_FLSocketDemo 5 | @end 6 | -------------------------------------------------------------------------------- /FLSocketDemo/Pods/Target Support Files/Pods-FLSocketDemo/Pods-FLSocketDemo-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements \"$1\"" 63 | /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1" 64 | fi 65 | } 66 | 67 | # Strip invalid architectures 68 | strip_invalid_archs() { 69 | binary="$1" 70 | # Get architectures for current file 71 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 72 | stripped="" 73 | for arch in $archs; do 74 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 75 | # Strip non-valid architectures in-place 76 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 77 | stripped="$stripped $arch" 78 | fi 79 | done 80 | if [[ "$stripped" ]]; then 81 | echo "Stripped $binary of architectures:$stripped" 82 | fi 83 | } 84 | 85 | -------------------------------------------------------------------------------- /FLSocketDemo/Pods/Target Support Files/Pods-FLSocketDemo/Pods-FLSocketDemo-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | case "${TARGETED_DEVICE_FAMILY}" in 12 | 1,2) 13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 14 | ;; 15 | 1) 16 | TARGET_DEVICE_ARGS="--target-device iphone" 17 | ;; 18 | 2) 19 | TARGET_DEVICE_ARGS="--target-device ipad" 20 | ;; 21 | *) 22 | TARGET_DEVICE_ARGS="--target-device mac" 23 | ;; 24 | esac 25 | 26 | realpath() { 27 | DIRECTORY="$(cd "${1%/*}" && pwd)" 28 | FILENAME="${1##*/}" 29 | echo "$DIRECTORY/$FILENAME" 30 | } 31 | 32 | install_resource() 33 | { 34 | if [[ "$1" = /* ]] ; then 35 | RESOURCE_PATH="$1" 36 | else 37 | RESOURCE_PATH="${PODS_ROOT}/$1" 38 | fi 39 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 40 | cat << EOM 41 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 42 | EOM 43 | exit 1 44 | fi 45 | case $RESOURCE_PATH in 46 | *.storyboard) 47 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 48 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 49 | ;; 50 | *.xib) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.framework) 55 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 56 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 57 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 58 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 59 | ;; 60 | *.xcdatamodel) 61 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" 62 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 63 | ;; 64 | *.xcdatamodeld) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 67 | ;; 68 | *.xcmappingmodel) 69 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" 70 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 71 | ;; 72 | *.xcassets) 73 | ABSOLUTE_XCASSET_FILE=$(realpath "$RESOURCE_PATH") 74 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 75 | ;; 76 | *) 77 | echo "$RESOURCE_PATH" 78 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 79 | ;; 80 | esac 81 | } 82 | 83 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 85 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 86 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 87 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | fi 89 | rm -f "$RESOURCES_TO_COPY" 90 | 91 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 92 | then 93 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 94 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 95 | while read line; do 96 | if [[ $line != "`realpath $PODS_ROOT`*" ]]; then 97 | XCASSET_FILES+=("$line") 98 | fi 99 | done <<<"$OTHER_XCASSETS" 100 | 101 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 102 | fi 103 | -------------------------------------------------------------------------------- /FLSocketDemo/Pods/Target Support Files/Pods-FLSocketDemo/Pods-FLSocketDemo.debug.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/SocketRocket" 3 | LIBRARY_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/SocketRocket" 4 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/SocketRocket" 5 | OTHER_LDFLAGS = $(inherited) -ObjC -l"SocketRocket" -l"icucore" -framework "CFNetwork" -framework "Security" 6 | PODS_BUILD_DIR = $BUILD_DIR 7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /FLSocketDemo/Pods/Target Support Files/Pods-FLSocketDemo/Pods-FLSocketDemo.release.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/SocketRocket" 3 | LIBRARY_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/SocketRocket" 4 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/SocketRocket" 5 | OTHER_LDFLAGS = $(inherited) -ObjC -l"SocketRocket" -l"icucore" -framework "CFNetwork" -framework "Security" 6 | PODS_BUILD_DIR = $BUILD_DIR 7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /FLSocketDemo/Pods/Target Support Files/SocketRocket/SocketRocket-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_SocketRocket : NSObject 3 | @end 4 | @implementation PodsDummy_SocketRocket 5 | @end 6 | -------------------------------------------------------------------------------- /FLSocketDemo/Pods/Target Support Files/SocketRocket/SocketRocket-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | -------------------------------------------------------------------------------- /FLSocketDemo/Pods/Target Support Files/SocketRocket/SocketRocket.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/SocketRocket 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/SocketRocket" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/SocketRocket" 4 | OTHER_LDFLAGS = -l"icucore" -framework "CFNetwork" -framework "Security" 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 一、简单介绍 2 | 3 | SocketRocket是一个WebSocket客户端([WebSocket](http://tools.ietf.org/html/rfc6455)是适用于Web应用的下一代全双工通讯协议,被成为“Web的TCP”,它实现了浏览器与服务器的双向通信),采用Object-C编写。SocketRocket遵循最新的WebSocket规范[RFC 6455](http://tools.ietf.org/html/rfc6455)。 4 | 5 | 这里是开发者描述的其一些特性/设计:  6 | > 7 | 支持TLS (wss)。 8 | 使用NSStream/CFNetworking。 9 | 使用ARC。 10 | 采用并行架构。大部分的工作由后端的工作队列(worker queues)完成。 11 | 基于委托编程。 12 | 13 | SocketRocket支持iOS 4.x系统(应该也可以运行于OS X),不需要任何UI包依赖。详细信息可以查看[此文介绍](http://corner.squareup.com/2012/02/socketrocket-websockets.html)。  14 | 15 | ### [socketRocket 传送门](https://github.com/facebook/SocketRocket) 16 | 17 | 18 | ## 二、如何使用 19 | - ####socketRocket 支持pod,因此直接添加然后install,文件不多喔~ 20 | 21 | ![Paste_Image.png](http://upload-images.jianshu.io/upload_images/1085031-c34c45a1d56a86ee.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 22 | 23 | - ####简单使用socketRocket实现通信的话,只需要用那么几个API就行了 24 | 25 | > 1.创建一个请求:(有多种方法) 26 | 27 | ``` 28 | - (id)initWithURLRequest:(NSURLRequest *)request; 29 | ``` 30 | 31 | >2.遵守并指定代理 32 | 33 | ``` 34 | @property (nonatomic, weak) id delegate; 35 | ``` 36 | 37 | >3.打开连接加载请求 38 | 39 | ``` 40 | - (void)open; 41 | ``` 42 | 43 | >4.关闭连接 44 | 45 | ``` 46 | - (void)close; 47 | ``` 48 | 49 | >5.发送消息 50 | 51 | ``` 52 | // Send a UTF8 String or Data. 53 | - (void)send:(id)data; 54 | // Send Data (can be nil) in a ping message. 55 | - (void)sendPing:(NSData *)data; 56 | ``` 57 | 58 | >6.监听socketRocket是通过代理方法来实现的 59 | 60 | ``` 61 | @protocol SRWebSocketDelegate 62 | // message will either be an NSString if the server is using text 63 | // or NSData if the server is using binary. 64 | - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message; 65 | @optional 66 | - (void)webSocketDidOpen:(SRWebSocket *)webSocket; 67 | - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error; 68 | - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean; 69 | - (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload; 70 | // Return YES to convert messages sent as Text to an NSString. Return NO to skip NSData -> NSString conversion for Text messages. Defaults to YES. 71 | - (BOOL)webSocketShouldConvertTextFrameToString:(SRWebSocket *)webSocket; 72 | ``` 73 | 74 | - ####注意:发送的参数必须跟后台商量,保持一致才能发送,不然一发送就自动关闭连接的 75 | 76 | ## 三、封装隔离 77 | 78 | - #### 为什么要封装 79 | - 我就按上面的6个步骤就可以实现通信了,不用考虑太多的逻辑处理,但我还是觉得有点麻烦,我希望一句代码就搞定,使用代理就实现不了。 80 | - 第三方框架必须要封装隔离,不然Facebook突然改了这个框架的API,那么你项目多次使用的话,改动工作量就非常大了。 81 | - 我需要它有个重连机制,如果连接失败或者系统异常原因导致连接关闭的话,它会自动重连,如果是用户手动关闭,则不需要重连,直到下次重新打开。 82 | 83 | - #### 封装思路 84 | - 需要单例工具类,管理socket的状态,当然状态是不允许外界修改,因此是readonly 85 | - 对外提供超时重连的时间,允许外界修改 86 | - 对外提供开启连接方法,使用block进行回调,不使用代理,实现一句代码创建并监听 87 | - 有开启必须有关闭连接的方法,同样使用block回调,告诉调用者关闭的状态码以及原因 88 | - 当然需要一个发送方法,参数模仿框架,传id类型就行 89 | 90 | - ####封装后的API(.h文件) 91 | 92 | >自定义的枚举,socket状态,比框架多一个枚举是用户关闭 93 | 94 | ``` 95 | /** 96 |  *  @author 孔凡列, 16-09-21 07:09:52 97 |  * 98 |  *  socket状态 99 |  */ 100 | typedef NS_ENUM(NSInteger,FLSocketStatus){ 101 |     FLSocketStatusConnected,// 已连接 102 |     FLSocketStatusFailed,// 失败 103 |     FLSocketStatusClosedByServer,// 系统关闭 104 |     FLSocketStatusClosedByUser,// 用户关闭 105 |     FLSocketStatusReceived// 接收消息 106 | }; 107 | /** 108 |  *  @author 孔凡列, 16-09-21 07:09:52 109 |  * 110 |  *  消息类型 111 |  */ 112 | typedef NS_ENUM(NSInteger,FLSocketReceiveType){ 113 |     FLSocketReceiveTypeForMessage, 114 |     FLSocketReceiveTypeForPong 115 | }; 116 | 117 | ``` 118 | 119 | > 连接回调,成功连接后执行 120 | 121 | ``` 122 | 123 | /** 124 |  *  @author 孔凡列, 16-09-21 08:09:06 125 |  * 126 |  *  连接回调 127 |  */ 128 | @property (nonatomic,copy)FLSocketDidConnectBlock connect; 129 | 130 | ``` 131 | 132 | >接收到socket消息的时候就会执行 133 | 134 | ``` 135 | /** 136 |  *  @author 孔凡列, 16-09-21 08:09:06 137 |  * 138 |  *  接收消息回调 139 |  */ 140 | @property (nonatomic,copy)FLSocketDidReceiveBlock receive; 141 | 142 | ``` 143 | 144 | >连接或发送失败会执行 145 | 146 | ``` 147 | /** 148 |  *  @author 孔凡列, 16-09-21 08:09:06 149 |  * 150 |  *  失败回调 151 |  */ 152 | @property (nonatomic,copy)FLSocketDidFailBlock failure; 153 | 154 | ``` 155 | 156 | >用户手动关闭或者系统关闭的时候会调用 157 | 158 | ``` 159 | /** 160 |  *  @author 孔凡列, 16-09-21 08:09:06 161 |  * 162 |  *  关闭回调 163 |  */ 164 | @property (nonatomic,copy)FLSocketDidCloseBlock close; 165 | 166 | ``` 167 | 168 | >socket状态,一共有5个状态 169 | 170 | ``` 171 | /** 172 |  *  @author 孔凡列, 16-09-21 08:09:28 173 |  * 174 |  *  当前的socket状态 175 |  */ 176 | @property (nonatomic,assign,readonly)FLSocketStatus fl_socketStatus; 177 | 178 | ``` 179 | 180 | >超时重连时间,默认一秒重连(框架没有,自己添加的) 181 | 182 | ``` 183 | /** 184 |  *  @author 孔凡列, 16-09-21 08:09:40 185 |  * 186 |  *  超时重连时间,默认1秒 187 |  */ 188 | @property (nonatomic,assign)NSTimeInterval overtime; 189 | ``` 190 | 191 | >超时重连次数,默认5次(框架没有,自己添加的) 192 | 193 | ``` 194 | /** 195 |  *  @author Clarence 196 |  * 197 |  *  重连次数,默认5次 198 |  */ 199 | @property (nonatomic, assign)NSUInteger reconnectCount; 200 | ``` 201 | 202 | >单例创建管理类,项目中唯一,方便管理 203 | 204 | ``` 205 | /** 206 |  *  @author 孔凡列, 16-09-21 08:09:06 207 |  * 208 |  *  单例调用 209 |  */ 210 | + (instancetype)shareManager; 211 | ``` 212 | 213 | >开启socket,block监听 214 | 215 | ``` 216 | /** 217 |  *  @author 孔凡列, 16-09-21 08:09:16 218 |  * 219 |  *  开启socket 220 |  * 221 |  *  @param urlStr  服务器地址 222 |  *  @param connect 连接成功回调 223 |  *  @param receive 接收消息回调 224 |  *  @param failure 失败回调 225 |  */ 226 | - (void)fl_open:(NSString *)urlStr connect:(FLSocketDidConnectBlock)connect receive:(FLSocketDidReceiveBlock)receive failure:(FLSocketDidFailBlock)failure; 227 | ``` 228 | 229 | >关闭socket,有两个状态,一个是用户关闭,一个是系统关闭 230 | 231 | ``` 232 | /** 233 |  *  @author 孔凡列, 16-09-21 08:09:06 234 |  * 235 |  *  关闭socket 236 |  * 237 |  *  @param close 关闭回调 238 |  */ 239 | - (void)fl_close:(FLSocketDidCloseBlock)close; 240 | ``` 241 | 242 | >发送消息,可发送NSString 或者 NSData 243 | 244 | ``` 245 | /** 246 |  *  @author 孔凡列, 16-09-21 08:09:25 247 |  * 248 |  *  发送消息,NSString 或者 NSData 249 |  * 250 |  *  @param data Send a UTF8 String or Data. 251 |  */ 252 | - (void)fl_send:(id)data; 253 | 254 | ``` 255 | 256 | 257 | 258 | ## 四、调用 259 | 260 | >#### 1、开启连接并监听 261 | ``` 262 | NSString *url = @"服务器给你的地址"; 263 | [[FLSocketManager shareManager] fl_open:url connect:^{ 264 |         NSLog(@"成功连接"); 265 |     } receive:^(id message, FLSocketReceiveType type) { 266 |         if (type == FLSocketReceiveTypeForMessage) { 267 |             NSLog(@"接收 类型1--%@",message); 268 |         } 269 |         else if (type == FLSocketReceiveTypeForPong){ 270 |             NSLog(@"接收 类型2--%@",message); 271 |         } 272 |     } failure:^(NSError *error) { 273 |         NSLog(@"连接失败"); 274 |     }]; 275 | ``` 276 | #### 2、发送消息 277 | ``` 278 | [[FLSocketManager shareManager] fl_send:@"hello world"]; 279 | ``` 280 | #### 3、关闭连接 281 | ``` 282 | [[FLSocketManager shareManager] fl_close:^(NSInteger code, NSString *reason, BOOL wasClean) { 283 |         NSLog(@"code = %zd,reason = %@",code,reason); 284 |     }]; 285 | ``` 286 | 287 | ## 五、总结 288 | - 使用block回调,用法只需要三步,监听都在同一个方法里面,方便管理,关键是看起来简单,用起来爽,而且不怕框架API修改 289 | 290 | - 有重连机制,连接失败或者系统异常原因导致关闭的就会自动重连,默认一秒就重连,如果调用者手动关闭就不重连,有最大重连次数,可自定义,默认5次 291 | 292 | - 实现部分的代码就拷贝上来了,喜欢的话就去clone吧,demo没有给服务器地址,实测没问题的 293 | 294 | - 一般socket开启后就不用关闭,此时作者封装的这个block是单例对象的,因此如果另一个控制器监听了接收block,那么前一个控制器就没办法监听接收,建议大家使用通知去实现,只需要在一个控制器去做监听,然后发通知,其他控制器监听这个通知就行,这样就可以实现整个项目多个控制器都能同时监听socket改变 295 | 296 | ## 欢迎大家去[我的简书](http://www.jianshu.com/users/fe5700cfb223/latest_articles)关注我,随时发干货,喜欢就给个star && like,有问题留言哟~~~ 297 | -------------------------------------------------------------------------------- /看我.md: -------------------------------------------------------------------------------- 1 | ## 一、简单介绍 2 | 3 | SocketRocket是一个WebSocket客户端([WebSocket](http://tools.ietf.org/html/rfc6455)是适用于Web应用的下一代全双工通讯协议,被成为“Web的TCP”,它实现了浏览器与服务器的双向通信),采用Object-C编写。SocketRocket遵循最新的WebSocket规范[RFC 6455](http://tools.ietf.org/html/rfc6455)。 4 | 5 | 这里是开发者描述的其一些特性/设计:  6 | > 7 | 支持TLS (wss)。 8 | 使用NSStream/CFNetworking。 9 | 使用ARC。 10 | 采用并行架构。大部分的工作由后端的工作队列(worker queues)完成。 11 | 基于委托编程。 12 | 13 | SocketRocket支持iOS 4.x系统(应该也可以运行于OS X),不需要任何UI包依赖。详细信息可以查看[此文介绍](http://corner.squareup.com/2012/02/socketrocket-websockets.html)。  14 | 15 | ### [socketRocket 传送门](https://github.com/facebook/SocketRocket) 16 | 17 | 18 | ## 二、如何使用 19 | - ####socketRocket 支持pod,因此直接添加然后install,文件不多喔~ 20 | 21 | ![Paste_Image.png](http://upload-images.jianshu.io/upload_images/1085031-c34c45a1d56a86ee.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 22 | 23 | - ####简单使用socketRocket实现通信的话,只需要用那么几个API就行了 24 | 25 | > 1.创建一个请求:(有多种方法) 26 | 27 | ``` 28 | - (id)initWithURLRequest:(NSURLRequest *)request; 29 | ``` 30 | 31 | >2.遵守并指定代理 32 | 33 | ``` 34 | @property (nonatomic, weak) id delegate; 35 | ``` 36 | 37 | >3.打开连接加载请求 38 | 39 | ``` 40 | - (void)open; 41 | ``` 42 | 43 | >4.关闭连接 44 | 45 | ``` 46 | - (void)close; 47 | ``` 48 | 49 | >5.发送消息 50 | 51 | ``` 52 | // Send a UTF8 String or Data. 53 | - (void)send:(id)data; 54 | // Send Data (can be nil) in a ping message. 55 | - (void)sendPing:(NSData *)data; 56 | ``` 57 | 58 | >6.监听socketRocket是通过代理方法来实现的 59 | 60 | ``` 61 | @protocol SRWebSocketDelegate 62 | // message will either be an NSString if the server is using text 63 | // or NSData if the server is using binary. 64 | - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message; 65 | @optional 66 | - (void)webSocketDidOpen:(SRWebSocket *)webSocket; 67 | - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error; 68 | - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean; 69 | - (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload; 70 | // Return YES to convert messages sent as Text to an NSString. Return NO to skip NSData -> NSString conversion for Text messages. Defaults to YES. 71 | - (BOOL)webSocketShouldConvertTextFrameToString:(SRWebSocket *)webSocket; 72 | ``` 73 | 74 | - ####注意:发送的参数必须跟后台商量,保持一致才能发送,不然一发送就自动关闭连接的 75 | 76 | ## 三、封装隔离 77 | 78 | - #### 为什么要封装 79 | - 我就按上面的6个步骤就可以实现通信了,不用考虑太多的逻辑处理,但我还是觉得有点麻烦,我希望一句代码就搞定,使用代理就实现不了。 80 | - 第三方框架必须要封装隔离,不然Facebook突然改了这个框架的API,那么你项目多次使用的话,改动工作量就非常大了。 81 | - 我需要它有个重连机制,如果连接失败或者系统异常原因导致连接关闭的话,它会自动重连,如果是用户手动关闭,则不需要重连,直到下次重新打开。 82 | 83 | - #### 封装思路 84 | - 需要单例工具类,管理socket的状态,当然状态是不允许外界修改,因此是readonly 85 | - 对外提供超时重连的时间,允许外界修改 86 | - 对外提供开启连接方法,使用block进行回调,不使用代理,实现一句代码创建并监听 87 | - 有开启必须有关闭连接的方法,同样使用block回调,告诉调用者关闭的状态码以及原因 88 | - 当然需要一个发送方法,参数模仿框架,传id类型就行 89 | 90 | - ####封装后的API(.h文件) 91 | 92 | >自定义的枚举,socket状态,比框架多一个枚举是用户关闭 93 | 94 | ``` 95 | /** 96 |  *  @author 孔凡列, 16-09-21 07:09:52 97 |  * 98 |  *  socket状态 99 |  */ 100 | typedef NS_ENUM(NSInteger,FLSocketStatus){ 101 |     FLSocketStatusConnected,// 已连接 102 |     FLSocketStatusFailed,// 失败 103 |     FLSocketStatusClosedByServer,// 系统关闭 104 |     FLSocketStatusClosedByUser,// 用户关闭 105 |     FLSocketStatusReceived// 接收消息 106 | }; 107 | /** 108 |  *  @author 孔凡列, 16-09-21 07:09:52 109 |  * 110 |  *  消息类型 111 |  */ 112 | typedef NS_ENUM(NSInteger,FLSocketReceiveType){ 113 |     FLSocketReceiveTypeForMessage, 114 |     FLSocketReceiveTypeForPong 115 | }; 116 | 117 | ``` 118 | 119 | > 连接回调,成功连接后执行 120 | 121 | ``` 122 | 123 | /** 124 |  *  @author 孔凡列, 16-09-21 08:09:06 125 |  * 126 |  *  连接回调 127 |  */ 128 | @property (nonatomic,copy)FLSocketDidConnectBlock connect; 129 | 130 | ``` 131 | 132 | >接收到socket消息的时候就会执行 133 | 134 | ``` 135 | /** 136 |  *  @author 孔凡列, 16-09-21 08:09:06 137 |  * 138 |  *  接收消息回调 139 |  */ 140 | @property (nonatomic,copy)FLSocketDidReceiveBlock receive; 141 | 142 | ``` 143 | 144 | >连接或发送失败会执行 145 | 146 | ``` 147 | /** 148 |  *  @author 孔凡列, 16-09-21 08:09:06 149 |  * 150 |  *  失败回调 151 |  */ 152 | @property (nonatomic,copy)FLSocketDidFailBlock failure; 153 | 154 | ``` 155 | 156 | >用户手动关闭或者系统关闭的时候会调用 157 | 158 | ``` 159 | /** 160 |  *  @author 孔凡列, 16-09-21 08:09:06 161 |  * 162 |  *  关闭回调 163 |  */ 164 | @property (nonatomic,copy)FLSocketDidCloseBlock close; 165 | 166 | ``` 167 | 168 | >socket状态,一共有5个状态 169 | 170 | ``` 171 | /** 172 |  *  @author 孔凡列, 16-09-21 08:09:28 173 |  * 174 |  *  当前的socket状态 175 |  */ 176 | @property (nonatomic,assign,readonly)FLSocketStatus fl_socketStatus; 177 | 178 | ``` 179 | 180 | >超时重连时间,默认一秒重连(框架没有,自己添加的) 181 | 182 | ``` 183 | /** 184 |  *  @author 孔凡列, 16-09-21 08:09:40 185 |  * 186 |  *  超时重连时间,默认1秒 187 |  */ 188 | @property (nonatomic,assign)NSTimeInterval overtime; 189 | ``` 190 | 191 | >超时重连次数,默认5次(框架没有,自己添加的) 192 | 193 | ``` 194 | /** 195 |  *  @author Clarence 196 |  * 197 |  *  重连次数,默认5次 198 |  */ 199 | @property (nonatomic, assign)NSUInteger reconnectCount; 200 | ``` 201 | 202 | >单例创建管理类,项目中唯一,方便管理 203 | 204 | ``` 205 | /** 206 |  *  @author 孔凡列, 16-09-21 08:09:06 207 |  * 208 |  *  单例调用 209 |  */ 210 | + (instancetype)shareManager; 211 | ``` 212 | 213 | >开启socket,block监听 214 | 215 | ``` 216 | /** 217 |  *  @author 孔凡列, 16-09-21 08:09:16 218 |  * 219 |  *  开启socket 220 |  * 221 |  *  @param urlStr  服务器地址 222 |  *  @param connect 连接成功回调 223 |  *  @param receive 接收消息回调 224 |  *  @param failure 失败回调 225 |  */ 226 | - (void)fl_open:(NSString *)urlStr connect:(FLSocketDidConnectBlock)connect receive:(FLSocketDidReceiveBlock)receive failure:(FLSocketDidFailBlock)failure; 227 | ``` 228 | 229 | >关闭socket,有两个状态,一个是用户关闭,一个是系统关闭 230 | 231 | ``` 232 | /** 233 |  *  @author 孔凡列, 16-09-21 08:09:06 234 |  * 235 |  *  关闭socket 236 |  * 237 |  *  @param close 关闭回调 238 |  */ 239 | - (void)fl_close:(FLSocketDidCloseBlock)close; 240 | ``` 241 | 242 | >发送消息,可发送NSString 或者 NSData 243 | 244 | ``` 245 | /** 246 |  *  @author 孔凡列, 16-09-21 08:09:25 247 |  * 248 |  *  发送消息,NSString 或者 NSData 249 |  * 250 |  *  @param data Send a UTF8 String or Data. 251 |  */ 252 | - (void)fl_send:(id)data; 253 | 254 | ``` 255 | 256 | 257 | 258 | ## 四、调用 259 | 260 | >#### 1、开启连接并监听 261 | ``` 262 | NSString *url = @"服务器给你的地址"; 263 | [[FLSocketManager shareManager] fl_open:url connect:^{ 264 |         NSLog(@"成功连接"); 265 |     } receive:^(id message, FLSocketReceiveType type) { 266 |         if (type == FLSocketReceiveTypeForMessage) { 267 |             NSLog(@"接收 类型1--%@",message); 268 |         } 269 |         else if (type == FLSocketReceiveTypeForPong){ 270 |             NSLog(@"接收 类型2--%@",message); 271 |         } 272 |     } failure:^(NSError *error) { 273 |         NSLog(@"连接失败"); 274 |     }]; 275 | ``` 276 | #### 2、发送消息 277 | ``` 278 | [[FLSocketManager shareManager] fl_send:@"hello world"]; 279 | ``` 280 | #### 3、关闭连接 281 | ``` 282 | [[FLSocketManager shareManager] fl_close:^(NSInteger code, NSString *reason, BOOL wasClean) { 283 |         NSLog(@"code = %zd,reason = %@",code,reason); 284 |     }]; 285 | ``` 286 | 287 | ## 五、总结 288 | - 使用block回调,用法只需要三步,监听都在同一个方法里面,方便管理,关键是看起来简单,用起来爽,而且不怕框架API修改 289 | 290 | - 有重连机制,连接失败或者系统异常原因导致关闭的就会自动重连,默认一秒就重连,如果调用者手动关闭就不重连,有最大重连次数,可自定义,默认5次 291 | 292 | - 实现部分的代码就拷贝上来了,喜欢的话就去clone吧,demo没有给服务器地址,实测没问题的 293 | 294 | - 一般socket开启后就不用关闭,此时作者封装的这个block是单例对象的,因此如果另一个控制器监听了接收block,那么前一个控制器就没办法监听接收,建议大家使用通知去实现,只需要在一个控制器去做监听,然后发通知,其他控制器监听这个通知就行,这样就可以实现整个项目多个控制器都能同时监听socket改变 295 | 296 | ## 欢迎大家去[我的简书](http://www.jianshu.com/users/fe5700cfb223/latest_articles)关注我,随时发干货,喜欢就给个star && like,有问题留言哟~~~ 297 | --------------------------------------------------------------------------------