Browse Source

重构代码

leon 2 years ago
parent
commit
e7704f3638
29 changed files with 5124 additions and 356 deletions
  1. BIN
      .DS_Store
  2. BIN
      DanceGame/Classes/FitfunTool/FitfunSDK_V2.0/KpSupport/SDK/.DS_Store
  3. 9 2
      DanceGame/Classes/FitfunTool/FitfunSDK_V2.0/KpSupport/SDK/BLE/BTDataProcess.mm
  4. BIN
      DanceGame/Classes/FitfunTool/FitfunSDK_V2.0/KpSupport/SDK/HTTP/.DS_Store
  5. 154 0
      DanceGame/Classes/FitfunTool/FitfunSDK_V2.0/KpSupport/SDK/HTTP/socket/SRWebSocket.h
  6. 1916 0
      DanceGame/Classes/FitfunTool/FitfunSDK_V2.0/KpSupport/SDK/HTTP/socket/SRWebSocket.m
  7. 23 0
      DanceGame/Classes/FitfunTool/FitfunSDK_V2.0/KpSupport/SDK/HTTP/socket/SocketRocketUtility.h
  8. 301 0
      DanceGame/Classes/FitfunTool/FitfunSDK_V2.0/KpSupport/SDK/HTTP/socket/SocketRocketUtility.m
  9. 1 0
      DanceGame/Classes/FitfunTool/FitfunSDK_V2.0/KpSupport/SDK/shoes_sdk/InertialTrajProcess.cpp
  10. 43 23
      DanceGame/Unity-iPhone.xcodeproj/project.pbxproj
  11. BIN
      DanceGame/Unity-iPhone.xcodeproj/project.xcworkspace/xcuserdata/duowan123.xcuserdatad/UserInterfaceState.xcuserstate
  12. BIN
      RunGame/SDK/.DS_Store
  13. 6 22
      RunGame/SDK/BLE/BTDataProcess.h
  14. 202 40
      RunGame/SDK/BLE/BTDataProcess.mm
  15. BIN
      RunGame/SDK/HTTP/.DS_Store
  16. 154 0
      RunGame/SDK/HTTP/socket/SRWebSocket.h
  17. 1916 0
      RunGame/SDK/HTTP/socket/SRWebSocket.m
  18. 23 0
      RunGame/SDK/HTTP/socket/SocketRocketUtility.h
  19. 301 0
      RunGame/SDK/HTTP/socket/SocketRocketUtility.m
  20. 11 0
      RunGame/SDK/IOSPlatformSDK.h
  21. 33 179
      RunGame/SDK/IOSPlatformSDK.mm
  22. 0 78
      RunGame/SDK/PrefixHeader.h
  23. 0 2
      RunGame/SDK/Tool/MYFactoryManager.h
  24. 0 2
      RunGame/SDK/UI/SearchDeviceViewController.h
  25. 1 1
      RunGame/SDK/UI/SearchDeviceViewController.m
  26. 1 0
      RunGame/SDK/shoes_sdk/InertialTrajProcess.cpp
  27. 1 3
      RunGame/SDK/shoes_sdk/SingleFootAction.h
  28. 28 4
      RunGame/Unity-iPhone.xcodeproj/project.pbxproj
  29. BIN
      RunGame/Unity-iPhone.xcodeproj/project.xcworkspace/xcuserdata/duowan123.xcuserdatad/UserInterfaceState.xcuserstate

BIN
.DS_Store


BIN
DanceGame/Classes/FitfunTool/FitfunSDK_V2.0/KpSupport/SDK/.DS_Store


+ 9 - 2
DanceGame/Classes/FitfunTool/FitfunSDK_V2.0/KpSupport/SDK/BLE/BTDataProcess.mm

@@ -10,7 +10,7 @@
 #import "IOSPlatformSDK.h"
 #include "Game.h"
 #import <AudioToolbox/AudioToolbox.h>
