├── README.md └── ReadWebsocket ├── Podfile.lock ├── Pods ├── Headers │ ├── Private │ │ └── SocketRocket │ │ │ ├── SRWebSocket.h │ │ │ └── SocketRocket.h │ └── Public │ │ └── SocketRocket │ │ ├── SRWebSocket.h │ │ └── SocketRocket.h ├── Manifest.lock ├── Pods.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ │ └── tyh.xcuserdatad │ │ └── xcschemes │ │ ├── Pods-ReadWebsocket.xcscheme │ │ ├── SocketRocket.xcscheme │ │ └── xcschememanagement.plist ├── SocketRocket │ ├── LICENSE │ ├── README.rst │ └── SocketRocket │ │ ├── SRWebSocket.h │ │ ├── SRWebSocket.m │ │ └── SocketRocket.h └── Target Support Files │ ├── Pods-ReadWebsocket │ ├── Pods-ReadWebsocket-acknowledgements.markdown │ ├── Pods-ReadWebsocket-acknowledgements.plist │ ├── Pods-ReadWebsocket-dummy.m │ ├── Pods-ReadWebsocket-frameworks.sh │ ├── Pods-ReadWebsocket-resources.sh │ ├── Pods-ReadWebsocket.debug.xcconfig │ └── Pods-ReadWebsocket.release.xcconfig │ └── SocketRocket │ ├── SocketRocket-dummy.m │ ├── SocketRocket-prefix.pch │ └── SocketRocket.xcconfig ├── ReadWebsocket.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── tyh.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── tyh.xcuserdatad │ └── xcschemes │ ├── ReadWebsocket.xcscheme │ └── xcschememanagement.plist ├── ReadWebsocket.xcworkspace ├── contents.xcworkspacedata └── xcuserdata │ └── tyh.xcuserdatad │ ├── UserInterfaceState.xcuserstate │ └── xcdebugger │ └── Breakpoints_v2.xcbkptlist ├── ReadWebsocket ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── ViewController.h ├── ViewController.m └── main.m ├── ReadWebsocketTests ├── Info.plist └── ReadWebsocketTests.m ├── ReadWebsocketUITests ├── Info.plist └── ReadWebsocketUITests.m └── podfile /README.md: -------------------------------------------------------------------------------- 1 | # SRWebSocketTip 2 | SRWebSocket源码浅析 3 | http://www.jianshu.com/u/14431e509ae8 4 | -------------------------------------------------------------------------------- /ReadWebsocket/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: f1ebf17bd18ffae1d37c892b25835bff2beb149a 11 | 12 | COCOAPODS: 1.0.1 13 | -------------------------------------------------------------------------------- /ReadWebsocket/Pods/Headers/Private/SocketRocket/SRWebSocket.h: -------------------------------------------------------------------------------- 1 | ../../../SocketRocket/SocketRocket/SRWebSocket.h -------------------------------------------------------------------------------- /ReadWebsocket/Pods/Headers/Private/SocketRocket/SocketRocket.h: -------------------------------------------------------------------------------- 1 | ../../../SocketRocket/SocketRocket/SocketRocket.h -------------------------------------------------------------------------------- /ReadWebsocket/Pods/Headers/Public/SocketRocket/SRWebSocket.h: -------------------------------------------------------------------------------- 1 | ../../../SocketRocket/SocketRocket/SRWebSocket.h -------------------------------------------------------------------------------- /ReadWebsocket/Pods/Headers/Public/SocketRocket/SocketRocket.h: -------------------------------------------------------------------------------- 1 | ../../../SocketRocket/SocketRocket/SocketRocket.h -------------------------------------------------------------------------------- /ReadWebsocket/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: f1ebf17bd18ffae1d37c892b25835bff2beb149a 11 | 12 | COCOAPODS: 1.0.1 13 | -------------------------------------------------------------------------------- /ReadWebsocket/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 | 1518E1FDB3FC4B3DF913A45B843D51D9 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC76A27B6AC5FF40DBB7B0E2B8BF8014 /* Security.framework */; }; 11 | 4D83E01A403CB701B5B8C2AFCEFDFD9F /* SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 1095759AC714C71F66EB69CA0E335A64 /* SRWebSocket.m */; }; 12 | 59FA099FD2A82E13820E4FF99D6EF124 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5D67AC30769732F1FF0BD1854B2E225 /* Foundation.framework */; }; 13 | 809066173CA8462B4BD356630892727D /* SocketRocket-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 1AD643BBD53C0BC9592126E22C82B77A /* SocketRocket-dummy.m */; }; 14 | 837D0A0CA53A124008C987346E5B9FA3 /* Pods-ReadWebsocket-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = B024B0541FB764F109D24D64185372CB /* Pods-ReadWebsocket-dummy.m */; }; 15 | AEC61E2F0A6D4E461D586456F9356E4C /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 898EE84856EB5E52F00A964689E9F67E /* CFNetwork.framework */; }; 16 | C63CD1349006267F3CF7E55F1C7542FB /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5D67AC30769732F1FF0BD1854B2E225 /* Foundation.framework */; }; 17 | D409CB281B027B949638213F96E65FE2 /* SocketRocket.h in Headers */ = {isa = PBXBuildFile; fileRef = E3AD439A80B2F1ABD299002CAA341262 /* SocketRocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18 | F6A98C82F340F9678CD90A9AB7A0F240 /* SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 5AF4E5EED2BE7953295FCE1F8F29887B /* SRWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXContainerItemProxy section */ 22 | 0E70883F7E06C6C885E9EC5F6ADAEC38 /* PBXContainerItemProxy */ = { 23 | isa = PBXContainerItemProxy; 24 | containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; 25 | proxyType = 1; 26 | remoteGlobalIDString = B711249C42550DF0DAB450627B08AD42; 27 | remoteInfo = SocketRocket; 28 | }; 29 | /* End PBXContainerItemProxy section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 0F7527CA608082C5B3EA001A8D8AB9C4 /* libPods-ReadWebsocket.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libPods-ReadWebsocket.a"; path = "libPods-ReadWebsocket.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | 1095759AC714C71F66EB69CA0E335A64 /* SRWebSocket.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SRWebSocket.m; path = SocketRocket/SRWebSocket.m; sourceTree = ""; }; 34 | 1663A042FFA452DAFCE14D9B423ECF9D /* libSocketRocket.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = libSocketRocket.a; path = libSocketRocket.a; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | 1AD643BBD53C0BC9592126E22C82B77A /* SocketRocket-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SocketRocket-dummy.m"; sourceTree = ""; }; 36 | 1F4BDBCC991F34C146316F0597AFF57B /* Pods-ReadWebsocket-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-ReadWebsocket-frameworks.sh"; sourceTree = ""; }; 37 | 20449F4EDFD6F2066D311DE3C8B16830 /* Pods-ReadWebsocket.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-ReadWebsocket.release.xcconfig"; sourceTree = ""; }; 38 | 348DD97970E42CEC701B2768A0ECBDEA /* Pods-ReadWebsocket.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-ReadWebsocket.debug.xcconfig"; sourceTree = ""; }; 39 | 5AF4E5EED2BE7953295FCE1F8F29887B /* SRWebSocket.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SRWebSocket.h; path = SocketRocket/SRWebSocket.h; sourceTree = ""; }; 40 | 81ED5606DF56484C11C9E852E586A23C /* SocketRocket-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SocketRocket-prefix.pch"; sourceTree = ""; }; 41 | 898EE84856EB5E52F00A964689E9F67E /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.0.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; }; 42 | 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 43 | AA70A890B416FCA772A479E5620A0BF2 /* Pods-ReadWebsocket-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-ReadWebsocket-acknowledgements.plist"; sourceTree = ""; }; 44 | B024B0541FB764F109D24D64185372CB /* Pods-ReadWebsocket-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-ReadWebsocket-dummy.m"; sourceTree = ""; }; 45 | B5D67AC30769732F1FF0BD1854B2E225 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 46 | BABB636A176CAB2282E3EE8964F93FC9 /* Pods-ReadWebsocket-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-ReadWebsocket-resources.sh"; sourceTree = ""; }; 47 | BC76A27B6AC5FF40DBB7B0E2B8BF8014 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.0.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; }; 48 | BF929A7F54D498746EAD85CBC97A1B7C /* Pods-ReadWebsocket-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-ReadWebsocket-acknowledgements.markdown"; sourceTree = ""; }; 49 | D271E1ACC894BAE9E6FC8AAAF017C9F4 /* SocketRocket.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SocketRocket.xcconfig; sourceTree = ""; }; 50 | E3AD439A80B2F1ABD299002CAA341262 /* SocketRocket.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SocketRocket.h; path = SocketRocket/SocketRocket.h; sourceTree = ""; }; 51 | /* End PBXFileReference section */ 52 | 53 | /* Begin PBXFrameworksBuildPhase section */ 54 | A9BE654E7B2F399971027C105D1C6268 /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | AEC61E2F0A6D4E461D586456F9356E4C /* CFNetwork.framework in Frameworks */, 59 | 59FA099FD2A82E13820E4FF99D6EF124 /* Foundation.framework in Frameworks */, 60 | 1518E1FDB3FC4B3DF913A45B843D51D9 /* Security.framework in Frameworks */, 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | BAB8C67C627FD743C7F70C118FBC4365 /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | C63CD1349006267F3CF7E55F1C7542FB /* 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 | 122DA2E5084A4393C29BE363C764795C /* Frameworks */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 4A9D6FF81BFBB1B195CCEBC8D335E2CF /* iOS */, 90 | ); 91 | name = Frameworks; 92 | sourceTree = ""; 93 | }; 94 | 4A9D6FF81BFBB1B195CCEBC8D335E2CF /* iOS */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | 898EE84856EB5E52F00A964689E9F67E /* CFNetwork.framework */, 98 | B5D67AC30769732F1FF0BD1854B2E225 /* Foundation.framework */, 99 | BC76A27B6AC5FF40DBB7B0E2B8BF8014 /* Security.framework */, 100 | ); 101 | name = iOS; 102 | sourceTree = ""; 103 | }; 104 | 4FF663BB937700D1580BDAB7F3EF0D97 /* Pods */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 702DD509C6164C2492FCB546BD72BB07 /* SocketRocket */, 108 | ); 109 | name = Pods; 110 | sourceTree = ""; 111 | }; 112 | 702DD509C6164C2492FCB546BD72BB07 /* SocketRocket */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | E3AD439A80B2F1ABD299002CAA341262 /* SocketRocket.h */, 116 | 5AF4E5EED2BE7953295FCE1F8F29887B /* SRWebSocket.h */, 117 | 1095759AC714C71F66EB69CA0E335A64 /* SRWebSocket.m */, 118 | 04310F8822AFFD42107F8DA169E0A47A /* Support Files */, 119 | ); 120 | name = SocketRocket; 121 | path = SocketRocket; 122 | sourceTree = ""; 123 | }; 124 | 7DB346D0F39D3F0E887471402A8071AB = { 125 | isa = PBXGroup; 126 | children = ( 127 | 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */, 128 | 122DA2E5084A4393C29BE363C764795C /* Frameworks */, 129 | 4FF663BB937700D1580BDAB7F3EF0D97 /* Pods */, 130 | C65407848DF7587556CD7A5A57401308 /* Products */, 131 | D46C863978B3B123510E8794A3DD10F5 /* Targets Support Files */, 132 | ); 133 | sourceTree = ""; 134 | }; 135 | 9345C3AE3E886EAEEBB375658E450B7B /* Pods-ReadWebsocket */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | BF929A7F54D498746EAD85CBC97A1B7C /* Pods-ReadWebsocket-acknowledgements.markdown */, 139 | AA70A890B416FCA772A479E5620A0BF2 /* Pods-ReadWebsocket-acknowledgements.plist */, 140 | B024B0541FB764F109D24D64185372CB /* Pods-ReadWebsocket-dummy.m */, 141 | 1F4BDBCC991F34C146316F0597AFF57B /* Pods-ReadWebsocket-frameworks.sh */, 142 | BABB636A176CAB2282E3EE8964F93FC9 /* Pods-ReadWebsocket-resources.sh */, 143 | 348DD97970E42CEC701B2768A0ECBDEA /* Pods-ReadWebsocket.debug.xcconfig */, 144 | 20449F4EDFD6F2066D311DE3C8B16830 /* Pods-ReadWebsocket.release.xcconfig */, 145 | ); 146 | name = "Pods-ReadWebsocket"; 147 | path = "Target Support Files/Pods-ReadWebsocket"; 148 | sourceTree = ""; 149 | }; 150 | C65407848DF7587556CD7A5A57401308 /* Products */ = { 151 | isa = PBXGroup; 152 | children = ( 153 | 0F7527CA608082C5B3EA001A8D8AB9C4 /* libPods-ReadWebsocket.a */, 154 | 1663A042FFA452DAFCE14D9B423ECF9D /* libSocketRocket.a */, 155 | ); 156 | name = Products; 157 | sourceTree = ""; 158 | }; 159 | D46C863978B3B123510E8794A3DD10F5 /* Targets Support Files */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | 9345C3AE3E886EAEEBB375658E450B7B /* Pods-ReadWebsocket */, 163 | ); 164 | name = "Targets Support Files"; 165 | sourceTree = ""; 166 | }; 167 | /* End PBXGroup section */ 168 | 169 | /* Begin PBXHeadersBuildPhase section */ 170 | CF079BCDBA0D01DF090BFF114A1DEF23 /* Headers */ = { 171 | isa = PBXHeadersBuildPhase; 172 | buildActionMask = 2147483647; 173 | files = ( 174 | D409CB281B027B949638213F96E65FE2 /* SocketRocket.h in Headers */, 175 | F6A98C82F340F9678CD90A9AB7A0F240 /* SRWebSocket.h in Headers */, 176 | ); 177 | runOnlyForDeploymentPostprocessing = 0; 178 | }; 179 | /* End PBXHeadersBuildPhase section */ 180 | 181 | /* Begin PBXNativeTarget section */ 182 | 8A792D8D7D7ECCA985176C3CF62B9F27 /* Pods-ReadWebsocket */ = { 183 | isa = PBXNativeTarget; 184 | buildConfigurationList = 582C5F49AD5A3AC1284718A0E58FC005 /* Build configuration list for PBXNativeTarget "Pods-ReadWebsocket" */; 185 | buildPhases = ( 186 | 57DC9FFDB5D97C7E588D855C2BD1F642 /* Sources */, 187 | BAB8C67C627FD743C7F70C118FBC4365 /* Frameworks */, 188 | ); 189 | buildRules = ( 190 | ); 191 | dependencies = ( 192 | 75B8922F6B22CD19384F41ACE9AFAB89 /* PBXTargetDependency */, 193 | ); 194 | name = "Pods-ReadWebsocket"; 195 | productName = "Pods-ReadWebsocket"; 196 | productReference = 0F7527CA608082C5B3EA001A8D8AB9C4 /* libPods-ReadWebsocket.a */; 197 | productType = "com.apple.product-type.library.static"; 198 | }; 199 | B711249C42550DF0DAB450627B08AD42 /* SocketRocket */ = { 200 | isa = PBXNativeTarget; 201 | buildConfigurationList = 212453FF0ECEA610C13CA3C2DA754DB5 /* Build configuration list for PBXNativeTarget "SocketRocket" */; 202 | buildPhases = ( 203 | 414DF53F6A1D9E93337487744685350E /* Sources */, 204 | A9BE654E7B2F399971027C105D1C6268 /* Frameworks */, 205 | CF079BCDBA0D01DF090BFF114A1DEF23 /* Headers */, 206 | ); 207 | buildRules = ( 208 | ); 209 | dependencies = ( 210 | ); 211 | name = SocketRocket; 212 | productName = SocketRocket; 213 | productReference = 1663A042FFA452DAFCE14D9B423ECF9D /* libSocketRocket.a */; 214 | productType = "com.apple.product-type.library.static"; 215 | }; 216 | /* End PBXNativeTarget section */ 217 | 218 | /* Begin PBXProject section */ 219 | D41D8CD98F00B204E9800998ECF8427E /* Project object */ = { 220 | isa = PBXProject; 221 | attributes = { 222 | LastSwiftUpdateCheck = 0730; 223 | LastUpgradeCheck = 0700; 224 | }; 225 | buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */; 226 | compatibilityVersion = "Xcode 3.2"; 227 | developmentRegion = English; 228 | hasScannedForEncodings = 0; 229 | knownRegions = ( 230 | en, 231 | ); 232 | mainGroup = 7DB346D0F39D3F0E887471402A8071AB; 233 | productRefGroup = C65407848DF7587556CD7A5A57401308 /* Products */; 234 | projectDirPath = ""; 235 | projectRoot = ""; 236 | targets = ( 237 | 8A792D8D7D7ECCA985176C3CF62B9F27 /* Pods-ReadWebsocket */, 238 | B711249C42550DF0DAB450627B08AD42 /* SocketRocket */, 239 | ); 240 | }; 241 | /* End PBXProject section */ 242 | 243 | /* Begin PBXSourcesBuildPhase section */ 244 | 414DF53F6A1D9E93337487744685350E /* Sources */ = { 245 | isa = PBXSourcesBuildPhase; 246 | buildActionMask = 2147483647; 247 | files = ( 248 | 809066173CA8462B4BD356630892727D /* SocketRocket-dummy.m in Sources */, 249 | 4D83E01A403CB701B5B8C2AFCEFDFD9F /* SRWebSocket.m in Sources */, 250 | ); 251 | runOnlyForDeploymentPostprocessing = 0; 252 | }; 253 | 57DC9FFDB5D97C7E588D855C2BD1F642 /* Sources */ = { 254 | isa = PBXSourcesBuildPhase; 255 | buildActionMask = 2147483647; 256 | files = ( 257 | 837D0A0CA53A124008C987346E5B9FA3 /* Pods-ReadWebsocket-dummy.m in Sources */, 258 | ); 259 | runOnlyForDeploymentPostprocessing = 0; 260 | }; 261 | /* End PBXSourcesBuildPhase section */ 262 | 263 | /* Begin PBXTargetDependency section */ 264 | 75B8922F6B22CD19384F41ACE9AFAB89 /* PBXTargetDependency */ = { 265 | isa = PBXTargetDependency; 266 | name = SocketRocket; 267 | target = B711249C42550DF0DAB450627B08AD42 /* SocketRocket */; 268 | targetProxy = 0E70883F7E06C6C885E9EC5F6ADAEC38 /* PBXContainerItemProxy */; 269 | }; 270 | /* End PBXTargetDependency section */ 271 | 272 | /* Begin XCBuildConfiguration section */ 273 | 157BE41AF33D4C30DF6DD855F2D32AB5 /* Debug */ = { 274 | isa = XCBuildConfiguration; 275 | baseConfigurationReference = 348DD97970E42CEC701B2768A0ECBDEA /* Pods-ReadWebsocket.debug.xcconfig */; 276 | buildSettings = { 277 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 278 | DEBUG_INFORMATION_FORMAT = dwarf; 279 | ENABLE_STRICT_OBJC_MSGSEND = YES; 280 | GCC_NO_COMMON_BLOCKS = YES; 281 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 282 | MACH_O_TYPE = staticlib; 283 | MTL_ENABLE_DEBUG_INFO = YES; 284 | OTHER_LDFLAGS = ""; 285 | OTHER_LIBTOOLFLAGS = ""; 286 | PODS_ROOT = "$(SRCROOT)"; 287 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; 288 | PRODUCT_NAME = "$(TARGET_NAME)"; 289 | SDKROOT = iphoneos; 290 | SKIP_INSTALL = YES; 291 | }; 292 | name = Debug; 293 | }; 294 | 2C1BD66F8870743EFBFFF68402F1C5B9 /* Debug */ = { 295 | isa = XCBuildConfiguration; 296 | baseConfigurationReference = D271E1ACC894BAE9E6FC8AAAF017C9F4 /* SocketRocket.xcconfig */; 297 | buildSettings = { 298 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 299 | DEBUG_INFORMATION_FORMAT = dwarf; 300 | ENABLE_STRICT_OBJC_MSGSEND = YES; 301 | GCC_NO_COMMON_BLOCKS = YES; 302 | GCC_PREFIX_HEADER = "Target Support Files/SocketRocket/SocketRocket-prefix.pch"; 303 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 304 | MTL_ENABLE_DEBUG_INFO = YES; 305 | OTHER_LDFLAGS = ""; 306 | OTHER_LIBTOOLFLAGS = ""; 307 | PRIVATE_HEADERS_FOLDER_PATH = ""; 308 | PRODUCT_NAME = "$(TARGET_NAME)"; 309 | PUBLIC_HEADERS_FOLDER_PATH = ""; 310 | SDKROOT = iphoneos; 311 | SKIP_INSTALL = YES; 312 | }; 313 | name = Debug; 314 | }; 315 | 3FA2475660FFC880302AD18E29B2E5FC /* Debug */ = { 316 | isa = XCBuildConfiguration; 317 | buildSettings = { 318 | ALWAYS_SEARCH_USER_PATHS = NO; 319 | CLANG_ANALYZER_NONNULL = YES; 320 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 321 | CLANG_CXX_LIBRARY = "libc++"; 322 | CLANG_ENABLE_MODULES = YES; 323 | CLANG_ENABLE_OBJC_ARC = YES; 324 | CLANG_WARN_BOOL_CONVERSION = YES; 325 | CLANG_WARN_CONSTANT_CONVERSION = YES; 326 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; 327 | CLANG_WARN_EMPTY_BODY = YES; 328 | CLANG_WARN_ENUM_CONVERSION = YES; 329 | CLANG_WARN_INT_CONVERSION = YES; 330 | CLANG_WARN_OBJC_ROOT_CLASS = YES; 331 | CLANG_WARN_UNREACHABLE_CODE = YES; 332 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 333 | COPY_PHASE_STRIP = NO; 334 | ENABLE_TESTABILITY = YES; 335 | GCC_C_LANGUAGE_STANDARD = gnu99; 336 | GCC_DYNAMIC_NO_PIC = NO; 337 | GCC_OPTIMIZATION_LEVEL = 0; 338 | GCC_PREPROCESSOR_DEFINITIONS = ( 339 | "POD_CONFIGURATION_DEBUG=1", 340 | "DEBUG=1", 341 | "$(inherited)", 342 | ); 343 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 344 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 345 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 346 | GCC_WARN_UNDECLARED_SELECTOR = YES; 347 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 348 | GCC_WARN_UNUSED_FUNCTION = YES; 349 | GCC_WARN_UNUSED_VARIABLE = YES; 350 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 351 | ONLY_ACTIVE_ARCH = YES; 352 | STRIP_INSTALLED_PRODUCT = NO; 353 | SYMROOT = "${SRCROOT}/../build"; 354 | }; 355 | name = Debug; 356 | }; 357 | 6550121D860D51889320A1914665B8DF /* Release */ = { 358 | isa = XCBuildConfiguration; 359 | baseConfigurationReference = D271E1ACC894BAE9E6FC8AAAF017C9F4 /* SocketRocket.xcconfig */; 360 | buildSettings = { 361 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 362 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 363 | ENABLE_STRICT_OBJC_MSGSEND = YES; 364 | GCC_NO_COMMON_BLOCKS = YES; 365 | GCC_PREFIX_HEADER = "Target Support Files/SocketRocket/SocketRocket-prefix.pch"; 366 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 367 | MTL_ENABLE_DEBUG_INFO = NO; 368 | OTHER_LDFLAGS = ""; 369 | OTHER_LIBTOOLFLAGS = ""; 370 | PRIVATE_HEADERS_FOLDER_PATH = ""; 371 | PRODUCT_NAME = "$(TARGET_NAME)"; 372 | PUBLIC_HEADERS_FOLDER_PATH = ""; 373 | SDKROOT = iphoneos; 374 | SKIP_INSTALL = YES; 375 | }; 376 | name = Release; 377 | }; 378 | 9F85B643A89B327B706CC9820D2EF544 /* Release */ = { 379 | isa = XCBuildConfiguration; 380 | baseConfigurationReference = 20449F4EDFD6F2066D311DE3C8B16830 /* Pods-ReadWebsocket.release.xcconfig */; 381 | buildSettings = { 382 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 383 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 384 | ENABLE_STRICT_OBJC_MSGSEND = YES; 385 | GCC_NO_COMMON_BLOCKS = YES; 386 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 387 | MACH_O_TYPE = staticlib; 388 | MTL_ENABLE_DEBUG_INFO = NO; 389 | OTHER_LDFLAGS = ""; 390 | OTHER_LIBTOOLFLAGS = ""; 391 | PODS_ROOT = "$(SRCROOT)"; 392 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; 393 | PRODUCT_NAME = "$(TARGET_NAME)"; 394 | SDKROOT = iphoneos; 395 | SKIP_INSTALL = YES; 396 | }; 397 | name = Release; 398 | }; 399 | F325026B55060D3B22BEC0BAB5FA7344 /* Release */ = { 400 | isa = XCBuildConfiguration; 401 | buildSettings = { 402 | ALWAYS_SEARCH_USER_PATHS = NO; 403 | CLANG_ANALYZER_NONNULL = YES; 404 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 405 | CLANG_CXX_LIBRARY = "libc++"; 406 | CLANG_ENABLE_MODULES = YES; 407 | CLANG_ENABLE_OBJC_ARC = YES; 408 | CLANG_WARN_BOOL_CONVERSION = YES; 409 | CLANG_WARN_CONSTANT_CONVERSION = YES; 410 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; 411 | CLANG_WARN_EMPTY_BODY = YES; 412 | CLANG_WARN_ENUM_CONVERSION = YES; 413 | CLANG_WARN_INT_CONVERSION = YES; 414 | CLANG_WARN_OBJC_ROOT_CLASS = YES; 415 | CLANG_WARN_UNREACHABLE_CODE = YES; 416 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 417 | COPY_PHASE_STRIP = YES; 418 | ENABLE_NS_ASSERTIONS = NO; 419 | GCC_C_LANGUAGE_STANDARD = gnu99; 420 | GCC_PREPROCESSOR_DEFINITIONS = ( 421 | "POD_CONFIGURATION_RELEASE=1", 422 | "$(inherited)", 423 | ); 424 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 425 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 426 | GCC_WARN_UNDECLARED_SELECTOR = YES; 427 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 428 | GCC_WARN_UNUSED_FUNCTION = YES; 429 | GCC_WARN_UNUSED_VARIABLE = YES; 430 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 431 | STRIP_INSTALLED_PRODUCT = NO; 432 | SYMROOT = "${SRCROOT}/../build"; 433 | VALIDATE_PRODUCT = YES; 434 | }; 435 | name = Release; 436 | }; 437 | /* End XCBuildConfiguration section */ 438 | 439 | /* Begin XCConfigurationList section */ 440 | 212453FF0ECEA610C13CA3C2DA754DB5 /* Build configuration list for PBXNativeTarget "SocketRocket" */ = { 441 | isa = XCConfigurationList; 442 | buildConfigurations = ( 443 | 2C1BD66F8870743EFBFFF68402F1C5B9 /* Debug */, 444 | 6550121D860D51889320A1914665B8DF /* Release */, 445 | ); 446 | defaultConfigurationIsVisible = 0; 447 | defaultConfigurationName = Release; 448 | }; 449 | 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */ = { 450 | isa = XCConfigurationList; 451 | buildConfigurations = ( 452 | 3FA2475660FFC880302AD18E29B2E5FC /* Debug */, 453 | F325026B55060D3B22BEC0BAB5FA7344 /* Release */, 454 | ); 455 | defaultConfigurationIsVisible = 0; 456 | defaultConfigurationName = Release; 457 | }; 458 | 582C5F49AD5A3AC1284718A0E58FC005 /* Build configuration list for PBXNativeTarget "Pods-ReadWebsocket" */ = { 459 | isa = XCConfigurationList; 460 | buildConfigurations = ( 461 | 157BE41AF33D4C30DF6DD855F2D32AB5 /* Debug */, 462 | 9F85B643A89B327B706CC9820D2EF544 /* Release */, 463 | ); 464 | defaultConfigurationIsVisible = 0; 465 | defaultConfigurationName = Release; 466 | }; 467 | /* End XCConfigurationList section */ 468 | }; 469 | rootObject = D41D8CD98F00B204E9800998ECF8427E /* Project object */; 470 | } 471 | -------------------------------------------------------------------------------- /ReadWebsocket/Pods/Pods.xcodeproj/xcuserdata/tyh.xcuserdatad/xcschemes/Pods-ReadWebsocket.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 | -------------------------------------------------------------------------------- /ReadWebsocket/Pods/Pods.xcodeproj/xcuserdata/tyh.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 | -------------------------------------------------------------------------------- /ReadWebsocket/Pods/Pods.xcodeproj/xcuserdata/tyh.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Pods-ReadWebsocket.xcscheme 8 | 9 | isShown 10 | 11 | 12 | SocketRocket.xcscheme 13 | 14 | isShown 15 | 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 8A792D8D7D7ECCA985176C3CF62B9F27 21 | 22 | primary 23 | 24 | 25 | B711249C42550DF0DAB450627B08AD42 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /ReadWebsocket/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 | -------------------------------------------------------------------------------- /ReadWebsocket/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. -------------------------------------------------------------------------------- /ReadWebsocket/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 | -------------------------------------------------------------------------------- /ReadWebsocket/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 | //6.0以上的宏,ARC GCD 38 | #if OS_OBJECT_USE_OBJC_RETAIN_RELEASE 39 | #define sr_dispatch_retain(x) 40 | #define sr_dispatch_release(x) 41 | #define maybe_bridge(x) ((__bridge void *) x) 42 | #else 43 | #define sr_dispatch_retain(x) dispatch_retain(x) 44 | #define sr_dispatch_release(x) dispatch_release(x) 45 | #define maybe_bridge(x) (x) 46 | #endif 47 | 48 | #if !__has_feature(objc_arc) 49 | #error SocketRocket must be compiled with ARC enabled 50 | #endif 51 | 52 | 53 | typedef enum { 54 | SROpCodeTextFrame = 0x1, 55 | SROpCodeBinaryFrame = 0x2, 56 | // 3-7 reserved. 57 | SROpCodeConnectionClose = 0x8, 58 | SROpCodePing = 0x9, 59 | SROpCodePong = 0xA, 60 | // B-F reserved. 61 | } SROpCode; 62 | 63 | typedef struct { 64 | BOOL fin; 65 | // BOOL rsv1; 66 | // BOOL rsv2; 67 | // BOOL rsv3; 68 | uint8_t opcode; 69 | BOOL masked; 70 | uint64_t payload_length; 71 | } frame_header; 72 | 73 | //RFC规定的4122 74 | //https://tools.ietf.org/html/rfc4122 75 | //连接后的结果使用 SHA-1(160数位)FIPS.180-3 进行一个哈希操作,对哈希操作的结果,采用 base64 进行编码,然后作为服务端响应握手的一部分返回给浏览器。 76 | static NSString *const SRWebSocketAppendToSecKeyString = @"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 77 | 78 | static inline int32_t validate_dispatch_data_partial_string(NSData *data); 79 | static inline void SRFastLog(NSString *format, ...); 80 | 81 | @interface NSData (SRWebSocket) 82 | 83 | - (NSString *)stringBySHA1ThenBase64Encoding; 84 | 85 | @end 86 | 87 | 88 | @interface NSString (SRWebSocket) 89 | 90 | - (NSString *)stringBySHA1ThenBase64Encoding; 91 | 92 | @end 93 | 94 | 95 | @interface NSURL (SRWebSocket) 96 | 97 | // The origin isn't really applicable for a native application. 98 | // So instead, just map ws -> http and wss -> https. 99 | - (NSString *)SR_origin; 100 | 101 | @end 102 | 103 | 104 | //带runloop的 thread 105 | @interface _SRRunLoopThread : NSThread 106 | 107 | @property (nonatomic, readonly) NSRunLoop *runLoop; 108 | 109 | @end 110 | 111 | 112 | static NSString *newSHA1String(const char *bytes, size_t length) { 113 | uint8_t md[CC_SHA1_DIGEST_LENGTH]; 114 | 115 | assert(length >= 0); 116 | assert(length <= UINT32_MAX); 117 | CC_SHA1(bytes, (CC_LONG)length, md); 118 | 119 | NSData *data = [NSData dataWithBytes:md length:CC_SHA1_DIGEST_LENGTH]; 120 | 121 | if ([data respondsToSelector:@selector(base64EncodedStringWithOptions:)]) { 122 | return [data base64EncodedStringWithOptions:0]; 123 | } 124 | 125 | #pragma clang diagnostic push 126 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 127 | return [data base64Encoding]; 128 | #pragma clang diagnostic pop 129 | } 130 | 131 | @implementation NSData (SRWebSocket) 132 | 133 | - (NSString *)stringBySHA1ThenBase64Encoding; 134 | { 135 | return newSHA1String(self.bytes, self.length); 136 | } 137 | 138 | @end 139 | 140 | 141 | @implementation NSString (SRWebSocket) 142 | 143 | - (NSString *)stringBySHA1ThenBase64Encoding; 144 | { 145 | return newSHA1String(self.UTF8String, self.length); 146 | } 147 | 148 | @end 149 | 150 | NSString *const SRWebSocketErrorDomain = @"SRWebSocketErrorDomain"; 151 | NSString *const SRHTTPResponseErrorKey = @"HTTPResponseStatusCode"; 152 | 153 | // Returns number of bytes consumed. Returning 0 means you didn't match. 154 | // Sends bytes to callback handler; 155 | //标识消费的数据,意思是读取的数据? 156 | typedef size_t (^stream_scanner)(NSData *collected_data); 157 | 158 | typedef void (^data_callback)(SRWebSocket *webSocket, NSData *data); 159 | 160 | //消费者对象 161 | @interface SRIOConsumer : NSObject 162 | { 163 | stream_scanner _scanner; 164 | data_callback _handler; 165 | size_t _bytesNeeded; 166 | BOOL _readToCurrentFrame; 167 | BOOL _unmaskBytes; 168 | } 169 | @property (nonatomic, copy, readonly) stream_scanner consumer; 170 | @property (nonatomic, copy, readonly) data_callback handler; 171 | @property (nonatomic, assign) size_t bytesNeeded; 172 | @property (nonatomic, assign, readonly) BOOL readToCurrentFrame; 173 | @property (nonatomic, assign, readonly) BOOL unmaskBytes; 174 | 175 | @end 176 | 177 | // This class is not thread-safe, and is expected to always be run on the same queue. 178 | @interface SRIOConsumerPool : NSObject 179 | 180 | - (id)initWithBufferCapacity:(NSUInteger)poolSize; 181 | 182 | - (SRIOConsumer *)consumerWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; 183 | - (void)returnConsumer:(SRIOConsumer *)consumer; 184 | 185 | @end 186 | 187 | @interface SRWebSocket () 188 | 189 | @property (nonatomic) SRReadyState readyState; 190 | 191 | @property (nonatomic) NSOperationQueue *delegateOperationQueue; 192 | @property (nonatomic) dispatch_queue_t delegateDispatchQueue; 193 | 194 | // Specifies whether SSL trust chain should NOT be evaluated. 195 | // By default this flag is set to NO, meaning only secure SSL connections are allowed. 196 | // For DEBUG builds this flag is ignored, and SSL connections are allowed regardless 197 | // of the certificate trust configuration 198 | @property (nonatomic, readwrite) BOOL allowsUntrustedSSLCertificates; 199 | 200 | @end 201 | 202 | 203 | @implementation SRWebSocket { 204 | NSInteger _webSocketVersion; 205 | 206 | NSOperationQueue *_delegateOperationQueue; 207 | dispatch_queue_t _delegateDispatchQueue; 208 | 209 | dispatch_queue_t _workQueue; 210 | NSMutableArray *_consumers; 211 | 212 | NSInputStream *_inputStream; 213 | NSOutputStream *_outputStream; 214 | 215 | NSMutableData *_readBuffer; 216 | NSUInteger _readBufferOffset; 217 | 218 | NSMutableData *_outputBuffer; 219 | NSUInteger _outputBufferOffset; 220 | 221 | uint8_t _currentFrameOpcode; 222 | size_t _currentFrameCount; 223 | //读取的帧数 224 | size_t _readOpCount; 225 | uint32_t _currentStringScanPosition; 226 | NSMutableData *_currentFrameData; 227 | 228 | NSString *_closeReason; 229 | //对称密钥 230 | NSString *_secKey; 231 | NSString *_basicAuthorizationString; 232 | 233 | BOOL _pinnedCertFound; 234 | 235 | uint8_t _currentReadMaskKey[4]; 236 | size_t _currentReadMaskOffset; 237 | 238 | BOOL _consumerStopped; 239 | 240 | BOOL _closeWhenFinishedWriting; 241 | BOOL _failed; 242 | 243 | BOOL _secure; 244 | NSURLRequest *_urlRequest; 245 | 246 | BOOL _sentClose; 247 | BOOL _didFail; 248 | BOOL _cleanupScheduled; 249 | int _closeCode; 250 | 251 | BOOL _isPumping; 252 | 253 | NSMutableSet *_scheduledRunloops; 254 | 255 | // We use this to retain ourselves. 256 | __strong SRWebSocket *_selfRetain; 257 | 258 | NSArray *_requestedProtocols; 259 | SRIOConsumerPool *_consumerPool; 260 | } 261 | 262 | @synthesize delegate = _delegate; 263 | @synthesize url = _url; 264 | @synthesize readyState = _readyState; 265 | @synthesize protocol = _protocol; 266 | 267 | static __strong NSData *CRLFCRLF; 268 | 269 | + (void)initialize; 270 | { 271 | CRLFCRLF = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4]; 272 | } 273 | 274 | //最终的初始化方法 275 | - (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates; 276 | { 277 | 278 | self = [super init]; 279 | if (self) { 280 | assert(request.URL); 281 | _url = request.URL; 282 | _urlRequest = request; 283 | //是否允许非信任的证书 284 | _allowsUntrustedSSLCertificates = allowsUntrustedSSLCertificates; 285 | //拿到协议的数组 286 | _requestedProtocols = [protocols copy]; 287 | 288 | [self _SR_commonInit]; 289 | } 290 | 291 | return self; 292 | } 293 | 294 | - (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols; 295 | { 296 | return [self initWithURLRequest:request protocols:protocols allowsUntrustedSSLCertificates:NO]; 297 | } 298 | 299 | - (id)initWithURLRequest:(NSURLRequest *)request; 300 | { 301 | return [self initWithURLRequest:request protocols:nil]; 302 | } 303 | 304 | - (id)initWithURL:(NSURL *)url; 305 | { 306 | return [self initWithURL:url protocols:nil]; 307 | } 308 | 309 | - (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols; 310 | { 311 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; 312 | return [self initWithURLRequest:request protocols:protocols]; 313 | } 314 | 315 | - (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates; 316 | { 317 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; 318 | return [self initWithURLRequest:request protocols:protocols allowsUntrustedSSLCertificates:allowsUntrustedSSLCertificates]; 319 | } 320 | 321 | //初始化 322 | - (void)_SR_commonInit; 323 | { 324 | //得到url schem小写 325 | NSString *scheme = _url.scheme.lowercaseString; 326 | 327 | //如果不是这几种,则断言错误 328 | assert([scheme isEqualToString:@"ws"] || [scheme isEqualToString:@"http"] || [scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]); 329 | 330 | //ssl 331 | if ([scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]) { 332 | _secure = YES; 333 | } 334 | //标识状态为正在连接 335 | _readyState = SR_CONNECTING; 336 | 337 | //用户已停止? 338 | _consumerStopped = YES; 339 | //标识版本 340 | _webSocketVersion = 13; 341 | 342 | //初始化工作的队列,串行 343 | _workQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); 344 | 345 | // Going to set a specific on the queue so we can validate we're on the work queue 346 | //给队列设置一个标识,标识为指向自己的,上下文对象为这个队列 347 | dispatch_queue_set_specific(_workQueue, (__bridge void *)self, maybe_bridge(_workQueue), NULL); 348 | 349 | //设置代理queue为主队列 350 | _delegateDispatchQueue = dispatch_get_main_queue(); 351 | 352 | //retain主队列? 353 | sr_dispatch_retain(_delegateDispatchQueue); 354 | 355 | //读Buffer 356 | _readBuffer = [[NSMutableData alloc] init]; 357 | //输出Buffer 358 | _outputBuffer = [[NSMutableData alloc] init]; 359 | 360 | //wtf? 361 | _currentFrameData = [[NSMutableData alloc] init]; 362 | 363 | //消费者? 364 | _consumers = [[NSMutableArray alloc] init]; 365 | 366 | _consumerPool = [[SRIOConsumerPool alloc] init]; 367 | //注册的runloop 368 | _scheduledRunloops = [[NSMutableSet alloc] init]; 369 | //初始化流 370 | [self _initializeStreams]; 371 | 372 | // default handlers 373 | } 374 | 375 | //断言当前是_workQueue 376 | - (void)assertOnWorkQueue; 377 | { 378 | //因为设置了上下文对象,所以这里如果是在当前队列调用,则返回的是_workQueue 379 | //dispatch_get_specific((__bridge void *)self) 拿到当前queue的指针 判断和后者是不是相同 380 | assert(dispatch_get_specific((__bridge void *)self) == maybe_bridge(_workQueue)); 381 | } 382 | 383 | - (void)dealloc 384 | { 385 | _inputStream.delegate = nil; 386 | _outputStream.delegate = nil; 387 | 388 | [_inputStream close]; 389 | [_outputStream close]; 390 | 391 | if (_workQueue) { 392 | sr_dispatch_release(_workQueue); 393 | _workQueue = NULL; 394 | } 395 | 396 | if (_receivedHTTPHeaders) { 397 | CFRelease(_receivedHTTPHeaders); 398 | _receivedHTTPHeaders = NULL; 399 | } 400 | 401 | if (_delegateDispatchQueue) { 402 | sr_dispatch_release(_delegateDispatchQueue); 403 | _delegateDispatchQueue = NULL; 404 | } 405 | } 406 | 407 | #ifndef NDEBUG 408 | 409 | - (void)setReadyState:(SRReadyState)aReadyState; 410 | { 411 | assert(aReadyState > _readyState); 412 | _readyState = aReadyState; 413 | } 414 | 415 | #endif 416 | //开始连接 417 | - (void)open; 418 | { 419 | assert(_url); 420 | //如果状态是正在连接,直接断言出错 421 | NSAssert(_readyState == SR_CONNECTING, @"Cannot call -(void)open on SRWebSocket more than once"); 422 | 423 | //自己持有自己 424 | _selfRetain = self; 425 | //判断超时时长 426 | if (_urlRequest.timeoutInterval > 0) 427 | { 428 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, _urlRequest.timeoutInterval * NSEC_PER_SEC); 429 | //在超时时间执行 430 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 431 | //如果还在连接,报错 432 | if (self.readyState == SR_CONNECTING) 433 | [self _failWithError:[NSError errorWithDomain:@"com.squareup.SocketRocket" code:504 userInfo:@{NSLocalizedDescriptionKey: @"Timeout Connecting to Server"}]]; 434 | }); 435 | } 436 | //开始建立连接 437 | [self openConnection]; 438 | } 439 | 440 | // Calls block on delegate queue 441 | - (void)_performDelegateBlock:(dispatch_block_t)block; 442 | { 443 | if (_delegateOperationQueue) { 444 | [_delegateOperationQueue addOperationWithBlock:block]; 445 | } else { 446 | assert(_delegateDispatchQueue); 447 | dispatch_async(_delegateDispatchQueue, block); 448 | } 449 | } 450 | 451 | - (void)setDelegateDispatchQueue:(dispatch_queue_t)queue; 452 | { 453 | if (queue) { 454 | sr_dispatch_retain(queue); 455 | } 456 | 457 | if (_delegateDispatchQueue) { 458 | sr_dispatch_release(_delegateDispatchQueue); 459 | } 460 | 461 | _delegateDispatchQueue = queue; 462 | } 463 | 464 | //检查握手信息 465 | - (BOOL)_checkHandshake:(CFHTTPMessageRef)httpMessage; 466 | { 467 | //是否是允许的header 468 | NSString *acceptHeader = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(httpMessage, CFSTR("Sec-WebSocket-Accept"))); 469 | 470 | //为空则被服务器拒绝 471 | if (acceptHeader == nil) { 472 | return NO; 473 | } 474 | 475 | //得到 476 | NSString *concattedString = [_secKey stringByAppendingString:SRWebSocketAppendToSecKeyString]; 477 | //期待accept的字符串 478 | NSString *expectedAccept = [concattedString stringBySHA1ThenBase64Encoding]; 479 | 480 | //判断是否相同,相同就握手信息对了 481 | return [acceptHeader isEqualToString:expectedAccept]; 482 | } 483 | 484 | //读完数据处理 485 | - (void)_HTTPHeadersDidFinish; 486 | { 487 | //得到resonse code 488 | NSInteger responseCode = CFHTTPMessageGetResponseStatusCode(_receivedHTTPHeaders); 489 | 490 | //失败code 491 | if (responseCode >= 400) { 492 | SRFastLog(@"Request failed with response code %d", responseCode); 493 | [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2132 userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"received bad response code from server %ld", (long)responseCode], SRHTTPResponseErrorKey:@(responseCode)}]]; 494 | return; 495 | } 496 | 497 | //检查握手信息 498 | if(![self _checkHandshake:_receivedHTTPHeaders]) { 499 | [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2133 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Invalid Sec-WebSocket-Accept response"] forKey:NSLocalizedDescriptionKey]]]; 500 | return; 501 | } 502 | 503 | //得到协议 504 | NSString *negotiatedProtocol = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(_receivedHTTPHeaders, CFSTR("Sec-WebSocket-Protocol"))); 505 | if (negotiatedProtocol) { 506 | // Make sure we requested the protocol 507 | //如果请求的协议里没找到,服务端要求的协议,则失败 508 | if ([_requestedProtocols indexOfObject:negotiatedProtocol] == NSNotFound) { 509 | [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2133 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Server specified Sec-WebSocket-Protocol that wasn't requested"] forKey:NSLocalizedDescriptionKey]]]; 510 | return; 511 | } 512 | 513 | _protocol = negotiatedProtocol; 514 | } 515 | //修改状态,open 516 | self.readyState = SR_OPEN; 517 | //开始读取新的消息帧 518 | if (!_didFail) { 519 | [self _readFrameNew]; 520 | } 521 | //调用已经连接的代理 522 | [self _performDelegateBlock:^{ 523 | if ([self.delegate respondsToSelector:@selector(webSocketDidOpen:)]) { 524 | [self.delegate webSocketDidOpen:self]; 525 | }; 526 | }]; 527 | } 528 | 529 | 530 | //读取http头部 531 | - (void)_readHTTPHeader; 532 | { 533 | if (_receivedHTTPHeaders == NULL) { 534 | //序列化的http消息 535 | _receivedHTTPHeaders = CFHTTPMessageCreateEmpty(NULL, NO); 536 | } 537 | 538 | //不停的add consumer去读数据 539 | [self _readUntilHeaderCompleteWithCallback:^(SRWebSocket *self, NSData *data) { 540 | 541 | //拼接数据,拼到头部 542 | CFHTTPMessageAppendBytes(_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length); 543 | 544 | //判断是否接受完 545 | if (CFHTTPMessageIsHeaderComplete(_receivedHTTPHeaders)) { 546 | SRFastLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_receivedHTTPHeaders))); 547 | [self _HTTPHeadersDidFinish]; 548 | } else { 549 | //没读完递归调 550 | [self _readHTTPHeader]; 551 | } 552 | }]; 553 | } 554 | 555 | //流打开成功后的操作,开始发送http请求建立连接 556 | - (void)didConnect; 557 | { 558 | SRFastLog(@"Connected"); 559 | //创建一个http request url 560 | CFHTTPMessageRef request = CFHTTPMessageCreateRequest(NULL, CFSTR("GET"), (__bridge CFURLRef)_url, kCFHTTPVersion1_1); 561 | 562 | // Set host first so it defaults 563 | //设置head, host: url+port 564 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Host"), (__bridge CFStringRef)(_url.port ? [NSString stringWithFormat:@"%@:%@", _url.host, _url.port] : _url.host)); 565 | //密钥数据(生成对称密钥) 566 | NSMutableData *keyBytes = [[NSMutableData alloc] initWithLength:16]; 567 | //生成随机密钥 568 | SecRandomCopyBytes(kSecRandomDefault, keyBytes.length, keyBytes.mutableBytes); 569 | 570 | //根据版本用base64转码 571 | if ([keyBytes respondsToSelector:@selector(base64EncodedStringWithOptions:)]) { 572 | _secKey = [keyBytes base64EncodedStringWithOptions:0]; 573 | } else { 574 | #pragma clang diagnostic push 575 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 576 | _secKey = [keyBytes base64Encoding]; 577 | #pragma clang diagnostic pop 578 | } 579 | 580 | //断言编码后长度为24 581 | assert([_secKey length] == 24); 582 | 583 | // Apply cookies if any have been provided 584 | //提供cookies 585 | NSDictionary * cookies = [NSHTTPCookie requestHeaderFieldsWithCookies:[self requestCookies]]; 586 | for (NSString * cookieKey in cookies) { 587 | //拿到cookie值 588 | NSString * cookieValue = [cookies objectForKey:cookieKey]; 589 | if ([cookieKey length] && [cookieValue length]) { 590 | //设置到request的 head里 591 | CFHTTPMessageSetHeaderFieldValue(request, (__bridge CFStringRef)cookieKey, (__bridge CFStringRef)cookieValue); 592 | } 593 | } 594 | 595 | // set header for http basic auth 596 | //设置http的基础auth,用户名密码认证 597 | if (_url.user.length && _url.password.length) { 598 | NSData *userAndPassword = [[NSString stringWithFormat:@"%@:%@", _url.user, _url.password] dataUsingEncoding:NSUTF8StringEncoding]; 599 | NSString *userAndPasswordBase64Encoded; 600 | if ([keyBytes respondsToSelector:@selector(base64EncodedStringWithOptions:)]) { 601 | userAndPasswordBase64Encoded = [userAndPassword base64EncodedStringWithOptions:0]; 602 | } else { 603 | #pragma clang diagnostic push 604 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 605 | userAndPasswordBase64Encoded = [userAndPassword base64Encoding]; 606 | #pragma clang diagnostic pop 607 | } 608 | //编码后用户名密码 609 | _basicAuthorizationString = [NSString stringWithFormat:@"Basic %@", userAndPasswordBase64Encoded]; 610 | //设置head Authorization 611 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Authorization"), (__bridge CFStringRef)_basicAuthorizationString); 612 | } 613 | //web socket规范head 614 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Upgrade"), CFSTR("websocket")); 615 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Connection"), CFSTR("Upgrade")); 616 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Key"), (__bridge CFStringRef)_secKey); 617 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Version"), (__bridge CFStringRef)[NSString stringWithFormat:@"%ld", (long)_webSocketVersion]); 618 | 619 | //设置request的原始 Url 620 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Origin"), (__bridge CFStringRef)_url.SR_origin); 621 | 622 | //用户初始化的协议数组,可以约束websocket的一些行为 623 | if (_requestedProtocols) { 624 | CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Protocol"), (__bridge CFStringRef)[_requestedProtocols componentsJoinedByString:@", "]); 625 | } 626 | 627 | //吧 _urlRequest中原有的head 设置到request中去 628 | [_urlRequest.allHTTPHeaderFields enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 629 | 630 | CFHTTPMessageSetHeaderFieldValue(request, (__bridge CFStringRef)key, (__bridge CFStringRef)obj); 631 | }]; 632 | 633 | //返回一个序列化 , CFBridgingRelease和 __bridge transfer一个意思, CFHTTPMessageCopySerializedMessage copy一份新的并且序列化,返回CFDataRef 634 | NSData *message = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(request)); 635 | 636 | //释放request 637 | CFRelease(request); 638 | 639 | //把这个request当成data去写 640 | [self _writeData:message]; 641 | //读取http的头部 642 | [self _readHTTPHeader]; 643 | } 644 | 645 | //初始化流 646 | - (void)_initializeStreams; 647 | { 648 | //断言 port值小于UINT32_MAX 649 | assert(_url.port.unsignedIntValue <= UINT32_MAX); 650 | //拿到端口 651 | uint32_t port = _url.port.unsignedIntValue; 652 | //如果端口号为0,给个默认值,http 80 https 443; 653 | if (port == 0) { 654 | if (!_secure) { 655 | port = 80; 656 | } else { 657 | port = 443; 658 | } 659 | } 660 | NSString *host = _url.host; 661 | 662 | CFReadStreamRef readStream = NULL; 663 | CFWriteStreamRef writeStream = NULL; 664 | //用host创建读写stream,Host和port就绑定在一起了 665 | CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &readStream, &writeStream); 666 | 667 | //绑定生命周期给ARC _outputStream = __bridge transfer 668 | _outputStream = CFBridgingRelease(writeStream); 669 | _inputStream = CFBridgingRelease(readStream); 670 | 671 | //代理设为自己 672 | _inputStream.delegate = self; 673 | _outputStream.delegate = self; 674 | } 675 | 676 | - (void)_updateSecureStreamOptions; 677 | { 678 | //如果是安全 679 | if (_secure) { 680 | //创建ssl配置 681 | NSMutableDictionary *SSLOptions = [[NSMutableDictionary alloc] init]; 682 | 683 | //kCFStreamSocketSecurityLevelNegotiatedSSL 可以回滚的https策略? 684 | [_outputStream setProperty:(__bridge id)kCFStreamSocketSecurityLevelNegotiatedSSL forKey:(__bridge id)kCFStreamPropertySocketSecurityLevel]; 685 | 686 | // If we're using pinned certs, don't validate the certificate chain 687 | // 如果用了自签的证书? 不验证证书链 688 | if ([_urlRequest SR_SSLPinnedCertificates].count) { 689 | //只要有一个节点匹配即可 690 | [SSLOptions setValue:@NO forKey:(__bridge id)kCFStreamSSLValidatesCertificateChain]; 691 | } 692 | #if DEBUG 693 | //是否支持不信任的证书 694 | self.allowsUntrustedSSLCertificates = YES; 695 | #endif 696 | 697 | if (self.allowsUntrustedSSLCertificates) { 698 | //不验证证书链 699 | [SSLOptions setValue:@NO forKey:(__bridge id)kCFStreamSSLValidatesCertificateChain]; 700 | SRFastLog(@"Allowing connection to any root cert"); 701 | } 702 | //设置给_outputStream 703 | [_outputStream setProperty:SSLOptions 704 | forKey:(__bridge id)kCFStreamPropertySSLSettings]; 705 | } 706 | //流代理 707 | _inputStream.delegate = self; 708 | _outputStream.delegate = self; 709 | //初始化网络类型 710 | [self setupNetworkServiceType:_urlRequest.networkServiceType]; 711 | } 712 | 713 | - (void)setupNetworkServiceType:(NSURLRequestNetworkServiceType)requestNetworkServiceType 714 | { 715 | NSString *networkServiceType; 716 | switch (requestNetworkServiceType) { 717 | //默认的不做任何处理 718 | case NSURLNetworkServiceTypeDefault: 719 | break; 720 | //VOIP,表明这个socket用于voip 721 | case NSURLNetworkServiceTypeVoIP: { 722 | networkServiceType = NSStreamNetworkServiceTypeVoIP; 723 | #if TARGET_OS_IPHONE && __IPHONE_9_0 724 | 725 | //判断版本,8.3后用PushKit来控制,废弃了NSStreamNetworkServiceTypeVoIP 726 | if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_8_3) { 727 | static dispatch_once_t predicate; 728 | dispatch_once(&predicate, ^{ 729 | NSLog(@"SocketRocket: %@ - this service type is deprecated in favor of using PushKit for VoIP control", networkServiceType); 730 | }); 731 | } 732 | #endif 733 | break; 734 | } 735 | //视频传输 736 | case NSURLNetworkServiceTypeVideo: 737 | networkServiceType = NSStreamNetworkServiceTypeVideo; 738 | break; 739 | //后台传输 740 | case NSURLNetworkServiceTypeBackground: 741 | networkServiceType = NSStreamNetworkServiceTypeBackground; 742 | break; 743 | //声音数据 744 | case NSURLNetworkServiceTypeVoice: 745 | networkServiceType = NSStreamNetworkServiceTypeVoice; 746 | break; 747 | } 748 | 749 | if (networkServiceType != nil) { 750 | //设置给输入输出流 751 | [_inputStream setProperty:networkServiceType forKey:NSStreamNetworkServiceType]; 752 | [_outputStream setProperty:networkServiceType forKey:NSStreamNetworkServiceType]; 753 | } 754 | } 755 | //开始连接 756 | - (void)openConnection; 757 | { 758 | //更新安全、流配置 759 | [self _updateSecureStreamOptions]; 760 | 761 | //判断有没有runloop 762 | if (!_scheduledRunloops.count) { 763 | //SR_networkRunLoop会创建一个带runloop的常驻线程,模式为NSDefaultRunLoopMode。 764 | [self scheduleInRunLoop:[NSRunLoop SR_networkRunLoop] forMode:NSDefaultRunLoopMode]; 765 | } 766 | 767 | //开启输入输出流 768 | [_outputStream open]; 769 | [_inputStream open]; 770 | } 771 | 772 | - (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; 773 | { 774 | [_outputStream scheduleInRunLoop:aRunLoop forMode:mode]; 775 | [_inputStream scheduleInRunLoop:aRunLoop forMode:mode]; 776 | 777 | //添加到集合里,数组 778 | [_scheduledRunloops addObject:@[aRunLoop, mode]]; 779 | } 780 | 781 | - (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; 782 | { 783 | [_outputStream removeFromRunLoop:aRunLoop forMode:mode]; 784 | [_inputStream removeFromRunLoop:aRunLoop forMode:mode]; 785 | 786 | //移除 787 | [_scheduledRunloops removeObject:@[aRunLoop, mode]]; 788 | } 789 | 790 | - (void)close; 791 | { 792 | [self closeWithCode:SRStatusCodeNormal reason:nil]; 793 | } 794 | //用code来关闭 795 | - (void)closeWithCode:(NSInteger)code reason:(NSString *)reason; 796 | { 797 | assert(code); 798 | dispatch_async(_workQueue, ^{ 799 | if (self.readyState == SR_CLOSING || self.readyState == SR_CLOSED) { 800 | return; 801 | } 802 | 803 | BOOL wasConnecting = self.readyState == SR_CONNECTING; 804 | 805 | self.readyState = SR_CLOSING; 806 | 807 | SRFastLog(@"Closing with code %d reason %@", code, reason); 808 | 809 | if (wasConnecting) { 810 | [self closeConnection]; 811 | return; 812 | } 813 | 814 | size_t maxMsgSize = [reason maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 815 | NSMutableData *mutablePayload = [[NSMutableData alloc] initWithLength:sizeof(uint16_t) + maxMsgSize]; 816 | NSData *payload = mutablePayload; 817 | 818 | ((uint16_t *)mutablePayload.mutableBytes)[0] = EndianU16_BtoN(code); 819 | 820 | if (reason) { 821 | NSRange remainingRange = {0}; 822 | 823 | NSUInteger usedLength = 0; 824 | 825 | 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]; 826 | #pragma unused (success) 827 | 828 | assert(success); 829 | assert(remainingRange.length == 0); 830 | 831 | if (usedLength != maxMsgSize) { 832 | payload = [payload subdataWithRange:NSMakeRange(0, usedLength + sizeof(uint16_t))]; 833 | } 834 | } 835 | 836 | 837 | [self _sendFrameWithOpcode:SROpCodeConnectionClose data:payload]; 838 | }); 839 | } 840 | 841 | //因为协议的错误,关闭 842 | - (void)_closeWithProtocolError:(NSString *)message; 843 | { 844 | // Need to shunt this on the _callbackQueue first to see if they received any messages 845 | [self _performDelegateBlock:^{ 846 | [self closeWithCode:SRStatusCodeProtocolError reason:message]; 847 | dispatch_async(_workQueue, ^{ 848 | [self closeConnection]; 849 | }); 850 | }]; 851 | } 852 | 853 | //报错的方法 854 | - (void)_failWithError:(NSError *)error; 855 | { 856 | dispatch_async(_workQueue, ^{ 857 | if (self.readyState != SR_CLOSED) { 858 | _failed = YES; 859 | [self _performDelegateBlock:^{ 860 | if ([self.delegate respondsToSelector:@selector(webSocket:didFailWithError:)]) { 861 | [self.delegate webSocket:self didFailWithError:error]; 862 | } 863 | }]; 864 | 865 | self.readyState = SR_CLOSED; 866 | 867 | SRFastLog(@"Failing with error %@", error.localizedDescription); 868 | 869 | [self closeConnection]; 870 | [self _scheduleCleanup]; 871 | } 872 | }); 873 | } 874 | 875 | //写数据 876 | - (void)_writeData:(NSData *)data; 877 | { 878 | //断言当前queue 879 | [self assertOnWorkQueue]; 880 | //如果标记为写完成关闭,则直接返回 881 | if (_closeWhenFinishedWriting) { 882 | return; 883 | } 884 | //输出buffer拼接数据 885 | [_outputBuffer appendData:data]; 886 | //开始写 887 | [self _pumpWriting]; 888 | } 889 | 890 | //发送数据 891 | - (void)send:(id)data; 892 | { 893 | NSAssert(self.readyState != SR_CONNECTING, @"Invalid State: Cannot call send: until connection is open"); 894 | // TODO: maybe not copy this for performance 895 | data = [data copy]; 896 | dispatch_async(_workQueue, ^{ 897 | //根据类型给帧类型 SROpCodeTextFrame文本,SROpCodeBinaryFrame二进制类型 898 | if ([data isKindOfClass:[NSString class]]) { 899 | [self _sendFrameWithOpcode:SROpCodeTextFrame data:[(NSString *)data dataUsingEncoding:NSUTF8StringEncoding]]; 900 | } else if ([data isKindOfClass:[NSData class]]) { 901 | [self _sendFrameWithOpcode:SROpCodeBinaryFrame data:data]; 902 | } else if (data == nil) { 903 | [self _sendFrameWithOpcode:SROpCodeTextFrame data:data]; 904 | } else { 905 | assert(NO); 906 | } 907 | }); 908 | } 909 | 910 | - (void)sendPing:(NSData *)data; 911 | { 912 | NSAssert(self.readyState == SR_OPEN, @"Invalid State: Cannot call send: until connection is open"); 913 | // TODO: maybe not copy this for performance 914 | data = [data copy] ?: [NSData data]; // It's okay for a ping to be empty 915 | dispatch_async(_workQueue, ^{ 916 | [self _sendFrameWithOpcode:SROpCodePing data:data]; 917 | }); 918 | } 919 | 920 | - (void)handlePing:(NSData *)pingData; 921 | { 922 | // Need to pingpong this off _callbackQueue first to make sure messages happen in order 923 | [self _performDelegateBlock:^{ 924 | dispatch_async(_workQueue, ^{ 925 | [self _sendFrameWithOpcode:SROpCodePong data:pingData]; 926 | }); 927 | }]; 928 | } 929 | 930 | - (void)handlePong:(NSData *)pongData; 931 | { 932 | SRFastLog(@"Received pong"); 933 | [self _performDelegateBlock:^{ 934 | if ([self.delegate respondsToSelector:@selector(webSocket:didReceivePong:)]) { 935 | [self.delegate webSocket:self didReceivePong:pongData]; 936 | } 937 | }]; 938 | } 939 | 940 | //回调收到消息代理 941 | - (void)_handleMessage:(id)message 942 | { 943 | SRFastLog(@"Received message"); 944 | [self _performDelegateBlock:^{ 945 | [self.delegate webSocket:self didReceiveMessage:message]; 946 | }]; 947 | } 948 | 949 | 950 | static inline BOOL closeCodeIsValid(int closeCode) { 951 | if (closeCode < 1000) { 952 | return NO; 953 | } 954 | 955 | if (closeCode >= 1000 && closeCode <= 1011) { 956 | if (closeCode == 1004 || 957 | closeCode == 1005 || 958 | closeCode == 1006) { 959 | return NO; 960 | } 961 | return YES; 962 | } 963 | 964 | if (closeCode >= 3000 && closeCode <= 3999) { 965 | return YES; 966 | } 967 | 968 | if (closeCode >= 4000 && closeCode <= 4999) { 969 | return YES; 970 | } 971 | 972 | return NO; 973 | } 974 | 975 | // Note from RFC: 976 | // 977 | // If there is a body, the first two 978 | // bytes of the body MUST be a 2-byte unsigned integer (in network byte 979 | // order) representing a status code with value /code/ defined in 980 | // Section 7.4. Following the 2-byte integer the body MAY contain UTF-8 981 | // encoded data with value /reason/, the interpretation of which is not 982 | // defined by this specification. 983 | 984 | - (void)handleCloseWithData:(NSData *)data; 985 | { 986 | size_t dataSize = data.length; 987 | __block uint16_t closeCode = 0; 988 | 989 | SRFastLog(@"Received close frame"); 990 | //关闭data 991 | if (dataSize == 1) { 992 | // TODO handle error 993 | [self _closeWithProtocolError:@"Payload for close must be larger than 2 bytes"]; 994 | return; 995 | } else if (dataSize >= 2) { 996 | [data getBytes:&closeCode length:sizeof(closeCode)]; 997 | _closeCode = EndianU16_BtoN(closeCode); 998 | if (!closeCodeIsValid(_closeCode)) { 999 | [self _closeWithProtocolError:[NSString stringWithFormat:@"Cannot have close code of %d", _closeCode]]; 1000 | return; 1001 | } 1002 | if (dataSize > 2) { 1003 | 1004 | _closeReason = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(2, dataSize - 2)] encoding:NSUTF8StringEncoding]; 1005 | if (!_closeReason) { 1006 | [self _closeWithProtocolError:@"Close reason MUST be valid UTF-8"]; 1007 | return; 1008 | } 1009 | } 1010 | } else { 1011 | _closeCode = SRStatusNoStatusReceived; 1012 | } 1013 | 1014 | [self assertOnWorkQueue]; 1015 | 1016 | if (self.readyState == SR_OPEN) { 1017 | [self closeWithCode:1000 reason:nil]; 1018 | } 1019 | dispatch_async(_workQueue, ^{ 1020 | [self closeConnection]; 1021 | }); 1022 | } 1023 | 1024 | - (void)closeConnection; 1025 | { 1026 | [self assertOnWorkQueue]; 1027 | SRFastLog(@"Trying to disconnect"); 1028 | _closeWhenFinishedWriting = YES; 1029 | [self _pumpWriting]; 1030 | } 1031 | 1032 | //当前帧读取完成 1033 | - (void)_handleFrameWithData:(NSData *)frameData opCode:(NSInteger)opcode; 1034 | { 1035 | // Check that the current data is valid UTF8 1036 | 1037 | BOOL isControlFrame = (opcode == SROpCodePing || opcode == SROpCodePong || opcode == SROpCodeConnectionClose); 1038 | //如果不是控制帧,就去读下一帧 1039 | if (!isControlFrame) { 1040 | [self _readFrameNew]; 1041 | } else { 1042 | //继续读当前帧 1043 | dispatch_async(_workQueue, ^{ 1044 | [self _readFrameContinue]; 1045 | }); 1046 | } 1047 | 1048 | //frameData will be copied before passing to handlers 1049 | //otherwise there can be misbehaviours when value at the pointer is changed 1050 | switch (opcode) { 1051 | //文本类型 1052 | case SROpCodeTextFrame: { 1053 | if ([self.delegate respondsToSelector:@selector(webSocketShouldConvertTextFrameToString:)] && ![self.delegate webSocketShouldConvertTextFrameToString:self]) { 1054 | //给data 1055 | [self _handleMessage:[frameData copy]]; 1056 | } else { 1057 | //给string 1058 | NSString *str = [[NSString alloc] initWithData:frameData encoding:NSUTF8StringEncoding]; 1059 | //string转出错,关闭连接 1060 | if (str == nil && frameData) { 1061 | [self closeWithCode:SRStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"]; 1062 | dispatch_async(_workQueue, ^{ 1063 | [self closeConnection]; 1064 | }); 1065 | return; 1066 | } 1067 | //回调 1068 | [self _handleMessage:str]; 1069 | } 1070 | break; 1071 | } 1072 | case SROpCodeBinaryFrame: 1073 | //回调data 1074 | [self _handleMessage:[frameData copy]]; 1075 | break; 1076 | case SROpCodeConnectionClose: 1077 | //关闭data 1078 | [self handleCloseWithData:[frameData copy]]; 1079 | break; 1080 | case SROpCodePing: 1081 | [self handlePing:[frameData copy]]; 1082 | break; 1083 | case SROpCodePong: 1084 | [self handlePong:[frameData copy]]; 1085 | break; 1086 | default: 1087 | //关闭 1088 | [self _closeWithProtocolError:[NSString stringWithFormat:@"Unknown opcode %ld", (long)opcode]]; 1089 | // TODO: Handle invalid opcode 1090 | break; 1091 | } 1092 | } 1093 | 1094 | //处理数据帧 1095 | - (void)_handleFrameHeader:(frame_header)frame_header curData:(NSData *)curData; 1096 | { 1097 | assert(frame_header.opcode != 0); 1098 | 1099 | if (self.readyState == SR_CLOSED) { 1100 | return; 1101 | } 1102 | 1103 | 1104 | BOOL isControlFrame = (frame_header.opcode == SROpCodePing || frame_header.opcode == SROpCodePong || frame_header.opcode == SROpCodeConnectionClose); 1105 | 1106 | if (isControlFrame && !frame_header.fin) { 1107 | [self _closeWithProtocolError:@"Fragmented control frames not allowed"]; 1108 | return; 1109 | } 1110 | 1111 | if (isControlFrame && frame_header.payload_length >= 126) { 1112 | [self _closeWithProtocolError:@"Control frames cannot have payloads larger than 126 bytes"]; 1113 | return; 1114 | } 1115 | //如果不是控制帧 1116 | if (!isControlFrame) { 1117 | //等于头部的opcode 1118 | _currentFrameOpcode = frame_header.opcode; 1119 | _currentFrameCount += 1; 1120 | } 1121 | //如果数据长度为0 1122 | if (frame_header.payload_length == 0) { 1123 | if (isControlFrame) { 1124 | //读完回调 1125 | [self _handleFrameWithData:curData opCode:frame_header.opcode]; 1126 | } else { 1127 | if (frame_header.fin) { 1128 | //读完回调 1129 | [self _handleFrameWithData:_currentFrameData opCode:frame_header.opcode]; 1130 | } else { 1131 | // TODO add assert that opcode is not a control; 1132 | //开始读取数据 1133 | [self _readFrameContinue]; 1134 | } 1135 | } 1136 | } else { 1137 | //断言帧长度小于 SIZE_T_MAX 1138 | assert(frame_header.payload_length <= SIZE_T_MAX); 1139 | //添加consumer ,去读payload长度 1140 | [self _addConsumerWithDataLength:(size_t)frame_header.payload_length callback:^(SRWebSocket *self, NSData *newData) { 1141 | //读完回调 1142 | if (isControlFrame) { 1143 | [self _handleFrameWithData:newData opCode:frame_header.opcode]; 1144 | } else { 1145 | //如果有fin,读完回调 1146 | if (frame_header.fin) { 1147 | [self _handleFrameWithData:self->_currentFrameData opCode:frame_header.opcode]; 1148 | } else { 1149 | // TODO add assert that opcode is not a control; 1150 | //继续读消息帧 1151 | [self _readFrameContinue]; 1152 | } 1153 | 1154 | } 1155 | } readToCurrentFrame:!isControlFrame unmaskBytes:frame_header.masked]; 1156 | } 1157 | } 1158 | 1159 | /* From RFC: 1160 | 1161 | 0 1 2 3 1162 | 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 1163 | +-+-+-+-+-------+-+-------------+-------------------------------+ 1164 | |F|R|R|R| opcode|M| Payload len | Extended payload length | 1165 | |I|S|S|S| (4) |A| (7) | (16/64) | 1166 | |N|V|V|V| |S| | (if payload len==126/127) | 1167 | | |1|2|3| |K| | | 1168 | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + 1169 | | Extended payload length continued, if payload len == 127 | 1170 | + - - - - - - - - - - - - - - - +-------------------------------+ 1171 | | |Masking-key, if MASK set to 1 | 1172 | +-------------------------------+-------------------------------+ 1173 | | Masking-key (continued) | Payload Data | 1174 | +-------------------------------- - - - - - - - - - - - - - - - + 1175 | : Payload Data continued ... : 1176 | + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 1177 | | Payload Data continued ... | 1178 | +---------------------------------------------------------------+ 1179 | */ 1180 | 1181 | /* 1182 | FIN 1bit 表示信息的最后一帧,flag,也就是标记符 1183 | RSV 1-3 1bit each 以后备用的 默认都为 0 1184 | Opcode 4bit 帧类型,稍后细说 1185 | Mask 1bit 掩码,是否加密数据,默认必须置为1 (这里很蛋疼) 1186 | Payload 7bit 数据的长度 (2^7 最大到128?) 1187 | Masking-key 1 or 4 bit 掩码 //用来加密? 1188 | Payload data (x + y) bytes 数据 // 1189 | Extension data x bytes 扩展数据 1190 | Application data y bytes 程序数据 1191 | */ 1192 | 1193 | static const uint8_t SRFinMask = 0x80; 1194 | static const uint8_t SROpCodeMask = 0x0F; 1195 | static const uint8_t SRRsvMask = 0x70; 1196 | static const uint8_t SRMaskMask = 0x80; 1197 | static const uint8_t SRPayloadLenMask = 0x7F; 1198 | 1199 | 1200 | //开始读取当前消息帧 1201 | - (void)_readFrameContinue; 1202 | { 1203 | //断言要么都为空,要么都有值 1204 | assert((_currentFrameCount == 0 && _currentFrameOpcode == 0) || (_currentFrameCount > 0 && _currentFrameOpcode > 0)); 1205 | //添加一个consumer,数据长度为2字节 frame_header 2个字节 1206 | [self _addConsumerWithDataLength:2 callback:^(SRWebSocket *self, NSData *data) { 1207 | 1208 | // 1209 | __block frame_header header = {0}; 1210 | 1211 | const uint8_t *headerBuffer = data.bytes; 1212 | assert(data.length >= 2); 1213 | 1214 | //判断第一帧 FIN 1215 | if (headerBuffer[0] & SRRsvMask) { 1216 | [self _closeWithProtocolError:@"Server used RSV bits"]; 1217 | return; 1218 | } 1219 | //得到Qpcode 1220 | uint8_t receivedOpcode = (SROpCodeMask & headerBuffer[0]); 1221 | 1222 | //判断帧类型,是否是指定的控制帧 1223 | BOOL isControlFrame = (receivedOpcode == SROpCodePing || receivedOpcode == SROpCodePong || receivedOpcode == SROpCodeConnectionClose); 1224 | 1225 | //如果不是指定帧,而且receivedOpcode不等于0,而且_currentFrameCount消息帧大于0,错误关闭 1226 | if (!isControlFrame && receivedOpcode != 0 && self->_currentFrameCount > 0) { 1227 | [self _closeWithProtocolError:@"all data frames after the initial data frame must have opcode 0"]; 1228 | return; 1229 | } 1230 | // 没消息 1231 | if (receivedOpcode == 0 && self->_currentFrameCount == 0) { 1232 | [self _closeWithProtocolError:@"cannot continue a message"]; 1233 | return; 1234 | } 1235 | 1236 | //正常读取 1237 | //得到opcode 1238 | header.opcode = receivedOpcode == 0 ? self->_currentFrameOpcode : receivedOpcode; 1239 | //得到fin 1240 | header.fin = !!(SRFinMask & headerBuffer[0]); 1241 | 1242 | //得到Mask 1243 | header.masked = !!(SRMaskMask & headerBuffer[1]); 1244 | //得到数据长度 1245 | header.payload_length = SRPayloadLenMask & headerBuffer[1]; 1246 | 1247 | headerBuffer = NULL; 1248 | 1249 | //如果是带掩码的,则报错,因为客户端是无法得知掩码的值得。 1250 | if (header.masked) { 1251 | [self _closeWithProtocolError:@"Client must receive unmasked data"]; 1252 | } 1253 | 1254 | size_t extra_bytes_needed = header.masked ? sizeof(_currentReadMaskKey) : 0; 1255 | //得到长度 1256 | if (header.payload_length == 126) { 1257 | extra_bytes_needed += sizeof(uint16_t); 1258 | } else if (header.payload_length == 127) { 1259 | extra_bytes_needed += sizeof(uint64_t); 1260 | } 1261 | 1262 | //如果多余的需要的bytes为0 1263 | if (extra_bytes_needed == 0) { 1264 | // 1265 | [self _handleFrameHeader:header curData:self->_currentFrameData]; 1266 | } else { 1267 | //读取payload 1268 | [self _addConsumerWithDataLength:extra_bytes_needed callback:^(SRWebSocket *self, NSData *data) { 1269 | 1270 | size_t mapped_size = data.length; 1271 | #pragma unused (mapped_size) 1272 | const void *mapped_buffer = data.bytes; 1273 | size_t offset = 0; 1274 | 1275 | if (header.payload_length == 126) { 1276 | assert(mapped_size >= sizeof(uint16_t)); 1277 | uint16_t newLen = EndianU16_BtoN(*(uint16_t *)(mapped_buffer)); 1278 | header.payload_length = newLen; 1279 | offset += sizeof(uint16_t); 1280 | } else if (header.payload_length == 127) { 1281 | assert(mapped_size >= sizeof(uint64_t)); 1282 | header.payload_length = EndianU64_BtoN(*(uint64_t *)(mapped_buffer)); 1283 | offset += sizeof(uint64_t); 1284 | } else { 1285 | assert(header.payload_length < 126 && header.payload_length >= 0); 1286 | } 1287 | 1288 | if (header.masked) { 1289 | assert(mapped_size >= sizeof(_currentReadMaskOffset) + offset); 1290 | memcpy(self->_currentReadMaskKey, ((uint8_t *)mapped_buffer) + offset, sizeof(self->_currentReadMaskKey)); 1291 | } 1292 | //把已读到的数据,和header传出去 1293 | [self _handleFrameHeader:header curData:self->_currentFrameData]; 1294 | } readToCurrentFrame:NO unmaskBytes:NO]; 1295 | } 1296 | } readToCurrentFrame:NO unmaskBytes:NO]; 1297 | } 1298 | 1299 | //读取新的消息帧 1300 | - (void)_readFrameNew; 1301 | { 1302 | dispatch_async(_workQueue, ^{ 1303 | //清空上一帧的 1304 | [_currentFrameData setLength:0]; 1305 | 1306 | _currentFrameOpcode = 0; 1307 | _currentFrameCount = 0; 1308 | _readOpCount = 0; 1309 | _currentStringScanPosition = 0; 1310 | //继续读取 1311 | [self _readFrameContinue]; 1312 | }); 1313 | } 1314 | 1315 | //开始写 1316 | - (void)_pumpWriting; 1317 | { 1318 | //断言queue 1319 | [self assertOnWorkQueue]; 1320 | 1321 | //得到输出的buffer长度 1322 | NSUInteger dataLength = _outputBuffer.length; 1323 | //如果有没写完的数据而且输出流还有空间 1324 | if (dataLength - _outputBufferOffset > 0 && _outputStream.hasSpaceAvailable) { 1325 | 1326 | //写入,并获取长度 1327 | //写入进去,就会直接发送给对方了!这一步send 1328 | NSInteger bytesWritten = [_outputStream write:_outputBuffer.bytes + _outputBufferOffset maxLength:dataLength - _outputBufferOffset]; 1329 | //写入错误 1330 | if (bytesWritten == -1) { 1331 | [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2145 userInfo:[NSDictionary dictionaryWithObject:@"Error writing to stream" forKey:NSLocalizedDescriptionKey]]]; 1332 | return; 1333 | } 1334 | //加上写入长度 1335 | _outputBufferOffset += bytesWritten; 1336 | 1337 | //释放掉一部分已经写完的内存 1338 | if (_outputBufferOffset > 4096 && _outputBufferOffset > (_outputBuffer.length >> 1)) { 1339 | _outputBuffer = [[NSMutableData alloc] initWithBytes:(char *)_outputBuffer.bytes + _outputBufferOffset length:_outputBuffer.length - _outputBufferOffset]; 1340 | _outputBufferOffset = 0; 1341 | } 1342 | } 1343 | //如果关闭当完成写,输出的bufeer - 偏移量 = 0, 1344 | if (_closeWhenFinishedWriting && 1345 | _outputBuffer.length - _outputBufferOffset == 0 && 1346 | (_inputStream.streamStatus != NSStreamStatusNotOpen && 1347 | _inputStream.streamStatus != NSStreamStatusClosed) && 1348 | !_sentClose) { 1349 | 1350 | ///发送关闭 1351 | _sentClose = YES; 1352 | 1353 | //关闭输入输出流 1354 | @synchronized(self) { 1355 | [_outputStream close]; 1356 | [_inputStream close]; 1357 | 1358 | //移除runloop 1359 | for (NSArray *runLoop in [_scheduledRunloops copy]) { 1360 | [self unscheduleFromRunLoop:[runLoop objectAtIndex:0] forMode:[runLoop objectAtIndex:1]]; 1361 | } 1362 | } 1363 | 1364 | if (!_failed) { 1365 | //调用关闭代理 1366 | [self _performDelegateBlock:^{ 1367 | if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) { 1368 | [self.delegate webSocket:self didCloseWithCode:_closeCode reason:_closeReason wasClean:YES]; 1369 | } 1370 | }]; 1371 | } 1372 | //清除注册 1373 | [self _scheduleCleanup]; 1374 | } 1375 | } 1376 | 1377 | //指定数据读取 1378 | - (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback; 1379 | { 1380 | [self assertOnWorkQueue]; 1381 | [self _addConsumerWithScanner:consumer callback:callback dataLength:0]; 1382 | } 1383 | 1384 | //添加消费者,用一个指定的长度,是否读到当前帧 1385 | - (void)_addConsumerWithDataLength:(size_t)dataLength callback:(data_callback)callback readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; 1386 | { 1387 | [self assertOnWorkQueue]; 1388 | assert(dataLength); 1389 | //添加到消费者队列去 1390 | [_consumers addObject:[_consumerPool consumerWithScanner:nil handler:callback bytesNeeded:dataLength readToCurrentFrame:readToCurrentFrame unmaskBytes:unmaskBytes]]; 1391 | [self _pumpScanner]; 1392 | } 1393 | 1394 | - (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback dataLength:(size_t)dataLength; 1395 | { 1396 | [self assertOnWorkQueue]; 1397 | [_consumers addObject:[_consumerPool consumerWithScanner:consumer handler:callback bytesNeeded:dataLength readToCurrentFrame:NO unmaskBytes:NO]]; 1398 | [self _pumpScanner]; 1399 | } 1400 | 1401 | 1402 | - (void)_scheduleCleanup 1403 | { 1404 | //用@synchronized来同步? 1405 | @synchronized(self) { 1406 | if (_cleanupScheduled) { 1407 | return; 1408 | } 1409 | 1410 | _cleanupScheduled = YES; 1411 | 1412 | // Cleanup NSStream delegate's in the same RunLoop used by the streams themselves: 1413 | // This way we'll prevent race conditions between handleEvent and SRWebsocket's dealloc 1414 | NSTimer *timer = [NSTimer timerWithTimeInterval:(0.0f) target:self selector:@selector(_cleanupSelfReference:) userInfo:nil repeats:NO]; 1415 | [[NSRunLoop SR_networkRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; 1416 | } 1417 | } 1418 | 1419 | - (void)_cleanupSelfReference:(NSTimer *)timer 1420 | { 1421 | @synchronized(self) { 1422 | //清除代理和流 1423 | // Nuke NSStream delegate's 1424 | _inputStream.delegate = nil; 1425 | _outputStream.delegate = nil; 1426 | 1427 | // Remove the streams, right now, from the networkRunLoop 1428 | [_inputStream close]; 1429 | [_outputStream close]; 1430 | } 1431 | 1432 | // Cleanup selfRetain in the same GCD queue as usual 1433 | // 断开对自己的引用 1434 | dispatch_async(_workQueue, ^{ 1435 | _selfRetain = nil; 1436 | }); 1437 | } 1438 | 1439 | 1440 | static const char CRLFCRLFBytes[] = {'\r', '\n', '\r', '\n'}; 1441 | 1442 | //读取CRLFCRLFBytes,直到回调回来 1443 | - (void)_readUntilHeaderCompleteWithCallback:(data_callback)dataHandler; 1444 | { 1445 | [self _readUntilBytes:CRLFCRLFBytes length:sizeof(CRLFCRLFBytes) callback:dataHandler]; 1446 | } 1447 | 1448 | //读取数据 CRLFCRLFBytes,边界符 1449 | - (void)_readUntilBytes:(const void *)bytes length:(size_t)length callback:(data_callback)dataHandler; 1450 | { 1451 | // TODO optimize so this can continue from where we last searched 1452 | 1453 | //消费者需要消费的数据大小 1454 | stream_scanner consumer = ^size_t(NSData *data) { 1455 | __block size_t found_size = 0; 1456 | __block size_t match_count = 0; 1457 | //得到数据长度 1458 | size_t size = data.length; 1459 | //得到数据指针 1460 | const unsigned char *buffer = data.bytes; 1461 | for (size_t i = 0; i < size; i++ ) { 1462 | //匹配字符 1463 | if (((const unsigned char *)buffer)[i] == ((const unsigned char *)bytes)[match_count]) { 1464 | //匹配数+1 1465 | match_count += 1; 1466 | //如果匹配了 1467 | if (match_count == length) { 1468 | //读取数据长度等于 i+ 1 1469 | found_size = i + 1; 1470 | break; 1471 | } 1472 | } else { 1473 | match_count = 0; 1474 | } 1475 | } 1476 | //返回要读取数据的长度,没匹配成功就是0 1477 | return found_size; 1478 | }; 1479 | [self _addConsumerWithScanner:consumer callback:dataHandler]; 1480 | } 1481 | 1482 | 1483 | // Returns true if did work 1484 | //判断当前是否正在工作 1485 | - (BOOL)_innerPumpScanner { 1486 | 1487 | //正在工作的标记 1488 | BOOL didWork = NO; 1489 | //如果就绪状态为已关闭 返回NO 1490 | if (self.readyState >= SR_CLOSED) { 1491 | return didWork; 1492 | } 1493 | //如果消费者为空,返回NO 1494 | if (!_consumers.count) { 1495 | return didWork; 1496 | } 1497 | 1498 | //读取的buffer长度 - 偏移量 = 未读数据长 1499 | size_t curSize = _readBuffer.length - _readBufferOffset; 1500 | //如果未读为空,返回NO 1501 | if (!curSize) { 1502 | return didWork; 1503 | } 1504 | //拿到第一个消费者 1505 | SRIOConsumer *consumer = [_consumers objectAtIndex:0]; 1506 | //得到需要的字节数 1507 | size_t bytesNeeded = consumer.bytesNeeded; 1508 | // 1509 | size_t foundSize = 0; 1510 | 1511 | //得到本次需要读取大小 1512 | //判断有没有consumer,来获取实际消费的数据大小 1513 | if (consumer.consumer) { 1514 | 1515 | //把未读数据从readBuffer中赋值到tempView里,直接持有,非copy 1516 | NSData *tempView = [NSData dataWithBytesNoCopy:(char *)_readBuffer.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset freeWhenDone:NO]; 1517 | //得到消费的大小 1518 | foundSize = consumer.consumer(tempView); 1519 | } else { 1520 | //断言需要字节 1521 | assert(consumer.bytesNeeded); 1522 | //如果未读字节大于需要字节,直接等于需要字节 1523 | if (curSize >= bytesNeeded) { 1524 | foundSize = bytesNeeded; 1525 | } 1526 | //如果为读取当前帧 1527 | else if (consumer.readToCurrentFrame) { 1528 | //消费大小等于当前未读字节 1529 | foundSize = curSize; 1530 | } 1531 | } 1532 | 1533 | //得到需要读取的数据,并且释放已读的空间 1534 | //切片 1535 | NSData *slice = nil; 1536 | //如果读取当前帧或者foundSize大于0 1537 | if (consumer.readToCurrentFrame || foundSize) { 1538 | //从已读偏移到要读的字节处 1539 | NSRange sliceRange = NSMakeRange(_readBufferOffset, foundSize); 1540 | //得到data 1541 | slice = [_readBuffer subdataWithRange:sliceRange]; 1542 | //增加已读偏移 1543 | _readBufferOffset += foundSize; 1544 | //如果读取偏移的大小大于4096,或者读取偏移大于 1/2的buffer大小 1545 | if (_readBufferOffset > 4096 && _readBufferOffset > (_readBuffer.length >> 1)) { 1546 | //重新创建,释放已读那部分的data空间 1547 | _readBuffer = [[NSMutableData alloc] initWithBytes:(char *)_readBuffer.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset]; _readBufferOffset = 0; 1548 | } 1549 | 1550 | //如果用户未掩码的数据 1551 | if (consumer.unmaskBytes) { 1552 | //copy切片 1553 | NSMutableData *mutableSlice = [slice mutableCopy]; 1554 | //得到长度字节数 1555 | NSUInteger len = mutableSlice.length; 1556 | uint8_t *bytes = mutableSlice.mutableBytes; 1557 | 1558 | for (NSUInteger i = 0; i < len; i++) { 1559 | //得到一个读取掩码key,为偏移量_currentReadMaskOffset取余_currentReadMaskKey,当前掩码key, 1560 | //再和字节异或运算(相同为0,不同为1) 1561 | bytes[i] = bytes[i] ^ _currentReadMaskKey[_currentReadMaskOffset % sizeof(_currentReadMaskKey)]; 1562 | //偏移量+1 1563 | _currentReadMaskOffset += 1; 1564 | } 1565 | //把数据改成掩码后的 1566 | slice = mutableSlice; 1567 | } 1568 | 1569 | //如果读取当前帧 1570 | if (consumer.readToCurrentFrame) { 1571 | //拼接数据 1572 | [_currentFrameData appendData:slice]; 1573 | //+1 1574 | _readOpCount += 1; 1575 | //判断Opcode,如果是文本帧 1576 | if (_currentFrameOpcode == SROpCodeTextFrame) { 1577 | // Validate UTF8 stuff. 1578 | //得到当前帧数据的长度 1579 | size_t currentDataSize = _currentFrameData.length; 1580 | //如果currentDataSize 大于0 1581 | if (_currentFrameOpcode == SROpCodeTextFrame && currentDataSize > 0) { 1582 | // TODO: Optimize the crap out of this. Don't really have to copy all the data each time 1583 | //判断需要scan的大小 1584 | size_t scanSize = currentDataSize - _currentStringScanPosition; 1585 | //得到要sacn的data 1586 | NSData *scan_data = [_currentFrameData subdataWithRange:NSMakeRange(_currentStringScanPosition, scanSize)]; 1587 | //验证数据 1588 | int32_t valid_utf8_size = validate_dispatch_data_partial_string(scan_data); 1589 | 1590 | //-1为错误,关闭连接 1591 | if (valid_utf8_size == -1) { 1592 | [self closeWithCode:SRStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"]; 1593 | dispatch_async(_workQueue, ^{ 1594 | [self closeConnection]; 1595 | }); 1596 | return didWork; 1597 | } else { 1598 | //扫描的位置+上合法数据的size 1599 | _currentStringScanPosition += valid_utf8_size; 1600 | } 1601 | } 1602 | 1603 | } 1604 | //需要的size - 已操作的size 1605 | consumer.bytesNeeded -= foundSize; 1606 | //如果还需要的字节数 = 0,移除消费者 1607 | if (consumer.bytesNeeded == 0) { 1608 | [_consumers removeObjectAtIndex:0]; 1609 | //回调读取完成 1610 | consumer.handler(self, nil); 1611 | //把要返回的consumer,先放在_consumerPool缓冲池中 1612 | [_consumerPool returnConsumer:consumer]; 1613 | //已经工作为YES 1614 | didWork = YES; 1615 | } 1616 | } else if (foundSize) { 1617 | //移除消费者 1618 | [_consumers removeObjectAtIndex:0]; 1619 | //回调回去当前接受到的数据 1620 | consumer.handler(self, slice); 1621 | 1622 | [_consumerPool returnConsumer:consumer]; 1623 | didWork = YES; 1624 | } 1625 | } 1626 | return didWork; 1627 | } 1628 | //开始扫描 1629 | -(void)_pumpScanner; 1630 | { 1631 | [self assertOnWorkQueue]; 1632 | //判断是否在扫描 1633 | if (!_isPumping) { 1634 | _isPumping = YES; 1635 | } else { 1636 | return; 1637 | } 1638 | 1639 | //只有为NO能走到这里,开始循环检测,可读可写数据 1640 | while ([self _innerPumpScanner]) { 1641 | 1642 | } 1643 | 1644 | _isPumping = NO; 1645 | } 1646 | 1647 | //#define NOMASK 1648 | 1649 | static const size_t SRFrameHeaderOverhead = 32; 1650 | 1651 | //发送帧数据 1652 | - (void)_sendFrameWithOpcode:(SROpCode)opcode data:(id)data; 1653 | { 1654 | [self assertOnWorkQueue]; 1655 | 1656 | if (nil == data) { 1657 | return; 1658 | } 1659 | 1660 | NSAssert([data isKindOfClass:[NSData class]] || [data isKindOfClass:[NSString class]], @"NSString or NSData"); 1661 | //得到发送数据长度 1662 | size_t payloadLength = [data isKindOfClass:[NSString class]] ? [(NSString *)data lengthOfBytesUsingEncoding:NSUTF8StringEncoding] : [data length]; 1663 | //+上外层包裹的帧长度 32个字节? 1664 | NSMutableData *frame = [[NSMutableData alloc] initWithLength:payloadLength + SRFrameHeaderOverhead]; 1665 | //如果没数据,则报数据太大出错 1666 | if (!frame) { 1667 | [self closeWithCode:SRStatusCodeMessageTooBig reason:@"Message too big"]; 1668 | return; 1669 | } 1670 | //得到帧指针 1671 | uint8_t *frame_buffer = (uint8_t *)[frame mutableBytes]; 1672 | 1673 | // set fin 1674 | //写fin,还有opcode 1675 | frame_buffer[0] = SRFinMask | opcode; 1676 | 1677 | BOOL useMask = YES; 1678 | #ifdef NOMASK 1679 | useMask = NO; 1680 | #endif 1681 | 1682 | if (useMask) { 1683 | // set the mask and header 1684 | //设置mask 1685 | frame_buffer[1] |= SRMaskMask; 1686 | } 1687 | 1688 | size_t frame_buffer_size = 2; 1689 | 1690 | //得到未掩码数据 1691 | const uint8_t *unmasked_payload = NULL; 1692 | if ([data isKindOfClass:[NSData class]]) { 1693 | unmasked_payload = (uint8_t *)[data bytes]; 1694 | } else if ([data isKindOfClass:[NSString class]]) { 1695 | unmasked_payload = (const uint8_t *)[data UTF8String]; 1696 | } else { 1697 | return; 1698 | } 1699 | 1700 | //赋值长度 1701 | if (payloadLength < 126) { 1702 | //取或 1703 | frame_buffer[1] |= payloadLength; 1704 | } else if (payloadLength <= UINT16_MAX) { 1705 | frame_buffer[1] |= 126; 1706 | //再加上2个字节来存储长度 1707 | *((uint16_t *)(frame_buffer + frame_buffer_size)) = EndianU16_BtoN((uint16_t)payloadLength); 1708 | frame_buffer_size += sizeof(uint16_t); 1709 | } else { 1710 | //第一位与127取或 1711 | frame_buffer[1] |= 127; 1712 | //再加上 8个字节来存储长度 1713 | *((uint64_t *)(frame_buffer + frame_buffer_size)) = EndianU64_BtoN((uint64_t)payloadLength); 1714 | frame_buffer_size += sizeof(uint64_t); 1715 | } 1716 | 1717 | //如果没用掩码,直接填充数据 1718 | if (!useMask) { 1719 | for (size_t i = 0; i < payloadLength; i++) { 1720 | frame_buffer[frame_buffer_size] = unmasked_payload[i]; 1721 | frame_buffer_size += 1; 1722 | } 1723 | } else { 1724 | //先创建mask_key 1725 | uint8_t *mask_key = frame_buffer + frame_buffer_size; 1726 | SecRandomCopyBytes(kSecRandomDefault, sizeof(uint32_t), (uint8_t *)mask_key); 1727 | frame_buffer_size += sizeof(uint32_t); 1728 | 1729 | // TODO: could probably optimize this with SIMD 1730 | //存mask_key 1731 | for (size_t i = 0; i < payloadLength; i++) { 1732 | //加上带掩码的数据 1733 | frame_buffer[frame_buffer_size] = unmasked_payload[i] ^ mask_key[i % sizeof(uint32_t)]; 1734 | frame_buffer_size += 1; 1735 | } 1736 | } 1737 | 1738 | assert(frame_buffer_size <= [frame length]); 1739 | //设置帧长度 1740 | frame.length = frame_buffer_size; 1741 | 1742 | [self _writeData:frame]; 1743 | } 1744 | 1745 | //开启流后,收到事件回调 1746 | - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode; 1747 | { 1748 | __weak typeof(self) weakSelf = self; 1749 | 1750 | // 如果是ssl,而且_pinnedCertFound 为NO,而且事件类型是有可读数据未读,或者事件类型是还有空余空间可写 1751 | if (_secure && !_pinnedCertFound && (eventCode == NSStreamEventHasBytesAvailable || eventCode == NSStreamEventHasSpaceAvailable)) { 1752 | 1753 | //拿到证书 1754 | NSArray *sslCerts = [_urlRequest SR_SSLPinnedCertificates]; 1755 | if (sslCerts) { 1756 | //拿到流中SSL信息结构体 1757 | SecTrustRef secTrust = (__bridge SecTrustRef)[aStream propertyForKey:(__bridge id)kCFStreamPropertySSLPeerTrust]; 1758 | if (secTrust) { 1759 | //得到数量 1760 | NSInteger numCerts = SecTrustGetCertificateCount(secTrust); 1761 | 1762 | for (NSInteger i = 0; i < numCerts && !_pinnedCertFound; i++) { 1763 | //拿到证书链上的证书 1764 | SecCertificateRef cert = SecTrustGetCertificateAtIndex(secTrust, i); 1765 | //得到data 1766 | NSData *certData = CFBridgingRelease(SecCertificateCopyData(cert)); 1767 | 1768 | //从request中拿到SSL去匹配流中的 1769 | for (id ref in sslCerts) { 1770 | SecCertificateRef trustedCert = (__bridge SecCertificateRef)ref; 1771 | NSData *trustedCertData = CFBridgingRelease(SecCertificateCopyData(trustedCert)); 1772 | //如果有一对匹配了,就设置为YES 1773 | if ([trustedCertData isEqualToData:certData]) { 1774 | _pinnedCertFound = YES; 1775 | break; 1776 | } 1777 | } 1778 | } 1779 | } 1780 | //如果为NO,则验证失败,报错关闭 1781 | if (!_pinnedCertFound) { 1782 | dispatch_async(_workQueue, ^{ 1783 | NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : @"Invalid server cert" }; 1784 | [weakSelf _failWithError:[NSError errorWithDomain:@"org.lolrus.SocketRocket" code:23556 userInfo:userInfo]]; 1785 | }); 1786 | return; 1787 | } else if (aStream == _outputStream) { 1788 | //如果流是输出流,则打开流成功 1789 | dispatch_async(_workQueue, ^{ 1790 | [self didConnect]; 1791 | }); 1792 | } 1793 | } 1794 | } 1795 | dispatch_async(_workQueue, ^{ 1796 | [weakSelf safeHandleEvent:eventCode stream:aStream]; 1797 | }); 1798 | } 1799 | 1800 | //安全的处理事件 1801 | - (void)safeHandleEvent:(NSStreamEvent)eventCode stream:(NSStream *)aStream 1802 | { 1803 | switch (eventCode) { 1804 | //连接完成 1805 | case NSStreamEventOpenCompleted: { 1806 | SRFastLog(@"NSStreamEventOpenCompleted %@", aStream); 1807 | //如果就绪状态为关闭或者正在关闭,直接返回 1808 | if (self.readyState >= SR_CLOSING) { 1809 | return; 1810 | } 1811 | //断言_readBuffer 1812 | assert(_readBuffer); 1813 | 1814 | // didConnect fires after certificate verification if we're using pinned certificates. 1815 | 1816 | BOOL usingPinnedCerts = [[_urlRequest SR_SSLPinnedCertificates] count] > 0; 1817 | //如果是http,或者无自签证书,而且是正准备连接,而且aStream是输入流 1818 | if ((!_secure || !usingPinnedCerts) && self.readyState == SR_CONNECTING && aStream == _inputStream) { 1819 | //连接 1820 | [self didConnect]; 1821 | } 1822 | //开始写(http握手) 1823 | [self _pumpWriting]; 1824 | //开始扫描读写 1825 | [self _pumpScanner]; 1826 | break; 1827 | } 1828 | //流事件错误 1829 | case NSStreamEventErrorOccurred: { 1830 | SRFastLog(@"NSStreamEventErrorOccurred %@ %@", aStream, [[aStream streamError] copy]); 1831 | /// TODO specify error better! 1832 | [self _failWithError:aStream.streamError]; 1833 | _readBufferOffset = 0; 1834 | [_readBuffer setLength:0]; 1835 | break; 1836 | 1837 | } 1838 | //流读到末尾 1839 | case NSStreamEventEndEncountered: { 1840 | //扫描下,防止有未操作完的数据 1841 | [self _pumpScanner]; 1842 | SRFastLog(@"NSStreamEventEndEncountered %@", aStream); 1843 | //出错的话直接关闭 1844 | if (aStream.streamError) { 1845 | [self _failWithError:aStream.streamError]; 1846 | } else { 1847 | 1848 | dispatch_async(_workQueue, ^{ 1849 | if (self.readyState != SR_CLOSED) { 1850 | self.readyState = SR_CLOSED; 1851 | [self _scheduleCleanup]; 1852 | } 1853 | //如果是正常关闭 1854 | if (!_sentClose && !_failed) { 1855 | _sentClose = YES; 1856 | // If we get closed in this state it's probably not clean because we should be sending this when we send messages 1857 | [self _performDelegateBlock:^{ 1858 | if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) { 1859 | [self.delegate webSocket:self didCloseWithCode:SRStatusCodeGoingAway reason:@"Stream end encountered" wasClean:NO]; 1860 | } 1861 | }]; 1862 | } 1863 | }); 1864 | } 1865 | 1866 | break; 1867 | } 1868 | //正常读取数据 1869 | case NSStreamEventHasBytesAvailable: { 1870 | SRFastLog(@"NSStreamEventHasBytesAvailable %@", aStream); 1871 | const int bufferSize = 2048; 1872 | uint8_t buffer[bufferSize]; 1873 | //如果有可读字节 1874 | while (_inputStream.hasBytesAvailable) { 1875 | //读取数据,一次读2048 1876 | NSInteger bytes_read = [_inputStream read:buffer maxLength:bufferSize]; 1877 | 1878 | if (bytes_read > 0) { 1879 | //拼接数据 1880 | [_readBuffer appendBytes:buffer length:bytes_read]; 1881 | } else if (bytes_read < 0) { 1882 | //读取错误 1883 | [self _failWithError:_inputStream.streamError]; 1884 | } 1885 | //如果读取的不等于最大的,说明读完了,跳出循环 1886 | if (bytes_read != bufferSize) { 1887 | break; 1888 | } 1889 | }; 1890 | //开始扫描,看消费者什么时候消费数据 1891 | [self _pumpScanner]; 1892 | break; 1893 | } 1894 | //有可写空间,一直会回调,除非写满了 1895 | case NSStreamEventHasSpaceAvailable: { 1896 | SRFastLog(@"NSStreamEventHasSpaceAvailable %@", aStream); 1897 | //开始写数据 1898 | [self _pumpWriting]; 1899 | break; 1900 | } 1901 | 1902 | default: 1903 | SRFastLog(@"(default) %@", aStream); 1904 | break; 1905 | } 1906 | } 1907 | 1908 | @end 1909 | 1910 | 1911 | @implementation SRIOConsumer 1912 | 1913 | @synthesize bytesNeeded = _bytesNeeded; 1914 | @synthesize consumer = _scanner; 1915 | @synthesize handler = _handler; 1916 | @synthesize readToCurrentFrame = _readToCurrentFrame; 1917 | @synthesize unmaskBytes = _unmaskBytes; 1918 | 1919 | //初始化IO消费者 1920 | - (void)setupWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; 1921 | { 1922 | _scanner = [scanner copy]; 1923 | _handler = [handler copy]; 1924 | _bytesNeeded = bytesNeeded; 1925 | _readToCurrentFrame = readToCurrentFrame; 1926 | _unmaskBytes = unmaskBytes; 1927 | assert(_scanner || _bytesNeeded); 1928 | } 1929 | 1930 | 1931 | @end 1932 | 1933 | 1934 | @implementation SRIOConsumerPool { 1935 | //大小 1936 | NSUInteger _poolSize; 1937 | //缓冲的消费者 1938 | NSMutableArray *_bufferedConsumers; 1939 | } 1940 | 1941 | - (id)initWithBufferCapacity:(NSUInteger)poolSize; 1942 | { 1943 | self = [super init]; 1944 | if (self) { 1945 | _poolSize = poolSize; 1946 | //池子大小复用消费者的数组 1947 | _bufferedConsumers = [[NSMutableArray alloc] initWithCapacity:poolSize]; 1948 | } 1949 | return self; 1950 | } 1951 | 1952 | - (id)init 1953 | { 1954 | //默认大小为8个 1955 | return [self initWithBufferCapacity:8]; 1956 | } 1957 | 1958 | //用扫描消费 scanner block,data_callback 数据返回的Block 1959 | - (SRIOConsumer *)consumerWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; 1960 | { 1961 | SRIOConsumer *consumer = nil; 1962 | //复用 1963 | if (_bufferedConsumers.count) { 1964 | consumer = [_bufferedConsumers lastObject]; 1965 | [_bufferedConsumers removeLastObject]; 1966 | } else { 1967 | consumer = [[SRIOConsumer alloc] init]; 1968 | } 1969 | 1970 | [consumer setupWithScanner:scanner handler:handler bytesNeeded:bytesNeeded readToCurrentFrame:readToCurrentFrame unmaskBytes:unmaskBytes]; 1971 | 1972 | return consumer; 1973 | } 1974 | 1975 | - (void)returnConsumer:(SRIOConsumer *)consumer; 1976 | { 1977 | if (_bufferedConsumers.count < _poolSize) { 1978 | //用完了,返回复用池 1979 | [_bufferedConsumers addObject:consumer]; 1980 | } 1981 | } 1982 | 1983 | @end 1984 | 1985 | 1986 | 1987 | @implementation NSURLRequest (SRCertificateAdditions) 1988 | 1989 | - (NSArray *)SR_SSLPinnedCertificates; 1990 | { 1991 | //得到SSL证书 1992 | return [NSURLProtocol propertyForKey:@"SR_SSLPinnedCertificates" inRequest:self]; 1993 | } 1994 | 1995 | @end 1996 | 1997 | @implementation NSMutableURLRequest (SRCertificateAdditions) 1998 | 1999 | - (NSArray *)SR_SSLPinnedCertificates; 2000 | { 2001 | return [NSURLProtocol propertyForKey:@"SR_SSLPinnedCertificates" inRequest:self]; 2002 | } 2003 | 2004 | //设置SSL证书 2005 | - (void)setSR_SSLPinnedCertificates:(NSArray *)SR_SSLPinnedCertificates; 2006 | { 2007 | [NSURLProtocol setProperty:SR_SSLPinnedCertificates forKey:@"SR_SSLPinnedCertificates" inRequest:self]; 2008 | } 2009 | 2010 | @end 2011 | 2012 | @implementation NSURL (SRWebSocket) 2013 | 2014 | //ws://host:port -> (http/https)://host:port 2015 | - (NSString *)SR_origin; 2016 | { 2017 | //小写 2018 | NSString *scheme = [self.scheme lowercaseString]; 2019 | 2020 | if ([scheme isEqualToString:@"wss"]) { 2021 | scheme = @"https"; 2022 | } else if ([scheme isEqualToString:@"ws"]) { 2023 | scheme = @"http"; 2024 | } 2025 | 2026 | BOOL portIsDefault = !self.port || 2027 | ([scheme isEqualToString:@"http"] && self.port.integerValue == 80) || 2028 | ([scheme isEqualToString:@"https"] && self.port.integerValue == 443); 2029 | 2030 | if (!portIsDefault) { 2031 | return [NSString stringWithFormat:@"%@://%@:%@", scheme, self.host, self.port]; 2032 | } else { 2033 | return [NSString stringWithFormat:@"%@://%@", scheme, self.host]; 2034 | } 2035 | } 2036 | 2037 | @end 2038 | 2039 | //#define SR_ENABLE_LOG 2040 | //输出模块 2041 | static inline void SRFastLog(NSString *format, ...) { 2042 | #ifdef SR_ENABLE_LOG 2043 | __block va_list arg_list; 2044 | va_start (arg_list, format); 2045 | 2046 | NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list]; 2047 | 2048 | va_end(arg_list); 2049 | 2050 | NSLog(@"[SR] %@", formattedString); 2051 | #endif 2052 | } 2053 | 2054 | 2055 | #ifdef HAS_ICU 2056 | 2057 | //iphone的处理 2058 | //验证data合法性 2059 | static inline int32_t validate_dispatch_data_partial_string(NSData *data) { 2060 | //大于32的最大数 2061 | if ([data length] > INT32_MAX) { 2062 | // INT32_MAX is the limit so long as this Framework is using 32 bit ints everywhere. 2063 | return -1; 2064 | } 2065 | //得到长度 2066 | int32_t size = (int32_t)[data length]; 2067 | //得到内容指针 2068 | const void * contents = [data bytes]; 2069 | const uint8_t *str = (const uint8_t *)contents; 2070 | 2071 | UChar32 codepoint = 1; 2072 | int32_t offset = 0; 2073 | int32_t lastOffset = 0; 2074 | while(offset < size && codepoint > 0) { 2075 | // 2076 | lastOffset = offset; 2077 | //wtf? 指针验证差错么? 2078 | U8_NEXT(str, offset, size, codepoint); 2079 | } 2080 | 2081 | //判断 codepoint 2082 | if (codepoint == -1) { 2083 | // Check to see if the last byte is valid or whether it was just continuing 2084 | if (!U8_IS_LEAD(str[lastOffset]) || U8_COUNT_TRAIL_BYTES(str[lastOffset]) + lastOffset < (int32_t)size) { 2085 | 2086 | size = -1; 2087 | } else { 2088 | uint8_t leadByte = str[lastOffset]; 2089 | U8_MASK_LEAD_BYTE(leadByte, U8_COUNT_TRAIL_BYTES(leadByte)); 2090 | 2091 | for (int i = lastOffset + 1; i < offset; i++) { 2092 | if (U8_IS_SINGLE(str[i]) || U8_IS_LEAD(str[i]) || !U8_IS_TRAIL(str[i])) { 2093 | size = -1; 2094 | } 2095 | } 2096 | 2097 | if (size != -1) { 2098 | size = lastOffset; 2099 | } 2100 | } 2101 | } 2102 | 2103 | if (size != -1 && ![[NSString alloc] initWithBytesNoCopy:(char *)[data bytes] length:size encoding:NSUTF8StringEncoding freeWhenDone:NO]) { 2104 | size = -1; 2105 | } 2106 | 2107 | return size; 2108 | } 2109 | 2110 | #else 2111 | 2112 | //非iphone的处理 2113 | 2114 | // This is a hack, and probably not optimal 2115 | static inline int32_t validate_dispatch_data_partial_string(NSData *data) { 2116 | static const int maxCodepointSize = 3; 2117 | 2118 | for (int i = 0; i < maxCodepointSize; i++) { 2119 | NSString *str = [[NSString alloc] initWithBytesNoCopy:(char *)data.bytes length:data.length - i encoding:NSUTF8StringEncoding freeWhenDone:NO]; 2120 | if (str) { 2121 | return (int32_t)data.length - i; 2122 | } 2123 | } 2124 | 2125 | return -1; 2126 | } 2127 | 2128 | #endif 2129 | 2130 | static _SRRunLoopThread *networkThread = nil; 2131 | static NSRunLoop *networkRunLoop = nil; 2132 | 2133 | @implementation NSRunLoop (SRWebSocket) 2134 | 2135 | 2136 | + (NSRunLoop *)SR_networkRunLoop { 2137 | static dispatch_once_t onceToken; 2138 | dispatch_once(&onceToken, ^{ 2139 | networkThread = [[_SRRunLoopThread alloc] init]; 2140 | networkThread.name = @"com.squareup.SocketRocket.NetworkThread"; 2141 | [networkThread start]; 2142 | //阻塞方式拿到当前runloop 2143 | networkRunLoop = networkThread.runLoop; 2144 | }); 2145 | 2146 | return networkRunLoop; 2147 | } 2148 | 2149 | @end 2150 | 2151 | 2152 | @implementation _SRRunLoopThread { 2153 | dispatch_group_t _waitGroup; 2154 | } 2155 | 2156 | @synthesize runLoop = _runLoop; 2157 | 2158 | - (void)dealloc 2159 | { 2160 | sr_dispatch_release(_waitGroup); 2161 | } 2162 | 2163 | - (id)init 2164 | { 2165 | self = [super init]; 2166 | if (self) { 2167 | //创建一个group 2168 | _waitGroup = dispatch_group_create(); 2169 | //等待的group里 + 1 2170 | dispatch_group_enter(_waitGroup); 2171 | } 2172 | return self; 2173 | } 2174 | 2175 | //线程的执行 2176 | - (void)main; 2177 | { 2178 | @autoreleasepool { 2179 | //初始化runloop 2180 | _runLoop = [NSRunLoop currentRunLoop]; 2181 | //开始执行,group - 1 2182 | dispatch_group_leave(_waitGroup); 2183 | 2184 | // Add an empty run loop source to prevent runloop from spinning. 2185 | //添加一个空的runloop source,防止runloop退出 2186 | CFRunLoopSourceContext sourceCtx = { 2187 | .version = 0, 2188 | .info = NULL, 2189 | .retain = NULL, 2190 | .release = NULL, 2191 | .copyDescription = NULL, 2192 | .equal = NULL, 2193 | .hash = NULL, 2194 | .schedule = NULL, 2195 | .cancel = NULL, 2196 | .perform = NULL 2197 | }; 2198 | //创建source 2199 | CFRunLoopSourceRef source = CFRunLoopSourceCreate(NULL, 0, &sourceCtx); 2200 | //默认模式 2201 | CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); 2202 | CFRelease(source); 2203 | 2204 | //一直让runloop运行在 NSDefaultRunLoopMode 模式下 2205 | while ([_runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) { 2206 | 2207 | } 2208 | assert(NO); 2209 | } 2210 | } 2211 | 2212 | - (NSRunLoop *)runLoop; 2213 | { 2214 | //阻塞的方式获取runloop,等group里的任务都完成了,才能获取到 2215 | //确保线程开始执行的时候,拿到正确的runloop 2216 | dispatch_group_wait(_waitGroup, DISPATCH_TIME_FOREVER); 2217 | return _runLoop; 2218 | } 2219 | 2220 | @end 2221 | -------------------------------------------------------------------------------- /ReadWebsocket/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 | -------------------------------------------------------------------------------- /ReadWebsocket/Pods/Target Support Files/Pods-ReadWebsocket/Pods-ReadWebsocket-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 | -------------------------------------------------------------------------------- /ReadWebsocket/Pods/Target Support Files/Pods-ReadWebsocket/Pods-ReadWebsocket-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 | -------------------------------------------------------------------------------- /ReadWebsocket/Pods/Target Support Files/Pods-ReadWebsocket/Pods-ReadWebsocket-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_ReadWebsocket : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_ReadWebsocket 5 | @end 6 | -------------------------------------------------------------------------------- /ReadWebsocket/Pods/Target Support Files/Pods-ReadWebsocket/Pods-ReadWebsocket-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 | -------------------------------------------------------------------------------- /ReadWebsocket/Pods/Target Support Files/Pods-ReadWebsocket/Pods-ReadWebsocket-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 | -------------------------------------------------------------------------------- /ReadWebsocket/Pods/Target Support Files/Pods-ReadWebsocket/Pods-ReadWebsocket.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 | -------------------------------------------------------------------------------- /ReadWebsocket/Pods/Target Support Files/Pods-ReadWebsocket/Pods-ReadWebsocket.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 | -------------------------------------------------------------------------------- /ReadWebsocket/Pods/Target Support Files/SocketRocket/SocketRocket-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_SocketRocket : NSObject 3 | @end 4 | @implementation PodsDummy_SocketRocket 5 | @end 6 | -------------------------------------------------------------------------------- /ReadWebsocket/Pods/Target Support Files/SocketRocket/SocketRocket-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | -------------------------------------------------------------------------------- /ReadWebsocket/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 | -------------------------------------------------------------------------------- /ReadWebsocket/ReadWebsocket.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0C8EB5951E88FA5E000DF4F5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C8EB5941E88FA5E000DF4F5 /* main.m */; }; 11 | 0C8EB5981E88FA5E000DF4F5 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C8EB5971E88FA5E000DF4F5 /* AppDelegate.m */; }; 12 | 0C8EB59B1E88FA5E000DF4F5 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C8EB59A1E88FA5E000DF4F5 /* ViewController.m */; }; 13 | 0C8EB59E1E88FA5E000DF4F5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0C8EB59C1E88FA5E000DF4F5 /* Main.storyboard */; }; 14 | 0C8EB5A01E88FA5E000DF4F5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0C8EB59F1E88FA5E000DF4F5 /* Assets.xcassets */; }; 15 | 0C8EB5A31E88FA5E000DF4F5 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0C8EB5A11E88FA5E000DF4F5 /* LaunchScreen.storyboard */; }; 16 | 0C8EB5AE1E88FA5E000DF4F5 /* ReadWebsocketTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C8EB5AD1E88FA5E000DF4F5 /* ReadWebsocketTests.m */; }; 17 | 0C8EB5B91E88FA5E000DF4F5 /* ReadWebsocketUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C8EB5B81E88FA5E000DF4F5 /* ReadWebsocketUITests.m */; }; 18 | 3C754F6EAD770A82479F4607 /* libPods-ReadWebsocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 242E3CC6022C24B90D21BE95 /* libPods-ReadWebsocket.a */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXContainerItemProxy section */ 22 | 0C8EB5AA1E88FA5E000DF4F5 /* PBXContainerItemProxy */ = { 23 | isa = PBXContainerItemProxy; 24 | containerPortal = 0C8EB5881E88FA5E000DF4F5 /* Project object */; 25 | proxyType = 1; 26 | remoteGlobalIDString = 0C8EB58F1E88FA5E000DF4F5; 27 | remoteInfo = ReadWebsocket; 28 | }; 29 | 0C8EB5B51E88FA5E000DF4F5 /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = 0C8EB5881E88FA5E000DF4F5 /* Project object */; 32 | proxyType = 1; 33 | remoteGlobalIDString = 0C8EB58F1E88FA5E000DF4F5; 34 | remoteInfo = ReadWebsocket; 35 | }; 36 | /* End PBXContainerItemProxy section */ 37 | 38 | /* Begin PBXFileReference section */ 39 | 0C8EB5901E88FA5E000DF4F5 /* ReadWebsocket.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ReadWebsocket.app; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | 0C8EB5941E88FA5E000DF4F5 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 41 | 0C8EB5961E88FA5E000DF4F5 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 42 | 0C8EB5971E88FA5E000DF4F5 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 43 | 0C8EB5991E88FA5E000DF4F5 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 44 | 0C8EB59A1E88FA5E000DF4F5 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 45 | 0C8EB59D1E88FA5E000DF4F5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 46 | 0C8EB59F1E88FA5E000DF4F5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 47 | 0C8EB5A21E88FA5E000DF4F5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 48 | 0C8EB5A41E88FA5E000DF4F5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 49 | 0C8EB5A91E88FA5E000DF4F5 /* ReadWebsocketTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReadWebsocketTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 50 | 0C8EB5AD1E88FA5E000DF4F5 /* ReadWebsocketTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReadWebsocketTests.m; sourceTree = ""; }; 51 | 0C8EB5AF1E88FA5E000DF4F5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 52 | 0C8EB5B41E88FA5E000DF4F5 /* ReadWebsocketUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReadWebsocketUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | 0C8EB5B81E88FA5E000DF4F5 /* ReadWebsocketUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReadWebsocketUITests.m; sourceTree = ""; }; 54 | 0C8EB5BA1E88FA5E000DF4F5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 55 | 242E3CC6022C24B90D21BE95 /* libPods-ReadWebsocket.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReadWebsocket.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | C8AAF986CEB671236B392C1C /* Pods-ReadWebsocket.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReadWebsocket.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ReadWebsocket/Pods-ReadWebsocket.debug.xcconfig"; sourceTree = ""; }; 57 | FB4DD786F861EC579C14F368 /* Pods-ReadWebsocket.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReadWebsocket.release.xcconfig"; path = "Pods/Target Support Files/Pods-ReadWebsocket/Pods-ReadWebsocket.release.xcconfig"; sourceTree = ""; }; 58 | /* End PBXFileReference section */ 59 | 60 | /* Begin PBXFrameworksBuildPhase section */ 61 | 0C8EB58D1E88FA5E000DF4F5 /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | 3C754F6EAD770A82479F4607 /* libPods-ReadWebsocket.a in Frameworks */, 66 | ); 67 | runOnlyForDeploymentPostprocessing = 0; 68 | }; 69 | 0C8EB5A61E88FA5E000DF4F5 /* Frameworks */ = { 70 | isa = PBXFrameworksBuildPhase; 71 | buildActionMask = 2147483647; 72 | files = ( 73 | ); 74 | runOnlyForDeploymentPostprocessing = 0; 75 | }; 76 | 0C8EB5B11E88FA5E000DF4F5 /* Frameworks */ = { 77 | isa = PBXFrameworksBuildPhase; 78 | buildActionMask = 2147483647; 79 | files = ( 80 | ); 81 | runOnlyForDeploymentPostprocessing = 0; 82 | }; 83 | /* End PBXFrameworksBuildPhase section */ 84 | 85 | /* Begin PBXGroup section */ 86 | 0C8EB5871E88FA5E000DF4F5 = { 87 | isa = PBXGroup; 88 | children = ( 89 | 0C8EB5921E88FA5E000DF4F5 /* ReadWebsocket */, 90 | 0C8EB5AC1E88FA5E000DF4F5 /* ReadWebsocketTests */, 91 | 0C8EB5B71E88FA5E000DF4F5 /* ReadWebsocketUITests */, 92 | 0C8EB5911E88FA5E000DF4F5 /* Products */, 93 | C6F461AC9236B618CE21F8A2 /* Pods */, 94 | BF789920FA2D9501C55A12A8 /* Frameworks */, 95 | ); 96 | sourceTree = ""; 97 | }; 98 | 0C8EB5911E88FA5E000DF4F5 /* Products */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | 0C8EB5901E88FA5E000DF4F5 /* ReadWebsocket.app */, 102 | 0C8EB5A91E88FA5E000DF4F5 /* ReadWebsocketTests.xctest */, 103 | 0C8EB5B41E88FA5E000DF4F5 /* ReadWebsocketUITests.xctest */, 104 | ); 105 | name = Products; 106 | sourceTree = ""; 107 | }; 108 | 0C8EB5921E88FA5E000DF4F5 /* ReadWebsocket */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | 0C8EB5961E88FA5E000DF4F5 /* AppDelegate.h */, 112 | 0C8EB5971E88FA5E000DF4F5 /* AppDelegate.m */, 113 | 0C8EB5991E88FA5E000DF4F5 /* ViewController.h */, 114 | 0C8EB59A1E88FA5E000DF4F5 /* ViewController.m */, 115 | 0C8EB59C1E88FA5E000DF4F5 /* Main.storyboard */, 116 | 0C8EB59F1E88FA5E000DF4F5 /* Assets.xcassets */, 117 | 0C8EB5A11E88FA5E000DF4F5 /* LaunchScreen.storyboard */, 118 | 0C8EB5A41E88FA5E000DF4F5 /* Info.plist */, 119 | 0C8EB5931E88FA5E000DF4F5 /* Supporting Files */, 120 | ); 121 | path = ReadWebsocket; 122 | sourceTree = ""; 123 | }; 124 | 0C8EB5931E88FA5E000DF4F5 /* Supporting Files */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 0C8EB5941E88FA5E000DF4F5 /* main.m */, 128 | ); 129 | name = "Supporting Files"; 130 | sourceTree = ""; 131 | }; 132 | 0C8EB5AC1E88FA5E000DF4F5 /* ReadWebsocketTests */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | 0C8EB5AD1E88FA5E000DF4F5 /* ReadWebsocketTests.m */, 136 | 0C8EB5AF1E88FA5E000DF4F5 /* Info.plist */, 137 | ); 138 | path = ReadWebsocketTests; 139 | sourceTree = ""; 140 | }; 141 | 0C8EB5B71E88FA5E000DF4F5 /* ReadWebsocketUITests */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | 0C8EB5B81E88FA5E000DF4F5 /* ReadWebsocketUITests.m */, 145 | 0C8EB5BA1E88FA5E000DF4F5 /* Info.plist */, 146 | ); 147 | path = ReadWebsocketUITests; 148 | sourceTree = ""; 149 | }; 150 | BF789920FA2D9501C55A12A8 /* Frameworks */ = { 151 | isa = PBXGroup; 152 | children = ( 153 | 242E3CC6022C24B90D21BE95 /* libPods-ReadWebsocket.a */, 154 | ); 155 | name = Frameworks; 156 | sourceTree = ""; 157 | }; 158 | C6F461AC9236B618CE21F8A2 /* Pods */ = { 159 | isa = PBXGroup; 160 | children = ( 161 | C8AAF986CEB671236B392C1C /* Pods-ReadWebsocket.debug.xcconfig */, 162 | FB4DD786F861EC579C14F368 /* Pods-ReadWebsocket.release.xcconfig */, 163 | ); 164 | name = Pods; 165 | sourceTree = ""; 166 | }; 167 | /* End PBXGroup section */ 168 | 169 | /* Begin PBXNativeTarget section */ 170 | 0C8EB58F1E88FA5E000DF4F5 /* ReadWebsocket */ = { 171 | isa = PBXNativeTarget; 172 | buildConfigurationList = 0C8EB5BD1E88FA5E000DF4F5 /* Build configuration list for PBXNativeTarget "ReadWebsocket" */; 173 | buildPhases = ( 174 | A2AFC9BF80296D090F61EF46 /* [CP] Check Pods Manifest.lock */, 175 | 0C8EB58C1E88FA5E000DF4F5 /* Sources */, 176 | 0C8EB58D1E88FA5E000DF4F5 /* Frameworks */, 177 | 0C8EB58E1E88FA5E000DF4F5 /* Resources */, 178 | B631DF8E2FF0D3980A88D12F /* [CP] Embed Pods Frameworks */, 179 | F2D3E0C40EC8497EB6AF1BE4 /* [CP] Copy Pods Resources */, 180 | ); 181 | buildRules = ( 182 | ); 183 | dependencies = ( 184 | ); 185 | name = ReadWebsocket; 186 | productName = ReadWebsocket; 187 | productReference = 0C8EB5901E88FA5E000DF4F5 /* ReadWebsocket.app */; 188 | productType = "com.apple.product-type.application"; 189 | }; 190 | 0C8EB5A81E88FA5E000DF4F5 /* ReadWebsocketTests */ = { 191 | isa = PBXNativeTarget; 192 | buildConfigurationList = 0C8EB5C01E88FA5E000DF4F5 /* Build configuration list for PBXNativeTarget "ReadWebsocketTests" */; 193 | buildPhases = ( 194 | 0C8EB5A51E88FA5E000DF4F5 /* Sources */, 195 | 0C8EB5A61E88FA5E000DF4F5 /* Frameworks */, 196 | 0C8EB5A71E88FA5E000DF4F5 /* Resources */, 197 | ); 198 | buildRules = ( 199 | ); 200 | dependencies = ( 201 | 0C8EB5AB1E88FA5E000DF4F5 /* PBXTargetDependency */, 202 | ); 203 | name = ReadWebsocketTests; 204 | productName = ReadWebsocketTests; 205 | productReference = 0C8EB5A91E88FA5E000DF4F5 /* ReadWebsocketTests.xctest */; 206 | productType = "com.apple.product-type.bundle.unit-test"; 207 | }; 208 | 0C8EB5B31E88FA5E000DF4F5 /* ReadWebsocketUITests */ = { 209 | isa = PBXNativeTarget; 210 | buildConfigurationList = 0C8EB5C31E88FA5E000DF4F5 /* Build configuration list for PBXNativeTarget "ReadWebsocketUITests" */; 211 | buildPhases = ( 212 | 0C8EB5B01E88FA5E000DF4F5 /* Sources */, 213 | 0C8EB5B11E88FA5E000DF4F5 /* Frameworks */, 214 | 0C8EB5B21E88FA5E000DF4F5 /* Resources */, 215 | ); 216 | buildRules = ( 217 | ); 218 | dependencies = ( 219 | 0C8EB5B61E88FA5E000DF4F5 /* PBXTargetDependency */, 220 | ); 221 | name = ReadWebsocketUITests; 222 | productName = ReadWebsocketUITests; 223 | productReference = 0C8EB5B41E88FA5E000DF4F5 /* ReadWebsocketUITests.xctest */; 224 | productType = "com.apple.product-type.bundle.ui-testing"; 225 | }; 226 | /* End PBXNativeTarget section */ 227 | 228 | /* Begin PBXProject section */ 229 | 0C8EB5881E88FA5E000DF4F5 /* Project object */ = { 230 | isa = PBXProject; 231 | attributes = { 232 | LastUpgradeCheck = 0820; 233 | ORGANIZATIONNAME = tyh; 234 | TargetAttributes = { 235 | 0C8EB58F1E88FA5E000DF4F5 = { 236 | CreatedOnToolsVersion = 8.2.1; 237 | ProvisioningStyle = Automatic; 238 | }; 239 | 0C8EB5A81E88FA5E000DF4F5 = { 240 | CreatedOnToolsVersion = 8.2.1; 241 | ProvisioningStyle = Automatic; 242 | TestTargetID = 0C8EB58F1E88FA5E000DF4F5; 243 | }; 244 | 0C8EB5B31E88FA5E000DF4F5 = { 245 | CreatedOnToolsVersion = 8.2.1; 246 | ProvisioningStyle = Automatic; 247 | TestTargetID = 0C8EB58F1E88FA5E000DF4F5; 248 | }; 249 | }; 250 | }; 251 | buildConfigurationList = 0C8EB58B1E88FA5E000DF4F5 /* Build configuration list for PBXProject "ReadWebsocket" */; 252 | compatibilityVersion = "Xcode 3.2"; 253 | developmentRegion = English; 254 | hasScannedForEncodings = 0; 255 | knownRegions = ( 256 | en, 257 | Base, 258 | ); 259 | mainGroup = 0C8EB5871E88FA5E000DF4F5; 260 | productRefGroup = 0C8EB5911E88FA5E000DF4F5 /* Products */; 261 | projectDirPath = ""; 262 | projectRoot = ""; 263 | targets = ( 264 | 0C8EB58F1E88FA5E000DF4F5 /* ReadWebsocket */, 265 | 0C8EB5A81E88FA5E000DF4F5 /* ReadWebsocketTests */, 266 | 0C8EB5B31E88FA5E000DF4F5 /* ReadWebsocketUITests */, 267 | ); 268 | }; 269 | /* End PBXProject section */ 270 | 271 | /* Begin PBXResourcesBuildPhase section */ 272 | 0C8EB58E1E88FA5E000DF4F5 /* Resources */ = { 273 | isa = PBXResourcesBuildPhase; 274 | buildActionMask = 2147483647; 275 | files = ( 276 | 0C8EB5A31E88FA5E000DF4F5 /* LaunchScreen.storyboard in Resources */, 277 | 0C8EB5A01E88FA5E000DF4F5 /* Assets.xcassets in Resources */, 278 | 0C8EB59E1E88FA5E000DF4F5 /* Main.storyboard in Resources */, 279 | ); 280 | runOnlyForDeploymentPostprocessing = 0; 281 | }; 282 | 0C8EB5A71E88FA5E000DF4F5 /* Resources */ = { 283 | isa = PBXResourcesBuildPhase; 284 | buildActionMask = 2147483647; 285 | files = ( 286 | ); 287 | runOnlyForDeploymentPostprocessing = 0; 288 | }; 289 | 0C8EB5B21E88FA5E000DF4F5 /* Resources */ = { 290 | isa = PBXResourcesBuildPhase; 291 | buildActionMask = 2147483647; 292 | files = ( 293 | ); 294 | runOnlyForDeploymentPostprocessing = 0; 295 | }; 296 | /* End PBXResourcesBuildPhase section */ 297 | 298 | /* Begin PBXShellScriptBuildPhase section */ 299 | A2AFC9BF80296D090F61EF46 /* [CP] Check Pods Manifest.lock */ = { 300 | isa = PBXShellScriptBuildPhase; 301 | buildActionMask = 2147483647; 302 | files = ( 303 | ); 304 | inputPaths = ( 305 | ); 306 | name = "[CP] Check Pods Manifest.lock"; 307 | outputPaths = ( 308 | ); 309 | runOnlyForDeploymentPostprocessing = 0; 310 | shellPath = /bin/sh; 311 | 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"; 312 | showEnvVarsInLog = 0; 313 | }; 314 | B631DF8E2FF0D3980A88D12F /* [CP] Embed Pods Frameworks */ = { 315 | isa = PBXShellScriptBuildPhase; 316 | buildActionMask = 2147483647; 317 | files = ( 318 | ); 319 | inputPaths = ( 320 | ); 321 | name = "[CP] Embed Pods Frameworks"; 322 | outputPaths = ( 323 | ); 324 | runOnlyForDeploymentPostprocessing = 0; 325 | shellPath = /bin/sh; 326 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ReadWebsocket/Pods-ReadWebsocket-frameworks.sh\"\n"; 327 | showEnvVarsInLog = 0; 328 | }; 329 | F2D3E0C40EC8497EB6AF1BE4 /* [CP] Copy Pods Resources */ = { 330 | isa = PBXShellScriptBuildPhase; 331 | buildActionMask = 2147483647; 332 | files = ( 333 | ); 334 | inputPaths = ( 335 | ); 336 | name = "[CP] Copy Pods Resources"; 337 | outputPaths = ( 338 | ); 339 | runOnlyForDeploymentPostprocessing = 0; 340 | shellPath = /bin/sh; 341 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ReadWebsocket/Pods-ReadWebsocket-resources.sh\"\n"; 342 | showEnvVarsInLog = 0; 343 | }; 344 | /* End PBXShellScriptBuildPhase section */ 345 | 346 | /* Begin PBXSourcesBuildPhase section */ 347 | 0C8EB58C1E88FA5E000DF4F5 /* Sources */ = { 348 | isa = PBXSourcesBuildPhase; 349 | buildActionMask = 2147483647; 350 | files = ( 351 | 0C8EB59B1E88FA5E000DF4F5 /* ViewController.m in Sources */, 352 | 0C8EB5981E88FA5E000DF4F5 /* AppDelegate.m in Sources */, 353 | 0C8EB5951E88FA5E000DF4F5 /* main.m in Sources */, 354 | ); 355 | runOnlyForDeploymentPostprocessing = 0; 356 | }; 357 | 0C8EB5A51E88FA5E000DF4F5 /* Sources */ = { 358 | isa = PBXSourcesBuildPhase; 359 | buildActionMask = 2147483647; 360 | files = ( 361 | 0C8EB5AE1E88FA5E000DF4F5 /* ReadWebsocketTests.m in Sources */, 362 | ); 363 | runOnlyForDeploymentPostprocessing = 0; 364 | }; 365 | 0C8EB5B01E88FA5E000DF4F5 /* Sources */ = { 366 | isa = PBXSourcesBuildPhase; 367 | buildActionMask = 2147483647; 368 | files = ( 369 | 0C8EB5B91E88FA5E000DF4F5 /* ReadWebsocketUITests.m in Sources */, 370 | ); 371 | runOnlyForDeploymentPostprocessing = 0; 372 | }; 373 | /* End PBXSourcesBuildPhase section */ 374 | 375 | /* Begin PBXTargetDependency section */ 376 | 0C8EB5AB1E88FA5E000DF4F5 /* PBXTargetDependency */ = { 377 | isa = PBXTargetDependency; 378 | target = 0C8EB58F1E88FA5E000DF4F5 /* ReadWebsocket */; 379 | targetProxy = 0C8EB5AA1E88FA5E000DF4F5 /* PBXContainerItemProxy */; 380 | }; 381 | 0C8EB5B61E88FA5E000DF4F5 /* PBXTargetDependency */ = { 382 | isa = PBXTargetDependency; 383 | target = 0C8EB58F1E88FA5E000DF4F5 /* ReadWebsocket */; 384 | targetProxy = 0C8EB5B51E88FA5E000DF4F5 /* PBXContainerItemProxy */; 385 | }; 386 | /* End PBXTargetDependency section */ 387 | 388 | /* Begin PBXVariantGroup section */ 389 | 0C8EB59C1E88FA5E000DF4F5 /* Main.storyboard */ = { 390 | isa = PBXVariantGroup; 391 | children = ( 392 | 0C8EB59D1E88FA5E000DF4F5 /* Base */, 393 | ); 394 | name = Main.storyboard; 395 | sourceTree = ""; 396 | }; 397 | 0C8EB5A11E88FA5E000DF4F5 /* LaunchScreen.storyboard */ = { 398 | isa = PBXVariantGroup; 399 | children = ( 400 | 0C8EB5A21E88FA5E000DF4F5 /* Base */, 401 | ); 402 | name = LaunchScreen.storyboard; 403 | sourceTree = ""; 404 | }; 405 | /* End PBXVariantGroup section */ 406 | 407 | /* Begin XCBuildConfiguration section */ 408 | 0C8EB5BB1E88FA5E000DF4F5 /* Debug */ = { 409 | isa = XCBuildConfiguration; 410 | buildSettings = { 411 | ALWAYS_SEARCH_USER_PATHS = NO; 412 | CLANG_ANALYZER_NONNULL = YES; 413 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 414 | CLANG_CXX_LIBRARY = "libc++"; 415 | CLANG_ENABLE_MODULES = YES; 416 | CLANG_ENABLE_OBJC_ARC = YES; 417 | CLANG_WARN_BOOL_CONVERSION = YES; 418 | CLANG_WARN_CONSTANT_CONVERSION = YES; 419 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 420 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 421 | CLANG_WARN_EMPTY_BODY = YES; 422 | CLANG_WARN_ENUM_CONVERSION = YES; 423 | CLANG_WARN_INFINITE_RECURSION = YES; 424 | CLANG_WARN_INT_CONVERSION = YES; 425 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 426 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 427 | CLANG_WARN_UNREACHABLE_CODE = YES; 428 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 429 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 430 | COPY_PHASE_STRIP = NO; 431 | DEBUG_INFORMATION_FORMAT = dwarf; 432 | ENABLE_STRICT_OBJC_MSGSEND = YES; 433 | ENABLE_TESTABILITY = YES; 434 | GCC_C_LANGUAGE_STANDARD = gnu99; 435 | GCC_DYNAMIC_NO_PIC = NO; 436 | GCC_NO_COMMON_BLOCKS = YES; 437 | GCC_OPTIMIZATION_LEVEL = 0; 438 | GCC_PREPROCESSOR_DEFINITIONS = ( 439 | "DEBUG=1", 440 | "$(inherited)", 441 | ); 442 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 443 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 444 | GCC_WARN_UNDECLARED_SELECTOR = YES; 445 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 446 | GCC_WARN_UNUSED_FUNCTION = YES; 447 | GCC_WARN_UNUSED_VARIABLE = YES; 448 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 449 | MTL_ENABLE_DEBUG_INFO = YES; 450 | ONLY_ACTIVE_ARCH = YES; 451 | SDKROOT = iphoneos; 452 | TARGETED_DEVICE_FAMILY = "1,2"; 453 | }; 454 | name = Debug; 455 | }; 456 | 0C8EB5BC1E88FA5E000DF4F5 /* Release */ = { 457 | isa = XCBuildConfiguration; 458 | buildSettings = { 459 | ALWAYS_SEARCH_USER_PATHS = NO; 460 | CLANG_ANALYZER_NONNULL = YES; 461 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 462 | CLANG_CXX_LIBRARY = "libc++"; 463 | CLANG_ENABLE_MODULES = YES; 464 | CLANG_ENABLE_OBJC_ARC = YES; 465 | CLANG_WARN_BOOL_CONVERSION = YES; 466 | CLANG_WARN_CONSTANT_CONVERSION = YES; 467 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 468 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 469 | CLANG_WARN_EMPTY_BODY = YES; 470 | CLANG_WARN_ENUM_CONVERSION = YES; 471 | CLANG_WARN_INFINITE_RECURSION = YES; 472 | CLANG_WARN_INT_CONVERSION = YES; 473 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 474 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 475 | CLANG_WARN_UNREACHABLE_CODE = YES; 476 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 477 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 478 | COPY_PHASE_STRIP = NO; 479 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 480 | ENABLE_NS_ASSERTIONS = NO; 481 | ENABLE_STRICT_OBJC_MSGSEND = YES; 482 | GCC_C_LANGUAGE_STANDARD = gnu99; 483 | GCC_NO_COMMON_BLOCKS = YES; 484 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 485 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 486 | GCC_WARN_UNDECLARED_SELECTOR = YES; 487 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 488 | GCC_WARN_UNUSED_FUNCTION = YES; 489 | GCC_WARN_UNUSED_VARIABLE = YES; 490 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 491 | MTL_ENABLE_DEBUG_INFO = NO; 492 | SDKROOT = iphoneos; 493 | TARGETED_DEVICE_FAMILY = "1,2"; 494 | VALIDATE_PRODUCT = YES; 495 | }; 496 | name = Release; 497 | }; 498 | 0C8EB5BE1E88FA5E000DF4F5 /* Debug */ = { 499 | isa = XCBuildConfiguration; 500 | baseConfigurationReference = C8AAF986CEB671236B392C1C /* Pods-ReadWebsocket.debug.xcconfig */; 501 | buildSettings = { 502 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 503 | INFOPLIST_FILE = ReadWebsocket/Info.plist; 504 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 505 | PRODUCT_BUNDLE_IDENTIFIER = com.tt.tyh.ReadWebsocket; 506 | PRODUCT_NAME = "$(TARGET_NAME)"; 507 | }; 508 | name = Debug; 509 | }; 510 | 0C8EB5BF1E88FA5E000DF4F5 /* Release */ = { 511 | isa = XCBuildConfiguration; 512 | baseConfigurationReference = FB4DD786F861EC579C14F368 /* Pods-ReadWebsocket.release.xcconfig */; 513 | buildSettings = { 514 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 515 | INFOPLIST_FILE = ReadWebsocket/Info.plist; 516 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 517 | PRODUCT_BUNDLE_IDENTIFIER = com.tt.tyh.ReadWebsocket; 518 | PRODUCT_NAME = "$(TARGET_NAME)"; 519 | }; 520 | name = Release; 521 | }; 522 | 0C8EB5C11E88FA5E000DF4F5 /* Debug */ = { 523 | isa = XCBuildConfiguration; 524 | buildSettings = { 525 | BUNDLE_LOADER = "$(TEST_HOST)"; 526 | INFOPLIST_FILE = ReadWebsocketTests/Info.plist; 527 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 528 | PRODUCT_BUNDLE_IDENTIFIER = com.tt.tyh.ReadWebsocketTests; 529 | PRODUCT_NAME = "$(TARGET_NAME)"; 530 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ReadWebsocket.app/ReadWebsocket"; 531 | }; 532 | name = Debug; 533 | }; 534 | 0C8EB5C21E88FA5E000DF4F5 /* Release */ = { 535 | isa = XCBuildConfiguration; 536 | buildSettings = { 537 | BUNDLE_LOADER = "$(TEST_HOST)"; 538 | INFOPLIST_FILE = ReadWebsocketTests/Info.plist; 539 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 540 | PRODUCT_BUNDLE_IDENTIFIER = com.tt.tyh.ReadWebsocketTests; 541 | PRODUCT_NAME = "$(TARGET_NAME)"; 542 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ReadWebsocket.app/ReadWebsocket"; 543 | }; 544 | name = Release; 545 | }; 546 | 0C8EB5C41E88FA5E000DF4F5 /* Debug */ = { 547 | isa = XCBuildConfiguration; 548 | buildSettings = { 549 | INFOPLIST_FILE = ReadWebsocketUITests/Info.plist; 550 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 551 | PRODUCT_BUNDLE_IDENTIFIER = com.tt.tyh.ReadWebsocketUITests; 552 | PRODUCT_NAME = "$(TARGET_NAME)"; 553 | TEST_TARGET_NAME = ReadWebsocket; 554 | }; 555 | name = Debug; 556 | }; 557 | 0C8EB5C51E88FA5E000DF4F5 /* Release */ = { 558 | isa = XCBuildConfiguration; 559 | buildSettings = { 560 | INFOPLIST_FILE = ReadWebsocketUITests/Info.plist; 561 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 562 | PRODUCT_BUNDLE_IDENTIFIER = com.tt.tyh.ReadWebsocketUITests; 563 | PRODUCT_NAME = "$(TARGET_NAME)"; 564 | TEST_TARGET_NAME = ReadWebsocket; 565 | }; 566 | name = Release; 567 | }; 568 | /* End XCBuildConfiguration section */ 569 | 570 | /* Begin XCConfigurationList section */ 571 | 0C8EB58B1E88FA5E000DF4F5 /* Build configuration list for PBXProject "ReadWebsocket" */ = { 572 | isa = XCConfigurationList; 573 | buildConfigurations = ( 574 | 0C8EB5BB1E88FA5E000DF4F5 /* Debug */, 575 | 0C8EB5BC1E88FA5E000DF4F5 /* Release */, 576 | ); 577 | defaultConfigurationIsVisible = 0; 578 | defaultConfigurationName = Release; 579 | }; 580 | 0C8EB5BD1E88FA5E000DF4F5 /* Build configuration list for PBXNativeTarget "ReadWebsocket" */ = { 581 | isa = XCConfigurationList; 582 | buildConfigurations = ( 583 | 0C8EB5BE1E88FA5E000DF4F5 /* Debug */, 584 | 0C8EB5BF1E88FA5E000DF4F5 /* Release */, 585 | ); 586 | defaultConfigurationIsVisible = 0; 587 | }; 588 | 0C8EB5C01E88FA5E000DF4F5 /* Build configuration list for PBXNativeTarget "ReadWebsocketTests" */ = { 589 | isa = XCConfigurationList; 590 | buildConfigurations = ( 591 | 0C8EB5C11E88FA5E000DF4F5 /* Debug */, 592 | 0C8EB5C21E88FA5E000DF4F5 /* Release */, 593 | ); 594 | defaultConfigurationIsVisible = 0; 595 | }; 596 | 0C8EB5C31E88FA5E000DF4F5 /* Build configuration list for PBXNativeTarget "ReadWebsocketUITests" */ = { 597 | isa = XCConfigurationList; 598 | buildConfigurations = ( 599 | 0C8EB5C41E88FA5E000DF4F5 /* Debug */, 600 | 0C8EB5C51E88FA5E000DF4F5 /* Release */, 601 | ); 602 | defaultConfigurationIsVisible = 0; 603 | }; 604 | /* End XCConfigurationList section */ 605 | }; 606 | rootObject = 0C8EB5881E88FA5E000DF4F5 /* Project object */; 607 | } 608 | -------------------------------------------------------------------------------- /ReadWebsocket/ReadWebsocket.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ReadWebsocket/ReadWebsocket.xcodeproj/project.xcworkspace/xcuserdata/tyh.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuyaohui/SRWebSocketTip/2ee7e1d92b4829a9e6c463342ae56945c4c70de1/ReadWebsocket/ReadWebsocket.xcodeproj/project.xcworkspace/xcuserdata/tyh.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ReadWebsocket/ReadWebsocket.xcodeproj/xcuserdata/tyh.xcuserdatad/xcschemes/ReadWebsocket.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 | -------------------------------------------------------------------------------- /ReadWebsocket/ReadWebsocket.xcodeproj/xcuserdata/tyh.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ReadWebsocket.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 0C8EB58F1E88FA5E000DF4F5 16 | 17 | primary 18 | 19 | 20 | 0C8EB5A81E88FA5E000DF4F5 21 | 22 | primary 23 | 24 | 25 | 0C8EB5B31E88FA5E000DF4F5 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /ReadWebsocket/ReadWebsocket.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ReadWebsocket/ReadWebsocket.xcworkspace/xcuserdata/tyh.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuyaohui/SRWebSocketTip/2ee7e1d92b4829a9e6c463342ae56945c4c70de1/ReadWebsocket/ReadWebsocket.xcworkspace/xcuserdata/tyh.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ReadWebsocket/ReadWebsocket.xcworkspace/xcuserdata/tyh.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ReadWebsocket/ReadWebsocket/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // ReadWebsocket 4 | // 5 | // Created by tyh on 2017/3/27. 6 | // Copyright © 2017年 tyh. 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 | -------------------------------------------------------------------------------- /ReadWebsocket/ReadWebsocket/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // ReadWebsocket 4 | // 5 | // Created by tyh on 2017/3/27. 6 | // Copyright © 2017年 tyh. 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 | -------------------------------------------------------------------------------- /ReadWebsocket/ReadWebsocket/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 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /ReadWebsocket/ReadWebsocket/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 | -------------------------------------------------------------------------------- /ReadWebsocket/ReadWebsocket/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 | -------------------------------------------------------------------------------- /ReadWebsocket/ReadWebsocket/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 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ReadWebsocket/ReadWebsocket/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // ReadWebsocket 4 | // 5 | // Created by tyh on 2017/3/27. 6 | // Copyright © 2017年 tyh. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /ReadWebsocket/ReadWebsocket/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // ReadWebsocket 4 | // 5 | // Created by tyh on 2017/3/27. 6 | // Copyright © 2017年 tyh. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "SRWebSocket.h" 11 | 12 | @interface ViewController () 13 | { 14 | SRWebSocket *_socket; 15 | } 16 | @end 17 | 18 | @implementation ViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | [self connectServer:@"localhost" port:@"3000"]; 23 | } 24 | 25 | 26 | - (void)didReceiveMemoryWarning { 27 | [super didReceiveMemoryWarning]; 28 | // Dispose of any resources that can be recreated. 29 | } 30 | 31 | //初始化socket并且连接 32 | - (void)connectServer:(NSString *)server port:(NSString *)port 33 | { 34 | 35 | NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"ws://%@:%@",server,port]]]; 36 | _socket = [[SRWebSocket alloc] initWithURLRequest:request]; 37 | _socket.delegate = self; 38 | [_socket open]; 39 | } 40 | 41 | - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message 42 | { 43 | 44 | } 45 | - (void)webSocketDidOpen:(SRWebSocket *)webSocket 46 | { 47 | 48 | } 49 | - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error 50 | { 51 | 52 | } 53 | - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean 54 | { 55 | 56 | } 57 | - (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload 58 | { 59 | 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /ReadWebsocket/ReadWebsocket/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ReadWebsocket 4 | // 5 | // Created by tyh on 2017/3/27. 6 | // Copyright © 2017年 tyh. 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 | -------------------------------------------------------------------------------- /ReadWebsocket/ReadWebsocketTests/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 | -------------------------------------------------------------------------------- /ReadWebsocket/ReadWebsocketTests/ReadWebsocketTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ReadWebsocketTests.m 3 | // ReadWebsocketTests 4 | // 5 | // Created by tyh on 2017/3/27. 6 | // Copyright © 2017年 tyh. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ReadWebsocketTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation ReadWebsocketTests 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 | -------------------------------------------------------------------------------- /ReadWebsocket/ReadWebsocketUITests/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 | -------------------------------------------------------------------------------- /ReadWebsocket/ReadWebsocketUITests/ReadWebsocketUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ReadWebsocketUITests.m 3 | // ReadWebsocketUITests 4 | // 5 | // Created by tyh on 2017/3/27. 6 | // Copyright © 2017年 tyh. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ReadWebsocketUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation ReadWebsocketUITests 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 | -------------------------------------------------------------------------------- /ReadWebsocket/podfile: -------------------------------------------------------------------------------- 1 | platform :ios,"7.0" 2 | 3 | target "ReadWebsocket" do 4 | pod 'SocketRocket' 5 | 6 | end 7 | 8 | --------------------------------------------------------------------------------