objective-cconnectionwebsocketsocketrocket

WebSocket Connection is not closing using SocketRocket


I use the SocketRocket library for Objective-C to connect to a websocket:

-(void)open {

if( self.webSocket ) {
    [self.webSocket close];
    self.webSocket.delegate = nil;
}

self.webSocket = [[SRWebSocket alloc] initWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"ws://192.168.0.254:5864"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20]];
self.webSocket.delegate = self;
[self.webSocket open];
}

Opening the connection works totally fine. The delegate is called after the connection was established.

-(void)webSocketDidOpen:(SRWebSocket *)webSocket {

NSLog(@"WebSocket is open");

}

But when I want to close the connection, nothing happens.

-(void)close {

if( !self.webSocket )
    return;

[self.webSocket close];
self.webSocket.delegate = nil;

}

The delegate for successfully closing the connection is not called. Can anyone tell me why this happens?

Thank you for reading my question.


Solution

  • I figured out that the delegate is never called, because the websocket is never really closed. The closing of the websocket in the SRWebSocket happens in the method pumpWriting like this:

    if (_closeWhenFinishedWriting && 
        _outputBuffer.length - _outputBufferOffset == 0 && 
        (_inputStream.streamStatus != NSStreamStatusNotOpen &&
         _inputStream.streamStatus != NSStreamStatusClosed) &&
        !_sentClose) {
        _sentClose = YES;
    
        [_outputStream close];
        [_inputStream close];
    
        if (!_failed) {
            dispatch_async(_callbackQueue, ^{
                if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) {
                    [self.delegate webSocket:self didCloseWithCode:_closeCode reason:_closeReason wasClean:YES];
                }
            });
        }
    
        _selfRetain = nil;
    
        NSLog(@" Is really closed and released ");
    }
    else {
    
        NSLog(@" Is NOT closed and released ");
    }
    

    All streams and an object to retain the websocket are closed or deleted there. As long as they are still open, the socket won´t be closed appropriately. But the closing never happened in my program, because when I tried to close the websocket, _closeWhenFinishedWriting was always NO.

    This boolean is only set once in the disconnect method.

    - (void)_disconnect;
    {
    
    assert(dispatch_get_current_queue() == _workQueue);
    SRFastLog(@"Trying to disconnect");
    _closeWhenFinishedWriting = YES;
    [self _pumpWriting];
    
    }
    

    But when calling the closeWithCode method in SRWebSocket, disconnect is only called in one case and that is, if the websocket is in the connecting state.

    BOOL wasConnecting = self.readyState == SR_CONNECTING;
    
    SRFastLog(@"Closing with code %d reason %@", code, reason);
    dispatch_async(_workQueue, ^{
    
        if (wasConnecting) {
            [self _disconnect];
            return;
        }
    

    This means, if the socket is in another state, the websocket will never really close. One workaround is to always call the disconnect method. At least it worked for me and everything seems to be alright.

    If anyone has an idea, why SRWebSocket is implemented like that, please leave a comment for this answer and help me out.