-
+#import "SocketRocketUtility.h"
 //#define NSLog(format, ...) printf("TIME:%s FILE:%s(%d行) FUNCTION:%s %s\n",__TIME__, [[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__, __PRETTY_FUNCTION__, [[NSString stringWithFormat:(format), ##__VA_ARGS__] UTF8String])
 @interface BTDataProcess ()<NSURLSessionDataDelegate>
 {
@@ -159,6 +159,10 @@ void disConnectBle (CFNotificationCenterRef center, void *observer, CFStringRef
 #pragma mark ============================================================== leon Ble manager
 //测试自己封装的蓝牙类
 -(void)initCBCentralManager{
+    
+    //建立socket链接
+    SocketRocketUtility * install = [SocketRocketUtility sharedInstance];
+    [install SRWebSocketOpen];
 
     if (LEManager.centralManager == nil && LEManager.peripheral == nil && LEManager.vicePeripheral == nil){
         [LEManager initCBCentralManager];
@@ -308,7 +312,8 @@ void disConnectBle (CFNotificationCenterRef center, void *observer, CFStringRef
                 break;
         }
         //
-        self.game_type = self.game_id;
+        self.game_id = GAME_TYPE_DANCE;
+        self.game_type = GAME_TYPE_DANCE;
         self.currentTS = 1000;
         //
         NSLog(@"BTDataProcess connectPeripheral self.deviceType ==>> %ld  self.game_id ==>> %d   mac ==>>  %@",(long)self.deviceType,self.game_id,self.macAddress);
@@ -749,6 +754,8 @@ void disConnectBle (CFNotificationCenterRef center, void *observer, CFStringRef
     
     /********************剑波 & 威严 要的图表数据 *****************/
     NSString * aString = [NSString stringWithUTF8String:game->getGameDataStr().c_str()];
+    //上传即使动作数据
+    [[SocketRocketUtility sharedInstance] sendData:nil withRequestURI:aString];
     if (self.RSSI==nil){
         self.RSSI = 0;
     }

BIN
DanceGame/Classes/FitfunTool/FitfunSDK_V2.0/KpSupport/SDK/HTTP/.DS_Store


+ 154 - 0
DanceGame/Classes/FitfunTool/FitfunSDK_V2.0/KpSupport/SDK/HTTP/socket/SRWebSocket.h

@@ -0,0 +1,154 @@
+//
+//   Copyright 2012 Square Inc.
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+//
+
+#import <Foundation/Foundation.h>
+#import <Security/SecCertificate.h>
+
+typedef NS_ENUM(NSInteger, SRReadyState) {
+    SR_CONNECTING   = 0,
+    SR_OPEN         = 1,
+    SR_CLOSING      = 2,
+    SR_CLOSED       = 3,
+};
+
+typedef enum SRStatusCode : NSInteger {
+    // 0–999: Reserved and not used.
+    SRStatusCodeNormal = 1000,
+    SRStatusCodeGoingAway = 1001,
+    SRStatusCodeProtocolError = 1002,
+    SRStatusCodeUnhandledType = 1003,
+    // 1004 reserved.
+    SRStatusNoStatusReceived = 1005,
+    SRStatusCodeAbnormal = 1006,
+    SRStatusCodeInvalidUTF8 = 1007,
+    SRStatusCodePolicyViolated = 1008,
+    SRStatusCodeMessageTooBig = 1009,
+    SRStatusCodeMissingExtension = 1010,
+    SRStatusCodeInternalError = 1011,
+    SRStatusCodeServiceRestart = 1012,
+    SRStatusCodeTryAgainLater = 1013,
+    // 1014: Reserved for future use by the WebSocket standard.
+    SRStatusCodeTLSHandshake = 1015,
+    // 1016–1999: Reserved for future use by the WebSocket standard.
+    // 2000–2999: Reserved for use by WebSocket extensions.
+    // 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.
+    // 4000–4999: Available for use by applications.
+} SRStatusCode;
+
+@class SRWebSocket;
+
+extern NSString *const SRWebSocketErrorDomain;
+extern NSString *const SRHTTPResponseErrorKey;
+
+#pragma mark - SRWebSocketDelegate
+
+@protocol SRWebSocketDelegate;
+
+#pragma mark - SRWebSocket
+
+@interface SRWebSocket : NSObject <NSStreamDelegate>
+
+@property (nonatomic, weak) id <SRWebSocketDelegate> delegate;
+
+@property (nonatomic, readonly) SRReadyState readyState;
+@property (nonatomic, readonly, retain) NSURL *url;
+
+
+@property (nonatomic, readonly) CFHTTPMessageRef receivedHTTPHeaders;
+
+// Optional array of cookies (NSHTTPCookie objects) to apply to the connections
+@property (nonatomic, readwrite) NSArray * requestCookies;
+
+// This returns the negotiated protocol.
+// It will be nil until after the handshake completes.
+@property (nonatomic, readonly, copy) NSString *protocol;
+
+// Protocols should be an array of strings that turn into Sec-WebSocket-Protocol.
+- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates;
+- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols;
+- (id)initWithURLRequest:(NSURLRequest *)request;
+
+// Some helper constructors.
+- (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates;
+- (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols;
+- (id)initWithURL:(NSURL *)url;
+
+// Delegate queue will be dispatch_main_queue by default.
+// You cannot set both OperationQueue and dispatch_queue.
+- (void)setDelegateOperationQueue:(NSOperationQueue*) queue;
+- (void)setDelegateDispatchQueue:(dispatch_queue_t) queue;
+
+// By default, it will schedule itself on +[NSRunLoop SR_networkRunLoop] using defaultModes.
+- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
+- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
+
+// SRWebSockets are intended for one-time-use only.  Open should be called once and only once.
+- (void)open;
+
+- (void)close;
+- (void)closeWithCode:(NSInteger)code reason:(NSString *)reason;
+
+// Send a UTF8 String or Data.
+- (void)send:(id)data;
+
+// Send Data (can be nil) in a ping message.
+- (void)sendPing:(NSData *)data;
+
+@end
+
+#pragma mark - SRWebSocketDelegate
+
+@protocol SRWebSocketDelegate <NSObject>
+
+// message will either be an NSString if the server is using text
+// or NSData if the server is using binary.
+- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message;
+
+@optional
+
+- (void)webSocketDidOpen:(SRWebSocket *)webSocket;
+- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;
+- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;
+- (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload;
+
+// Return YES to convert messages sent as Text to an NSString. Return NO to skip NSData -> NSString conversion for Text messages. Defaults to YES.
+- (BOOL)webSocketShouldConvertTextFrameToString:(SRWebSocket *)webSocket;
+
+@end
+
+#pragma mark - NSURLRequest (SRCertificateAdditions)
+
+@interface NSURLRequest (SRCertificateAdditions)
+
+@property (nonatomic, retain, readonly) NSArray *SR_SSLPinnedCertificates;
+
+@end
+
+#pragma mark - NSMutableURLRequest (SRCertificateAdditions)
+
+@interface NSMutableURLRequest (SRCertificateAdditions)
+
+@property (nonatomic, retain) NSArray *SR_SSLPinnedCertificates;
+
+@end
+
+#pragma mark - NSRunLoop (SRWebSocket)
+
+@interface NSRunLoop (SRWebSocket)
+
++ (NSRunLoop *)SR_networkRunLoop;
+
+@end

+ 1916 - 0
DanceGame/Classes/FitfunTool/FitfunSDK_V2.0/KpSupport/SDK/HTTP/socket/SRWebSocket.m

@@ -0,0 +1,1916 @@
+//
+//   Copyright 2012 Square Inc.
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+//
+
+
+#import "SRWebSocket.h"
+
+#if TARGET_OS_IPHONE
+#define HAS_ICU
+#endif
+
+#ifdef HAS_ICU
+#import <unicode/utf8.h>
+#endif
+
+#if TARGET_OS_IPHONE
+#import <Endian.h>
+#else
+#import <CoreServices/CoreServices.h>
+#endif
+
+#import <CommonCrypto/CommonDigest.h>
+#import <Security/SecRandom.h>
+
+#if OS_OBJECT_USE_OBJC_RETAIN_RELEASE
+#define sr_dispatch_retain(x)
+#define sr_dispatch_release(x)
+#define maybe_bridge(x) ((__bridge void *) x)
+#else
+#define sr_dispatch_retain(x) dispatch_retain(x)
+#define sr_dispatch_release(x) dispatch_release(x)
+#define maybe_bridge(x) (x)
+#endif
+
+#if !__has_feature(objc_arc) 
+#error SocketRocket must be compiled with ARC enabled
+#endif
+
+
+typedef enum  {
+    SROpCodeTextFrame = 0x1,
+    SROpCodeBinaryFrame = 0x2,
+    // 3-7 reserved.
+    SROpCodeConnectionClose = 0x8,
+    SROpCodePing = 0x9,
+    SROpCodePong = 0xA,
+    // B-F reserved.
+} SROpCode;
+
+typedef struct {
+    BOOL fin;
+//  BOOL rsv1;
+//  BOOL rsv2;
+//  BOOL rsv3;
+    uint8_t opcode;
+    BOOL masked;
+    uint64_t payload_length;
+} frame_header;
+
+static NSString *const SRWebSocketAppendToSecKeyString = @"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+
+static inline int32_t validate_dispatch_data_partial_string(NSData *data);
+static inline void SRFastLog(NSString *format, ...);
+
+@interface NSData (SRWebSocket)
+
+- (NSString *)stringBySHA1ThenBase64Encoding;
+
+@end
+
+
+@interface NSString (SRWebSocket)
+
+- (NSString *)stringBySHA1ThenBase64Encoding;
+
+@end
+
+
+@interface NSURL (SRWebSocket)
+
+// The origin isn't really applicable for a native application.
+// So instead, just map ws -> http and wss -> https.
+- (NSString *)SR_origin;
+
+@end
+
+
+@interface _SRRunLoopThread : NSThread
+
+@property (nonatomic, readonly) NSRunLoop *runLoop;
+
+@end
+
+
+static NSString *newSHA1String(const char *bytes, size_t length) {
+    uint8_t md[CC_SHA1_DIGEST_LENGTH];
+
+    assert(length >= 0);
+    assert(length <= UINT32_MAX);
+    CC_SHA1(bytes, (CC_LONG)length, md);
+    
+    NSData *data = [NSData dataWithBytes:md length:CC_SHA1_DIGEST_LENGTH];
+    
+    if ([data respondsToSelector:@selector(base64EncodedStringWithOptions:)]) {
+        return [data base64EncodedStringWithOptions:0];
+    }
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    return [data base64Encoding];
+#pragma clang diagnostic pop
+}
+
+@implementation NSData (SRWebSocket)
+
+- (NSString *)stringBySHA1ThenBase64Encoding;
+{
+    return newSHA1String(self.bytes, self.length);
+}
+
+@end
+
+
+@implementation NSString (SRWebSocket)
+
+- (NSString *)stringBySHA1ThenBase64Encoding;
+{
+    return newSHA1String(self.UTF8String, self.length);
+}
+
+@end
+
+NSString *const SRWebSocketErrorDomain = @"SRWebSocketErrorDomain";
+NSString *const SRHTTPResponseErrorKey = @"HTTPResponseStatusCode";
+
+// Returns number of bytes consumed. Returning 0 means you didn't match.
+// Sends bytes to callback handler;
+typedef size_t (^stream_scanner)(NSData *collected_data);
+
+typedef void (^data_callback)(SRWebSocket *webSocket,  NSData *data);
+
+@interface SRIOConsumer : NSObject {
+    stream_scanner _scanner;
+    data_callback _handler;
+    size_t _bytesNeeded;
+    BOOL _readToCurrentFrame;
+    BOOL _unmaskBytes;
+}
+@property (nonatomic, copy, readonly) stream_scanner consumer;
+@property (nonatomic, copy, readonly) data_callback handler;
+@property (nonatomic, assign) size_t bytesNeeded;
+@property (nonatomic, assign, readonly) BOOL readToCurrentFrame;
+@property (nonatomic, assign, readonly) BOOL unmaskBytes;
+
+@end
+
+// This class is not thread-safe, and is expected to always be run on the same queue.
+@interface SRIOConsumerPool : NSObject
+
+- (id)initWithBufferCapacity:(NSUInteger)poolSize;
+
+- (SRIOConsumer *)consumerWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes;
+- (void)returnConsumer:(SRIOConsumer *)consumer;
+
+@end
+
+@interface SRWebSocket ()  <NSStreamDelegate>
+
+@property (nonatomic) SRReadyState readyState;
+
+@property (nonatomic) NSOperationQueue *delegateOperationQueue;
+@property (nonatomic) dispatch_queue_t delegateDispatchQueue;
+
+// Specifies whether SSL trust chain should NOT be evaluated.
+// By default this flag is set to NO, meaning only secure SSL connections are allowed.
+// For DEBUG builds this flag is ignored, and SSL connections are allowed regardless
+// of the certificate trust configuration
+@property (nonatomic, readwrite) BOOL allowsUntrustedSSLCertificates;
+
+@end
+
+
+@implementation SRWebSocket {
+    NSInteger _webSocketVersion;
+    
+    NSOperationQueue *_delegateOperationQueue;
+    dispatch_queue_t _delegateDispatchQueue;
+    
+    dispatch_queue_t _workQueue;
+    NSMutableArray *_consumers;
+
+    NSInputStream *_inputStream;
+    NSOutputStream *_outputStream;
+   
+    NSMutableData *_readBuffer;
+    NSUInteger _readBufferOffset;
+ 
+    NSMutableData *_outputBuffer;
+    NSUInteger _outputBufferOffset;
+
+    uint8_t _currentFrameOpcode;
+    size_t _currentFrameCount;
+    size_t _readOpCount;
+    uint32_t _currentStringScanPosition;
+    NSMutableData *_currentFrameData;
+    
+    NSString *_closeReason;
+    
+    NSString *_secKey;
+    NSString *_basicAuthorizationString;
+    
+    BOOL _pinnedCertFound;
+    
+    uint8_t _currentReadMaskKey[4];
+    size_t _currentReadMaskOffset;
+
+    BOOL _consumerStopped;
+    
+    BOOL _closeWhenFinishedWriting;
+    BOOL _failed;
+
+    BOOL _secure;
+    NSURLRequest *_urlRequest;
+
+    BOOL _sentClose;
+    BOOL _didFail;
+    BOOL _cleanupScheduled;
+    int _closeCode;
+    
+    BOOL _isPumping;
+    
+    NSMutableSet *_scheduledRunloops;
+    
+    // We use this to retain ourselves.
+    __strong SRWebSocket *_selfRetain;
+    
+    NSArray *_requestedProtocols;
+    SRIOConsumerPool *_consumerPool;
+}
+
+@synthesize delegate = _delegate;
+@synthesize url = _url;
+@synthesize readyState = _readyState;
+@synthesize protocol = _protocol;
+
+static __strong NSData *CRLFCRLF;
+
++ (void)initialize;
+{
+    CRLFCRLF = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4];
+}
+
+- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates;
+{
+    self = [super init];
+    if (self) {
+        assert(request.URL);
+        _url = request.URL;
+        _urlRequest = request;
+        _allowsUntrustedSSLCertificates = allowsUntrustedSSLCertificates;
+        
+        _requestedProtocols = [protocols copy];
+        
+        [self _SR_commonInit];
+    }
+    
+    return self;
+}
+
+- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols;
+{
+    return [self initWithURLRequest:request protocols:protocols allowsUntrustedSSLCertificates:NO];
+}
+
+- (id)initWithURLRequest:(NSURLRequest *)request;
+{
+    return [self initWithURLRequest:request protocols:nil];
+}
+
+- (id)initWithURL:(NSURL *)url;
+{
+    return [self initWithURL:url protocols:nil];
+}
+
+- (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols;
+{
+    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];    
+    return [self initWithURLRequest:request protocols:protocols];
+}
+
+- (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates;
+{
+    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
+    return [self initWithURLRequest:request protocols:protocols allowsUntrustedSSLCertificates:allowsUntrustedSSLCertificates];
+}
+
+- (void)_SR_commonInit;
+{
+    NSString *scheme = _url.scheme.lowercaseString;
+    assert([scheme isEqualToString:@"ws"] || [scheme isEqualToString:@"http"] || [scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]);
+    
+    if ([scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]) {
+        _secure = YES;
+    }
+    
+    _readyState = SR_CONNECTING;
+    _consumerStopped = YES;
+    _webSocketVersion = 13;
+    
+    _workQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
+    
+    // Going to set a specific on the queue so we can validate we're on the work queue
+    dispatch_queue_set_specific(_workQueue, (__bridge void *)self, maybe_bridge(_workQueue), NULL);
+    
+    _delegateDispatchQueue = dispatch_get_main_queue();
+    sr_dispatch_retain(_delegateDispatchQueue);
+    
+    _readBuffer = [[NSMutableData alloc] init];
+    _outputBuffer = [[NSMutableData alloc] init];
+    
+    _currentFrameData = [[NSMutableData alloc] init];
+
+    _consumers = [[NSMutableArray alloc] init];
+    
+    _consumerPool = [[SRIOConsumerPool alloc] init];
+    
+    _scheduledRunloops = [[NSMutableSet alloc] init];
+    
+    [self _initializeStreams];
+    
+    // default handlers
+}
+
+- (void)assertOnWorkQueue;
+{
+    assert(dispatch_get_specific((__bridge void *)self) == maybe_bridge(_workQueue));
+}
+
+- (void)dealloc
+{
+    _inputStream.delegate = nil;
+    _outputStream.delegate = nil;
+
+    [_inputStream close];
+    [_outputStream close];
+    
+    if (_workQueue) {
+        sr_dispatch_release(_workQueue);
+        _workQueue = NULL;
+    }
+    
+    if (_receivedHTTPHeaders) {
+        CFRelease(_receivedHTTPHeaders);
+        _receivedHTTPHeaders = NULL;
+    }
+    
+    if (_delegateDispatchQueue) {
+        sr_dispatch_release(_delegateDispatchQueue);
+        _delegateDispatchQueue = NULL;
+    }
+}
+
+#ifndef NDEBUG
+
+- (void)setReadyState:(SRReadyState)aReadyState;
+{
+    assert(aReadyState > _readyState);
+    _readyState = aReadyState;
+}
+
+#endif
+
+- (void)open;
+{
+    assert(_url);
+    NSAssert(_readyState == SR_CONNECTING, @"Cannot call -(void)open on SRWebSocket more than once");
+
+    _selfRetain = self;
+
+    if (_urlRequest.timeoutInterval > 0)
+    {
+        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, _urlRequest.timeoutInterval * NSEC_PER_SEC);
+        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
+            if (self.readyState == SR_CONNECTING)
+                [self _failWithError:[NSError errorWithDomain:@"com.squareup.SocketRocket" code:504 userInfo:@{NSLocalizedDescriptionKey: @"Timeout Connecting to Server"}]];
+        });
+    }
+
+    [self openConnection];
+}
+
+// Calls block on delegate queue
+- (void)_performDelegateBlock:(dispatch_block_t)block;
+{
+    if (_delegateOperationQueue) {
+        [_delegateOperationQueue addOperationWithBlock:block];
+    } else {
+        assert(_delegateDispatchQueue);
+        dispatch_async(_delegateDispatchQueue, block);
+    }
+}
+
+- (void)setDelegateDispatchQueue:(dispatch_queue_t)queue;
+{
+    if (queue) {
+        sr_dispatch_retain(queue);
+    }
+    
+    if (_delegateDispatchQueue) {
+        sr_dispatch_release(_delegateDispatchQueue);
+    }
+    
+    _delegateDispatchQueue = queue;
+}
+
+- (BOOL)_checkHandshake:(CFHTTPMessageRef)httpMessage;
+{
+    NSString *acceptHeader = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(httpMessage, CFSTR("Sec-WebSocket-Accept")));
+
+    if (acceptHeader == nil) {
+        return NO;
+    }
+    
+    NSString *concattedString = [_secKey stringByAppendingString:SRWebSocketAppendToSecKeyString];
+    NSString *expectedAccept = [concattedString stringBySHA1ThenBase64Encoding];
+    
+    return [acceptHeader isEqualToString:expectedAccept];
+}
+
+- (void)_HTTPHeadersDidFinish;
+{
+    NSInteger responseCode = CFHTTPMessageGetResponseStatusCode(_receivedHTTPHeaders);
+    
+    if (responseCode >= 400) {
+        SRFastLog(@"Request failed with response code %d", responseCode);
+        [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2132 userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"received bad response code from server %ld", (long)responseCode], SRHTTPResponseErrorKey:@(responseCode)}]];
+        return;
+    }
+    
+    if(![self _checkHandshake:_receivedHTTPHeaders]) {
+        [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2133 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Invalid Sec-WebSocket-Accept response"] forKey:NSLocalizedDescriptionKey]]];
+        return;
+    }
+    
+    NSString *negotiatedProtocol = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(_receivedHTTPHeaders, CFSTR("Sec-WebSocket-Protocol")));
+    if (negotiatedProtocol) {
+        // Make sure we requested the protocol
+        if ([_requestedProtocols indexOfObject:negotiatedProtocol] == NSNotFound) {
+            [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2133 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Server specified Sec-WebSocket-Protocol that wasn't requested"] forKey:NSLocalizedDescriptionKey]]];
+            return;
+        }
+        
+        _protocol = negotiatedProtocol;
+    }
+    
+    self.readyState = SR_OPEN;
+    
+    if (!_didFail) {
+        [self _readFrameNew];
+    }
+
+    [self _performDelegateBlock:^{
+        if ([self.delegate respondsToSelector:@selector(webSocketDidOpen:)]) {
+            [self.delegate webSocketDidOpen:self];
+        };
+    }];
+}
+
+
+- (void)_readHTTPHeader;
+{
+    if (_receivedHTTPHeaders == NULL) {
+        _receivedHTTPHeaders = CFHTTPMessageCreateEmpty(NULL, NO);
+    }
+                        
+    [self _readUntilHeaderCompleteWithCallback:^(SRWebSocket *self,  NSData *data) {
+        CFHTTPMessageAppendBytes(_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length);
+        
+        if (CFHTTPMessageIsHeaderComplete(_receivedHTTPHeaders)) {
+            SRFastLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_receivedHTTPHeaders)));
+            [self _HTTPHeadersDidFinish];
+        } else {
+            [self _readHTTPHeader];
+        }
+    }];
+}
+
+- (void)didConnect;
+{
+    SRFastLog(@"Connected");
+    CFHTTPMessageRef request = CFHTTPMessageCreateRequest(NULL, CFSTR("GET"), (__bridge CFURLRef)_url, kCFHTTPVersion1_1);
+    
+    // Set host first so it defaults
+    CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Host"), (__bridge CFStringRef)(_url.port ? [NSString stringWithFormat:@"%@:%@", _url.host, _url.port] : _url.host));
+        
+    NSMutableData *keyBytes = [[NSMutableData alloc] initWithLength:16];
+    SecRandomCopyBytes(kSecRandomDefault, keyBytes.length, keyBytes.mutableBytes);
+    
+    if ([keyBytes respondsToSelector:@selector(base64EncodedStringWithOptions:)]) {
+        _secKey = [keyBytes base64EncodedStringWithOptions:0];
+    } else {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+        _secKey = [keyBytes base64Encoding];
+#pragma clang diagnostic pop
+    }
+    
+    assert([_secKey length] == 24);
+
+    // Apply cookies if any have been provided
+    NSDictionary * cookies = [NSHTTPCookie requestHeaderFieldsWithCookies:[self requestCookies]];
+    for (NSString * cookieKey in cookies) {
+        NSString * cookieValue = [cookies objectForKey:cookieKey];
+        if ([cookieKey length] && [cookieValue length]) {
+            CFHTTPMessageSetHeaderFieldValue(request, (__bridge CFStringRef)cookieKey, (__bridge CFStringRef)cookieValue);
+        }
+    }
+ 
+    // set header for http basic auth
+    if (_url.user.length && _url.password.length) {
+        NSData *userAndPassword = [[NSString stringWithFormat:@"%@:%@", _url.user, _url.password] dataUsingEncoding:NSUTF8StringEncoding];
+        NSString *userAndPasswordBase64Encoded;
+        if ([keyBytes respondsToSelector:@selector(base64EncodedStringWithOptions:)]) {
+            userAndPasswordBase64Encoded = [userAndPassword base64EncodedStringWithOptions:0];
+        } else {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+            userAndPasswordBase64Encoded = [userAndPassword base64Encoding];
+#pragma clang diagnostic pop
+        }
+        _basicAuthorizationString = [NSString stringWithFormat:@"Basic %@", userAndPasswordBase64Encoded];
+        CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Authorization"), (__bridge CFStringRef)_basicAuthorizationString);
+    }
+
+    CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Upgrade"), CFSTR("websocket"));
+    CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Connection"), CFSTR("Upgrade"));
+    CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Key"), (__bridge CFStringRef)_secKey);
+    CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Version"), (__bridge CFStringRef)[NSString stringWithFormat:@"%ld", (long)_webSocketVersion]);
+    
+    CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Origin"), (__bridge CFStringRef)_url.SR_origin);
+    
+    if (_requestedProtocols) {
+        CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Protocol"), (__bridge CFStringRef)[_requestedProtocols componentsJoinedByString:@", "]);
+    }
+
+    [_urlRequest.allHTTPHeaderFields enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
+        CFHTTPMessageSetHeaderFieldValue(request, (__bridge CFStringRef)key, (__bridge CFStringRef)obj);
+    }];
+    
+    NSData *message = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(request));
+    
+    CFRelease(request);
+
+    [self _writeData:message];
+    [self _readHTTPHeader];
+}
+
+- (void)_initializeStreams;
+{
+    assert(_url.port.unsignedIntValue <= UINT32_MAX);
+    uint32_t port = _url.port.unsignedIntValue;
+    if (port == 0) {
+        if (!_secure) {
+            port = 80;
+        } else {
+            port = 443;
+        }
+    }
+    NSString *host = _url.host;
+    
+    CFReadStreamRef readStream = NULL;
+    CFWriteStreamRef writeStream = NULL;
+    
+    CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &readStream, &writeStream);
+    
+    _outputStream = CFBridgingRelease(writeStream);
+    _inputStream = CFBridgingRelease(readStream);
+    
+    _inputStream.delegate = self;
+    _outputStream.delegate = self;
+}
+
+- (void)_updateSecureStreamOptions;
+{
+    if (_secure) {
+        NSMutableDictionary *SSLOptions = [[NSMutableDictionary alloc] init];
+        
+        [_outputStream setProperty:(__bridge id)kCFStreamSocketSecurityLevelNegotiatedSSL forKey:(__bridge id)kCFStreamPropertySocketSecurityLevel];
+        
+        // If we're using pinned certs, don't validate the certificate chain
+        if ([_urlRequest SR_SSLPinnedCertificates].count) {
+            [SSLOptions setValue:@NO forKey:(__bridge id)kCFStreamSSLValidatesCertificateChain];
+        }
+        
+#if DEBUG
+        self.allowsUntrustedSSLCertificates = YES;
+#endif
+
+        if (self.allowsUntrustedSSLCertificates) {
+            [SSLOptions setValue:@NO forKey:(__bridge id)kCFStreamSSLValidatesCertificateChain];
+            SRFastLog(@"Allowing connection to any root cert");
+        }
+        
+        [_outputStream setProperty:SSLOptions
+                            forKey:(__bridge id)kCFStreamPropertySSLSettings];
+    }
+    
+    _inputStream.delegate = self;
+    _outputStream.delegate = self;
+    
+    [self setupNetworkServiceType:_urlRequest.networkServiceType];
+}
+
+- (void)setupNetworkServiceType:(NSURLRequestNetworkServiceType)requestNetworkServiceType
+{
+    NSString *networkServiceType;
+    switch (requestNetworkServiceType) {
+        case NSURLNetworkServiceTypeDefault:
+            break;
+        case NSURLNetworkServiceTypeVoIP: {
+            networkServiceType = NSStreamNetworkServiceTypeVoIP;
+#if TARGET_OS_IPHONE && __IPHONE_9_0
+            if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_8_3) {
+                static dispatch_once_t predicate;
+                dispatch_once(&predicate, ^{
+                    NSLog(@"SocketRocket: %@ - this service type is deprecated in favor of using PushKit for VoIP control", networkServiceType);
+                });
+            }
+#endif
+            break;
+        }
+        case NSURLNetworkServiceTypeVideo:
+            networkServiceType = NSStreamNetworkServiceTypeVideo;
+            break;
+        case NSURLNetworkServiceTypeBackground:
+            networkServiceType = NSStreamNetworkServiceTypeBackground;
+            break;
+        case NSURLNetworkServiceTypeVoice:
+            networkServiceType = NSStreamNetworkServiceTypeVoice;
+            break;
+    }
+    
+    if (networkServiceType != nil) {
+        [_inputStream setProperty:networkServiceType forKey:NSStreamNetworkServiceType];
+        [_outputStream setProperty:networkServiceType forKey:NSStreamNetworkServiceType];
+    }
+}
+
+- (void)openConnection;
+{
+    [self _updateSecureStreamOptions];
+    
+    if (!_scheduledRunloops.count) {
+        [self scheduleInRunLoop:[NSRunLoop SR_networkRunLoop] forMode:NSDefaultRunLoopMode];
+    }
+    
+    
+    [_outputStream open];
+    [_inputStream open];
+}
+
+- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
+{
+    [_outputStream scheduleInRunLoop:aRunLoop forMode:mode];
+    [_inputStream scheduleInRunLoop:aRunLoop forMode:mode];
+    
+    [_scheduledRunloops addObject:@[aRunLoop, mode]];
+}
+
+- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
+{
+    [_outputStream removeFromRunLoop:aRunLoop forMode:mode];
+    [_inputStream removeFromRunLoop:aRunLoop forMode:mode];
+    
+    [_scheduledRunloops removeObject:@[aRunLoop, mode]];
+}
+
+- (void)close;
+{
+    [self closeWithCode:SRStatusCodeNormal reason:nil];
+}
+
+- (void)closeWithCode:(NSInteger)code reason:(NSString *)reason;
+{
+    assert(code);
+    dispatch_async(_workQueue, ^{
+        if (self.readyState == SR_CLOSING || self.readyState == SR_CLOSED) {
+            return;
+        }
+        
+        BOOL wasConnecting = self.readyState == SR_CONNECTING;
+        
+        self.readyState = SR_CLOSING;
+        
+        SRFastLog(@"Closing with code %d reason %@", code, reason);
+        
+        if (wasConnecting) {
+            [self closeConnection];
+            return;
+        }
+
+        size_t maxMsgSize = [reason maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+        NSMutableData *mutablePayload = [[NSMutableData alloc] initWithLength:sizeof(uint16_t) + maxMsgSize];
+        NSData *payload = mutablePayload;
+        
+        ((uint16_t *)mutablePayload.mutableBytes)[0] = EndianU16_BtoN(code);
+        
+        if (reason) {
+            NSRange remainingRange = {0};
+            
+            NSUInteger usedLength = 0;
+            
+            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];
+            #pragma unused (success)
+            
+            assert(success);
+            assert(remainingRange.length == 0);
+
+            if (usedLength != maxMsgSize) {
+                payload = [payload subdataWithRange:NSMakeRange(0, usedLength + sizeof(uint16_t))];
+            }
+        }
+        
+        
+        [self _sendFrameWithOpcode:SROpCodeConnectionClose data:payload];
+    });
+}
+
+- (void)_closeWithProtocolError:(NSString *)message;
+{
+    // Need to shunt this on the _callbackQueue first to see if they received any messages 
+    [self _performDelegateBlock:^{
+        [self closeWithCode:SRStatusCodeProtocolError reason:message];
+        dispatch_async(_workQueue, ^{
+            [self closeConnection];
+        });
+    }];
+}
+
+- (void)_failWithError:(NSError *)error;
+{
+    dispatch_async(_workQueue, ^{
+        if (self.readyState != SR_CLOSED) {
+            _failed = YES;
+            [self _performDelegateBlock:^{
+                if ([self.delegate respondsToSelector:@selector(webSocket:didFailWithError:)]) {
+                    [self.delegate webSocket:self didFailWithError:error];
+                }
+            }];
+
+            self.readyState = SR_CLOSED;
+
+            SRFastLog(@"Failing with error %@", error.localizedDescription);
+            
+            [self closeConnection];
+            [self _scheduleCleanup];
+        }
+    });
+}
+
+- (void)_writeData:(NSData *)data;
+{    
+    [self assertOnWorkQueue];
+
+    if (_closeWhenFinishedWriting) {
+            return;
+    }
+    [_outputBuffer appendData:data];
+    [self _pumpWriting];
+}
+
+- (void)send:(id)data;
+{
+    NSAssert(self.readyState != SR_CONNECTING, @"Invalid State: Cannot call send: until connection is open");
+    // TODO: maybe not copy this for performance
+    data = [data copy];
+    dispatch_async(_workQueue, ^{
+        if ([data isKindOfClass:[NSString class]]) {
+            [self _sendFrameWithOpcode:SROpCodeTextFrame data:[(NSString *)data dataUsingEncoding:NSUTF8StringEncoding]];
+        } else if ([data isKindOfClass:[NSData class]]) {
+            [self _sendFrameWithOpcode:SROpCodeBinaryFrame data:data];
+        } else if (data == nil) {
+            [self _sendFrameWithOpcode:SROpCodeTextFrame data:data];
+        } else {
+            assert(NO);
+        }
+    });
+}
+
+- (void)sendPing:(NSData *)data;
+{
+    NSAssert(self.readyState == SR_OPEN, @"Invalid State: Cannot call send: until connection is open");
+    // TODO: maybe not copy this for performance
+    data = [data copy] ?: [NSData data]; // It's okay for a ping to be empty
+    dispatch_async(_workQueue, ^{
+        [self _sendFrameWithOpcode:SROpCodePing data:data];
+    });
+}
+
+- (void)handlePing:(NSData *)pingData;
+{
+    // Need to pingpong this off _callbackQueue first to make sure messages happen in order
+    [self _performDelegateBlock:^{
+        dispatch_async(_workQueue, ^{
+            [self _sendFrameWithOpcode:SROpCodePong data:pingData];
+        });
+    }];
+}
+
+- (void)handlePong:(NSData *)pongData;
+{
+    SRFastLog(@"Received pong");
+    [self _performDelegateBlock:^{
+        if ([self.delegate respondsToSelector:@selector(webSocket:didReceivePong:)]) {
+            [self.delegate webSocket:self didReceivePong:pongData];
+        }
+    }];
+}
+
+- (void)_handleMessage:(id)message
+{
+    SRFastLog(@"Received message");
+    [self _performDelegateBlock:^{
+        [self.delegate webSocket:self didReceiveMessage:message];
+    }];
+}
+
+
+static inline BOOL closeCodeIsValid(int closeCode) {
+    if (closeCode < 1000) {
+        return NO;
+    }
+    
+    if (closeCode >= 1000 && closeCode <= 1011) {
+        if (closeCode == 1004 ||
+            closeCode == 1005 ||
+            closeCode == 1006) {
+            return NO;
+        }
+        return YES;
+    }
+    
+    if (closeCode >= 3000 && closeCode <= 3999) {
+        return YES;
+    }
+    
+    if (closeCode >= 4000 && closeCode <= 4999) {
+        return YES;
+    }
+
+    return NO;
+}
+
+//  Note from RFC:
+//
+//  If there is a body, the first two
+//  bytes of the body MUST be a 2-byte unsigned integer (in network byte
+//  order) representing a status code with value /code/ defined in
+//  Section 7.4.  Following the 2-byte integer the body MAY contain UTF-8
+//  encoded data with value /reason/, the interpretation of which is not
+//  defined by this specification.
+
+- (void)handleCloseWithData:(NSData *)data;
+{
+    size_t dataSize = data.length;
+    __block uint16_t closeCode = 0;
+    
+    SRFastLog(@"Received close frame");
+    
+    if (dataSize == 1) {
+        // TODO handle error
+        [self _closeWithProtocolError:@"Payload for close must be larger than 2 bytes"];
+        return;
+    } else if (dataSize >= 2) {
+        [data getBytes:&closeCode length:sizeof(closeCode)];
+        _closeCode = EndianU16_BtoN(closeCode);
+        if (!closeCodeIsValid(_closeCode)) {
+            [self _closeWithProtocolError:[NSString stringWithFormat:@"Cannot have close code of %d", _closeCode]];
+            return;
+        }
+        if (dataSize > 2) {
+            _closeReason = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(2, dataSize - 2)] encoding:NSUTF8StringEncoding];
+            if (!_closeReason) {
+                [self _closeWithProtocolError:@"Close reason MUST be valid UTF-8"];
+                return;
+            }
+        }
+    } else {
+        _closeCode = SRStatusNoStatusReceived;
+    }
+    
+    [self assertOnWorkQueue];
+    
+    if (self.readyState == SR_OPEN) {
+        [self closeWithCode:1000 reason:nil];
+    }
+    dispatch_async(_workQueue, ^{
+        [self closeConnection];
+    });
+}
+
+- (void)closeConnection;
+{
+    [self assertOnWorkQueue];
+    SRFastLog(@"Trying to disconnect");
+    _closeWhenFinishedWriting = YES;
+    [self _pumpWriting];
+}
+
+- (void)_handleFrameWithData:(NSData *)frameData opCode:(NSInteger)opcode;
+{                
+    // Check that the current data is valid UTF8
+    
+    BOOL isControlFrame = (opcode == SROpCodePing || opcode == SROpCodePong || opcode == SROpCodeConnectionClose);
+    if (!isControlFrame) {
+        [self _readFrameNew];
+    } else {
+        dispatch_async(_workQueue, ^{
+            [self _readFrameContinue];
+        });
+    }
+    
+    //frameData will be copied before passing to handlers
+    //otherwise there can be misbehaviours when value at the pointer is changed
+    switch (opcode) {
+        case SROpCodeTextFrame: {
+            if ([self.delegate respondsToSelector:@selector(webSocketShouldConvertTextFrameToString:)] && ![self.delegate webSocketShouldConvertTextFrameToString:self]) {
+                [self _handleMessage:[frameData copy]];
+            } else {
+                NSString *str = [[NSString alloc] initWithData:frameData encoding:NSUTF8StringEncoding];
+                if (str == nil && frameData) {
+                    [self closeWithCode:SRStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"];
+                    dispatch_async(_workQueue, ^{
+                        [self closeConnection];
+                    });
+                    return;
+                }
+                [self _handleMessage:str];
+            }
+            break;
+        }
+        case SROpCodeBinaryFrame:
+            [self _handleMessage:[frameData copy]];
+            break;
+        case SROpCodeConnectionClose:
+            [self handleCloseWithData:[frameData copy]];
+            break;
+        case SROpCodePing:
+            [self handlePing:[frameData copy]];
+            break;
+        case SROpCodePong:
+            [self handlePong:[frameData copy]];
+            break;
+        default:
+            [self _closeWithProtocolError:[NSString stringWithFormat:@"Unknown opcode %ld", (long)opcode]];
+            // TODO: Handle invalid opcode
+            break;
+    }
+}
+
+- (void)_handleFrameHeader:(frame_header)frame_header curData:(NSData *)curData;
+{
+    assert(frame_header.opcode != 0);
+    
+    if (self.readyState == SR_CLOSED) {
+        return;
+    }
+    
+    
+    BOOL isControlFrame = (frame_header.opcode == SROpCodePing || frame_header.opcode == SROpCodePong || frame_header.opcode == SROpCodeConnectionClose);
+    
+    if (isControlFrame && !frame_header.fin) {
+        [self _closeWithProtocolError:@"Fragmented control frames not allowed"];
+        return;
+    }
+    
+    if (isControlFrame && frame_header.payload_length >= 126) {
+        [self _closeWithProtocolError:@"Control frames cannot have payloads larger than 126 bytes"];
+        return;
+    }
+    
+    if (!isControlFrame) {
+        _currentFrameOpcode = frame_header.opcode;
+        _currentFrameCount += 1;
+    }
+    
+    if (frame_header.payload_length == 0) {
+        if (isControlFrame) {
+            [self _handleFrameWithData:curData opCode:frame_header.opcode];
+        } else {
+            if (frame_header.fin) {
+                [self _handleFrameWithData:_currentFrameData opCode:frame_header.opcode];
+            } else {
+                // TODO add assert that opcode is not a control;
+                [self _readFrameContinue];
+            }
+        }
+    } else {
+        assert(frame_header.payload_length <= SIZE_T_MAX);
+        [self _addConsumerWithDataLength:(size_t)frame_header.payload_length callback:^(SRWebSocket *self, NSData *newData) {
+            if (isControlFrame) {
+                [self _handleFrameWithData:newData opCode:frame_header.opcode];
+            } else {
+                if (frame_header.fin) {
+                    [self _handleFrameWithData:self->_currentFrameData opCode:frame_header.opcode];
+                } else {
+                    // TODO add assert that opcode is not a control;
+                    [self _readFrameContinue];
+                }
+                
+            }
+        } readToCurrentFrame:!isControlFrame unmaskBytes:frame_header.masked];
+    }
+}
+
+/* From RFC:
+
+ 0                   1                   2                   3
+ 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
+ +-+-+-+-+-------+-+-------------+-------------------------------+
+ |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
+ |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
+ |N|V|V|V|       |S|             |   (if payload len==126/127)   |
+ | |1|2|3|       |K|             |                               |
+ +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
+ |     Extended payload length continued, if payload len == 127  |
+ + - - - - - - - - - - - - - - - +-------------------------------+
+ |                               |Masking-key, if MASK set to 1  |
+ +-------------------------------+-------------------------------+
+ | Masking-key (continued)       |          Payload Data         |
+ +-------------------------------- - - - - - - - - - - - - - - - +
+ :                     Payload Data continued ...                :
+ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+ |                     Payload Data continued ...                |
+ +---------------------------------------------------------------+
+ */
+
+static const uint8_t SRFinMask          = 0x80;
+static const uint8_t SROpCodeMask       = 0x0F;
+static const uint8_t SRRsvMask          = 0x70;
+static const uint8_t SRMaskMask         = 0x80;
+static const uint8_t SRPayloadLenMask   = 0x7F;
+
+
+- (void)_readFrameContinue;
+{
+    assert((_currentFrameCount == 0 && _currentFrameOpcode == 0) || (_currentFrameCount > 0 && _currentFrameOpcode > 0));
+
+    [self _addConsumerWithDataLength:2 callback:^(SRWebSocket *self, NSData *data) {
+        __block frame_header header = {0};
+        
+        const uint8_t *headerBuffer = data.bytes;
+        assert(data.length >= 2);
+        
+        if (headerBuffer[0] & SRRsvMask) {
+            [self _closeWithProtocolError:@"Server used RSV bits"];
+            return;
+        }
+        
+        uint8_t receivedOpcode = (SROpCodeMask & headerBuffer[0]);
+        
+        BOOL isControlFrame = (receivedOpcode == SROpCodePing || receivedOpcode == SROpCodePong || receivedOpcode == SROpCodeConnectionClose);
+        
+        if (!isControlFrame && receivedOpcode != 0 && self->_currentFrameCount > 0) {
+            [self _closeWithProtocolError:@"all data frames after the initial data frame must have opcode 0"];
+            return;
+        }
+        
+        if (receivedOpcode == 0 && self->_currentFrameCount == 0) {
+            [self _closeWithProtocolError:@"cannot continue a message"];
+            return;
+        }
+        
+        header.opcode = receivedOpcode == 0 ? self->_currentFrameOpcode : receivedOpcode;
+        
+        header.fin = !!(SRFinMask & headerBuffer[0]);
+        
+        
+        header.masked = !!(SRMaskMask & headerBuffer[1]);
+        header.payload_length = SRPayloadLenMask & headerBuffer[1];
+        
+        headerBuffer = NULL;
+        
+        if (header.masked) {
+            [self _closeWithProtocolError:@"Client must receive unmasked data"];
+        }
+        
+        size_t extra_bytes_needed = header.masked ? sizeof(_currentReadMaskKey) : 0;
+        
+        if (header.payload_length == 126) {
+            extra_bytes_needed += sizeof(uint16_t);
+        } else if (header.payload_length == 127) {
+            extra_bytes_needed += sizeof(uint64_t);
+        }
+        
+        if (extra_bytes_needed == 0) {
+            [self _handleFrameHeader:header curData:self->_currentFrameData];
+        } else {
+            [self _addConsumerWithDataLength:extra_bytes_needed callback:^(SRWebSocket *self, NSData *data) {
+                size_t mapped_size = data.length;
+                #pragma unused (mapped_size)
+                const void *mapped_buffer = data.bytes;
+                size_t offset = 0;
+                
+                if (header.payload_length == 126) {
+                    assert(mapped_size >= sizeof(uint16_t));
+                    uint16_t newLen = EndianU16_BtoN(*(uint16_t *)(mapped_buffer));
+                    header.payload_length = newLen;
+                    offset += sizeof(uint16_t);
+                } else if (header.payload_length == 127) {
+                    assert(mapped_size >= sizeof(uint64_t));
+                    header.payload_length = EndianU64_BtoN(*(uint64_t *)(mapped_buffer));
+                    offset += sizeof(uint64_t);
+                } else {
+                    assert(header.payload_length < 126 && header.payload_length >= 0);
+                }
+                
+                if (header.masked) {
+                    assert(mapped_size >= sizeof(_currentReadMaskOffset) + offset);
+                    memcpy(self->_currentReadMaskKey, ((uint8_t *)mapped_buffer) + offset, sizeof(self->_currentReadMaskKey));
+                }
+                
+                [self _handleFrameHeader:header curData:self->_currentFrameData];
+            } readToCurrentFrame:NO unmaskBytes:NO];
+        }
+    } readToCurrentFrame:NO unmaskBytes:NO];
+}
+
+- (void)_readFrameNew;
+{
+    dispatch_async(_workQueue, ^{
+        [_currentFrameData setLength:0];
+        
+        _currentFrameOpcode = 0;
+        _currentFrameCount = 0;
+        _readOpCount = 0;
+        _currentStringScanPosition = 0;
+        
+        [self _readFrameContinue];
+    });
+}
+
+- (void)_pumpWriting;
+{
+    [self assertOnWorkQueue];
+    
+    NSUInteger dataLength = _outputBuffer.length;
+    if (dataLength - _outputBufferOffset > 0 && _outputStream.hasSpaceAvailable) {
+        NSInteger bytesWritten = [_outputStream write:_outputBuffer.bytes + _outputBufferOffset maxLength:dataLength - _outputBufferOffset];
+        if (bytesWritten == -1) {
+            [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2145 userInfo:[NSDictionary dictionaryWithObject:@"Error writing to stream" forKey:NSLocalizedDescriptionKey]]];
+             return;
+        }
+        
+        _outputBufferOffset += bytesWritten;
+        
+        if (_outputBufferOffset > 4096 && _outputBufferOffset > (_outputBuffer.length >> 1)) {
+            _outputBuffer = [[NSMutableData alloc] initWithBytes:(char *)_outputBuffer.bytes + _outputBufferOffset length:_outputBuffer.length - _outputBufferOffset];
+            _outputBufferOffset = 0;
+        }
+    }
+    
+    if (_closeWhenFinishedWriting && 
+        _outputBuffer.length - _outputBufferOffset == 0 && 
+        (_inputStream.streamStatus != NSStreamStatusNotOpen &&
+         _inputStream.streamStatus != NSStreamStatusClosed) &&
+        !_sentClose) {
+        _sentClose = YES;
+        
+        @synchronized(self) {
+            [_outputStream close];
+            [_inputStream close];
+            
+            
+            for (NSArray *runLoop in [_scheduledRunloops copy]) {
+                [self unscheduleFromRunLoop:[runLoop objectAtIndex:0] forMode:[runLoop objectAtIndex:1]];
+            }
+        }
+        
+        if (!_failed) {
+            [self _performDelegateBlock:^{
+                if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) {
+                    [self.delegate webSocket:self didCloseWithCode:_closeCode reason:_closeReason wasClean:YES];
+                }
+            }];
+        }
+        
+        [self _scheduleCleanup];
+    }
+}
+
+- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback;
+{
+    [self assertOnWorkQueue];
+    [self _addConsumerWithScanner:consumer callback:callback dataLength:0];
+}
+
+- (void)_addConsumerWithDataLength:(size_t)dataLength callback:(data_callback)callback readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes;
+{   
+    [self assertOnWorkQueue];
+    assert(dataLength);
+    
+    [_consumers addObject:[_consumerPool consumerWithScanner:nil handler:callback bytesNeeded:dataLength readToCurrentFrame:readToCurrentFrame unmaskBytes:unmaskBytes]];
+    [self _pumpScanner];
+}
+
+- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback dataLength:(size_t)dataLength;
+{    
+    [self assertOnWorkQueue];
+    [_consumers addObject:[_consumerPool consumerWithScanner:consumer handler:callback bytesNeeded:dataLength readToCurrentFrame:NO unmaskBytes:NO]];
+    [self _pumpScanner];
+}
+
+
+- (void)_scheduleCleanup
+{
+    @synchronized(self) {
+        if (_cleanupScheduled) {
+            return;
+        }
+        
+        _cleanupScheduled = YES;
+        
+        // Cleanup NSStream delegate's in the same RunLoop used by the streams themselves:
+        // This way we'll prevent race conditions between handleEvent and SRWebsocket's dealloc
+        NSTimer *timer = [NSTimer timerWithTimeInterval:(0.0f) target:self selector:@selector(_cleanupSelfReference:) userInfo:nil repeats:NO];
+        [[NSRunLoop SR_networkRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
+    }
+}
+
+- (void)_cleanupSelfReference:(NSTimer *)timer
+{
+    @synchronized(self) {
+        // Nuke NSStream delegate's
+        _inputStream.delegate = nil;
+        _outputStream.delegate = nil;
+        
+        // Remove the streams, right now, from the networkRunLoop
+        [_inputStream close];
+        [_outputStream close];
+    }
+    
+    // Cleanup selfRetain in the same GCD queue as usual
+    dispatch_async(_workQueue, ^{
+        _selfRetain = nil;
+    });
+}
+
+
+static const char CRLFCRLFBytes[] = {'\r', '\n', '\r', '\n'};
+
+- (void)_readUntilHeaderCompleteWithCallback:(data_callback)dataHandler;
+{
+    [self _readUntilBytes:CRLFCRLFBytes length:sizeof(CRLFCRLFBytes) callback:dataHandler];
+}
+
+- (void)_readUntilBytes:(const void *)bytes length:(size_t)length callback:(data_callback)dataHandler;
+{
+    // TODO optimize so this can continue from where we last searched
+    stream_scanner consumer = ^size_t(NSData *data) {
+        __block size_t found_size = 0;
+        __block size_t match_count = 0;
+        
+        size_t size = data.length;
+        const unsigned char *buffer = data.bytes;
+        for (size_t i = 0; i < size; i++ ) {
+            if (((const unsigned char *)buffer)[i] == ((const unsigned char *)bytes)[match_count]) {
+                match_count += 1;
+                if (match_count == length) {
+                    found_size = i + 1;
+                    break;
+                }
+            } else {
+                match_count = 0;
+            }
+        }
+        return found_size;
+    };
+    [self _addConsumerWithScanner:consumer callback:dataHandler];
+}
+
+
+// Returns true if did work
+- (BOOL)_innerPumpScanner {
+    
+    BOOL didWork = NO;
+    
+    if (self.readyState >= SR_CLOSED) {
+        return didWork;
+    }
+    
+    if (!_consumers.count) {
+        return didWork;
+    }
+    
+    size_t curSize = _readBuffer.length - _readBufferOffset;
+    if (!curSize) {
+        return didWork;
+    }
+    
+    SRIOConsumer *consumer = [_consumers objectAtIndex:0];
+    
+    size_t bytesNeeded = consumer.bytesNeeded;
+    
+    size_t foundSize = 0;
+    if (consumer.consumer) {
+        NSData *tempView = [NSData dataWithBytesNoCopy:(char *)_readBuffer.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset freeWhenDone:NO];  
+        foundSize = consumer.consumer(tempView);
+    } else {
+        assert(consumer.bytesNeeded);
+        if (curSize >= bytesNeeded) {
+            foundSize = bytesNeeded;
+        } else if (consumer.readToCurrentFrame) {
+            foundSize = curSize;
+        }
+    }
+    
+    NSData *slice = nil;
+    if (consumer.readToCurrentFrame || foundSize) {
+        NSRange sliceRange = NSMakeRange(_readBufferOffset, foundSize);
+        slice = [_readBuffer subdataWithRange:sliceRange];
+        
+        _readBufferOffset += foundSize;
+        
+        if (_readBufferOffset > 4096 && _readBufferOffset > (_readBuffer.length >> 1)) {
+            _readBuffer = [[NSMutableData alloc] initWithBytes:(char *)_readBuffer.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset];            _readBufferOffset = 0;
+        }
+        
+        if (consumer.unmaskBytes) {
+            NSMutableData *mutableSlice = [slice mutableCopy];
+            
+            NSUInteger len = mutableSlice.length;
+            uint8_t *bytes = mutableSlice.mutableBytes;
+            
+            for (NSUInteger i = 0; i < len; i++) {
+                bytes[i] = bytes[i] ^ _currentReadMaskKey[_currentReadMaskOffset % sizeof(_currentReadMaskKey)];
+                _currentReadMaskOffset += 1;
+            }
+            
+            slice = mutableSlice;
+        }
+        
+        if (consumer.readToCurrentFrame) {
+            [_currentFrameData appendData:slice];
+            
+            _readOpCount += 1;
+            
+            if (_currentFrameOpcode == SROpCodeTextFrame) {
+                // Validate UTF8 stuff.
+                size_t currentDataSize = _currentFrameData.length;
+                if (_currentFrameOpcode == SROpCodeTextFrame && currentDataSize > 0) {
+                    // TODO: Optimize the crap out of this.  Don't really have to copy all the data each time
+                    
+                    size_t scanSize = currentDataSize - _currentStringScanPosition;
+                    
+                    NSData *scan_data = [_currentFrameData subdataWithRange:NSMakeRange(_currentStringScanPosition, scanSize)];
+                    int32_t valid_utf8_size = validate_dispatch_data_partial_string(scan_data);
+                    
+                    if (valid_utf8_size == -1) {
+                        [self closeWithCode:SRStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"];
+                        dispatch_async(_workQueue, ^{
+                            [self closeConnection];
+                        });
+                        return didWork;
+                    } else {
+                        _currentStringScanPosition += valid_utf8_size;
+                    }
+                } 
+                
+            }
+            
+            consumer.bytesNeeded -= foundSize;
+            
+            if (consumer.bytesNeeded == 0) {
+                [_consumers removeObjectAtIndex:0];
+                consumer.handler(self, nil);
+                [_consumerPool returnConsumer:consumer];
+                didWork = YES;
+            }
+        } else if (foundSize) {
+            [_consumers removeObjectAtIndex:0];
+            consumer.handler(self, slice);
+            [_consumerPool returnConsumer:consumer];
+            didWork = YES;
+        }
+    }
+    return didWork;
+}
+
+-(void)_pumpScanner;
+{
+    [self assertOnWorkQueue];
+    
+    if (!_isPumping) {
+        _isPumping = YES;
+    } else {
+        return;
+    }
+    
+    while ([self _innerPumpScanner]) {
+        
+    }
+    
+    _isPumping = NO;
+}
+
+//#define NOMASK
+
+static const size_t SRFrameHeaderOverhead = 32;
+
+- (void)_sendFrameWithOpcode:(SROpCode)opcode data:(id)data;
+{
+    [self assertOnWorkQueue];
+    
+    if (nil == data) {
+        return;
+    }
+    
+    NSAssert([data isKindOfClass:[NSData class]] || [data isKindOfClass:[NSString class]], @"NSString or NSData");
+    
+    size_t payloadLength = [data isKindOfClass:[NSString class]] ? [(NSString *)data lengthOfBytesUsingEncoding:NSUTF8StringEncoding] : [data length];
+        
+    NSMutableData *frame = [[NSMutableData alloc] initWithLength:payloadLength + SRFrameHeaderOverhead];
+    if (!frame) {
+        [self closeWithCode:SRStatusCodeMessageTooBig reason:@"Message too big"];
+        return;
+    }
+    uint8_t *frame_buffer = (uint8_t *)[frame mutableBytes];
+    
+    // set fin
+    frame_buffer[0] = SRFinMask | opcode;
+    
+    BOOL useMask = YES;
+#ifdef NOMASK
+    useMask = NO;
+#endif
+    
+    if (useMask) {
+    // set the mask and header
+        frame_buffer[1] |= SRMaskMask;
+    }
+    
+    size_t frame_buffer_size = 2;
+    
+    const uint8_t *unmasked_payload = NULL;
+    if ([data isKindOfClass:[NSData class]]) {
+        unmasked_payload = (uint8_t *)[data bytes];
+    } else if ([data isKindOfClass:[NSString class]]) {
+        unmasked_payload =  (const uint8_t *)[data UTF8String];
+    } else {
+        return;
+    }
+    
+    if (payloadLength < 126) {
+        frame_buffer[1] |= payloadLength;
+    } else if (payloadLength <= UINT16_MAX) {
+        frame_buffer[1] |= 126;
+        *((uint16_t *)(frame_buffer + frame_buffer_size)) = EndianU16_BtoN((uint16_t)payloadLength);
+        frame_buffer_size += sizeof(uint16_t);
+    } else {
+        frame_buffer[1] |= 127;
+        *((uint64_t *)(frame_buffer + frame_buffer_size)) = EndianU64_BtoN((uint64_t)payloadLength);
+        frame_buffer_size += sizeof(uint64_t);
+    }
+        
+    if (!useMask) {
+        for (size_t i = 0; i < payloadLength; i++) {
+            frame_buffer[frame_buffer_size] = unmasked_payload[i];
+            frame_buffer_size += 1;
+        }
+    } else {
+        uint8_t *mask_key = frame_buffer + frame_buffer_size;
+        SecRandomCopyBytes(kSecRandomDefault, sizeof(uint32_t), (uint8_t *)mask_key);
+        frame_buffer_size += sizeof(uint32_t);
+        
+        // TODO: could probably optimize this with SIMD
+        for (size_t i = 0; i < payloadLength; i++) {
+            frame_buffer[frame_buffer_size] = unmasked_payload[i] ^ mask_key[i % sizeof(uint32_t)];
+            frame_buffer_size += 1;
+        }
+    }
+
+    assert(frame_buffer_size <= [frame length]);
+    frame.length = frame_buffer_size;
+    
+    [self _writeData:frame];
+}
+
+- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode;
+{
+    __weak typeof(self) weakSelf = self;
+    
+    if (_secure && !_pinnedCertFound && (eventCode == NSStreamEventHasBytesAvailable || eventCode == NSStreamEventHasSpaceAvailable)) {
+        
+        NSArray *sslCerts = [_urlRequest SR_SSLPinnedCertificates];
+        if (sslCerts) {
+            SecTrustRef secTrust = (__bridge SecTrustRef)[aStream propertyForKey:(__bridge id)kCFStreamPropertySSLPeerTrust];
+            if (secTrust) {
+                NSInteger numCerts = SecTrustGetCertificateCount(secTrust);
+                for (NSInteger i = 0; i < numCerts && !_pinnedCertFound; i++) {
+                    SecCertificateRef cert = SecTrustGetCertificateAtIndex(secTrust, i);
+                    NSData *certData = CFBridgingRelease(SecCertificateCopyData(cert));
+                    
+                    for (id ref in sslCerts) {
+                        SecCertificateRef trustedCert = (__bridge SecCertificateRef)ref;
+                        NSData *trustedCertData = CFBridgingRelease(SecCertificateCopyData(trustedCert));
+                        
+                        if ([trustedCertData isEqualToData:certData]) {
+                            _pinnedCertFound = YES;
+                            break;
+                        }
+                    }
+                }
+            }
+            
+            if (!_pinnedCertFound) {
+                dispatch_async(_workQueue, ^{
+                    NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : @"Invalid server cert" };
+                    [weakSelf _failWithError:[NSError errorWithDomain:@"org.lolrus.SocketRocket" code:23556 userInfo:userInfo]];
+                });
+                return;
+            } else if (aStream == _outputStream) {
+                dispatch_async(_workQueue, ^{
+                    [self didConnect];
+                });
+            }
+        }
+    }
+
+    dispatch_async(_workQueue, ^{
+        [weakSelf safeHandleEvent:eventCode stream:aStream];
+    });
+}
+
+- (void)safeHandleEvent:(NSStreamEvent)eventCode stream:(NSStream *)aStream
+{
+        switch (eventCode) {
+            case NSStreamEventOpenCompleted: {
+                SRFastLog(@"NSStreamEventOpenCompleted %@", aStream);
+                if (self.readyState >= SR_CLOSING) {
+                    return;
+                }
+                assert(_readBuffer);
+                
+                // didConnect fires after certificate verification if we're using pinned certificates.
+                BOOL usingPinnedCerts = [[_urlRequest SR_SSLPinnedCertificates] count] > 0;
+                if ((!_secure || !usingPinnedCerts) && self.readyState == SR_CONNECTING && aStream == _inputStream) {
+                    [self didConnect];
+                }
+                [self _pumpWriting];
+                [self _pumpScanner];
+                break;
+            }
+                
+            case NSStreamEventErrorOccurred: {
+                SRFastLog(@"NSStreamEventErrorOccurred %@ %@", aStream, [[aStream streamError] copy]);
+                /// TODO specify error better!
+                [self _failWithError:aStream.streamError];
+                _readBufferOffset = 0;
+                [_readBuffer setLength:0];
+                break;
+                
+            }
+                
+            case NSStreamEventEndEncountered: {
+                [self _pumpScanner];
+                SRFastLog(@"NSStreamEventEndEncountered %@", aStream);
+                if (aStream.streamError) {
+                    [self _failWithError:aStream.streamError];
+                } else {
+                    dispatch_async(_workQueue, ^{
+                        if (self.readyState != SR_CLOSED) {
+                            self.readyState = SR_CLOSED;
+                            [self _scheduleCleanup];
+                        }
+                        
+                        if (!_sentClose && !_failed) {
+                            _sentClose = YES;
+                            // If we get closed in this state it's probably not clean because we should be sending this when we send messages
+                            [self _performDelegateBlock:^{
+                                if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) {
+                                    [self.delegate webSocket:self didCloseWithCode:SRStatusCodeGoingAway reason:@"Stream end encountered" wasClean:NO];
+                                }
+                            }];
+                        }
+                    });
+                }
+                
+                break;
+            }
+                
+            case NSStreamEventHasBytesAvailable: {
+                SRFastLog(@"NSStreamEventHasBytesAvailable %@", aStream);
+                const int bufferSize = 2048;
+                uint8_t buffer[bufferSize];
+                
+                while (_inputStream.hasBytesAvailable) {
+                    NSInteger bytes_read = [_inputStream read:buffer maxLength:bufferSize];
+                    
+                    if (bytes_read > 0) {
+                        [_readBuffer appendBytes:buffer length:bytes_read];
+                    } else if (bytes_read < 0) {
+                        [self _failWithError:_inputStream.streamError];
+                    }
+                    
+                    if (bytes_read != bufferSize) {
+                        break;
+                    }
+                };
+                [self _pumpScanner];
+                break;
+            }
+                
+            case NSStreamEventHasSpaceAvailable: {
+                SRFastLog(@"NSStreamEventHasSpaceAvailable %@", aStream);
+                [self _pumpWriting];
+                break;
+            }
+                
+            default:
+                SRFastLog(@"(default)  %@", aStream);
+                break;
+        }
+}
+
+@end
+
+
+@implementation SRIOConsumer
+
+@synthesize bytesNeeded = _bytesNeeded;
+@synthesize consumer = _scanner;
+@synthesize handler = _handler;
+@synthesize readToCurrentFrame = _readToCurrentFrame;
+@synthesize unmaskBytes = _unmaskBytes;
+
+- (void)setupWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes;
+{
+    _scanner = [scanner copy];
+    _handler = [handler copy];
+    _bytesNeeded = bytesNeeded;
+    _readToCurrentFrame = readToCurrentFrame;
+    _unmaskBytes = unmaskBytes;
+    assert(_scanner || _bytesNeeded);
+}
+
+
+@end
+
+
+@implementation SRIOConsumerPool {
+    NSUInteger _poolSize;
+    NSMutableArray *_bufferedConsumers;
+}
+
+- (id)initWithBufferCapacity:(NSUInteger)poolSize;
+{
+    self = [super init];
+    if (self) {
+        _poolSize = poolSize;
+        _bufferedConsumers = [[NSMutableArray alloc] initWithCapacity:poolSize];
+    }
+    return self;
+}
+
+- (id)init
+{
+    return [self initWithBufferCapacity:8];
+}
+
+- (SRIOConsumer *)consumerWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes;
+{
+    SRIOConsumer *consumer = nil;
+    if (_bufferedConsumers.count) {
+        consumer = [_bufferedConsumers lastObject];
+        [_bufferedConsumers removeLastObject];
+    } else {
+        consumer = [[SRIOConsumer alloc] init];
+    }
+    
+    [consumer setupWithScanner:scanner handler:handler bytesNeeded:bytesNeeded readToCurrentFrame:readToCurrentFrame unmaskBytes:unmaskBytes];
+    
+    return consumer;
+}
+
+- (void)returnConsumer:(SRIOConsumer *)consumer;
+{
+    if (_bufferedConsumers.count < _poolSize) {
+        [_bufferedConsumers addObject:consumer];
+    }
+}
+
+@end
+
+
+@implementation  NSURLRequest (SRCertificateAdditions)
+
+- (NSArray *)SR_SSLPinnedCertificates;
+{
+    return [NSURLProtocol propertyForKey:@"SR_SSLPinnedCertificates" inRequest:self];
+}
+
+@end
+
+@implementation  NSMutableURLRequest (SRCertificateAdditions)
+
+- (NSArray *)SR_SSLPinnedCertificates;
+{
+    return [NSURLProtocol propertyForKey:@"SR_SSLPinnedCertificates" inRequest:self];
+}
+
+- (void)setSR_SSLPinnedCertificates:(NSArray *)SR_SSLPinnedCertificates;
+{
+    [NSURLProtocol setProperty:SR_SSLPinnedCertificates forKey:@"SR_SSLPinnedCertificates" inRequest:self];
+}
+
+@end
+
+@implementation NSURL (SRWebSocket)
+
+- (NSString *)SR_origin;
+{
+    NSString *scheme = [self.scheme lowercaseString];
+        
+    if ([scheme isEqualToString:@"wss"]) {
+        scheme = @"https";
+    } else if ([scheme isEqualToString:@"ws"]) {
+        scheme = @"http";
+    }
+    
+    BOOL portIsDefault = !self.port ||
+                         ([scheme isEqualToString:@"http"] && self.port.integerValue == 80) ||
+                         ([scheme isEqualToString:@"https"] && self.port.integerValue == 443);
+    
+    if (!portIsDefault) {
+        return [NSString stringWithFormat:@"%@://%@:%@", scheme, self.host, self.port];
+    } else {
+        return [NSString stringWithFormat:@"%@://%@", scheme, self.host];
+    }
+}
+
+@end
+
+//#define SR_ENABLE_LOG
+
+static inline void SRFastLog(NSString *format, ...)  {
+#ifdef SR_ENABLE_LOG
+    __block va_list arg_list;
+    va_start (arg_list, format);
+    
+    NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list];
+    
+    va_end(arg_list);
+    
+    NSLog(@"[SR] %@", formattedString);
+#endif
+}
+
+
+#ifdef HAS_ICU
+
+static inline int32_t validate_dispatch_data_partial_string(NSData *data) {
+    if ([data length] > INT32_MAX) {
+        // INT32_MAX is the limit so long as this Framework is using 32 bit ints everywhere.
+        return -1;
+    }
+
+    int32_t size = (int32_t)[data length];
+
+    const void * contents = [data bytes];
+    const uint8_t *str = (const uint8_t *)contents;
+    
+    UChar32 codepoint = 1;
+    int32_t offset = 0;
+    int32_t lastOffset = 0;
+    while(offset < size && codepoint > 0)  {
+        lastOffset = offset;
+        U8_NEXT(str, offset, size, codepoint);
+    }
+    
+    if (codepoint == -1) {
+        // Check to see if the last byte is valid or whether it was just continuing
+        if (!U8_IS_LEAD(str[lastOffset]) || U8_COUNT_TRAIL_BYTES(str[lastOffset]) + lastOffset < (int32_t)size) {
+            
+            size = -1;
+        } else {
+            uint8_t leadByte = str[lastOffset];
+            U8_MASK_LEAD_BYTE(leadByte, U8_COUNT_TRAIL_BYTES(leadByte));
+            
+            for (int i = lastOffset + 1; i < offset; i++) {
+                if (U8_IS_SINGLE(str[i]) || U8_IS_LEAD(str[i]) || !U8_IS_TRAIL(str[i])) {
+                    size = -1;
+                }
+            }
+            
+            if (size != -1) {
+                size = lastOffset;
+            }
+        }
+    }
+    
+    if (size != -1 && ![[NSString alloc] initWithBytesNoCopy:(char *)[data bytes] length:size encoding:NSUTF8StringEncoding freeWhenDone:NO]) {
+        size = -1;
+    }
+    
+    return size;
+}
+
+#else
+
+// This is a hack, and probably not optimal
+static inline int32_t validate_dispatch_data_partial_string(NSData *data) {
+    static const int maxCodepointSize = 3;
+    
+    for (int i = 0; i < maxCodepointSize; i++) {
+        NSString *str = [[NSString alloc] initWithBytesNoCopy:(char *)data.bytes length:data.length - i encoding:NSUTF8StringEncoding freeWhenDone:NO];
+        if (str) {
+            return (int32_t)data.length - i;
+        }
+    }
+    
+    return -1;
+}
+
+#endif
+
+static _SRRunLoopThread *networkThread = nil;
+static NSRunLoop *networkRunLoop = nil;
+
+@implementation NSRunLoop (SRWebSocket)
+
++ (NSRunLoop *)SR_networkRunLoop {
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        networkThread = [[_SRRunLoopThread alloc] init];
+        networkThread.name = @"com.squareup.SocketRocket.NetworkThread";
+        [networkThread start];
+        networkRunLoop = networkThread.runLoop;
+    });
+    
+    return networkRunLoop;
+}
+
+@end
+
+
+@implementation _SRRunLoopThread {
+    dispatch_group_t _waitGroup;
+}
+
+@synthesize runLoop = _runLoop;
+
+- (void)dealloc
+{
+    sr_dispatch_release(_waitGroup);
+}
+
+- (id)init
+{
+    self = [super init];
+    if (self) {
+        _waitGroup = dispatch_group_create();
+        dispatch_group_enter(_waitGroup);
+    }
+    return self;
+}
+
+- (void)main;
+{
+    @autoreleasepool {
+        _runLoop = [NSRunLoop currentRunLoop];
+        dispatch_group_leave(_waitGroup);
+        
+        // Add an empty run loop source to prevent runloop from spinning.
+        CFRunLoopSourceContext sourceCtx = {
+            .version = 0,
+            .info = NULL,
+            .retain = NULL,
+            .release = NULL,
+            .copyDescription = NULL,
+            .equal = NULL,
+            .hash = NULL,
+            .schedule = NULL,
+            .cancel = NULL,
+            .perform = NULL
+        };
+        CFRunLoopSourceRef source = CFRunLoopSourceCreate(NULL, 0, &sourceCtx);
+        CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
+        CFRelease(source);
+        
+        while ([_runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) {
+            
+        }
+        assert(NO);
+    }
+}
+
+- (NSRunLoop *)runLoop;
+{
+    dispatch_group_wait(_waitGroup, DISPATCH_TIME_FOREVER);
+    return _runLoop;
+}
+
+@end

+ 23 - 0
DanceGame/Classes/FitfunTool/FitfunSDK_V2.0/KpSupport/SDK/HTTP/socket/SocketRocketUtility.h

@@ -0,0 +1,23 @@
+//
+//  SocketRocketUtility.h
+
+//
+
+#import <Foundation/Foundation.h>
+#import "SRWebSocket.h"
+
+@interface SocketRocketUtility : NSObject
+
+/** 连接状态 */
+@property (nonatomic,assign) SRReadyState socketReadyState;
+@property (nonatomic,  copy) void (^didReceiveMessage)(id message);
+//+ (SocketRocketUtility *)instance;
+//单例->初始化蓝牙
++(instancetype)sharedInstance;
+
+-(void)SRWebSocketOpen;//开启连接
+-(void)SRWebSocketClose;//关闭连接
+- (void)sendData:(NSDictionary *)paramDic withRequestURI:(NSString*)requestURI;//发送数据
+- (void)registerNetworkNotifications;//监测网络状态
+
+@end

+ 301 - 0
DanceGame/Classes/FitfunTool/FitfunSDK_V2.0/KpSupport/SDK/HTTP/socket/SocketRocketUtility.m

@@ -0,0 +1,301 @@
+//
+//  SocketRocketUtility.m
+//  SUN
+//
+//  Created by 孙俊 on 17/2/16.
+//  Copyright © 2017年 SUN. All rights reserved.
+//
+
+#import "SocketRocketUtility.h"
+
+#define dispatch_main_async_safe(block)\
+if ([NSThread isMainThread]) {\
+block();\
+} else {\
+dispatch_async(dispatch_get_main_queue(), block);\
+}
+
+#define WeakSelf(type)  __weak typeof(type) weak##type = type;
+///*自定义打印 宏*/
+//#ifdef DEBUG
+//#define NSLog(format, ...) do {                                                                          \
+//fprintf(stderr, "<%s : %d> %s\n",                                           \
+//[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String],  \
+//__LINE__, __func__);                                                        \
+//(NSLog)((format), ##__VA_ARGS__);                                           \
+//fprintf(stderr, "-------\n");                                               \
+//} while (0)
+//
+//#else
+//#define NSLog( ...)
+//
+//#endif
+typedef NS_ENUM(NSUInteger, SocketDataType) {
+    distributeOrder,
+    cancelCall,
+    orderLost,
+    changeDeviceType,
+};
+@interface SocketRocketUtility()<SRWebSocketDelegate>
+{
+    NSTimer * heartBeat;
+    NSTimeInterval reConnectTime;
+    SocketDataType type;
+    NSString *host;
+}
+
+@property (nonatomic,strong) SRWebSocket *socket;
+
+@end
+
+@implementation SocketRocketUtility
+
+//单例静态
+static SocketRocketUtility* instance = nil;
++(instancetype)sharedInstance{
+     return [[self alloc] init];
+}
++ (instancetype)allocWithZone:(struct _NSZone *)zone{
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+      instance = [super allocWithZone:zone];
+  });
+  return instance;
+}
+- (instancetype)init{
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        instance = [super init];
+    });
+    return instance;
+}
+
+//+(SocketRocketUtility *)instance{
+//    static SocketRocketUtility *Instance = nil;
+//    static dispatch_once_t predicate;
+//    dispatch_once(&predicate, ^{
+//        Instance = [[SocketRocketUtility alloc] init];
+//    });
+//    return Instance;
+//}
+
+- (void)registerNetworkNotifications{
+//    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkChangedNote:) name:AFNetworkingReachabilityDidChangeNotification object:nil];
+}
+- (void)networkChangedNote:(NSNotification *)note{
+    
+//    AFNetworkReachabilityStatus status = [note.userInfo[AFNetworkingReachabilityNotificationStatusItem] integerValue];
+//    switch (status) {
+//        case AFNetworkReachabilityStatusUnknown:
+//            NSLog(@"网络类型:未知网络");
+//            break;
+//        case AFNetworkReachabilityStatusNotReachable:
+//            NSLog(@"网络类型:断网");
+//            break;
+//        case AFNetworkReachabilityStatusReachableViaWWAN:
+//            NSLog(@"网络类型:数据流量");
+//            [self SRWebSocketOpen];
+//            break;
+//        case AFNetworkReachabilityStatusReachableViaWiFi:
+//            NSLog(@"网络类型:WIFI");
+//            [self SRWebSocketOpen];
+//            break;
+//    }
+    
+}
+
+-(void)SRWebSocketOpen{
+    
+    
+    //如果是同一个url return
+    if (self.socket) {
+        return;
+    }
+    if(self.socket.readyState == SR_OPEN){
+        return;
+    }
+
+    self.socket = [[SRWebSocket alloc] initWithURLRequest:
+                   [NSURLRequest requestWithURL:[NSURL URLWithString:@"ws://172.16.14.127/examples/websocket/chat"]]];//这里填写你服务器的地址
+    
+    NSLog(@"请求的websocket地址:%@",self.socket.url.absoluteString);
+    self.socket.delegate = self;   //实现这个 SRWebSocketDelegate 协议
+    [self.socket open];     //open 就是直接连接了
+}
+
+-(void)SRWebSocketClose{
+    if (self.socket){
+        [self.socket close];
+        self.socket = nil;
+        //断开连接时销毁心跳
+        [self destoryHeartBeat];
+    }
+}
+
+#pragma mark - socket delegate
+- (void)webSocketDidOpen:(SRWebSocket *)webSocket {
+    
+    //每次正常连接的时候清零重连时间
+    reConnectTime = 0;
+    
+    //开启心跳
+    [self initHeartBeat];
+    if (webSocket == self.socket) {
+        NSLog(@"************************** socket 连接成功************************** ");
+    }
+}
+
+- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error {
+
+    if (webSocket == self.socket) {
+        NSLog(@"************************** socket 连接失败************************** ");
+        _socket = nil;
+        //连接失败就重连
+        [self reConnect];
+    }
+}
+
+- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean {
+    
+    if (webSocket == self.socket) {
+        NSLog(@"************************** socket连接断开************************** ");
+        NSLog(@"被关闭连接,code:%ld,reason:%@,wasClean:%d",(long)code,reason,wasClean);
+            [self SRWebSocketClose];
+    }
+
+}
+
+/*该函数是接收服务器发送的pong消息,其中最后一个是接受pong消息的,
+ 在这里就要提一下心跳包,一般情况下建立长连接都会建立一个心跳包,
+ 用于每隔一段时间通知一次服务端,客户端还是在线,这个心跳包其实就是一个ping消息,
+ 我的理解就是建立一个定时器,每隔十秒或者十五秒向服务端发送一个ping消息,这个消息可是是空的
+ */
+
+-(void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload{
+    NSString *reply = [[NSString alloc] initWithData:pongPayload encoding:NSUTF8StringEncoding];
+    NSLog(@"reply===%@",reply);
+}
+#pragma mark - 收到的回调
+- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message  {
+    
+    if (webSocket == self.socket) {
+//        NSLog(@"************************** socket收到数据了************************** ");
+//        NSLog(@"message:%@",message);
+        if(!message){
+            return;
+        }
+        [self handleReceivedMessage:message];
+
+    }
+}
+- (void)handleReceivedMessage:(id)message{
+
+        if(self.didReceiveMessage){
+            self.didReceiveMessage(message);
+        }
+
+}
+
+//重连机制
+- (void)reConnect
+{
+    
+   [self SRWebSocketClose];
+    //超过一分钟就不再重连 所以只会重连5次 2^5 = 64
+    if (reConnectTime > 64*2) {
+        //您的网络状况不是很好,请检查网络后重试
+        return;
+    }
+   
+    
+    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(reConnectTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+        self.socket = nil;
+        [self SRWebSocketOpen];
+        NSLog(@"重连");
+    });
+    
+    //重连时间2的指数级增长
+    if (reConnectTime == 0) {
+        reConnectTime = 2;
+    }else{
+        reConnectTime *= 2;
+    }
+    
+}
+
+//初始化心跳
+- (void)initHeartBeat
+{
+    dispatch_main_async_safe(^{
+        [self destoryHeartBeat];
+       
+        heartBeat = [NSTimer timerWithTimeInterval:30 target:self selector:@selector(ping) userInfo:nil repeats:YES];
+        //和服务端约定好发送什么作为心跳标识,尽可能的减小心跳包大小
+        [[NSRunLoop currentRunLoop]addTimer:heartBeat forMode:NSRunLoopCommonModes];
+    })
+}
+
+
+//取消心跳
+- (void)destoryHeartBeat
+{
+    dispatch_main_async_safe(^{
+        if (heartBeat) {
+            if ([heartBeat respondsToSelector:@selector(isValid)]){
+                if ([heartBeat isValid]){
+                    [heartBeat invalidate];
+                    heartBeat = nil;
+                }
+            }
+        }
+    })
+}
+
+//pingPong
+- (void)ping{
+    if (self.socket.readyState == SR_OPEN) {
+        [self.socket sendPing:nil];
+    }
+}
+
+- (void)sendData:(NSDictionary *)paramDic withRequestURI:(NSString *)requestURI{
+    
+    //这一块的数据格式由你们跟你们家服务器哥哥商定
+//     NSLog(@"socketSendData--configDic --------------- %@",requestURI);
+
+    WeakSelf(self);
+    dispatch_queue_t queue =  dispatch_queue_create("zy", NULL);
+    
+    dispatch_async(queue, ^{
+        if (weakself.socket != nil) {
+            // 只有 SR_OPEN 开启状态才能调 send 方法啊,不然要崩
+            if (weakself.socket.readyState == SR_OPEN) {
+                [weakself.socket send:requestURI];    // 发送数据
+                
+            } else if (weakself.socket.readyState == SR_CONNECTING) {
+
+                [weakself reConnect];
+                
+            } else if (weakself.socket.readyState == SR_CLOSING || weakself.socket.readyState == SR_CLOSED) {
+                // websocket 断开了,调用 reConnect 方法重连
+                NSLog(@"重连");
+                
+                [weakself reConnect];
+            }
+        } else {
+            // 这里要看你的具体业务需求;不过一般情况下,调用发送数据还是希望能把数据发送出去,所以可以再次打开链接;不用担心这里会有多个socketopen;因为如果当前有socket存在,会停止创建哒
+            [weakself SRWebSocketOpen];
+        }
+    });
+}
+
+-(SRReadyState)socketReadyState{
+    return self.socket.readyState;
+}
+-(void)dealloc{
+    [[NSNotificationCenter defaultCenter] removeObserver:self];
+    NSLog(@"SocketRocketUtility dealloced");
+}
+
+
+@end

