美文网首页
SocketRocket研究三 协议数据封装

SocketRocket研究三 协议数据封装

作者: 独孤流 | 来源:发表于2017-07-26 11:05 被阅读52次

    socket是一种http的长连接请求,只是发送的数据格式要符合协议标准

    /* 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 ...                |
     +---------------------------------------------------------------+
     */
    

    1、发送数据
    连接开启后会发送一次请求参数
    将request里的内容封装成NSData

    -(void)didConnect;
    {
    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];
    }
    - (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);
            }
        });
    }
    
    • 将数据转换成标准协议格式的NSData
    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];
    }
    
    • 数据封装
    typedef enum  {
        SROpCodeTextFrame = 0x1,
        SROpCodeBinaryFrame = 0x2,
        // 3-7 reserved.
        SROpCodeConnectionClose = 0x8,
        SROpCodePing = 0x9,
        SROpCodePong = 0xA,
        // B-F reserved.
    } SROpCode;
    // 第一个字节设置fin
    uint8_t *frame_buffer = (uint8_t *)[frame mutableBytes];
        
        // set fin
        frame_buffer[0] = SRFinMask | opcode;
    // 设置mask
    frame_buffer[1] |= SRMaskMask;
    //设置payload
    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);
        }
    // 设置mask_key
    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;
            }
    

    读取数据

    -(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];
    }
    
    

    相关文章

      网友评论

          本文标题:SocketRocket研究三 协议数据封装

          本文链接:https://www.haomeiwen.com/subject/yepwkxtx.html