+ 1 - 0
DanceGame/Classes/FitfunTool/FitfunSDK_V2.0/KpSupport/SDK/shoes_sdk/InertialTrajProcess.cpp

@@ -42,6 +42,7 @@ void InertialTrajProcess::TrajRotate(int* pos)
 
 	posTemp[0] = cos(curheading) * float(pos_f[0]) + sin(curheading) * float(pos_f[1]);
 	posTemp[1] = -sin(curheading) * float(pos_f[0]) + cos(curheading) * float(pos_f[1]);
+
 	
 	pos[0] = (int)(posTemp[0] * 100.0f);
 	pos[1] = (int)(posTemp[1] * 100.0f);

+ 43 - 23
DanceGame/Unity-iPhone.xcodeproj/project.pbxproj

@@ -190,6 +190,8 @@
 		8CBD47E49638FD0CF890B3BC /* Il2CppMetadataUsage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0C7A4AEC80BD848836F713B2 /* Il2CppMetadataUsage.cpp */; };
 		91E346D88C9D5D9F7D2FA874 /* Il2CppInteropDataTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E5DC4F7E951D1ECD94986099 /* Il2CppInteropDataTable.cpp */; };
 		92E64B4E99F11D7332C25194 /* Il2CppCompilerCalculateTypeValues_10Table.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2A1348B696429D055C356554 /* Il2CppCompilerCalculateTypeValues_10Table.cpp */; };
+		93BB27B32785A1820019B85C /* SocketRocketUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 93BB27AF2785A1820019B85C /* SocketRocketUtility.m */; };
+		93BB27B42785A1820019B85C /* SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 93BB27B02785A1820019B85C /* SRWebSocket.m */; };
 		93E62D3C276C86EA006975D1 /* TXCarouselCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 93E62CA5276C86E9006975D1 /* TXCarouselCollectionViewCell.xib */; };
 		93E62D3D276C86EA006975D1 /* TXCarouselCellModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 93E62CA8276C86E9006975D1 /* TXCarouselCellModel.m */; };
 		93E62D3E276C86EA006975D1 /* ShoesCollectionCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93E62CAA276C86E9006975D1 /* ShoesCollectionCell.m */; };
@@ -634,6 +636,10 @@
 		8DD1467686B0602CA38E0CC9 /* Bulk_UnityEngine.GridModule_0.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Bulk_UnityEngine.GridModule_0.cpp; path = Classes/Native/Bulk_UnityEngine.GridModule_0.cpp; sourceTree = SOURCE_ROOT; };
 		913F48D5B9075C1037EDE48D /* GenericMethods0.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = GenericMethods0.cpp; path = Classes/Native/GenericMethods0.cpp; sourceTree = SOURCE_ROOT; };
 		935642749A1C0E55C636858E /* Bulk_Assembly-CSharp_1.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "Bulk_Assembly-CSharp_1.cpp"; path = "Classes/Native/Bulk_Assembly-CSharp_1.cpp"; sourceTree = SOURCE_ROOT; };
+		93BB27AF2785A1820019B85C /* SocketRocketUtility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SocketRocketUtility.m; sourceTree = "<group>"; };
+		93BB27B02785A1820019B85C /* SRWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRWebSocket.m; sourceTree = "<group>"; };
+		93BB27B12785A1820019B85C /* SocketRocketUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SocketRocketUtility.h; sourceTree = "<group>"; };
+		93BB27B22785A1820019B85C /* SRWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRWebSocket.h; sourceTree = "<group>"; };
 		93E62CA5276C86E9006975D1 /* TXCarouselCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TXCarouselCollectionViewCell.xib; sourceTree = "<group>"; };
 		93E62CA6276C86E9006975D1 /* TXCarouselViewLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TXCarouselViewLayout.h; sourceTree = "<group>"; };
 		93E62CA7276C86E9006975D1 /* ShoesCarouselView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShoesCarouselView.h; sourceTree = "<group>"; };
@@ -1147,6 +1153,17 @@
 			path = Unity;
 			sourceTree = "<group>";
 		};
+		93BB27AE2785A1820019B85C /* socket */ = {
+			isa = PBXGroup;
+			children = (
+				93BB27AF2785A1820019B85C /* SocketRocketUtility.m */,
+				93BB27B02785A1820019B85C /* SRWebSocket.m */,
+				93BB27B12785A1820019B85C /* SocketRocketUtility.h */,
+				93BB27B22785A1820019B85C /* SRWebSocket.h */,
+			);
+			path = socket;
+			sourceTree = "<group>";
+		};
 		93E62CA2276C86E9006975D1 /* SDK */ = {
 			isa = PBXGroup;
 			children = (
@@ -1268,6 +1285,7 @@
 		93E62CCE276C86E9006975D1 /* HTTP */ = {
 			isa = PBXGroup;
 			children = (
+				93BB27AE2785A1820019B85C /* socket */,
 				93E62CCF276C86E9006975D1 /* HTTPDataProcession.m */,
 				93E62CD0276C86E9006975D1 /* HTTPDataProcession.h */,
 				93E62CD1276C86E9006975D1 /* AFNetworking */,
@@ -1299,36 +1317,36 @@
 		93E62CE0276C86E9006975D1 /* shoes_sdk */ = {
 			isa = PBXGroup;
 			children = (
-				93E62CE1276C86E9006975D1 /* MonsterH5.h */,
-				93E62CE2276C86E9006975D1 /* DanceFoot.cpp */,
-				93E62CE3276C86E9006975D1 /* RunGame.h */,
-				93E62CE4276C86E9006975D1 /* FootStep.cpp */,
-				93E62CE5276C86E9006975D1 /* Tricycle.h */,
-				93E62CE6276C86E9006975D1 /* RuinsExplore.h */,
-				93E62CE7276C86E9006975D1 /* DanceGame.h */,
-				93E62CE8276C86E9006975D1 /* OriginTraj.h */,
-				93E62CE9276C86E9006975D1 /* MonsterH5.cpp */,
 				93E62CEA276C86E9006975D1 /* BigFoot.cpp */,
-				93E62CEB276C86E9006975D1 /* Interaction.cpp */,
-				93E62CEC276C86E9006975D1 /* SingleFootAction.h */,
-				93E62CED276C86E9006975D1 /* Interaction.h */,
-				93E62CEE276C86E9006975D1 /* pub.h */,
+				93E62CF3276C86E9006975D1 /* BigFoot.h */,
+				93E62CE2276C86E9006975D1 /* DanceFoot.cpp */,
 				93E62CEF276C86E9006975D1 /* DanceFoot.h */,
-				93E62CF0276C86E9006975D1 /* Tricycle.cpp */,
+				93E62CF7276C86E9006975D1 /* DanceGame.cpp */,
+				93E62CE7276C86E9006975D1 /* DanceGame.h */,
+				93E62CE4276C86E9006975D1 /* FootStep.cpp */,
 				93E62CF1276C86E9006975D1 /* FootStep.h */,
-				93E62CF2276C86E9006975D1 /* SingleFootAction.cpp */,
-				93E62CF3276C86E9006975D1 /* BigFoot.h */,
-				93E62CF4276C86E9006975D1 /* RuinsExplore.cpp */,
-				93E62CF5276C86E9006975D1 /* InertialTrajProcess.h */,
+				93E62CFE276C86E9006975D1 /* Game.cpp */,
 				93E62CF6276C86E9006975D1 /* Game.h */,
-				93E62CF7276C86E9006975D1 /* DanceGame.cpp */,
+				93E62CFC276C86E9006975D1 /* InertialTrajProcess.cpp */,
+				93E62CF5276C86E9006975D1 /* InertialTrajProcess.h */,
+				93E62CEB276C86E9006975D1 /* Interaction.cpp */,
+				93E62CED276C86E9006975D1 /* Interaction.h */,
 				93E62CF8276C86E9006975D1 /* jumpH5.cpp */,
-				93E62CF9276C86E9006975D1 /* ReadMe.txt */,
+				93E62CFD276C86E9006975D1 /* jumpH5.h */,
+				93E62CE9276C86E9006975D1 /* MonsterH5.cpp */,
+				93E62CE1276C86E9006975D1 /* MonsterH5.h */,
 				93E62CFA276C86E9006975D1 /* OriginTraj.cpp */,
+				93E62CE8276C86E9006975D1 /* OriginTraj.h */,
+				93E62CEE276C86E9006975D1 /* pub.h */,
+				93E62CF9276C86E9006975D1 /* ReadMe.txt */,
+				93E62CF4276C86E9006975D1 /* RuinsExplore.cpp */,
+				93E62CE6276C86E9006975D1 /* RuinsExplore.h */,
 				93E62CFB276C86E9006975D1 /* RunGame.cpp */,
-				93E62CFC276C86E9006975D1 /* InertialTrajProcess.cpp */,
-				93E62CFD276C86E9006975D1 /* jumpH5.h */,
-				93E62CFE276C86E9006975D1 /* Game.cpp */,
+				93E62CE3276C86E9006975D1 /* RunGame.h */,
+				93E62CF2276C86E9006975D1 /* SingleFootAction.cpp */,
+				93E62CEC276C86E9006975D1 /* SingleFootAction.h */,
+				93E62CF0276C86E9006975D1 /* Tricycle.cpp */,
+				93E62CE5276C86E9006975D1 /* Tricycle.h */,
 			);
 			path = shoes_sdk;
 			sourceTree = "<group>";
@@ -1844,6 +1862,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				D82DCFC30E8000A5005D6AD8 /* main.mm in Sources */,
+				93BB27B32785A1820019B85C /* SocketRocketUtility.m in Sources */,
 				93E62D58276C86EA006975D1 /* BigFoot.cpp in Sources */,
 				8A793A081ED43EE100B44EF1 /* UnityViewControllerBase+iOS.mm in Sources */,
 				93E62D5C276C86EA006975D1 /* RuinsExplore.cpp in Sources */,
@@ -2014,6 +2033,7 @@
 				E3B9484CADE985963342F411 /* Bulk_mscorlib_1.cpp in Sources */,
 				C341462FB987297699821DE5 /* Bulk_mscorlib_10.cpp in Sources */,
 				AEBE4ED3B37329283715252C /* Bulk_mscorlib_11.cpp in Sources */,
+				93BB27B42785A1820019B85C /* SRWebSocket.m in Sources */,
 				93E62D4F276C86EA006975D1 /* AFURLResponseSerialization.m in Sources */,
 				3724408CAF1DFFED8AE6D2AC /* Bulk_mscorlib_12.cpp in Sources */,
 				6CA14F0783BC9B9A938CD8E7 /* Bulk_mscorlib_2.cpp in Sources */,

BIN
DanceGame/Unity-iPhone.xcodeproj/project.xcworkspace/xcuserdata/duowan123.xcuserdatad/UserInterfaceState.xcuserstate


BIN
RunGame/SDK/.DS_Store


+ 6 - 22
RunGame/SDK/BLE/BTDataProcess.h

@@ -7,7 +7,9 @@
 
 #import <UIKit/UIKit.h>
 #import "MYFactoryManager.h"
+
 #import "LEONBLManager.h"
+#import "SocketRocketUtility.h"
 
 //游戏类型
 typedef NS_ENUM(int, GAME_TYPE){
@@ -27,31 +29,13 @@ typedef NS_ENUM(NSInteger, BLETOOTH_STUTAS){
 
 @interface BTDataProcess : UIViewController
     
-//趣动跳转过来携带的mac 实际是蓝牙设备的identity
-@property(copy,nonatomic)NSString * macAddress;
-//弹窗 unity主动选择链接 主0 副1 设备
-@property(assign,nonatomic)DEVICE_TYPE deviceType;
-//游戏种类 1是跳舞 3是跑酷 2是赛达尔传说 4是demo->dance
-@property(assign,nonatomic)GAME_TYPE game_type;
-
-//蓝牙设备数据源回调给popSeaechView
-@property(copy,nonatomic)void (^deviceArrBLock)(NSMutableArray*arr);
-//脚步交互数据
-@property(copy,nonatomic)void (^getInteractionBlock)(int interaction);
-
-//程序在前台还是后台
-@property(assign,nonatomic)int isBackGround;
-
 //单例->初始化蓝牙
 +(instancetype)sharedInstance;
 
-//初始化中心管理
--(void)initCBCentralManager;
-//app跳转直接连 或 tableview 选中
--(void)connectPeripheral:(CBPeripheral*)peripheral;
-//unity或者趣动请求 外部断开蓝牙链接
--(void)disConnedctBle;
-
+//url 跳转
+-(void)startWithUrl:(NSURL*)url;
+//unity call 打开弹窗
+-(void)searchBLEAction:(int)type;
 
 //监听通知开始游戏
 -(void)gameStartInitData;

+ 202 - 40
RunGame/SDK/BLE/BTDataProcess.mm

@@ -8,20 +8,40 @@
 #include "Game.h"
 
 //#define NSLog(format, ...) printf("TIME:%s FILE:%s(%d行) FUNCTION:%s %s\n",__TIME__, [[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__, __PRETTY_FUNCTION__, [[NSString stringWithFormat:(format), ##__VA_ARGS__] UTF8String])
-@interface BTDataProcess ()<NSURLSessionDataDelegate>
+@interface BTDataProcess ()
 {
 @private
 Game *game;
 Game *viceDeviceGame;
 }
-@property (strong, nonatomic)NSMutableArray  * deviceArray;//蓝牙外设数据源
-@property (nonatomic,strong)IOSPlatformSDK * sdk;//ios_sdk
-@property(nonatomic,weak)NSTimer * timer;//定时请求设备状态
-@property(nonatomic,assign)BOOL gameModel;//鞋子是否开启游戏模式
-@property(nonatomic,strong)NSDate * firstTime;//当局游戏时间
+
+//趣动跳转过来携带的mac 实际是蓝牙设备的identity
+@property(copy,nonatomic)NSString * macAddress;
+//弹窗 unity主动选择链接 主0 副1 设备
+@property(assign,nonatomic)DEVICE_TYPE deviceType;
+//游戏种类
+@property(assign,nonatomic)GAME_TYPE game_type;
+
+//ios_sdk
+@property (nonatomic,strong)IOSPlatformSDK * sdk;
+
+//蓝牙弹窗
+@property (strong, nonatomic)SearchDeviceViewController * searchDeviceVC;
+//蓝牙外设数据源
+@property (strong, nonatomic)NSMutableArray  * deviceArray;
+
+//定时请求设备状态
+@property(nonatomic,weak)NSTimer * timer;
+//程序在前台还是后台
+@property(assign,nonatomic)int isBackGround;
+//鞋子是否开启游戏模式
+@property(nonatomic,assign)BOOL gameModel;
+//当局游戏时间
+@property(nonatomic,strong)NSDate * firstTime;
 //当局游戏跳起、下蹲次数
 @property(assign,nonatomic)int initial_jump_count;
 @property(assign,nonatomic)int initial_down_count;
+
 @end
 
 @implementation BTDataProcess
@@ -43,23 +63,82 @@ static BTDataProcess* instance = nil;
     static dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{
         instance = [super init];
-        //后台模式
-        self.isBackGround = NO;
-        //默认游戏模式关
-        self.gameModel = NO;
+        //游戏数据初始化
+        [self initData];
     });
     return instance;
 }
 
 #pragma mark ===============================================>> public method
-//趣动 ==>> 断开蓝牙
--(void)disConnedctBle{
-    NSLog(@"趣动 call 断开蓝牙");
-    [LEManager cancelPeripheralConnection];
+-(void)startWithUrl:(NSURL*)url{//
+    
+    if (url==nil){
+        return;
+    }
+    NSString * urlString = [url absoluteString];
+    NSArray * stringArr = [urlString componentsSeparatedByString:@"//"];
+    NSString * arr2 = stringArr[1];
+    NSString* finsalString = [arr2 stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+    NSDictionary * urlDict = [AlgorithmTool dictionaryWithJsonString:finsalString];
+    NSLog(@"Schemes_Url转码的dict ========= %@",urlDict);
+    //缓存用户信息数据
+    [IOS_NSUSERDEFAULT setObject:urlDict forKey:IOSSDK_USERINFO];
+    [IOS_NSUSERDEFAULT synchronize];
+  
+    if (urlDict!=nil){
+        //主副设备:deviceType  游戏种类:game_type  mac地址:macAddress
+        self.deviceType = DEVICETYPE_MAIN;
+        if ([urlDict objectForKey:@"game_type"]!=nil){
+            NSString * game_type = urlDict[@"game_type"];
+            self.game_type = (GAME_TYPE)[game_type intValue];
+        }
+        if ([urlDict objectForKey:@"mac"]!=nil){
+            self.macAddress = [urlDict objectForKey:@"mac"];
+        }
+        //链接蓝牙
+        [self initCBCentralManager];
+    }
+    
+}
+
+//unity call instance弹出蓝牙搜索框
+-(void)searchBLEAction:(int)type{
+    
+    /***************************初始化蓝牙数据处理工具***************************/
+    //主副设备:deviceType  游戏种类:game_type  mac地址:macAddress
+    self.deviceType = (DEVICE_TYPE)type;
+    NSDictionary * jsonDict = [IOS_NSUSERDEFAULT objectForKey:IOSSDK_USERINFO];
+    NSString * game_type = jsonDict[@"game_type"];
+    self.game_type = (GAME_TYPE)[game_type intValue];
+    self.macAddress = @"";
+    //初始化蓝牙CBCentralManager
+    [self initCBCentralManager];
+
+    /***************************搜索蓝牙外设弹窗***************************/
+    self.searchDeviceVC = [SearchDeviceViewController  new];
+    UIViewController *rootVC = [[UIApplication sharedApplication].delegate window].rootViewController;
+    UIViewController *parent = rootVC;
+    while ((parent = rootVC.presentedViewController) != nil ){
+    rootVC = parent;
+    }
+    while ([rootVC isKindOfClass:[UINavigationController class]]){
+    rootVC = [(UINavigationController *)rootVC topViewController];
+    }
+    [rootVC addChildViewController:self.searchDeviceVC];
+    [rootVC.view addSubview:self.searchDeviceVC.view];
+    //点击searchVC的tableViewCell链接选中的蓝牙
+    __weak __typeof(self)weakself = self;
+        self.searchDeviceVC.connectDeviceBlock = ^(CBPeripheral * peripheral){
+        weakself.deviceType = (DEVICE_TYPE)type;
+        [weakself connectPeripheral:peripheral];
+        weakself.macAddress = peripheral.identifier.UUIDString;
+    };
+    
 }
 
 //每重新开始一局游戏
 -(void)gameStartInitData{
+    
     //当局游戏时间
     self.firstTime = [NSDate date];
     //当局动作数据
@@ -69,8 +148,9 @@ static BTDataProcess* instance = nil;
     self.gameModel = YES;
     //读写到特征后 添加测试弹窗
     dispatch_async(dispatch_get_main_queue(), ^{
-//        [DebugViewInstance inittestLabel];
+        [DebugViewInstance inittestLabel];
     });
+    
 }
 
 //每结束一局游戏
@@ -83,9 +163,96 @@ static BTDataProcess* instance = nil;
     [BTDataSendInstance vibration:(DEVICE_TYPE)type duration:duration];
 }
 
-#pragma mark ============================================================== leon Ble manager
+#pragma mark ============================>> private
+-(void)initData{
+    
+    //后台模式
+    self.isBackGround = NO;
+    //默认游戏模式关
+    self.gameModel = NO;
+    
+    //固定一个时间戳
+    NSString * timestamp =   [AlgorithmTool returnTimestamp];
+    [IOS_NSUSERDEFAULT setObject:timestamp forKey:IOSSDK_TIMESTAMP];
+    [IOS_NSUSERDEFAULT synchronize];
+    
+    //监听 趣动 app group 消息 ==>> 好友邀请信息
+    CFStringRef invite = (__bridge CFStringRef)@"INVITE_NOTIFICATION";
+    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
+                                    NULL,
+                                    inviteFriends,
+                                    invite,
+                                    NULL,
+                                    CFNotificationSuspensionBehaviorDeliverImmediately);
+    
+    //监听 趣动 app group 消息 ==>> 断开蓝牙
+    CFStringRef strRef = (__bridge CFStringRef)@"DISCONNECT_BLE";
+    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
+                                    NULL,
+                                    disConnectBle,
+                                    strRef,
+                                    NULL,
+                                    CFNotificationSuspensionBehaviorDeliverImmediately);
+    //监听程序 退到 后台
+    [CUS_NOTIFICATIONCENTER addObserver:self selector:@selector(enterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
+    //监听程序 返回 前台
+    [CUS_NOTIFICATIONCENTER addObserver:self selector:@selector(willEnterForeground) name:UIApplicationWillEnterForegroundNotification object:nil];
+
+}
+
+-(void)enterBackground{
+    NSLog(@"leon UIApplicationDidEnterBackgroundNotification");
+    instance.isBackGround = YES;
+}
+-(void)willEnterForeground{
+    NSLog(@"leon UIApplicationWillEnterForegroundNotification");
+    instance.isBackGround = NO;
+    NSLog(@"程序从后台返回 重连蓝牙 peripheral.state = %ld",LEManager.peripheral.state);
+    [self initCBCentralManager];
+}
+
+void disConnectBle (CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo){
+    [instance disConnedctBle];
+}
+
+void inviteFriends (CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo){
+        
+    //group.com.Oujia.AppAndGame 沙河数据
+    NSUserDefaults* userDefault = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.Oujia.AppAndGame"];
+    //趣动传过来的 invite json字符串转字典
+    NSString * string = [userDefault objectForKey:@"invite"];
+    NSData * data = [string dataUsingEncoding:NSUTF8StringEncoding];
+    NSDictionary * inviteDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
+    if (inviteDict!=nil){
+        //字典取值
+        NSDictionary * invite = [inviteDict objectForKey:@"invite"];
+        NSDictionary * user =  [invite objectForKey:@"user"];
+        //user字典 -> 字符串 -> Char
+        NSData * userData = [NSJSONSerialization dataWithJSONObject:user options:NSJSONWritingPrettyPrinted error:nil];
+        NSString * userString =  [[NSString alloc] initWithData:userData encoding:NSUTF8StringEncoding];
+        const char * userChar =[userString UTF8String];
+        //info字符串 -> Char
+        NSString * infoString = invite[@"info"];
+        const char * infoChar =[infoString UTF8String];
+        NSLog(@"IOS_SKD 回调好友邀请信息 GetInviteInfo ===>>  %s , %s",userChar,infoChar);
+        [[IOSPlatformSDK sharedInstance] bridgingInvite:0 userChar:userChar infoChar:infoChar];
+    }
+    
+}
+
+//趣动 ==>> 断开蓝牙
+-(void)disConnedctBle{
+    NSLog(@"趣动 call 断开蓝牙");
+    [LEManager cancelPeripheralConnection];
+}
+
+#pragma mark ===============================================>> leon Ble manager
 //测试自己封装的蓝牙类
 -(void)initCBCentralManager{
+    
+    //建立socket链接
+    SocketRocketUtility * install = [SocketRocketUtility sharedInstance];
+    [install SRWebSocketOpen];
 
     if (LEManager.centralManager == nil && LEManager.peripheral == nil && LEManager.vicePeripheral == nil){
         [LEManager initCBCentralManager];
@@ -112,16 +279,14 @@ static BTDataProcess* instance = nil;
             case 4:{
                 NSLog(@"当前的蓝牙状态 ===>> 蓝牙已关闭");
                 if (LEManager.peripheral!=nil){
-                    
                     [self.sdk bridgingDeviceAction:DEVICETYPE_MAIN
                                                 name:LEManager.peripheral.name
                                              address:LEManager.peripheral.identifier.UUIDString
                                               status:CONNECT_LOST
                                          electricity:0];
-                    
                 }
+                
                 if (LEManager.vicePeripheral!=nil){
-                    
                     [self.sdk bridgingDeviceAction:DEVICETYPE_VICE
                                                 name:LEManager.vicePeripheral.name
                                              address:LEManager.vicePeripheral.identifier.UUIDString
@@ -179,9 +344,8 @@ static BTDataProcess* instance = nil;
                     }
                     
                     //数据回调给 搜索蓝牙弹窗
-                    if (self.deviceArrBLock != nil){
-                        self.deviceArrBLock(self.deviceArray);
-                    }
+                    self.searchDeviceVC.deviceArray = self.deviceArray;
+                    [self.searchDeviceVC reloadData];
 
             }
         
@@ -206,6 +370,7 @@ static BTDataProcess* instance = nil;
     if(instance!=NULL && peripheral!=NULL){
        
         self.sdk = [IOSPlatformSDK sharedInstance];//蓝牙数据经过sdk数据转换后 ios call unity
+        self.game_type = GAME_TYPE_RUN;
         DebugViewInstance.currentTS = 1000;
         NSLog(@"BTDataProcess connectPeripheral self.deviceType ==>> %ld  self.game_type ==>> %d   mac ==>>  %@",(long)self.deviceType,self.game_type,self.macAddress);
         
@@ -334,11 +499,10 @@ static BTDataProcess* instance = nil;
     
 }
 
-
 #pragma mark ===============================================>> 读取特征后 鞋子打开游戏模式 & 激光开关 &查询鞋子蓝牙状态 & 主设备初始化缓存游戏步数
 -(void)initGameAction:(DEVICE_TYPE)deviceType{
     
-    //开启游戏模式&查询设备信息 初始化步数数据
+    //开启游戏模式&查询设备信息 初始化步数数据 (防止读取到的读写为空 延迟2s)
     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(),^{
         [self timingDataTransmission:@"dispatch_after"];
     });
@@ -348,7 +512,7 @@ static BTDataProcess* instance = nil;
         [self.timer invalidate];
         self.timer = nil;
     }
-    self.timer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(timingDataTransmission:) userInfo:nil repeats:YES];
+    self.timer = [NSTimer scheduledTimerWithTimeInterval:15 target:self selector:@selector(timingDataTransmission:) userInfo:nil repeats:YES];
     [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
 
 }
@@ -357,17 +521,13 @@ static BTDataProcess* instance = nil;
 -(void)timingDataTransmission:(NSString*)style{
     
     if (self.isBackGround){//程序在后台
-        
     }else{//程序在前台
-//        NSLog(@"定时发送 == %@",style);
         [BTDataSendInstance startGameModel];
     }
-    
     [BTDataSendInstance queryDevideInfo];
 
 }
 
-
 #pragma mark ===============================================>> 校验 & 报文数据解析
 -(void)verifyData:(CBCharacteristic*)characteristic deviceType:(DEVICE_TYPE)deviceType{
     
@@ -375,15 +535,10 @@ static BTDataProcess* instance = nil;
          
          //带cmd位的有效数据  计算校验位
          if ([AlgorithmTool verificationRusult:characteristic.value]==YES){
-             
              [self analysisCharacteristic:characteristic.value deviceType:deviceType];
-                              
          }
-         
      }else{
-         
          NSLog(@"无效报文");
-         
      }
      
 }
@@ -421,7 +576,7 @@ static BTDataProcess* instance = nil;
            short righrAcc_x = [AlgorithmTool dataToSwapBigShortToHost:characteristic andRange:NSMakeRange(40, 2)];
            short righrAcc_Y = [AlgorithmTool dataToSwapBigShortToHost:characteristic andRange:NSMakeRange(42, 2)];
            short righrAcc_z = [AlgorithmTool dataToSwapBigShortToHost:characteristic andRange:NSMakeRange(44, 2)];
-           int  righrAcc[3] = {righrAcc_x,righrAcc_Y,righrAcc_z};
+           int  righr_acc[3] = {righrAcc_x,righrAcc_Y,righrAcc_z};
            //左脚三维数据
            short leftAcc_x = [AlgorithmTool dataToSwapBigShortToHost:characteristic andRange:NSMakeRange(46, 2)];
            short leftAcc_y = [AlgorithmTool dataToSwapBigShortToHost:characteristic andRange:NSMakeRange(48, 2)];
@@ -469,11 +624,11 @@ static BTDataProcess* instance = nil;
                if (deviceType==DEVICETYPE_MAIN){//主设备
 //                   NSLog(@"主设备 接收到 鞋子动作数据 data = %@",characteristic);
                    //调取鞋子SDK
-                   [self shoseSDKRight_pos:right_pos Right_att:right_att Right_acc:righrAcc LeftPos:left_pos Left_att:left_att Left_acc:left_acc ts:ts rightZupt:rightZupt leftZupt:leftZupt jump:jump down:down rssi:rssi girlShoes:girlShoes right_press:right_press left_press:left_press characteristic:characteristic];
+                   [self shoseSDKRight_pos:right_pos Right_att:right_att Right_acc:righr_acc LeftPos:left_pos Left_att:left_att Left_acc:left_acc ts:ts rightZupt:rightZupt leftZupt:leftZupt jump:jump down:down rssi:rssi girlShoes:girlShoes right_press:right_press left_press:left_press characteristic:characteristic];
                    
                }else if (deviceType==DEVICETYPE_VICE){//副设备动作数据
                    //调取鞋子SDK
-                   [self viceShoseSDKRight_pos:right_pos Right_att:right_att Right_acc:righrAcc LeftPos:left_pos Left_att:left_att Left_acc:left_acc ts:ts rightZupt:rightZupt leftZupt:leftZupt jump:jump down:down rssi:rssi girlShoes:girlShoes right_press:right_press left_press:left_press characteristic:characteristic];
+                   [self viceShoseSDKRight_pos:right_pos Right_att:right_att Right_acc:righr_acc LeftPos:left_pos Left_att:left_att Left_acc:left_acc ts:ts rightZupt:rightZupt leftZupt:leftZupt jump:jump down:down rssi:rssi girlShoes:girlShoes right_press:right_press left_press:left_press characteristic:characteristic];
                }
               
         }else if (dataType == -95 && characteristic.length == 26){//获取的是查询的数据 char-->int a1 = -95
@@ -576,9 +731,14 @@ static BTDataProcess* instance = nil;
 
             int interaction = game->getInteractionCMD();
 //            NSLog(@"主设备 当前的脚步交互动作 ===== >> %d",interaction);
+            //游戏交互
             [self.sdk bridgingInteraction:DEVICETYPE_MAIN code:interaction];
-            if (self.getInteractionBlock){
-                self.getInteractionBlock(interaction);
+            //自定义弹窗界面交互
+            if (interaction!=-1){
+                NSLog(@"BTDataProcess 有效交互动作 ===>> %d",interaction);
+//                [self.searchDeviceVC getInteraction:interaction];
+            }else{
+    //            NSLog(@"BTDataProcess 无效交互动作 ===>> %@",@"原地");
             }
         }
     
@@ -601,6 +761,8 @@ static BTDataProcess* instance = nil;
     
     /********************剑波 & 威严 要的图表数据 *****************/
     NSString * aString = [NSString stringWithUTF8String:game->getGameDataStr().c_str()];
+    //上传即使动作数据
+    [[SocketRocketUtility sharedInstance] sendData:nil withRequestURI:aString];
     if (DebugViewInstance.RSSI==nil){
         DebugViewInstance.RSSI = 0;
     }
@@ -609,7 +771,7 @@ static BTDataProcess* instance = nil;
     if (DebugViewInstance.tempStepString!=nil && DebugViewInstance.tempStepString.length>1){
         [DebugViewInstance writeFileToplist];
     }
-  
+    
     /********************调试窗数据 *****************/
     NSArray *aArray = [DebugViewInstance.tempStepString componentsSeparatedByString:@","];
 //    dispatch_async(dispatch_get_main_queue(), ^{

BIN
RunGame/SDK/HTTP/.DS_Store


+ 154 - 0
RunGame/SDK/HTTP/socket/SRWebSocket.h

@@ -0,0 +1,154 @@
+//
+//   Copyright 2012 Square Inc.
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+//
+
+#import <Foundation/Foundation.h>
+#import <Security/SecCertificate.h>
+
+typedef NS_ENUM(NSInteger, SRReadyState) {
+    SR_CONNECTING   = 0,
+    SR_OPEN         = 1,
+    SR_CLOSING      = 2,
+    SR_CLOSED       = 3,
+};
+
+typedef enum SRStatusCode : NSInteger {
+    // 0–999: Reserved and not used.
+    SRStatusCodeNormal = 1000,
+    SRStatusCodeGoingAway = 1001,
+    SRStatusCodeProtocolError = 1002,
+    SRStatusCodeUnhandledType = 1003,
+    // 1004 reserved.
+    SRStatusNoStatusReceived = 1005,
+    SRStatusCodeAbnormal = 1006,
+    SRStatusCodeInvalidUTF8 = 1007,
+    SRStatusCodePolicyViolated = 1008,
+    SRStatusCodeMessageTooBig = 1009,
+    SRStatusCodeMissingExtension = 1010,
+    SRStatusCodeInternalError = 1011,
+    SRStatusCodeServiceRestart = 1012,
+    SRStatusCodeTryAgainLater = 1013,
+    // 1014: Reserved for future use by the WebSocket standard.
+    SRStatusCodeTLSHandshake = 1015,
+    // 1016–1999: Reserved for future use by the WebSocket standard.
+    // 2000–2999: Reserved for use by WebSocket extensions.
+    // 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.
+    // 4000–4999: Available for use by applications.
+} SRStatusCode;
+
+@class SRWebSocket;
+
+extern NSString *const SRWebSocketErrorDomain;
+extern NSString *const SRHTTPResponseErrorKey;
+
+#pragma mark - SRWebSocketDelegate
+
+@protocol SRWebSocketDelegate;
+
+#pragma mark - SRWebSocket
+
+@interface SRWebSocket : NSObject <NSStreamDelegate>
+
+@property (nonatomic, weak) id <SRWebSocketDelegate> delegate;
+
+@property (nonatomic, readonly) SRReadyState readyState;
+@property (nonatomic, readonly, retain) NSURL *url;
+
+
+@property (nonatomic, readonly) CFHTTPMessageRef receivedHTTPHeaders;
+
+// Optional array of cookies (NSHTTPCookie objects) to apply to the connections
+@property (nonatomic, readwrite) NSArray * requestCookies;
+
+// This returns the negotiated protocol.
+// It will be nil until after the handshake completes.
+@property (nonatomic, readonly, copy) NSString *protocol;
+
+// Protocols should be an array of strings that turn into Sec-WebSocket-Protocol.
+- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates;
+- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols;
+- (id)initWithURLRequest:(NSURLRequest *)request;
+
+// Some helper constructors.
+- (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates;
+- (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols;
+- (id)initWithURL:(NSURL *)url;
+
+// Delegate queue will be dispatch_main_queue by default.
+// You cannot set both OperationQueue and dispatch_queue.
+- (void)setDelegateOperationQueue:(NSOperationQueue*) queue;
+- (void)setDelegateDispatchQueue:(dispatch_queue_t) queue;
+
+// By default, it will schedule itself on +[NSRunLoop SR_networkRunLoop] using defaultModes.
+- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
+- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
+
+// SRWebSockets are intended for one-time-use only.  Open should be called once and only once.
+- (void)open;
+
+- (void)close;
+- (void)closeWithCode:(NSInteger)code reason:(NSString *)reason;
+
+// Send a UTF8 String or Data.
+- (void)send:(id)data;
+
+// Send Data (can be nil) in a ping message.
+- (void)sendPing:(NSData *)data;
+
+@end
+
+#pragma mark - SRWebSocketDelegate
+
+@protocol SRWebSocketDelegate <NSObject>
+
+// message will either be an NSString if the server is using text
+// or NSData if the server is using binary.
+- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message;
+
+@optional
+
+- (void)webSocketDidOpen:(SRWebSocket *)webSocket;
+- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;
+- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;
+- (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload;
+
+// Return YES to convert messages sent as Text to an NSString. Return NO to skip NSData -> NSString conversion for Text messages. Defaults to YES.
+- (BOOL)webSocketShouldConvertTextFrameToString:(SRWebSocket *)webSocket;
+
+@end
+
+#pragma mark - NSURLRequest (SRCertificateAdditions)
+
+@interface NSURLRequest (SRCertificateAdditions)
+
+@property (nonatomic, retain, readonly) NSArray *SR_SSLPinnedCertificates;
+
+@end
+
+#pragma mark - NSMutableURLRequest (SRCertificateAdditions)
+
+@interface NSMutableURLRequest (SRCertificateAdditions)
+
+@property (nonatomic, retain) NSArray *SR_SSLPinnedCertificates;
+
+@end
+
+#pragma mark - NSRunLoop (SRWebSocket)
+
+@interface NSRunLoop (SRWebSocket)
+
++ (NSRunLoop *)SR_networkRunLoop;
+
+@end

+ 1916 - 0
RunGame/SDK/HTTP/socket/SRWebSocket.m

@@ -0,0 +1,1916 @@
+//
+//   Copyright 2012 Square Inc.
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+//
+
+
+#import "SRWebSocket.h"
+
+#if TARGET_OS_IPHONE
+#define HAS_ICU
+#endif
+
+#ifdef HAS_ICU
+#import <unicode/utf8.h>
+#endif
+
+#if TARGET_OS_IPHONE
+#import <Endian.h>
+#else
+#import <CoreServices/CoreServices.h>
+#endif
+
+#import <CommonCrypto/CommonDigest.h>
+#import <Security/SecRandom.h>
+
+#if OS_OBJECT_USE_OBJC_RETAIN_RELEASE
+#define sr_dispatch_retain(x)
+#define sr_dispatch_release(x)
+#define maybe_bridge(x) ((__bridge void *) x)
+#else
+#define sr_dispatch_retain(x) dispatch_retain(x)
+#define sr_dispatch_release(x) dispatch_release(x)
+#define maybe_bridge(x) (x)
+#endif
+
+#if !__has_feature(objc_arc) 
+#error SocketRocket must be compiled with ARC enabled
+#endif
+
+
+typedef enum  {
+    SROpCodeTextFrame = 0x1,
+    SROpCodeBinaryFrame = 0x2,
+    // 3-7 reserved.
+    SROpCodeConnectionClose = 0x8,
+    SROpCodePing = 0x9,
+    SROpCodePong = 0xA,
+    // B-F reserved.
+} SROpCode;
+
+typedef struct {
+    BOOL fin;
+//  BOOL rsv1;
+//  BOOL rsv2;
+//  BOOL rsv3;
+    uint8_t opcode;
+    BOOL masked;
+    uint64_t payload_length;
+} frame_header;
+
+static NSString *const SRWebSocketAppendToSecKeyString = @"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+
+static inline int32_t validate_dispatch_data_partial_string(NSData *data);
+static inline void SRFastLog(NSString *format, ...);
+
+@interface NSData (SRWebSocket)
+
+- (NSString *)stringBySHA1ThenBase64Encoding;
+
+@end
+
+
+@interface NSString (SRWebSocket)
+
+- (NSString *)stringBySHA1ThenBase64Encoding;
+
+@end
+
+
+@interface NSURL (SRWebSocket)
+
+// The origin isn't really applicable for a native application.
+// So instead, just map ws -> http and wss -> https.
+- (NSString *)SR_origin;
+
+@end
+
+
+@interface _SRRunLoopThread : NSThread
+
+@property (nonatomic, readonly) NSRunLoop *runLoop;
+
+@end
+
+
+static NSString *newSHA1String(const char *bytes, size_t length) {
+    uint8_t md[CC_SHA1_DIGEST_LENGTH];
+
+    assert(length >= 0);
+    assert(length <= UINT32_MAX);
+    CC_SHA1(bytes, (CC_LONG)length, md);
+    
+    NSData *data = [NSData dataWithBytes:md length:CC_SHA1_DIGEST_LENGTH];
+    
+    if ([data respondsToSelector:@selector(base64EncodedStringWithOptions:)]) {
+        return [data base64EncodedStringWithOptions:0];
+    }
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    return [data base64Encoding];
+#pragma clang diagnostic pop
+}
+
+@implementation NSData (SRWebSocket)
+
+- (NSString *)stringBySHA1ThenBase64Encoding;
+{
+    return newSHA1String(self.bytes, self.length);
+}
+
+@end
+
+
+@implementation NSString (SRWebSocket)
+
+- (NSString *)stringBySHA1ThenBase64Encoding;
+{
+    return newSHA1String(self.UTF8String, self.length);
+}
+
+@end
+
+NSString *const SRWebSocketErrorDomain = @"SRWebSocketErrorDomain";
+NSString *const SRHTTPResponseErrorKey = @"HTTPResponseStatusCode";
+
+// Returns number of bytes consumed. Returning 0 means you didn't match.
+// Sends bytes to callback handler;
+typedef size_t (^stream_scanner)(NSData *collected_data);
+
+typedef void (^data_callback)(SRWebSocket *webSocket,  NSData *data);
+
+@interface SRIOConsumer : NSObject {
+    stream_scanner _scanner;
+    data_callback _handler;
+    size_t _bytesNeeded;
+    BOOL _readToCurrentFrame;
+    BOOL _unmaskBytes;
+}
+@property (nonatomic, copy, readonly) stream_scanner consumer;
+@property (nonatomic, copy, readonly) data_callback handler;
+@property (nonatomic, assign) size_t bytesNeeded;
+@property (nonatomic, assign, readonly) BOOL readToCurrentFrame;
+@property (nonatomic, assign, readonly) BOOL unmaskBytes;
+
+@end
+
+// This class is not thread-safe, and is expected to always be run on the same queue.
+@interface SRIOConsumerPool : NSObject
+
+- (id)initWithBufferCapacity:(NSUInteger)poolSize;
+
+- (SRIOConsumer *)consumerWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes;
+- (void)returnConsumer:(SRIOConsumer *)consumer;
+
+@end
+
+@interface SRWebSocket ()  <NSStreamDelegate>
+
+@property (nonatomic) SRReadyState readyState;
+
+@property (nonatomic) NSOperationQueue *delegateOperationQueue;
+@property (nonatomic) dispatch_queue_t delegateDispatchQueue;
+
+// Specifies whether SSL trust chain should NOT be evaluated.
+// By default this flag is set to NO, meaning only secure SSL connections are allowed.
+// For DEBUG builds this flag is ignored, and SSL connections are allowed regardless
+// of the certificate trust configuration
+@property (nonatomic, readwrite) BOOL allowsUntrustedSSLCertificates;
+
+@end
+
+
+@implementation SRWebSocket {
+    NSInteger _webSocketVersion;
+    
+    NSOperationQueue *_delegateOperationQueue;
+    dispatch_queue_t _delegateDispatchQueue;
+    
+    dispatch_queue_t _workQueue;
+    NSMutableArray *_consumers;
+
+    NSInputStream *_inputStream;
+    NSOutputStream *_outputStream;
+   
+    NSMutableData *_readBuffer;
+    NSUInteger _readBufferOffset;
+ 
+    NSMutableData *_outputBuffer;
+    NSUInteger _outputBufferOffset;
+
+    uint8_t _currentFrameOpcode;
+    size_t _currentFrameCount;
+    size_t _readOpCount;
+    uint32_t _currentStringScanPosition;
+    NSMutableData *_currentFrameData;
+    
+    NSString *_closeReason;
+    
+    NSString *_secKey;
+    NSString *_basicAuthorizationString;
+    
+    BOOL _pinnedCertFound;
+    
+    uint8_t _currentReadMaskKey[4];
+    size_t _currentReadMaskOffset;
+
+    BOOL _consumerStopped;
+    
+    BOOL _closeWhenFinishedWriting;
+    BOOL _failed;
+
+    BOOL _secure;
+    NSURLRequest *_urlRequest;
+
+    BOOL _sentClose;
+    BOOL _didFail;
+    BOOL _cleanupScheduled;
+    int _closeCode;
+    
+    BOOL _isPumping;
+    
+    NSMutableSet *_scheduledRunloops;
+    
+    // We use this to retain ourselves.
+    __strong SRWebSocket *_selfRetain;
+    
+    NSArray *_requestedProtocols;
+    SRIOConsumerPool *_consumerPool;
+}
+
+@synthesize delegate = _delegate;
+@synthesize url = _url;
+@synthesize readyState = _readyState;
+@synthesize protocol = _protocol;
+
+static __strong NSData *CRLFCRLF;
+
++ (void)initialize;
+{
+    CRLFCRLF = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4];
+}
+
+- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates;
+{
+    self = [super init];
+    if (self) {
+        assert(request.URL);
+        _url = request.URL;
+        _urlRequest = request;
+        _allowsUntrustedSSLCertificates = allowsUntrustedSSLCertificates;
+        
+        _requestedProtocols = [protocols copy];
+        
+        [self _SR_commonInit];
+    }
+    
+    return self;
+}
+
+- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols;
+{
+    return [self initWithURLRequest:request protocols:protocols allowsUntrustedSSLCertificates:NO];
+}
+
+- (id)initWithURLRequest:(NSURLRequest *)request;
+{
+    return [self initWithURLRequest:request protocols:nil];
+}
+
+- (id)initWithURL:(NSURL *)url;
+{
+    return [self initWithURL:url protocols:nil];
+}
+
+- (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols;
+{
+    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];    
+    return [self initWithURLRequest:request protocols:protocols];
+}
+
+- (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates;
+{
+    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
+    return [self initWithURLRequest:request protocols:protocols allowsUntrustedSSLCertificates:allowsUntrustedSSLCertificates];
+}
+
+- (void)_SR_commonInit;
+{
+    NSString *scheme = _url.scheme.lowercaseString;
+    assert([scheme isEqualToString:@"ws"] || [scheme isEqualToString:@"http"] || [scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]);
+    
+    if ([scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]) {
+        _secure = YES;
+    }
+    
+    _readyState = SR_CONNECTING;
+    _consumerStopped = YES;
+    _webSocketVersion = 13;
+    
+    _workQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
+    
+    // Going to set a specific on the queue so we can validate we're on the work queue
+    dispatch_queue_set_specific(_workQueue, (__bridge void *)self, maybe_bridge(_workQueue), NULL);
+    
+    _delegateDispatchQueue = dispatch_get_main_queue();
+    sr_dispatch_retain(_delegateDispatchQueue);
+    
+    _readBuffer = [[NSMutableData alloc] init];
+    _outputBuffer = [[NSMutableData alloc] init];
+    
+    _currentFrameData = [[NSMutableData alloc] init];
+
+    _consumers = [[NSMutableArray alloc] init];
+    
+    _consumerPool = [[SRIOConsumerPool alloc] init];
+    
+    _scheduledRunloops = [[NSMutableSet alloc] init];
+    
+    [self _initializeStreams];
+    
+    // default handlers
+}
+
+- (void)assertOnWorkQueue;
+{
+    assert(dispatch_get_specific((__bridge void *)self) == maybe_bridge(_workQueue));
+}
+
+- (void)dealloc
+{
+    _inputStream.delegate = nil;
+    _outputStream.delegate = nil;
+
+    [_inputStream close];
+    [_outputStream close];
+    
+    if (_workQueue) {
+        sr_dispatch_release(_workQueue);
+        _workQueue = NULL;
+    }
+    
+    if (_receivedHTTPHeaders) {
+        CFRelease(_receivedHTTPHeaders);
+        _receivedHTTPHeaders = NULL;
+    }
+    
+    if (_delegateDispatchQueue) {
+        sr_dispatch_release(_delegateDispatchQueue);
+        _delegateDispatchQueue = NULL;
+    }
+}
+
+#ifndef NDEBUG
+
+- (void)setReadyState:(SRReadyState)aReadyState;
+{
+    assert(aReadyState > _readyState);
+    _readyState = aReadyState;
+}
+
+#endif
+
+- (void)open;
+{
+    assert(_url);
+    NSAssert(_readyState == SR_CONNECTING, @"Cannot call -(void)open on SRWebSocket more than once");
+
+    _selfRetain = self;
+
+    if (_urlRequest.timeoutInterval > 0)
+    {
+        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, _urlRequest.timeoutInterval * NSEC_PER_SEC);
+        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
+            if (self.readyState == SR_CONNECTING)
+                [self _failWithError:[NSError errorWithDomain:@"com.squareup.SocketRocket" code:504 userInfo:@{NSLocalizedDescriptionKey: @"Timeout Connecting to Server"}]];
+        });
+    }
+
+    [self openConnection];
+}
+
+// Calls block on delegate queue
+- (void)_performDelegateBlock:(dispatch_block_t)block;
+{
+    if (_delegateOperationQueue) {
+        [_delegateOperationQueue addOperationWithBlock:block];
+    } else {
+        assert(_delegateDispatchQueue);
+        dispatch_async(_delegateDispatchQueue, block);
+    }
+}
+
+- (void)setDelegateDispatchQueue:(dispatch_queue_t)queue;
+{
+    if (queue) {
+        sr_dispatch_retain(queue);
+    }
+    
+    if (_delegateDispatchQueue) {
+        sr_dispatch_release(_delegateDispatchQueue);
+    }
+    
+    _delegateDispatchQueue = queue;
+}
+
+- (BOOL)_checkHandshake:(CFHTTPMessageRef)httpMessage;
+{
+    NSString *acceptHeader = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(httpMessage, CFSTR("Sec-WebSocket-Accept")));
+
+    if (acceptHeader == nil) {
+        return NO;
+    }
+    
+    NSString *concattedString = [_secKey stringByAppendingString:SRWebSocketAppendToSecKeyString];
+    NSString *expectedAccept = [concattedString stringBySHA1ThenBase64Encoding];
+    
+    return [acceptHeader isEqualToString:expectedAccept];
+}
+
+- (void)_HTTPHeadersDidFinish;
+{
+    NSInteger responseCode = CFHTTPMessageGetResponseStatusCode(_receivedHTTPHeaders);
+    
+    if (responseCode >= 400) {
+        SRFastLog(@"Request failed with response code %d", responseCode);
+        [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2132 userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"received bad response code from server %ld", (long)responseCode], SRHTTPResponseErrorKey:@(responseCode)}]];
+        return;
+    }
+    
+    if(![self _checkHandshake:_receivedHTTPHeaders]) {
+        [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2133 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Invalid Sec-WebSocket-Accept response"] forKey:NSLocalizedDescriptionKey]]];
+        return;
+    }
+    
+    NSString *negotiatedProtocol = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(_receivedHTTPHeaders, CFSTR("Sec-WebSocket-Protocol")));
+    if (negotiatedProtocol) {
+        // Make sure we requested the protocol
+        if ([_requestedProtocols indexOfObject:negotiatedProtocol] == NSNotFound) {
+            [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2133 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Server specified Sec-WebSocket-Protocol that wasn't requested"] forKey:NSLocalizedDescriptionKey]]];
+            return;
+        }
+        
+        _protocol = negotiatedProtocol;
+    }
+    
+    self.readyState = SR_OPEN;
+    
+    if (!_didFail) {
+        [self _readFrameNew];
+    }
+
+    [self _performDelegateBlock:^{
+        if ([self.delegate respondsToSelector:@selector(webSocketDidOpen:)]) {
+            [self.delegate webSocketDidOpen:self];
+        };
+    }];
+}
+
+
+- (void)_readHTTPHeader;
+{
+    if (_receivedHTTPHeaders == NULL) {
+        _receivedHTTPHeaders = CFHTTPMessageCreateEmpty(NULL, NO);
+    }
+                        
+    [self _readUntilHeaderCompleteWithCallback:^(SRWebSocket *self,  NSData *data) {
+        CFHTTPMessageAppendBytes(_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length);
+        
+        if (CFHTTPMessageIsHeaderComplete(_receivedHTTPHeaders)) {
+            SRFastLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_receivedHTTPHeaders)));
+            [self _HTTPHeadersDidFinish];
+        } else {
+            [self _readHTTPHeader];
+        }
+    }];
+}
+
+- (void)didConnect;
+{
+    SRFastLog(@"Connected");
+    CFHTTPMessageRef request = CFHTTPMessageCreateRequest(NULL, CFSTR("GET"), (__bridge CFURLRef)_url, kCFHTTPVersion1_1);
+    
+    // Set host first so it defaults
+    CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Host"), (__bridge CFStringRef)(_url.port ? [NSString stringWithFormat:@"%@:%@", _url.host, _url.port] : _url.host));
+        
+    NSMutableData *keyBytes = [[NSMutableData alloc] initWithLength:16];
+    SecRandomCopyBytes(kSecRandomDefault, keyBytes.length, keyBytes.mutableBytes);
+    
+    if ([keyBytes respondsToSelector:@selector(base64EncodedStringWithOptions:)]) {
+        _secKey = [keyBytes base64EncodedStringWithOptions:0];
+    } else {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+        _secKey = [keyBytes base64Encoding];
+#pragma clang diagnostic pop
+    }
+    
+    assert([_secKey length] == 24);
+
+    // Apply cookies if any have been provided
+    NSDictionary * cookies = [NSHTTPCookie requestHeaderFieldsWithCookies:[self requestCookies]];
+    for (NSString * cookieKey in cookies) {
+        NSString * cookieValue = [cookies objectForKey:cookieKey];
+        if ([cookieKey length] && [cookieValue length]) {
+            CFHTTPMessageSetHeaderFieldValue(request, (__bridge CFStringRef)cookieKey, (__bridge CFStringRef)cookieValue);
+        }
+    }
+ 
+    // set header for http basic auth
+    if (_url.user.length && _url.password.length) {
+        NSData *userAndPassword = [[NSString stringWithFormat:@"%@:%@", _url.user, _url.password] dataUsingEncoding:NSUTF8StringEncoding];
+        NSString *userAndPasswordBase64Encoded;
+        if ([keyBytes respondsToSelector:@selector(base64EncodedStringWithOptions:)]) {
+            userAndPasswordBase64Encoded = [userAndPassword base64EncodedStringWithOptions:0];
+        } else {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+            userAndPasswordBase64Encoded = [userAndPassword base64Encoding];
+#pragma clang diagnostic pop
+        }
+        _basicAuthorizationString = [NSString stringWithFormat:@"Basic %@", userAndPasswordBase64Encoded];
+        CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Authorization"), (__bridge CFStringRef)_basicAuthorizationString);
+    }
+
+    CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Upgrade"), CFSTR("websocket"));
+    CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Connection"), CFSTR("Upgrade"));
+    CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Key"), (__bridge CFStringRef)_secKey);
+    CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Version"), (__bridge CFStringRef)[NSString stringWithFormat:@"%ld", (long)_webSocketVersion]);
+    
+    CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Origin"), (__bridge CFStringRef)_url.SR_origin);
+    
+    if (_requestedProtocols) {
+        CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Protocol"), (__bridge CFStringRef)[_requestedProtocols componentsJoinedByString:@", "]);
+    }
+
+    [_urlRequest.allHTTPHeaderFields enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
+        CFHTTPMessageSetHeaderFieldValue(request, (__bridge CFStringRef)key, (__bridge CFStringRef)obj);
+    }];
+    
+    NSData *message = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(request));
+    
+    CFRelease(request);
+
+    [self _writeData:message];
+    [self _readHTTPHeader];
+}
+
+- (void)_initializeStreams;
+{
+    assert(_url.port.unsignedIntValue <= UINT32_MAX);
+    uint32_t port = _url.port.unsignedIntValue;
+    if (port == 0) {
+        if (!_secure) {
+            port = 80;
+        } else {
+            port = 443;
+        }
+    }
+    NSString *host = _url.host;
+    
+    CFReadStreamRef readStream = NULL;
+    CFWriteStreamRef writeStream = NULL;
+    
+    CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &readStream, &writeStream);
+    
+    _outputStream = CFBridgingRelease(writeStream);
+    _inputStream = CFBridgingRelease(readStream);
+    
+    _inputStream.delegate = self;
+    _outputStream.delegate = self;
+}
+
+- (void)_updateSecureStreamOptions;
+{
+    if (_secure) {
+        NSMutableDictionary *SSLOptions = [[NSMutableDictionary alloc] init];
+        
+        [_outputStream setProperty:(__bridge id)kCFStreamSocketSecurityLevelNegotiatedSSL forKey:(__bridge id)kCFStreamPropertySocketSecurityLevel];
+        
+        // If we're using pinned certs, don't validate the certificate chain
+        if ([_urlRequest SR_SSLPinnedCertificates].count) {
+            [SSLOptions setValue:@NO forKey:(__bridge id)kCFStreamSSLValidatesCertificateChain];
+        }
+        
+#if DEBUG
+        self.allowsUntrustedSSLCertificates = YES;
+#endif
+
+        if (self.allowsUntrustedSSLCertificates) {
+            [SSLOptions setValue:@NO forKey:(__bridge id)kCFStreamSSLValidatesCertificateChain];
+            SRFastLog(@"Allowing connection to any root cert");
+        }
+        
+        [_outputStream setProperty:SSLOptions
+                            forKey:(__bridge id)kCFStreamPropertySSLSettings];
+    }
+    
+    _inputStream.delegate = self;
+    _outputStream.delegate = self;
+    
+    [self setupNetworkServiceType:_urlRequest.networkServiceType];
+}
+
+- (void)setupNetworkServiceType:(NSURLRequestNetworkServiceType)requestNetworkServiceType
+{
+    NSString *networkServiceType;
+    switch (requestNetworkServiceType) {
+        case NSURLNetworkServiceTypeDefault:
+            break;
+        case NSURLNetworkServiceTypeVoIP: {
+            networkServiceType = NSStreamNetworkServiceTypeVoIP;
+#if TARGET_OS_IPHONE && __IPHONE_9_0
+            if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_8_3) {
+                static dispatch_once_t predicate;
+                dispatch_once(&predicate, ^{
+                    NSLog(@"SocketRocket: %@ - this service type is deprecated in favor of using PushKit for VoIP control", networkServiceType);
+                });
+            }
+#endif
+            break;
+        }
+        case NSURLNetworkServiceTypeVideo:
+            networkServiceType = NSStreamNetworkServiceTypeVideo;
+            break;
+        case NSURLNetworkServiceTypeBackground:
+            networkServiceType = NSStreamNetworkServiceTypeBackground;
+            break;
+        case NSURLNetworkServiceTypeVoice:
+            networkServiceType = NSStreamNetworkServiceTypeVoice;
+            break;
+    }
+    
+    if (networkServiceType != nil) {
+        [_inputStream setProperty:networkServiceType forKey:NSStreamNetworkServiceType];
+        [_outputStream setProperty:networkServiceType forKey:NSStreamNetworkServiceType];
+    }
+}
+
+- (void)openConnection;
+{
+    [self _updateSecureStreamOptions];
+    
+    if (!_scheduledRunloops.count) {
+        [self scheduleInRunLoop:[NSRunLoop SR_networkRunLoop] forMode:NSDefaultRunLoopMode];
+    }
+    
+    
+    [_outputStream open];
+    [_inputStream open];
+}
+
+- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
+{
+    [_outputStream scheduleInRunLoop:aRunLoop forMode:mode];
+    [_inputStream scheduleInRunLoop:aRunLoop forMode:mode];
+    
+    [_scheduledRunloops addObject:@[aRunLoop, mode]];
+}
+
+- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
+{
+    [_outputStream removeFromRunLoop:aRunLoop forMode:mode];
+    [_inputStream removeFromRunLoop:aRunLoop forMode:mode];
+    
+    [_scheduledRunloops removeObject:@[aRunLoop, mode]];
+}
+
+- (void)close;
+{
+    [self closeWithCode:SRStatusCodeNormal reason:nil];
+}
+
+- (void)closeWithCode:(NSInteger)code reason:(NSString *)reason;
+{
+    assert(code);
+    dispatch_async(_workQueue, ^{
+        if (self.readyState == SR_CLOSING || self.readyState == SR_CLOSED) {
+            return;
+        }
+        
+        BOOL wasConnecting = self.readyState == SR_CONNECTING;
+        
+        self.readyState = SR_CLOSING;
+        
+        SRFastLog(@"Closing with code %d reason %@", code, reason);
+        
+        if (wasConnecting) {
+            [self closeConnection];
+            return;
+        }
+
+        size_t maxMsgSize = [reason maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+        NSMutableData *mutablePayload = [[NSMutableData alloc] initWithLength:sizeof(uint16_t) + maxMsgSize];
+        NSData *payload = mutablePayload;
+        
+        ((uint16_t *)mutablePayload.mutableBytes)[0] = EndianU16_BtoN(code);
+        
+        if (reason) {
+            NSRange remainingRange = {0};
+            
+            NSUInteger usedLength = 0;
+            
+            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];
+            #pragma unused (success)
+            
+            assert(success);
+            assert(remainingRange.length == 0);
+
+            if (usedLength != maxMsgSize) {
+                payload = [payload subdataWithRange:NSMakeRange(0, usedLength + sizeof(uint16_t))];
+            }
+        }
+        
+        
+        [self _sendFrameWithOpcode:SROpCodeConnectionClose data:payload];
+    });
+}
+
+- (void)_closeWithProtocolError:(NSString *)message;
+{
+    // Need to shunt this on the _callbackQueue first to see if they received any messages 
+    [self _performDelegateBlock:^{
+        [self closeWithCode:SRStatusCodeProtocolError reason:message];
+        dispatch_async(_workQueue, ^{
+            [self closeConnection];
+        });
+    }];
+}
+
+- (void)_failWithError:(NSError *)error;
+{
+    dispatch_async(_workQueue, ^{
+        if (self.readyState != SR_CLOSED) {
+            _failed = YES;
+            [self _performDelegateBlock:^{
+                if ([self.delegate respondsToSelector:@selector(webSocket:didFailWithError:)]) {
+                    [self.delegate webSocket:self didFailWithError:error];
+                }
+            }];
+
+            self.readyState = SR_CLOSED;
+
+            SRFastLog(@"Failing with error %@", error.localizedDescription);
+            
+            [self closeConnection];
+            [self _scheduleCleanup];
+        }
+    });
+}
+
+- (void)_writeData:(NSData *)data;
+{    
+    [self assertOnWorkQueue];
+
+    if (_closeWhenFinishedWriting) {
+            return;
+    }
+    [_outputBuffer appendData:data];
+    [self _pumpWriting];
+}
+
+- (void)send:(id)data;
+{
+    NSAssert(self.readyState != SR_CONNECTING, @"Invalid State: Cannot call send: until connection is open");
+    // TODO: maybe not copy this for performance
+    data = [data copy];
+    dispatch_async(_workQueue, ^{
+        if ([data isKindOfClass:[NSString class]]) {
+            [self _sendFrameWithOpcode:SROpCodeTextFrame data:[(NSString *)data dataUsingEncoding:NSUTF8StringEncoding]];
+        } else if ([data isKindOfClass:[NSData class]]) {
+            [self _sendFrameWithOpcode:SROpCodeBinaryFrame data:data];
+        } else if (data == nil) {
+            [self _sendFrameWithOpcode:SROpCodeTextFrame data:data];
+        } else {
+            assert(NO);
+        }
+    });
+}
+
+- (void)sendPing:(NSData *)data;
+{
+    NSAssert(self.readyState == SR_OPEN, @"Invalid State: Cannot call send: until connection is open");
+    // TODO: maybe not copy this for performance
+    data = [data copy] ?: [NSData data]; // It's okay for a ping to be empty
+    dispatch_async(_workQueue, ^{
+        [self _sendFrameWithOpcode:SROpCodePing data:data];
+    });
+}
+
+- (void)handlePing:(NSData *)pingData;
+{
+    // Need to pingpong this off _callbackQueue first to make sure messages happen in order
+    [self _performDelegateBlock:^{
+        dispatch_async(_workQueue, ^{
+            [self _sendFrameWithOpcode:SROpCodePong data:pingData];
+        });
+    }];
+}
+
+- (void)handlePong:(NSData *)pongData;
+{
+    SRFastLog(@"Received pong");
+    [self _performDelegateBlock:^{
+        if ([self.delegate respondsToSelector:@selector(webSocket:didReceivePong:)]) {
+            [self.delegate webSocket:self didReceivePong:pongData];
+        }
+    }];
+}
+
+- (void)_handleMessage:(id)message
+{
+    SRFastLog(@"Received message");
+    [self _performDelegateBlock:^{
+        [self.delegate webSocket:self didReceiveMessage:message];
+    }];
+}
+
+
+static inline BOOL closeCodeIsValid(int closeCode) {
+    if (closeCode < 1000) {
+        return NO;
+    }
+    
+    if (closeCode >= 1000 && closeCode <= 1011) {
+        if (closeCode == 1004 ||
+            closeCode == 1005 ||
+            closeCode == 1006) {
+            return NO;
+        }
+        return YES;
+    }
+    
+    if (closeCode >= 3000 && closeCode <= 3999) {
+        return YES;
+    }
+    
+    if (closeCode >= 4000 && closeCode <= 4999) {
+        return YES;
+    }
+
+    return NO;
+}
+
+//  Note from RFC:
+//
+//  If there is a body, the first two
+//  bytes of the body MUST be a 2-byte unsigned integer (in network byte
+//  order) representing a status code with value /code/ defined in
+//  Section 7.4.  Following the 2-byte integer the body MAY contain UTF-8
+//  encoded data with value /reason/, the interpretation of which is not
+//  defined by this specification.
+
+- (void)handleCloseWithData:(NSData *)data;
+{
+    size_t dataSize = data.length;
+    __block uint16_t closeCode = 0;
+    
+    SRFastLog(@"Received close frame");
+    
+    if (dataSize == 1) {
+        // TODO handle error
+        [self _closeWithProtocolError:@"Payload for close must be larger than 2 bytes"];
+        return;
+    } else if (dataSize >= 2) {
+        [data getBytes:&closeCode length:sizeof(closeCode)];
+        _closeCode = EndianU16_BtoN(closeCode);
+        if (!closeCodeIsValid(_closeCode)) {
+            [self _closeWithProtocolError:[NSString stringWithFormat:@"Cannot have close code of %d", _closeCode]];
+            return;
+        }
+        if (dataSize > 2) {
+            _closeReason = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(2, dataSize - 2)] encoding:NSUTF8StringEncoding];
+            if (!_closeReason) {
+                [self _closeWithProtocolError:@"Close reason MUST be valid UTF-8"];
+                return;
+            }
+        }
+    } else {
+        _closeCode = SRStatusNoStatusReceived;
+    }
+    
+    [self assertOnWorkQueue];
+    
+    if (self.readyState == SR_OPEN) {
+        [self closeWithCode:1000 reason:nil];
+    }
+    dispatch_async(_workQueue, ^{
+        [self closeConnection];
+    });
+}
+
+- (void)closeConnection;
+{
+    [self assertOnWorkQueue];
+    SRFastLog(@"Trying to disconnect");
+    _closeWhenFinishedWriting = YES;
+    [self _pumpWriting];
+}
+
+- (void)_handleFrameWithData:(NSData *)frameData opCode:(NSInteger)opcode;
+{                
+    // Check that the current data is valid UTF8
+    
+    BOOL isControlFrame = (opcode == SROpCodePing || opcode == SROpCodePong || opcode == SROpCodeConnectionClose);
+    if (!isControlFrame) {
+        [self _readFrameNew];
+    } else {
+        dispatch_async(_workQueue, ^{
+            [self _readFrameContinue];
+        });
+    }
+    
+    //frameData will be copied before passing to handlers
+    //otherwise there can be misbehaviours when value at the pointer is changed
+    switch (opcode) {
+        case SROpCodeTextFrame: {
+            if ([self.delegate respondsToSelector:@selector(webSocketShouldConvertTextFrameToString:)] && ![self.delegate webSocketShouldConvertTextFrameToString:self]) {
+                [self _handleMessage:[frameData copy]];
+            } else {
+                NSString *str = [[NSString alloc] initWithData:frameData encoding:NSUTF8StringEncoding];
+                if (str == nil && frameData) {
+                    [self closeWithCode:SRStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"];
+                    dispatch_async(_workQueue, ^{
+                        [self closeConnection];
+                    });
+                    return;
+                }
+                [self _handleMessage:str];
+            }
+            break;
+        }
+        case SROpCodeBinaryFrame:
+            [self _handleMessage:[frameData copy]];
+            break;
+        case SROpCodeConnectionClose:
+            [self handleCloseWithData:[frameData copy]];
+            break;
+        case SROpCodePing:
+            [self handlePing:[frameData copy]];
+            break;
+        case SROpCodePong:
+            [self handlePong:[frameData copy]];
+            break;
+        default:
+            [self _closeWithProtocolError:[NSString stringWithFormat:@"Unknown opcode %ld", (long)opcode]];
+            // TODO: Handle invalid opcode
+            break;
+    }
+}
+
+- (void)_handleFrameHeader:(frame_header)frame_header curData:(NSData *)curData;
+{
+    assert(frame_header.opcode != 0);
+    
+    if (self.readyState == SR_CLOSED) {
+        return;
+    }
+    
+    
+    BOOL isControlFrame = (frame_header.opcode == SROpCodePing || frame_header.opcode == SROpCodePong || frame_header.opcode == SROpCodeConnectionClose);
+    
+    if (isControlFrame && !frame_header.fin) {
+        [self _closeWithProtocolError:@"Fragmented control frames not allowed"];
+        return;
+    }
+    
+    if (isControlFrame && frame_header.payload_length >= 126) {
+        [self _closeWithProtocolError:@"Control frames cannot have payloads larger than 126 bytes"];
+        return;
+    }
+    
+    if (!isControlFrame) {
+        _currentFrameOpcode = frame_header.opcode;
+        _currentFrameCount += 1;
+    }
+    
+    if (frame_header.payload_length == 0) {
+        if (isControlFrame) {
+            [self _handleFrameWithData:curData opCode:frame_header.opcode];
+        } else {
+            if (frame_header.fin) {
+                [self _handleFrameWithData:_currentFrameData opCode:frame_header.opcode];
+            } else {
+                // TODO add assert that opcode is not a control;
+                [self _readFrameContinue];
+            }
+        }
+    } else {
+        assert(frame_header.payload_length <= SIZE_T_MAX);
+        [self _addConsumerWithDataLength:(size_t)frame_header.payload_length callback:^(SRWebSocket *self, NSData *newData) {
+            if (isControlFrame) {
+                [self _handleFrameWithData:newData opCode:frame_header.opcode];
+            } else {
+                if (frame_header.fin) {
+                    [self _handleFrameWithData:self->_currentFrameData opCode:frame_header.opcode];
+                } else {
+                    // TODO add assert that opcode is not a control;
+                    [self _readFrameContinue];
+                }
+                
+            }
+        } readToCurrentFrame:!isControlFrame unmaskBytes:frame_header.masked];
+    }
+}
+
+/* From RFC:
+
+ 0                   1                   2                   3
+ 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
+ +-+-+-+-+-------+-+-------------+-------------------------------+
+ |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
+ |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
+ |N|V|V|V|       |S|             |   (if payload len==126/127)   |
+ | |1|2|3|       |K|             |                               |
+ +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
+ |     Extended payload length continued, if payload len == 127  |
+ + - - - - - - - - - - - - - - - +-------------------------------+
+ |                               |Masking-key, if MASK set to 1  |
+ +-------------------------------+-------------------------------+
+ | Masking-key (continued)       |          Payload Data         |
+ +-------------------------------- - - - - - - - - - - - - - - - +
+ :                     Payload Data continued ...                :
+ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+ |                     Payload Data continued ...                |
+ +---------------------------------------------------------------+
+ */
+
+static const uint8_t SRFinMask          = 0x80;
+static const uint8_t SROpCodeMask       = 0x0F;
+static const uint8_t SRRsvMask          = 0x70;
+static const uint8_t SRMaskMask         = 0x80;
+static const uint8_t SRPayloadLenMask   = 0x7F;
+
+
+- (void)_readFrameContinue;
+{
+    assert((_currentFrameCount == 0 && _currentFrameOpcode == 0) || (_currentFrameCount > 0 && _currentFrameOpcode > 0));
+
+    [self _addConsumerWithDataLength:2 callback:^(SRWebSocket *self, NSData *data) {
+        __block frame_header header = {0};
+        
+        const uint8_t *headerBuffer = data.bytes;
+        assert(data.length >= 2);
+        
+        if (headerBuffer[0] & SRRsvMask) {
+            [self _closeWithProtocolError:@"Server used RSV bits"];
+            return;
+        }
+        
+        uint8_t receivedOpcode = (SROpCodeMask & headerBuffer[0]);
+        
+        BOOL isControlFrame = (receivedOpcode == SROpCodePing || receivedOpcode == SROpCodePong || receivedOpcode == SROpCodeConnectionClose);
+        
+        if (!isControlFrame && receivedOpcode != 0 && self->_currentFrameCount > 0) {
+            [self _closeWithProtocolError:@"all data frames after the initial data frame must have opcode 0"];
+            return;
+        }
+        
+        if (receivedOpcode == 0 && self->_currentFrameCount == 0) {
+            [self _closeWithProtocolError:@"cannot continue a message"];
+            return;
+        }
+        
+        header.opcode = receivedOpcode == 0 ? self->_currentFrameOpcode : receivedOpcode;
+        
+        header.fin = !!(SRFinMask & headerBuffer[0]);
+        
+        
+        header.masked = !!(SRMaskMask & headerBuffer[1]);
+        header.payload_length = SRPayloadLenMask & headerBuffer[1];
+        
+        headerBuffer = NULL;
+        
+        if (header.masked) {
+            [self _closeWithProtocolError:@"Client must receive unmasked data"];
+        }
+        
+        size_t extra_bytes_needed = header.masked ? sizeof(_currentReadMaskKey) : 0;
+        
+        if (header.payload_length == 126) {
+            extra_bytes_needed += sizeof(uint16_t);
+        } else if (header.payload_length == 127) {
+            extra_bytes_needed += sizeof(uint64_t);
+        }
+        
+        if (extra_bytes_needed == 0) {
+            [self _handleFrameHeader:header curData:self->_currentFrameData];
+        } else {
+            [self _addConsumerWithDataLength:extra_bytes_needed callback:^(SRWebSocket *self, NSData *data) {
+                size_t mapped_size = data.length;
+                #pragma unused (mapped_size)
+                const void *mapped_buffer = data.bytes;
+                size_t offset = 0;
+                
+                if (header.payload_length == 126) {
+                    assert(mapped_size >= sizeof(uint16_t));
+                    uint16_t newLen = EndianU16_BtoN(*(uint16_t *)(mapped_buffer));
+                    header.payload_length = newLen;
+                    offset += sizeof(uint16_t);
+                } else if (header.payload_length == 127) {
+                    assert(mapped_size >= sizeof(uint64_t));
+                    header.payload_length = EndianU64_BtoN(*(uint64_t *)(mapped_buffer));
+                    offset += sizeof(uint64_t);
+                } else {
+                    assert(header.payload_length < 126 && header.payload_length >= 0);
+                }
+                
+                if (header.masked) {
+                    assert(mapped_size >= sizeof(_currentReadMaskOffset) + offset);
+                    memcpy(self->_currentReadMaskKey, ((uint8_t *)mapped_buffer) + offset, sizeof(self->_currentReadMaskKey));
+                }
+                
+                [self _handleFrameHeader:header curData:self->_currentFrameData];
+            } readToCurrentFrame:NO unmaskBytes:NO];
+        }
+    } readToCurrentFrame:NO unmaskBytes:NO];
+}
+
+- (void)_readFrameNew;
+{
+    dispatch_async(_workQueue, ^{
+        [_currentFrameData setLength:0];
+        
+        _currentFrameOpcode = 0;
+        _currentFrameCount = 0;
+        _readOpCount = 0;
+        _currentStringScanPosition = 0;
+        
+        [self _readFrameContinue];
+    });
+}
+
+- (void)_pumpWriting;
+{
+    [self assertOnWorkQueue];
+    
+    NSUInteger dataLength = _outputBuffer.length;
+    if (dataLength - _outputBufferOffset > 0 && _outputStream.hasSpaceAvailable) {
+        NSInteger bytesWritten = [_outputStream write:_outputBuffer.bytes + _outputBufferOffset maxLength:dataLength - _outputBufferOffset];
+        if (bytesWritten == -1) {
+            [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2145 userInfo:[NSDictionary dictionaryWithObject:@"Error writing to stream" forKey:NSLocalizedDescriptionKey]]];
+             return;
+        }
+        
+        _outputBufferOffset += bytesWritten;
+        
+        if (_outputBufferOffset > 4096 && _outputBufferOffset > (_outputBuffer.length >> 1)) {
+            _outputBuffer = [[NSMutableData alloc] initWithBytes:(char *)_outputBuffer.bytes + _outputBufferOffset length:_outputBuffer.length - _outputBufferOffset];
+            _outputBufferOffset = 0;
+        }
+    }
+    
+    if (_closeWhenFinishedWriting && 
+        _outputBuffer.length - _outputBufferOffset == 0 && 
+        (_inputStream.streamStatus != NSStreamStatusNotOpen &&
+         _inputStream.streamStatus != NSStreamStatusClosed) &&
+        !_sentClose) {
+        _sentClose = YES;
+        
+        @synchronized(self) {
+            [_outputStream close];
+            [_inputStream close];
+            
+            
+            for (NSArray *runLoop in [_scheduledRunloops copy]) {
+                [self unscheduleFromRunLoop:[runLoop objectAtIndex:0] forMode:[runLoop objectAtIndex:1]];
+            }
+        }
+        
+        if (!_failed) {
+            [self _performDelegateBlock:^{
+                if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) {
+                    [self.delegate webSocket:self didCloseWithCode:_closeCode reason:_closeReason wasClean:YES];
+                }
+            }];
+        }
+        
+        [self _scheduleCleanup];
+    }
+}
+
+- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback;
+{
+    [self assertOnWorkQueue];
+    [self _addConsumerWithScanner:consumer callback:callback dataLength:0];
+}
+
+- (void)_addConsumerWithDataLength:(size_t)dataLength callback:(data_callback)callback readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes;
+{   
+    [self assertOnWorkQueue];
+    assert(dataLength);
+    
+    [_consumers addObject:[_consumerPool consumerWithScanner:nil handler:callback bytesNeeded:dataLength readToCurrentFrame:readToCurrentFrame unmaskBytes:unmaskBytes]];
+    [self _pumpScanner];
+}
+
+- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback dataLength:(size_t)dataLength;
+{    
+    [self assertOnWorkQueue];
+    [_consumers addObject:[_consumerPool consumerWithScanner:consumer handler:callback bytesNeeded:dataLength readToCurrentFrame:NO unmaskBytes:NO]];
+    [self _pumpScanner];
+}
+
+
+- (void)_scheduleCleanup
+{
+    @synchronized(self) {
+        if (_cleanupScheduled) {
+            return;
+        }
+        
+        _cleanupScheduled = YES;
+        
+        // Cleanup NSStream delegate's in the same RunLoop used by the streams themselves:
+        // This way we'll prevent race conditions between handleEvent and SRWebsocket's dealloc
+        NSTimer *timer = [NSTimer timerWithTimeInterval:(0.0f) target:self selector:@selector(_cleanupSelfReference:) userInfo:nil repeats:NO];
+        [[NSRunLoop SR_networkRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
+    }
+}
+
+- (void)_cleanupSelfReference:(NSTimer *)timer
+{
+    @synchronized(self) {
+        // Nuke NSStream delegate's
+        _inputStream.delegate = nil;
+        _outputStream.delegate = nil;
+        
+        // Remove the streams, right now, from the networkRunLoop
+        [_inputStream close];
+        [_outputStream close];
+    }
+    
+    // Cleanup selfRetain in the same GCD queue as usual
+    dispatch_async(_workQueue, ^{
+        _selfRetain = nil;
+    });
+}
+
+
+static const char CRLFCRLFBytes[] = {'\r', '\n', '\r', '\n'};
+
+- (void)_readUntilHeaderCompleteWithCallback:(data_callback)dataHandler;
+{
+    [self _readUntilBytes:CRLFCRLFBytes length:sizeof(CRLFCRLFBytes) callback:dataHandler];
+}
+
+- (void)_readUntilBytes:(const void *)bytes length:(size_t)length callback:(data_callback)dataHandler;
+{
+    // TODO optimize so this can continue from where we last searched
+    stream_scanner consumer = ^size_t(NSData *data) {
+        __block size_t found_size = 0;
+        __block size_t match_count = 0;
+        
+        size_t size = data.length;
+        const unsigned char *buffer = data.bytes;
+        for (size_t i = 0; i < size; i++ ) {
+            if (((const unsigned char *)buffer)[i] == ((const unsigned char *)bytes)[match_count]) {
+                match_count += 1;
+                if (match_count == length) {
+                    found_size = i + 1;
+                    break;
+                }
+            } else {
+                match_count = 0;
+            }
+        }
+        return found_size;
+    };
+    [self _addConsumerWithScanner:consumer callback:dataHandler];
+}
+
+
+// Returns true if did work
+- (BOOL)_innerPumpScanner {
+    
+    BOOL didWork = NO;
+    
+    if (self.readyState >= SR_CLOSED) {
+        return didWork;
+    }
+    
+    if (!_consumers.count) {
+        return didWork;
+    }
+    
+    size_t curSize = _readBuffer.length - _readBufferOffset;
+    if (!curSize) {
+        return didWork;
+    }
+    
+    SRIOConsumer *consumer = [_consumers objectAtIndex:0];
+    
+    size_t bytesNeeded = consumer.bytesNeeded;
+    
+    size_t foundSize = 0;
+    if (consumer.consumer) {
+        NSData *tempView = [NSData dataWithBytesNoCopy:(char *)_readBuffer.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset freeWhenDone:NO];  
+        foundSize = consumer.consumer(tempView);
+    } else {
+        assert(consumer.bytesNeeded);
+        if (curSize >= bytesNeeded) {
+            foundSize = bytesNeeded;
+        } else if (consumer.readToCurrentFrame) {
+            foundSize = curSize;
+        }
+    }
+    
+    NSData *slice = nil;
+    if (consumer.readToCurrentFrame || foundSize) {
+        NSRange sliceRange = NSMakeRange(_readBufferOffset, foundSize);
+        slice = [_readBuffer subdataWithRange:sliceRange];
+        
+        _readBufferOffset += foundSize;
+        
+        if (_readBufferOffset > 4096 && _readBufferOffset > (_readBuffer.length >> 1)) {
+            _readBuffer = [[NSMutableData alloc] initWithBytes:(char *)_readBuffer.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset];            _readBufferOffset = 0;
+        }
+        
+        if (consumer.unmaskBytes) {
+            NSMutableData *mutableSlice = [slice mutableCopy];
+            
+            NSUInteger len = mutableSlice.length;
+            uint8_t *bytes = mutableSlice.mutableBytes;
+            
+            for (NSUInteger i = 0; i < len; i++) {
+                bytes[i] = bytes[i] ^ _currentReadMaskKey[_currentReadMaskOffset % sizeof(_currentReadMaskKey)];
+                _currentReadMaskOffset += 1;
+            }
+            
+            slice = mutableSlice;
+        }
+        
+        if (consumer.readToCurrentFrame) {
+            [_currentFrameData appendData:slice];
+            
+            _readOpCount += 1;
+            
+            if (_currentFrameOpcode == SROpCodeTextFrame) {
+                // Validate UTF8 stuff.
+                size_t currentDataSize = _currentFrameData.length;
+                if (_currentFrameOpcode == SROpCodeTextFrame && currentDataSize > 0) {
+                    // TODO: Optimize the crap out of this.  Don't really have to copy all the data each time
+                    
+                    size_t scanSize = currentDataSize - _currentStringScanPosition;
+                    
+                    NSData *scan_data = [_currentFrameData subdataWithRange:NSMakeRange(_currentStringScanPosition, scanSize)];
+                    int32_t valid_utf8_size = validate_dispatch_data_partial_string(scan_data);
+                    
+                    if (valid_utf8_size == -1) {
+                        [self closeWithCode:SRStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"];
+                        dispatch_async(_workQueue, ^{
+                            [self closeConnection];
+                        });
+                        return didWork;
+                    } else {
+                        _currentStringScanPosition += valid_utf8_size;
+                    }
+                } 
+                
+            }
+            
+            consumer.bytesNeeded -= foundSize;
+            
+            if (consumer.bytesNeeded == 0) {
+                [_consumers removeObjectAtIndex:0];
+                consumer.handler(self, nil);
+                [_consumerPool returnConsumer:consumer];
+                didWork = YES;
+            }
+        } else if (foundSize) {
+            [_consumers removeObjectAtIndex:0];
+            consumer.handler(self, slice);
+            [_consumerPool returnConsumer:consumer];
+            didWork = YES;
+        }
+    }
+    return didWork;
+}
+
+-(void)_pumpScanner;
+{
+    [self assertOnWorkQueue];
+    
+    if (!_isPumping) {
+        _isPumping = YES;
+    } else {
+        return;
+    }
+    
+    while ([self _innerPumpScanner]) {
+        
+    }
+    
+    _isPumping = NO;
+}
+
+//#define NOMASK
+
+static const size_t SRFrameHeaderOverhead = 32;
+
+- (void)_sendFrameWithOpcode:(SROpCode)opcode data:(id)data;
+{
+    [self assertOnWorkQueue];
+    
+    if (nil == data) {
+        return;
+    }
+    
+    NSAssert([data isKindOfClass:[NSData class]] || [data isKindOfClass:[NSString class]], @"NSString or NSData");
+    
+    size_t payloadLength = [data isKindOfClass:[NSString class]] ? [(NSString *)data lengthOfBytesUsingEncoding:NSUTF8StringEncoding] : [data length];
+        
+    NSMutableData *frame = [[NSMutableData alloc] initWithLength:payloadLength + SRFrameHeaderOverhead];
+    if (!frame) {
+        [self closeWithCode:SRStatusCodeMessageTooBig reason:@"Message too big"];
+        return;
+    }
+    uint8_t *frame_buffer = (uint8_t *)[frame mutableBytes];
+    
+    // set fin
+    frame_buffer[0] = SRFinMask | opcode;
+    
+    BOOL useMask = YES;
+#ifdef NOMASK
+    useMask = NO;
+#endif
+    
+    if (useMask) {
+    // set the mask and header
+        frame_buffer[1] |= SRMaskMask;
+    }
+    
+    size_t frame_buffer_size = 2;
+    
+    const uint8_t *unmasked_payload = NULL;
+    if ([data isKindOfClass:[NSData class]]) {
+        unmasked_payload = (uint8_t *)[data bytes];
+    } else if ([data isKindOfClass:[NSString class]]) {
+        unmasked_payload =  (const uint8_t *)[data UTF8String];
+    } else {
+        return;
+    }
+    
+    if (payloadLength < 126) {
+        frame_buffer[1] |= payloadLength;
+    } else if (payloadLength <= UINT16_MAX) {
+        frame_buffer[1] |= 126;
+        *((uint16_t *)(frame_buffer + frame_buffer_size)) = EndianU16_BtoN((uint16_t)payloadLength);
+        frame_buffer_size += sizeof(uint16_t);
+    } else {
+        frame_buffer[1] |= 127;
+        *((uint64_t *)(frame_buffer + frame_buffer_size)) = EndianU64_BtoN((uint64_t)payloadLength);
+        frame_buffer_size += sizeof(uint64_t);
+    }
+        
+    if (!useMask) {
+        for (size_t i = 0; i < payloadLength; i++) {
+            frame_buffer[frame_buffer_size] = unmasked_payload[i];
+            frame_buffer_size += 1;
+        }
+    } else {
+        uint8_t *mask_key = frame_buffer + frame_buffer_size;
+        SecRandomCopyBytes(kSecRandomDefault, sizeof(uint32_t), (uint8_t *)mask_key);
+        frame_buffer_size += sizeof(uint32_t);
+        
+        // TODO: could probably optimize this with SIMD
+        for (size_t i = 0; i < payloadLength; i++) {
+            frame_buffer[frame_buffer_size] = unmasked_payload[i] ^ mask_key[i % sizeof(uint32_t)];
+            frame_buffer_size += 1;
+        }
+    }
+
+    assert(frame_buffer_size <= [frame length]);
+    frame.length = frame_buffer_size;
+    
+    [self _writeData:frame];
+}
+
+- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode;
+{
+    __weak typeof(self) weakSelf = self;
+    
+    if (_secure && !_pinnedCertFound && (eventCode == NSStreamEventHasBytesAvailable || eventCode == NSStreamEventHasSpaceAvailable)) {
+        
+        NSArray *sslCerts = [_urlRequest SR_SSLPinnedCertificates];
+        if (sslCerts) {
+            SecTrustRef secTrust = (__bridge SecTrustRef)[aStream propertyForKey:(__bridge id)kCFStreamPropertySSLPeerTrust];
+            if (secTrust) {
+                NSInteger numCerts = SecTrustGetCertificateCount(secTrust);
+                for (NSInteger i = 0; i < numCerts && !_pinnedCertFound; i++) {
+                    SecCertificateRef cert = SecTrustGetCertificateAtIndex(secTrust, i);
+                    NSData *certData = CFBridgingRelease(SecCertificateCopyData(cert));
+                    
+                    for (id ref in sslCerts) {
+                        SecCertificateRef trustedCert = (__bridge SecCertificateRef)ref;
+                        NSData *trustedCertData = CFBridgingRelease(SecCertificateCopyData(trustedCert));
+                        
+                        if ([trustedCertData isEqualToData:certData]) {
+                            _pinnedCertFound = YES;
+                            break;
+                        }
+                    }
+                }
+            }
+            
+            if (!_pinnedCertFound) {
+                dispatch_async(_workQueue, ^{
+                    NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : @"Invalid server cert" };
+                    [weakSelf _failWithError:[NSError errorWithDomain:@"org.lolrus.SocketRocket" code:23556 userInfo:userInfo]];
+                });
+                return;
+            } else if (aStream == _outputStream) {
+                dispatch_async(_workQueue, ^{
+                    [self didConnect];
+                });
+            }
+        }
+    }
+
+    dispatch_async(_workQueue, ^{
+        [weakSelf safeHandleEvent:eventCode stream:aStream];
+    });
+}
+
+- (void)safeHandleEvent:(NSStreamEvent)eventCode stream:(NSStream *)aStream
+{
+        switch (eventCode) {
+            case NSStreamEventOpenCompleted: {
+                SRFastLog(@"NSStreamEventOpenCompleted %@", aStream);
+                if (self.readyState >= SR_CLOSING) {
+                    return;
+                }
+                assert(_readBuffer);
+                
+                // didConnect fires after certificate verification if we're using pinned certificates.
+                BOOL usingPinnedCerts = [[_urlRequest SR_SSLPinnedCertificates] count] > 0;
+                if ((!_secure || !usingPinnedCerts) && self.readyState == SR_CONNECTING && aStream == _inputStream) {
+                    [self didConnect];
+                }
+                [self _pumpWriting];
+                [self _pumpScanner];
+                break;
+            }
+                
+            case NSStreamEventErrorOccurred: {
+                SRFastLog(@"NSStreamEventErrorOccurred %@ %@", aStream, [[aStream streamError] copy]);
+                /// TODO specify error better!
+                [self _failWithError:aStream.streamError];
+                _readBufferOffset = 0;
+                [_readBuffer setLength:0];
+                break;
+                
+            }
+                
+            case NSStreamEventEndEncountered: {
+                [self _pumpScanner];
+                SRFastLog(@"NSStreamEventEndEncountered %@", aStream);
+                if (aStream.streamError) {
+                    [self _failWithError:aStream.streamError];
+                } else {
+                    dispatch_async(_workQueue, ^{
+                        if (self.readyState != SR_CLOSED) {
+                            self.readyState = SR_CLOSED;
+                            [self _scheduleCleanup];
+                        }
+                        
+                        if (!_sentClose && !_failed) {
+                            _sentClose = YES;
+                            // If we get closed in this state it's probably not clean because we should be sending this when we send messages
+                            [self _performDelegateBlock:^{
+                                if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) {
+                                    [self.delegate webSocket:self didCloseWithCode:SRStatusCodeGoingAway reason:@"Stream end encountered" wasClean:NO];
+                                }
+                            }];
+                        }
+                    });
+                }
+                
+                break;
+            }
+                
+            case NSStreamEventHasBytesAvailable: {
+                SRFastLog(@"NSStreamEventHasBytesAvailable %@", aStream);
+                const int bufferSize = 2048;
+                uint8_t buffer[bufferSize];
+                
+                while (_inputStream.hasBytesAvailable) {
+                    NSInteger bytes_read = [_inputStream read:buffer maxLength:bufferSize];
+                    
+                    if (bytes_read > 0) {
+                        [_readBuffer appendBytes:buffer length:bytes_read];
+                    } else if (bytes_read < 0) {
+                        [self _failWithError:_inputStream.streamError];
+                    }
+                    
+                    if (bytes_read != bufferSize) {
+                        break;
+                    }
+                };
+                [self _pumpScanner];
+                break;
+            }
+                
+            case NSStreamEventHasSpaceAvailable: {
+                SRFastLog(@"NSStreamEventHasSpaceAvailable %@", aStream);
+                [self _pumpWriting];
+                break;
+            }
+                
+            default:
+                SRFastLog(@"(default)  %@", aStream);
+                break;
+        }
+}
+
+@end
+
+
+@implementation SRIOConsumer
+
+@synthesize bytesNeeded = _bytesNeeded;
+@synthesize consumer = _scanner;
+@synthesize handler = _handler;
+@synthesize readToCurrentFrame = _readToCurrentFrame;
+@synthesize unmaskBytes = _unmaskBytes;
+
+- (void)setupWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes;
+{
+    _scanner = [scanner copy];
+    _handler = [handler copy];
+    _bytesNeeded = bytesNeeded;
+    _readToCurrentFrame = readToCurrentFrame;
+    _unmaskBytes = unmaskBytes;
+    assert(_scanner || _bytesNeeded);
+}
+
+
+@end
+
+
+@implementation SRIOConsumerPool {
+    NSUInteger _poolSize;
+    NSMutableArray *_bufferedConsumers;
+}
+
+- (id)initWithBufferCapacity:(NSUInteger)poolSize;
+{
+    self = [super init];
+    if (self) {
+        _poolSize = poolSize;
+        _bufferedConsumers = [[NSMutableArray alloc] initWithCapacity:poolSize];
+    }
+    return self;
+}
+
+- (id)init
+{
+    return [self initWithBufferCapacity:8];
+}
+
+- (SRIOConsumer *)consumerWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes;
+{
+    SRIOConsumer *consumer = nil;
+    if (_bufferedConsumers.count) {
+        consumer = [_bufferedConsumers lastObject];
+        [_bufferedConsumers removeLastObject];
+    } else {
+        consumer = [[SRIOConsumer alloc] init];
+    }
+    
+    [consumer setupWithScanner:scanner handler:handler bytesNeeded:bytesNeeded readToCurrentFrame:readToCurrentFrame unmaskBytes:unmaskBytes];
+    
+    return consumer;
+}
+
+- (void)returnConsumer:(SRIOConsumer *)consumer;
+{
+    if (_bufferedConsumers.count < _poolSize) {
+        [_bufferedConsumers addObject:consumer];
+    }
+}
+
+@end
+
+
+@implementation  NSURLRequest (SRCertificateAdditions)
+
+- (NSArray *)SR_SSLPinnedCertificates;
+{
+    return [NSURLProtocol propertyForKey:@"SR_SSLPinnedCertificates" inRequest:self];
+}
+
+@end
+
+@implementation  NSMutableURLRequest (SRCertificateAdditions)
+
+- (NSArray *)SR_SSLPinnedCertificates;
+{
+    return [NSURLProtocol propertyForKey:@"SR_SSLPinnedCertificates" inRequest:self];
+}
+
+- (void)setSR_SSLPinnedCertificates:(NSArray *)SR_SSLPinnedCertificates;
+{
+    [NSURLProtocol setProperty:SR_SSLPinnedCertificates forKey:@"SR_SSLPinnedCertificates" inRequest:self];
+}
+
+@end
+
+@implementation NSURL (SRWebSocket)
+
+- (NSString *)SR_origin;
+{
+    NSString *scheme = [self.scheme lowercaseString];
+        
+    if ([scheme isEqualToString:@"wss"]) {
+        scheme = @"https";
+    } else if ([scheme isEqualToString:@"ws"]) {
+        scheme = @"http";
+    }
+    
+    BOOL portIsDefault = !self.port ||
+                         ([scheme isEqualToString:@"http"] && self.port.integerValue == 80) ||
+                         ([scheme isEqualToString:@"https"] && self.port.integerValue == 443);
+    
+    if (!portIsDefault) {
+        return [NSString stringWithFormat:@"%@://%@:%@", scheme, self.host, self.port];
+    } else {
+        return [NSString stringWithFormat:@"%@://%@", scheme, self.host];
+    }
+}
+
+@end
+
+//#define SR_ENABLE_LOG
+
+static inline void SRFastLog(NSString *format, ...)  {
+#ifdef SR_ENABLE_LOG
+    __block va_list arg_list;
+    va_start (arg_list, format);
+    
+    NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list];
+    
+    va_end(arg_list);
+    
+    NSLog(@"[SR] %@", formattedString);
+#endif
+}
+
+
+#ifdef HAS_ICU
+
+static inline int32_t validate_dispatch_data_partial_string(NSData *data) {
+    if ([data length] > INT32_MAX) {
+        // INT32_MAX is the limit so long as this Framework is using 32 bit ints everywhere.
+        return -1;
+    }
+
+    int32_t size = (int32_t)[data length];
+
+    const void * contents = [data bytes];
+    const uint8_t *str = (const uint8_t *)contents;
+    
+    UChar32 codepoint = 1;
+    int32_t offset = 0;
+    int32_t lastOffset = 0;
+    while(offset < size && codepoint > 0)  {
+        lastOffset = offset;
+        U8_NEXT(str, offset, size, codepoint);
+    }
+    
+    if (codepoint == -1) {
+        // Check to see if the last byte is valid or whether it was just continuing
+        if (!U8_IS_LEAD(str[lastOffset]) || U8_COUNT_TRAIL_BYTES(str[lastOffset]) + lastOffset < (int32_t)size) {
+            
+            size = -1;
+        } else {
+            uint8_t leadByte = str[lastOffset];
+            U8_MASK_LEAD_BYTE(leadByte, U8_COUNT_TRAIL_BYTES(leadByte));
+            
+            for (int i = lastOffset + 1; i < offset; i++) {
+                if (U8_IS_SINGLE(str[i]) || U8_IS_LEAD(str[i]) || !U8_IS_TRAIL(str[i])) {
+                    size = -1;
+                }
+            }
+            
+            if (size != -1) {
+                size = lastOffset;
+            }
+        }
+    }
+    
+    if (size != -1 && ![[NSString alloc] initWithBytesNoCopy:(char *)[data bytes] length:size encoding:NSUTF8StringEncoding freeWhenDone:NO]) {
+        size = -1;
+    }
+    
+    return size;
+}
+
+#else
+
+// This is a hack, and probably not optimal
+static inline int32_t validate_dispatch_data_partial_string(NSData *data) {
+    static const int maxCodepointSize = 3;
+    
+    for (int i = 0; i < maxCodepointSize; i++) {
+        NSString *str = [[NSString alloc] initWithBytesNoCopy:(char *)data.bytes length:data.length - i encoding:NSUTF8StringEncoding freeWhenDone:NO];
+        if (str) {
+            return (int32_t)data.length - i;
+        }
+    }
+    
+    return -1;
+}
+
+#endif
+
+static _SRRunLoopThread *networkThread = nil;
+static NSRunLoop *networkRunLoop = nil;
+
+@implementation NSRunLoop (SRWebSocket)
+
++ (NSRunLoop *)SR_networkRunLoop {
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        networkThread = [[_SRRunLoopThread alloc] init];
+        networkThread.name = @"com.squareup.SocketRocket.NetworkThread";
+        [networkThread start];
+        networkRunLoop = networkThread.runLoop;
+    });
+    
+    return networkRunLoop;
+}
+
+@end
+
+
+@implementation _SRRunLoopThread {
+    dispatch_group_t _waitGroup;
+}
+
+@synthesize runLoop = _runLoop;
+
+- (void)dealloc
+{
+    sr_dispatch_release(_waitGroup);
+}
+
+- (id)init
+{
+    self = [super init];
+    if (self) {
+        _waitGroup = dispatch_group_create();
+        dispatch_group_enter(_waitGroup);
+    }
+    return self;
+}
+
+- (void)main;
+{
+    @autoreleasepool {
+        _runLoop = [NSRunLoop currentRunLoop];
+        dispatch_group_leave(_waitGroup);
+        
+        // Add an empty run loop source to prevent runloop from spinning.
+        CFRunLoopSourceContext sourceCtx = {
+            .version = 0,
+            .info = NULL,
+            .retain = NULL,
+            .release = NULL,
+            .copyDescription = NULL,
+            .equal = NULL,
+            .hash = NULL,
+            .schedule = NULL,
+            .cancel = NULL,
+            .perform = NULL
+        };
+        CFRunLoopSourceRef source = CFRunLoopSourceCreate(NULL, 0, &sourceCtx);
+        CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
+        CFRelease(source);
+        
+        while ([_runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) {
+            
+        }
+        assert(NO);
+    }
+}
+
+- (NSRunLoop *)runLoop;
+{
+    dispatch_group_wait(_waitGroup, DISPATCH_TIME_FOREVER);
+    return _runLoop;
+}
+
+@end

+ 23 - 0
RunGame/SDK/HTTP/socket/SocketRocketUtility.h

@@ -0,0 +1,23 @@
+//
+//  SocketRocketUtility.h
+
+//
+
+#import <Foundation/Foundation.h>
+#import "SRWebSocket.h"
+
+@interface SocketRocketUtility : NSObject
+
+/** 连接状态 */
+@property (nonatomic,assign) SRReadyState socketReadyState;
+@property (nonatomic,  copy) void (^didReceiveMessage)(id message);
+//+ (SocketRocketUtility *)instance;
+//单例->初始化蓝牙
++(instancetype)sharedInstance;
+
+-(void)SRWebSocketOpen;//开启连接
+-(void)SRWebSocketClose;//关闭连接
+- (void)sendData:(NSDictionary *)paramDic withRequestURI:(NSString*)requestURI;//发送数据
+- (void)registerNetworkNotifications;//监测网络状态
+
+@end

+ 301 - 0
RunGame/SDK/HTTP/socket/SocketRocketUtility.m

@@ -0,0 +1,301 @@
+//
+//  SocketRocketUtility.m
+//  SUN
+//
+//  Created by 孙俊 on 17/2/16.
+//  Copyright © 2017年 SUN. All rights reserved.
+//
+
+#import "SocketRocketUtility.h"
+
+#define dispatch_main_async_safe(block)\
+if ([NSThread isMainThread]) {\
+block();\
+} else {\
+dispatch_async(dispatch_get_main_queue(), block);\
+}
+
+#define WeakSelf(type)  __weak typeof(type) weak##type = type;
+///*自定义打印 宏*/
+//#ifdef DEBUG
+//#define NSLog(format, ...) do {                                                                          \
+//fprintf(stderr, "<%s : %d> %s\n",                                           \
+//[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String],  \
+//__LINE__, __func__);                                                        \
+//(NSLog)((format), ##__VA_ARGS__);                                           \
+//fprintf(stderr, "-------\n");                                               \
+//} while (0)
+//
+//#else
+//#define NSLog( ...)
+//
+//#endif
+typedef NS_ENUM(NSUInteger, SocketDataType) {
+    distributeOrder,
+    cancelCall,
+    orderLost,
+    changeDeviceType,
+};
+@interface SocketRocketUtility()<SRWebSocketDelegate>
+{
+    NSTimer * heartBeat;
+    NSTimeInterval reConnectTime;
+    SocketDataType type;
+    NSString *host;
+}
+
+@property (nonatomic,strong) SRWebSocket *socket;
+
+@end
+
+@implementation SocketRocketUtility
+
+//单例静态
+static SocketRocketUtility* instance = nil;
++(instancetype)sharedInstance{
+     return [[self alloc] init];
+}
++ (instancetype)allocWithZone:(struct _NSZone *)zone{
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+      instance = [super allocWithZone:zone];
+  });
+  return instance;
+}
+- (instancetype)init{
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        instance = [super init];
+    });
+    return instance;
+}
+
+//+(SocketRocketUtility *)instance{
+//    static SocketRocketUtility *Instance = nil;
+//    static dispatch_once_t predicate;
+//    dispatch_once(&predicate, ^{
+//        Instance = [[SocketRocketUtility alloc] init];
+//    });
+//    return Instance;
+//}
+
+- (void)registerNetworkNotifications{
+//    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkChangedNote:) name:AFNetworkingReachabilityDidChangeNotification object:nil];
+}
+- (void)networkChangedNote:(NSNotification *)note{
+    
+//    AFNetworkReachabilityStatus status = [note.userInfo[AFNetworkingReachabilityNotificationStatusItem] integerValue];
+//    switch (status) {
+//        case AFNetworkReachabilityStatusUnknown:
+//            NSLog(@"网络类型:未知网络");
+//            break;
+//        case AFNetworkReachabilityStatusNotReachable:
+//            NSLog(@"网络类型:断网");
+//            break;
+//        case AFNetworkReachabilityStatusReachableViaWWAN:
+//            NSLog(@"网络类型:数据流量");
+//            [self SRWebSocketOpen];
+//            break;
+//        case AFNetworkReachabilityStatusReachableViaWiFi:
+//            NSLog(@"网络类型:WIFI");
+//            [self SRWebSocketOpen];
+//            break;
+//    }
+    
+}
+
+-(void)SRWebSocketOpen{
+    
+    
+    //如果是同一个url return
+    if (self.socket) {
+        return;
+    }
+    if(self.socket.readyState == SR_OPEN){
+        return;
+    }
+
+    self.socket = [[SRWebSocket alloc] initWithURLRequest:
+                   [NSURLRequest requestWithURL:[NSURL URLWithString:@"ws://172.16.14.127/examples/websocket/chat"]]];//这里填写你服务器的地址
+    
+    NSLog(@"请求的websocket地址:%@",self.socket.url.absoluteString);
+    self.socket.delegate = self;   //实现这个 SRWebSocketDelegate 协议
+    [self.socket open];     //open 就是直接连接了
+}
+
+-(void)SRWebSocketClose{
+    if (self.socket){
+        [self.socket close];
+        self.socket = nil;
+        //断开连接时销毁心跳
+        [self destoryHeartBeat];
+    }
+}
+
+#pragma mark - socket delegate
+- (void)webSocketDidOpen:(SRWebSocket *)webSocket {
+    
+    //每次正常连接的时候清零重连时间
+    reConnectTime = 0;
+    
+    //开启心跳
+    [self initHeartBeat];
+    if (webSocket == self.socket) {
+        NSLog(@"************************** socket 连接成功************************** ");
+    }
+}
+
+- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error {
+
+    if (webSocket == self.socket) {
+        NSLog(@"************************** socket 连接失败************************** ");
+        _socket = nil;
+        //连接失败就重连
+        [self reConnect];
+    }
+}
+
+- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean {
+    
+    if (webSocket == self.socket) {
+        NSLog(@"************************** socket连接断开************************** ");
+        NSLog(@"被关闭连接,code:%ld,reason:%@,wasClean:%d",(long)code,reason,wasClean);
+            [self SRWebSocketClose];
+    }
+
+}
+
+/*该函数是接收服务器发送的pong消息,其中最后一个是接受pong消息的,
+ 在这里就要提一下心跳包,一般情况下建立长连接都会建立一个心跳包,
+ 用于每隔一段时间通知一次服务端,客户端还是在线,这个心跳包其实就是一个ping消息,
+ 我的理解就是建立一个定时器,每隔十秒或者十五秒向服务端发送一个ping消息,这个消息可是是空的
+ */
+
+-(void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload{
+    NSString *reply = [[NSString alloc] initWithData:pongPayload encoding:NSUTF8StringEncoding];
+    NSLog(@"reply===%@",reply);
+}
+#pragma mark - 收到的回调
+- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message  {
+    
+    if (webSocket == self.socket) {
+//        NSLog(@"************************** socket收到数据了************************** ");
+//        NSLog(@"message:%@",message);
+        if(!message){
+            return;
+        }
+        [self handleReceivedMessage:message];
+
+    }
+}
+- (void)handleReceivedMessage:(id)message{
+
+        if(self.didReceiveMessage){
+            self.didReceiveMessage(message);
+        }
+
+}
+
+//重连机制
+- (void)reConnect
+{
+    
+   [self SRWebSocketClose];
+    //超过一分钟就不再重连 所以只会重连5次 2^5 = 64
+    if (reConnectTime > 64*2) {
+        //您的网络状况不是很好,请检查网络后重试
+        return;
+    }
+   
+    
+    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(reConnectTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+        self.socket = nil;
+        [self SRWebSocketOpen];
+        NSLog(@"重连");
+    });
+    
+    //重连时间2的指数级增长
+    if (reConnectTime == 0) {
+        reConnectTime = 2;
+    }else{
+        reConnectTime *= 2;
+    }
+    
+}
+
+//初始化心跳
+- (void)initHeartBeat
+{
+    dispatch_main_async_safe(^{
+        [self destoryHeartBeat];
+       
+        heartBeat = [NSTimer timerWithTimeInterval:30 target:self selector:@selector(ping) userInfo:nil repeats:YES];
+        //和服务端约定好发送什么作为心跳标识,尽可能的减小心跳包大小
+        [[NSRunLoop currentRunLoop]addTimer:heartBeat forMode:NSRunLoopCommonModes];
+    })
+}
+
+
+//取消心跳
+- (void)destoryHeartBeat
+{
+    dispatch_main_async_safe(^{
+        if (heartBeat) {
+            if ([heartBeat respondsToSelector:@selector(isValid)]){
+                if ([heartBeat isValid]){
+                    [heartBeat invalidate];
+                    heartBeat = nil;
+                }
+            }
+        }
+    })
+}
+
+//pingPong
+- (void)ping{
+    if (self.socket.readyState == SR_OPEN) {
+        [self.socket sendPing:nil];
+    }
+}
+
+- (void)sendData:(NSDictionary *)paramDic withRequestURI:(NSString *)requestURI{
+    
+    //这一块的数据格式由你们跟你们家服务器哥哥商定
+//     NSLog(@"socketSendData--configDic --------------- %@",requestURI);
+
+    WeakSelf(self);
+    dispatch_queue_t queue =  dispatch_queue_create("zy", NULL);
+    
+    dispatch_async(queue, ^{
+        if (weakself.socket != nil) {
+            // 只有 SR_OPEN 开启状态才能调 send 方法啊,不然要崩
+            if (weakself.socket.readyState == SR_OPEN) {
+                [weakself.socket send:requestURI];    // 发送数据
+                
+            } else if (weakself.socket.readyState == SR_CONNECTING) {
+
+                [weakself reConnect];
+                
+            } else if (weakself.socket.readyState == SR_CLOSING || weakself.socket.readyState == SR_CLOSED) {
+                // websocket 断开了,调用 reConnect 方法重连
+                NSLog(@"重连");
+                
+                [weakself reConnect];
+            }
+        } else {
+            // 这里要看你的具体业务需求;不过一般情况下,调用发送数据还是希望能把数据发送出去,所以可以再次打开链接;不用担心这里会有多个socketopen;因为如果当前有socket存在,会停止创建哒
+            [weakself SRWebSocketOpen];
+        }
+    });
+}
+
+-(SRReadyState)socketReadyState{
+    return self.socket.readyState;
+}
+-(void)dealloc{
+    [[NSNotificationCenter defaultCenter] removeObserver:self];
+    NSLog(@"SocketRocketUtility dealloced");
+}
+
+
+@end

+ 11 - 0
RunGame/SDK/IOSPlatformSDK.h

@@ -60,6 +60,17 @@
 */
 -(void)bridgingInteraction:(int)cusid
                       code:(int)code;
+
+/**
+*趣动通知好友邀请事件
+ * code:
+ * userChar:
+ * infoChar:
+*/
+-(void)bridgingInvite:(int)code
+                  userChar:(const char *)userChar
+                  infoChar:(const char *)infoChar;
+
 @end
 
 //****************** unity call  ios  start ******************//

+ 33 - 179
RunGame/SDK/IOSPlatformSDK.mm

@@ -8,7 +8,8 @@
 
 #import "IOSPlatformSDK.h"
 
-#pragma mark ============================>> ios call unity C语言指针声明 start
+#pragma mark ============================>> ios call unity (定义名字参数和C#类一样的方法)
+//大概流程就是先将C#的函数指针存入OC内存,在OC需要回调unity的时候就可以使用不同的指针来回调不同的unity方法
 /**
 *运动数据
 * left: 左鞋动作代码
@@ -61,13 +62,22 @@ typedef void (* ActionHandler) (int cusid, int code);
 */
 typedef void (* GetRankHandler)(int conde,const char * userJson);
 
-ActionHandler actionHandler;
-MotionHandler motionHandler;
-StepHandler stepHandler;
-DeviceHandler deviceHandler;
-UserFriendsHandler userFriendsHandler;
-InviteFriendHandler inviteFriendHandler;
-GetRankHandler getRankHandler;
+/** array: x,y,z姿态数据
+* 单位:弧度
+* 使用时要将数据 除以 10000
+* 转换角度可以使用 Mathf.Rad2Deg
+*/
+typedef void (* RotateHandler)(int cusid,int right_pos[3],int left_pos[3],int right_att[3],int left_att[3],int righrAcc[3],int leftAcc[3]);
+
+//声明 静态指针变量的实例 在unity call ios时候将该指针存储在内存中,在需要ios call unity时,用该指针调用unity的函数接口
+static ActionHandler actionHandler;
+static MotionHandler motionHandler;
+static StepHandler stepHandler;
+static DeviceHandler deviceHandler;
+static UserFriendsHandler userFriendsHandler;
+static InviteFriendHandler inviteFriendHandler;
+static GetRankHandler getRankHandler;
+static RotateHandler rotateHandler;
 
 //
 #pragma mark ============================>> ios call unity C语言指针声明 结束
@@ -95,173 +105,14 @@ static IOSPlatformSDK * instance;
     static dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{
         instance = [super init];
-        [self initData];
+        [BTDataProcess sharedInstance];
     });
     return instance;
 }
 
-#pragma mark ============================>> public method app跳转直链
--(void)startWithUrl:(NSURL*)url{//
-    
-    if (url==nil){
-        return;
-    }
-    NSString * urlString = [url absoluteString];
-    NSArray * stringArr = [urlString componentsSeparatedByString:@"//"];
-    NSString * arr2 = stringArr[1];
-    NSString* finsalString = [arr2 stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
-    NSDictionary * urlDict = [AlgorithmTool dictionaryWithJsonString:finsalString];
-    NSLog(@"Schemes_Url转码的dict ========= %@",urlDict);
-    //缓存用户信息数据
-    [IOS_NSUSERDEFAULT setObject:urlDict forKey:IOSSDK_USERINFO];
-    [IOS_NSUSERDEFAULT synchronize];
-  
-    if (urlDict!=nil){
-        //主副设备:deviceType  游戏种类:game_type  mac地址:macAddress
-        BTDataInstance.deviceType = DEVICETYPE_MAIN;
-        NSString * game_type = urlDict[@"game_type"];
-        BTDataInstance.game_type = (GAME_TYPE)[game_type intValue];
-        if ([urlDict objectForKey:@"mac"]!=nil){
-            BTDataInstance.macAddress = [urlDict objectForKey:@"mac"];
-        }
-        //链接蓝牙
-        [BTDataInstance initCBCentralManager];
-     
-    }
-    
-}
-
-#pragma mark ============================>> private 各类通知处理事件
--(void)initData{
-    
-    //固定一个时间戳
-    NSString * timestamp =   [AlgorithmTool returnTimestamp];
-    [IOS_NSUSERDEFAULT setObject:timestamp forKey:IOSSDK_TIMESTAMP];
-    [IOS_NSUSERDEFAULT synchronize];
-    
-    //监听 趣动 app group 消息 ==>> 好友邀请信息
-    CFStringRef invite = (__bridge CFStringRef)@"INVITE_NOTIFICATION";
-    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
-                                    NULL,
-                                    inviteFriends,
-                                    invite,
-                                    NULL,
-                                    CFNotificationSuspensionBehaviorDeliverImmediately);
-    
-    //监听 趣动 app group 消息 ==>> 断开蓝牙
-    CFStringRef strRef = (__bridge CFStringRef)@"DISCONNECT_BLE";
-    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
-                                    NULL,
-                                    disConnectBle,
-                                    strRef,
-                                    NULL,
-                                    CFNotificationSuspensionBehaviorDeliverImmediately);
-    //监听程序 退到 后台
-    [CUS_NOTIFICATIONCENTER addObserver:self selector:@selector(enterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
-    //监听程序 返回 前台
-    [CUS_NOTIFICATIONCENTER addObserver:self selector:@selector(willEnterForeground) name:UIApplicationWillEnterForegroundNotification object:nil];
-
-}
-
--(void)enterBackground{
-//    NSLog(@"leon UIApplicationDidEnterBackgroundNotification");
-    BTDataInstance.isBackGround = YES;
-}
--(void)willEnterForeground{
-//    NSLog(@"leon UIApplicationWillEnterForegroundNotification");
-    BTDataInstance.isBackGround = NO;
-    NSLog(@"程序从后台返回 重连蓝牙 peripheral.state = %ld",LEManager.peripheral.state);
-    [BTDataInstance initCBCentralManager];
-}
-
-void disConnectBle (CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo){
-    [BTDataInstance disConnedctBle];
-}
-
-void inviteFriends (CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo){
-        
-    //group.com.Oujia.AppAndGame 沙河数据
-    NSUserDefaults* userDefault = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.Oujia.AppAndGame"];
-    //趣动传过来的 invite json字符串转字典
-    NSString * string = [userDefault objectForKey:@"invite"];
-    NSData * data = [string dataUsingEncoding:NSUTF8StringEncoding];
-    NSDictionary * inviteDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
-    if (inviteDict!=nil){
-        //字典取值
-        NSDictionary * invite = [inviteDict objectForKey:@"invite"];
-        NSDictionary * user =  [invite objectForKey:@"user"];
-        //user字典 -> 字符串 -> Char
-        NSData * userData = [NSJSONSerialization dataWithJSONObject:user options:NSJSONWritingPrettyPrinted error:nil];
-        NSString * userString =  [[NSString alloc] initWithData:userData encoding:NSUTF8StringEncoding];
-        const char * userChar =[userString UTF8String];
-        //info字符串 -> Char
-        NSString * infoString = invite[@"info"];
-        const char * infoChar =[infoString UTF8String];
-        NSLog(@"IOS_SKD 回调好友邀请信息 GetInviteInfo ===>>  %s , %s",userChar,infoChar);
-        inviteFriendHandler(0,userChar,infoChar);
-    }
-    
-}
-
-//unity call instance弹出蓝牙搜索框
--(void)searchBLEAction:(int)type{
-    
-    /***************************初始化蓝牙数据处理工具***************************/
-    //主副设备:deviceType  游戏种类:game_type  mac地址:macAddress
-    BTDataInstance.deviceType = (DEVICE_TYPE)type;
-    NSDictionary * jsonDict = [IOS_NSUSERDEFAULT objectForKey:IOSSDK_USERINFO];
-    NSString * game_type = jsonDict[@"game_type"];
-    BTDataInstance.game_type = (GAME_TYPE)[game_type intValue];
-    BTDataInstance.macAddress = @"";
-    //初始化蓝牙CBCentralManager
-    [BTDataInstance initCBCentralManager];
-
-    /***************************搜索蓝牙外设弹窗***************************/
-    SearchDeviceViewController  * searchVC = [SearchDeviceViewController  new];
-    [self addTOwindow:searchVC];
-    //searchVC搜索蓝牙Button
-    searchVC.searchDeviceTask = ^{
-        [BTDataInstance initCBCentralManager];
-    };
-    //BTDataProces搜索到的蓝牙外设-->searchVC
-    BTDataInstance.deviceArrBLock = ^(NSMutableArray * arr){//蓝牙数据回调 刷新popView数据
-        searchVC.deviceArray = arr;
-        [searchVC reloadData];
-    };
-    //点击searchVC的tableViewCell链接选中的蓝牙
-    searchVC.connectDeviceBlock = ^(CBPeripheral * peripheral){
-        BTDataInstance.deviceType = (DEVICE_TYPE)type;
-        [BTDataInstance connectPeripheral:peripheral];
-        BTDataInstance.macAddress = peripheral.identifier.UUIDString;
-    };
-    
-    //bleInstance脚步交互动作-->searchVC
-    BTDataInstance.getInteractionBlock = ^(int interaction){
-        if (interaction!=-1){
-            NSLog(@"BTDataProcess 有效交互动作 ===>> %d",interaction);
-//            [searchVC getInteraction:interaction];
-        }else{
-//            NSLog(@"BTDataProcess 无效交互动作 ===>> %@",@"原地");
-        }
-    };
-    
-}
-
-#pragma mark ============================>> private
-//全局弹窗
--(void)addTOwindow:(UIViewController*)searchVC{
-    
-    UIViewController *rootVC = [[UIApplication sharedApplication].delegate window].rootViewController;
-    UIViewController *parent = rootVC;
-    while ((parent = rootVC.presentedViewController) != nil ){
-    rootVC = parent;
-    }
-    while ([rootVC isKindOfClass:[UINavigationController class]]){
-    rootVC = [(UINavigationController *)rootVC topViewController];
-    }
-    [rootVC addChildViewController:searchVC];
-    [rootVC.view addSubview:searchVC.view];
-    
+#pragma mark ============================>> public method app 跳转直链
+-(void)startWithUrl:(NSURL*)url{
+    [BTDataInstance startWithUrl:url];
 }
 
 #pragma mark ============================>> private
@@ -278,7 +129,7 @@ void inviteFriends (CFNotificationCenterRef center, void *observer, CFStringRef
     
 }
 
-#pragma mark ============================>> ios call unity
+#pragma mark ============================>> ios call unity (数据处理后的回调)
 -(void)bridgingMotionAction:(int)cusid
                        left:(int)left
                       right:(int)right{
@@ -318,10 +169,14 @@ void inviteFriends (CFNotificationCenterRef center, void *observer, CFStringRef
 //            actionHandler(cusid,code);
     }
 }
-
+-(void)bridgingInvite:(int)code
+             userChar:(const char *)userChar
+             infoChar:(const char *)infoChar{
+        inviteFriendHandler(0,userChar,infoChar);
+}
 @end
 
-#pragma mark ============================>> unity call ios
+#pragma mark ============================>> unity call ios (声明unity call ios的托管函数 & 接收unity传过来的指针)
 /**
  * c语言字符串指针malloc
  */
@@ -434,7 +289,6 @@ void GameEnd(int level, double score, int record, int mode, int opponentId){
     if ([instance existUserInfo]==NO){
         return;
     }
-    
     [BTDataInstance gameEndInitData];
     //http
     [HTTPDataProcession gameEnd];
@@ -451,10 +305,10 @@ void GameEnd(int level, double score, int record, int mode, int opponentId){
 void SearchDevice(int type){
     //unity 有两个search按钮 type 0是主设备 1是副设备
     NSLog(@"Unity 请求搜索设备 type ===>> %d",type);
-//    if ([instance existUserInfo]==NO){
-//        return;
-//    }
-    [instance searchBLEAction:type];
+    if ([instance existUserInfo]==NO){
+        return;
+    }
+    [BTDataInstance searchBLEAction:type];
 }
 
 /**

+ 0 - 78
RunGame/SDK/PrefixHeader.h

@@ -1,78 +0,0 @@
-////
-////  PrefixHeader.h
-////  Unity-iPhone
-////
-////  Created by duowan123 on 2021/11/2.
-////
-//
-//#ifndef PrefixHeader_h
-//#define PrefixHeader_h
-//
-//
-///*************************** 获取屏幕 宽度、高度 ***************************/
-//#define SCREEN_WIDTH ([UIScreen mainScreen].bounds.size.width)
-//#define SCREEN_HEIGHT ([UIScreen mainScreen].bounds.size.height)
-///*************************** 状态栏+导航栏 高度 ***************************/
-//#define getRectNavAndStatusHight  self.navigationController.navigationBar.frame.size.height+[[UIApplication sharedApplication] statusBarFrame].size.height
-//#define getTarbarHight (SCREEN_HEIGHT>=812?83:49)
-///*************************** 比例系数布局  ***************************/
-//#define SCALEoefficient(num)   (SCREEN_WIDTH/667.0)*(num)
-//#define SCALE_HIGHT_oefficient(num)   (SCREEN_HEIGHT/375.0)*(num)
-///*************************** 字号大小  ***************************/
-//#define SCALEoefficient_20 20.0*(SCREEN_WIDTH/320.0)///<大标题
-//#define SCALEoefficient_18 18.0*(SCREEN_WIDTH/320.0)///<附标题
-//#define SCALEoefficient_16 16.0*(SCREEN_WIDTH/320.0)///<中标题
-//#define SCALEoefficient_14 14.0*(SCREEN_WIDTH/320.0)///<小标题
-//
-////app跳转 用户信息
-//#define IOSSDK_USERINFO @"iosSdk_userInfo"
-////缓存的步频数据
-//#define IOSSDK_BLESDKDATA @"iosSdk_bleSdk_frequency"
-////custom System UserDefaults
-//#define IOS_NSUSERDEFAULT [NSUserDefaults standardUserDefaults]
-//#define CUS_NOTIFICATIONCENTER [NSNotificationCenter defaultCenter]
-//#define IOSSDK_TOKEN [[IOS_NSUSERDEFAULT objectForKey:IOSSDK_USERINFO] objectForKey:@"token"]
-//#define IOSSDK_GAMEID [[IOS_NSUSERDEFAULT objectForKey:IOSSDK_USERINFO] objectForKey:@"game_type"]
-////#define IOSSDK_GAMEID @"3"
-////#define IOSSDK_TOKEN @"shoes:61652aa99523f9.65681365I94"
-//
-//
-//#ifdef __OBJC__
-//
-//#import <UIKit/UIKit.h>
-//#import <Foundation/Foundation.h>
-//
-////tool
-//#import "MYFactoryManager.h"
-//#import "UIView+ST.h"
-//#import "AlgorithmTool.h"
-//#import "UIColor+Hex.h"
-//
-//////
-////#import "IOSPlatformSDK.h"
-////
-////蓝牙
-//#import <CoreBluetooth/CoreBluetooth.h>
-//#import "LEONBLManager.h"
-//#import "BTDataProcess.h"
-////蓝牙提供的sdk算法
-//#import "GameObjc.h"
-//#define LEFT_FOOT_OC 1
-//#define RIGHT_FOOT_OC 2
-//
-////http
-//#import "HTTPDataProcession.h"
-//#import "UserFriendsModel.h"
-////第三方
-//#import "AFNetworking.h"
-//#import "MBProgressHUD.h"
-////UI
-//#import "SearchDeviceViewController.h"
-//#import "AnimationView.h"
-//#import "PopupView.h"
-//#import "NoDeviceTip.h"
-//
-//#endif
-//
-//
-//#endif /* PrefixHeader_h */

+ 0 - 2
RunGame/SDK/Tool/MYFactoryManager.h

@@ -15,8 +15,6 @@
 #import "AlgorithmTool.h"
 #import "UIColor+Hex.h"
 
-//#import "IOSPlatformSDK.h"
-//
 //蓝牙
 #import <CoreBluetooth/CoreBluetooth.h>
 #import "BTDataProcess.h"

+ 0 - 2
RunGame/SDK/UI/SearchDeviceViewController.h

@@ -17,8 +17,6 @@
 
 @property (copy,nonatomic)void(^connectDeviceBlock)(CBPeripheral * peripheral);
 
-@property (nonatomic, copy)void(^searchDeviceTask)();
-
 //
 -(void)reloadData;
 

+ 1 - 1
RunGame/SDK/UI/SearchDeviceViewController.m

@@ -211,7 +211,7 @@
 //
 //    self.tipView.hidden = NO;
 //    if(self.searchDeviceTask){
-//        self.searchDeviceTask();
+//        [BTDataInstance initCBCentralManager];
 //    }
 //
 //}

+ 1 - 0
RunGame/SDK/shoes_sdk/InertialTrajProcess.cpp

@@ -42,6 +42,7 @@ void InertialTrajProcess::TrajRotate(int* pos)
 
 	posTemp[0] = cos(curheading) * float(pos_f[0]) + sin(curheading) * float(pos_f[1]);
 	posTemp[1] = -sin(curheading) * float(pos_f[0]) + cos(curheading) * float(pos_f[1]);
+
 	
 	pos[0] = (int)(posTemp[0] * 100.0f);
 	pos[1] = (int)(posTemp[1] * 100.0f);

+ 1 - 3
RunGame/SDK/shoes_sdk/SingleFootAction.h

@@ -5,8 +5,6 @@
 #define  STOP_ANGLE  -0.65f
 
 
-
-
 class  SingleFootAction {
 
 public:
@@ -73,4 +71,4 @@ private:
 	clock_t time_s = clock();
 
 	FootActionState footActionState = {0, 0 , 0};
-};
+};

+ 28 - 4
RunGame/Unity-iPhone.xcodeproj/project.pbxproj

@@ -130,6 +130,12 @@
 		917C4C54AB71F090E6A5CEA8 /* Generics17.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3B6C439C9D0853090FCADEB0 /* Generics17.cpp */; };
 		91984D84B3FE92002A071435 /* UnityEngine.SharedInternalsModule_CodeGen.c in Sources */ = {isa = PBXBuildFile; fileRef = 405E41C3995D0BF5696952C7 /* UnityEngine.SharedInternalsModule_CodeGen.c */; };
 		92C044C895A2F97CE833ACC2 /* QFramework.ResKit3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 508F4A078251FE52B15E1A30 /* QFramework.ResKit3.cpp */; };
+		93BB27A6278597830019B85C /* SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 93BB27A4278597830019B85C /* SRWebSocket.m */; };
+		93BB27A7278597830019B85C /* SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 93BB27A4278597830019B85C /* SRWebSocket.m */; };
+		93BB27A8278597830019B85C /* SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 93BB27A5278597830019B85C /* SRWebSocket.h */; };
+		93BB27AB2785978B0019B85C /* SocketRocketUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 93BB27A92785978A0019B85C /* SocketRocketUtility.m */; };
+		93BB27AC2785978B0019B85C /* SocketRocketUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 93BB27A92785978A0019B85C /* SocketRocketUtility.m */; };
+		93BB27AD2785978B0019B85C /* SocketRocketUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = 93BB27AA2785978B0019B85C /* SocketRocketUtility.h */; };
 		93D3D11F27797662006D26A0 /* TXCarouselCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 93D3D07E27797662006D26A0 /* TXCarouselCollectionViewCell.xib */; };
 		93D3D12027797662006D26A0 /* TXCarouselCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 93D3D07E27797662006D26A0 /* TXCarouselCollectionViewCell.xib */; };
 		93D3D12127797662006D26A0 /* TXCarouselViewLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D3D07F27797662006D26A0 /* TXCarouselViewLayout.h */; };
@@ -185,7 +191,6 @@
 		93D3D15327797662006D26A0 /* ViceShoesAnalysis.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D3D0A327797662006D26A0 /* ViceShoesAnalysis.h */; };
 		93D3D15427797662006D26A0 /* MainShoesAnalysis.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D3D0A427797662006D26A0 /* MainShoesAnalysis.h */; };
 		93D3D15527797662006D26A0 /* DebugView.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D3D0A527797662006D26A0 /* DebugView.h */; };
-		93D3D15627797662006D26A0 /* PrefixHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D3D0A627797662006D26A0 /* PrefixHeader.h */; };
 		93D3D15727797662006D26A0 /* UserFriendsModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3D0A827797662006D26A0 /* UserFriendsModel.m */; };
 		93D3D15827797662006D26A0 /* UserFriendsModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3D0A827797662006D26A0 /* UserFriendsModel.m */; };
 		93D3D15927797662006D26A0 /* UserFriendsModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D3D0A927797662006D26A0 /* UserFriendsModel.h */; };
@@ -836,6 +841,10 @@
 		914642A9ACEDBBFC7BE9E1A3 /* Il2CppMetadataRegistration.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = Il2CppMetadataRegistration.c; path = Classes/Native/Il2CppMetadataRegistration.c; sourceTree = SOURCE_ROOT; };
 		92584C48A14965A935847383 /* Assembly-CSharp8.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "Assembly-CSharp8.cpp"; path = "Classes/Native/Assembly-CSharp8.cpp"; sourceTree = SOURCE_ROOT; };
 		93A94264A833AF9295273FDD /* QFramework.CSharpExtension.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = QFramework.CSharpExtension.cpp; path = Classes/Native/QFramework.CSharpExtension.cpp; sourceTree = SOURCE_ROOT; };
+		93BB27A4278597830019B85C /* SRWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRWebSocket.m; sourceTree = "<group>"; };
+		93BB27A5278597830019B85C /* SRWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRWebSocket.h; sourceTree = "<group>"; };
+		93BB27A92785978A0019B85C /* SocketRocketUtility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SocketRocketUtility.m; sourceTree = "<group>"; };
+		93BB27AA2785978B0019B85C /* SocketRocketUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SocketRocketUtility.h; sourceTree = "<group>"; };
 		93D3D07E27797662006D26A0 /* TXCarouselCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TXCarouselCollectionViewCell.xib; sourceTree = "<group>"; };
 		93D3D07F27797662006D26A0 /* TXCarouselViewLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TXCarouselViewLayout.h; sourceTree = "<group>"; };
 		93D3D08027797662006D26A0 /* ShoesCarouselView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShoesCarouselView.h; sourceTree = "<group>"; };
@@ -872,7 +881,6 @@
 		93D3D0A327797662006D26A0 /* ViceShoesAnalysis.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViceShoesAnalysis.h; sourceTree = "<group>"; };
 		93D3D0A427797662006D26A0 /* MainShoesAnalysis.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainShoesAnalysis.h; sourceTree = "<group>"; };
 		93D3D0A527797662006D26A0 /* DebugView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebugView.h; sourceTree = "<group>"; };
-		93D3D0A627797662006D26A0 /* PrefixHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PrefixHeader.h; sourceTree = "<group>"; };
 		93D3D0A827797662006D26A0 /* UserFriendsModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UserFriendsModel.m; sourceTree = "<group>"; };
 		93D3D0A927797662006D26A0 /* UserFriendsModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserFriendsModel.h; sourceTree = "<group>"; };
 		93D3D0AC27797662006D26A0 /* MBProgressHUD.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBProgressHUD.m; sourceTree = "<group>"; };
@@ -1555,6 +1563,17 @@
 			path = Unity;
 			sourceTree = "<group>";
 		};
+		93BB2794278585480019B85C /* socket */ = {
+			isa = PBXGroup;
+			children = (
+				93BB27A5278597830019B85C /* SRWebSocket.h */,
+				93BB27A4278597830019B85C /* SRWebSocket.m */,
+				93BB27AA2785978B0019B85C /* SocketRocketUtility.h */,
+				93BB27A92785978A0019B85C /* SocketRocketUtility.m */,
+			);
+			path = socket;
+			sourceTree = "<group>";
+		};
 		93D3D07B27797662006D26A0 /* SDK */ = {
 			isa = PBXGroup;
 			children = (
@@ -1563,7 +1582,6 @@
 				93D3D0B127797662006D26A0 /* HTTP */,
 				93D3D0E227797662006D26A0 /* IOSPlatformSDK.h */,
 				93D3D0B027797662006D26A0 /* IOSPlatformSDK.mm */,
-				93D3D0A627797662006D26A0 /* PrefixHeader.h */,
 				93D3D0E327797662006D26A0 /* resources   */,
 				93D3D0C327797662006D26A0 /* shoes_sdk */,
 				93D3D0AA27797662006D26A0 /* ThirdClass */,
@@ -1684,6 +1702,7 @@
 		93D3D0B127797662006D26A0 /* HTTP */ = {
 			isa = PBXGroup;
 			children = (
+				93BB2794278585480019B85C /* socket */,
 				93D3D0B227797662006D26A0 /* HTTPDataProcession.m */,
 				93D3D0B327797662006D26A0 /* HTTPDataProcession.h */,
 				93D3D0B427797662006D26A0 /* AFNetworking */,
@@ -1912,6 +1931,7 @@
 				93D3D16327797662006D26A0 /* HTTPDataProcession.h in Headers */,
 				93D3D13A27797662006D26A0 /* NoDeviceTip.h in Headers */,
 				93D3D14627797662006D26A0 /* BTDataProcess.h in Headers */,
+				93BB27AD2785978B0019B85C /* SocketRocketUtility.h in Headers */,
 				93D3D12A27797662006D26A0 /* TXCarouselView.h in Headers */,
 				93D3D1A527797663006D26A0 /* IOSPlatformSDK.h in Headers */,
 				93D3D20927797663006D26A0 /* UIView+ST.h in Headers */,
@@ -1930,6 +1950,7 @@
 				93D3D14F27797662006D26A0 /* BTDataSend.h in Headers */,
 				9DC67E8521CBBEBB005F9FA1 /* UnityAppController.h in Headers */,
 				93D3D16427797662006D26A0 /* AFSecurityPolicy.h in Headers */,
+				93BB27A8278597830019B85C /* SRWebSocket.h in Headers */,
 				93D3D17127797663006D26A0 /* AFNetworking.h in Headers */,
 				93D3D17627797663006D26A0 /* AFCompatibilityMacros.h in Headers */,
 				9D25ABA1213FB47800354C27 /* UnityFramework.h in Headers */,
@@ -1952,7 +1973,6 @@
 				93D3D16527797662006D26A0 /* AFNetworkReachabilityManager.h in Headers */,
 				93D3D16C27797662006D26A0 /* AFURLResponseSerialization.h in Headers */,
 				93D3D12F27797662006D26A0 /* ShoesCollectionCell.h in Headers */,
-				93D3D15627797662006D26A0 /* PrefixHeader.h in Headers */,
 				93D3D15327797662006D26A0 /* ViceShoesAnalysis.h in Headers */,
 				93D3D15227797662006D26A0 /* CBPeripheral+ADName.h in Headers */,
 				93D3D17727797663006D26A0 /* AFHTTPSessionManager.h in Headers */,
@@ -2224,6 +2244,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				93BB27AB2785978B0019B85C /* SocketRocketUtility.m in Sources */,
 				93D3D17427797663006D26A0 /* AFSecurityPolicy.m in Sources */,
 				93D3D16D27797663006D26A0 /* AFURLSessionManager.m in Sources */,
 				93D3D13027797662006D26A0 /* TXCarouselCollectionViewCell.m in Sources */,
@@ -2243,6 +2264,7 @@
 				93D3D20F27797663006D26A0 /* MYFactoryManager.m in Sources */,
 				93D3D19C27797663006D26A0 /* OriginTraj.cpp in Sources */,
 				93D3D14D27797662006D26A0 /* MainShoesAnalysis.mm in Sources */,
+				93BB27A6278597830019B85C /* SRWebSocket.m in Sources */,
 				93D3D14B27797662006D26A0 /* BTDataProcess.mm in Sources */,
 				93D3D15027797662006D26A0 /* LEONBLManager.m in Sources */,
 				93D3D14427797662006D26A0 /* BTDataSend.m in Sources */,
@@ -2311,6 +2333,7 @@
 				9D25ABCD213FB6F800354C27 /* VideoPlayer.mm in Sources */,
 				9D25ABC3213FB6F800354C27 /* DeviceSettings.mm in Sources */,
 				9D25ABCA213FB6F800354C27 /* FullScreenVideoPlayer.mm in Sources */,
+				93BB27AC2785978B0019B85C /* SocketRocketUtility.m in Sources */,
 				9D25ABC8213FB6F800354C27 /* InternalProfiler.cpp in Sources */,
 				9D25ABB6213FB6E300354C27 /* StoreReview.m in Sources */,
 				9D25ABB4213FB6E300354C27 /* OrientationSupport.mm in Sources */,
@@ -2346,6 +2369,7 @@
 				B9794559AF9B63B8135109C8 /* Assembly-CSharp2.cpp in Sources */,
 				93D3D19727797663006D26A0 /* DanceGame.cpp in Sources */,
 				B1004C229B3941540880F905 /* Assembly-CSharp3.cpp in Sources */,
+				93BB27A7278597830019B85C /* SRWebSocket.m in Sources */,
 				93D3D13427797662006D26A0 /* TXCarouselView.m in Sources */,
 				28D549AB8A45149A582919B0 /* Assembly-CSharp4.cpp in Sources */,
 				E340480AA90BF572B74E1F69 /* Assembly-CSharp5.cpp in Sources */,

BIN
RunGame/Unity-iPhone.xcodeproj/project.xcworkspace/xcuserdata/duowan123.xcuserdatad/UserInterfaceState.xcuserstate