首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >未使用SocketRocket关闭WebSocket连接

未使用SocketRocket关闭WebSocket连接
EN

Stack Overflow用户
提问于 2013-03-21 01:19:45
回答 3查看 6.9K关注 0票数 14

我使用Objective-C的SocketRocket库连接到websocket:

代码语言:javascript
复制
-(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];
}

打开连接完全正常。在建立连接后调用委托。

代码语言:javascript
复制
-(void)webSocketDidOpen:(SRWebSocket *)webSocket {

NSLog(@"WebSocket is open");

}

但是当我想要关闭连接时,什么也没有发生。

代码语言:javascript
复制
-(void)close {

if( !self.webSocket )
    return;

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

}

未调用用于成功关闭连接的委托。有人能告诉我为什么会这样吗?

感谢您阅读我的问题。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-03-21 22:18:58

我发现委托永远不会被调用,因为websocket永远不会真正关闭。SRWebSocket中websocket的关闭发生在pumpWriting方法中,如下所示:

代码语言:javascript
复制
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 ");
}

所有的流和一个保留websocket的对象都在那里被关闭或删除。只要它们仍处于打开状态,插座就不会被适当地关闭。但是关闭在我的程序中从来没有发生过,因为当我试图关闭websocket时,_closeWhenFinishedWriting总是为NO。

此布尔值仅在disconnect方法中设置一次。

代码语言:javascript
复制
- (void)_disconnect;
{

assert(dispatch_get_current_queue() == _workQueue);
SRFastLog(@"Trying to disconnect");
_closeWhenFinishedWriting = YES;
[self _pumpWriting];

}

但在SRWebSocket中调用closeWithCode方法时,只会在一种情况下调用disconnect,即当websocket处于连接状态时。

代码语言:javascript
复制
BOOL wasConnecting = self.readyState == SR_CONNECTING;

SRFastLog(@"Closing with code %d reason %@", code, reason);
dispatch_async(_workQueue, ^{

    if (wasConnecting) {
        [self _disconnect];
        return;
    }

这意味着,如果套接字处于另一种状态,则websocket将永远不会真正关闭。一种解决方法是始终调用disconnect方法。至少它对我起作用了,看起来一切都很好。

如果任何人有一个想法,为什么SRWebSocket是这样实现的,请为这个答案留下评论,并帮助我。

票数 4
EN

Stack Overflow用户

发布于 2013-06-19 15:48:32

我想这是个bug。

当调用close时,服务器回送“close”消息。

它由SRWebSocket接收,但是_selfRetain永远不会设置为nil,套接字保持打开(流没有关闭),我们有一个内存泄漏。

我也在测试聊天应用程序中检查并观察到了这一点。

我做了以下更改:

代码语言:javascript
复制
-(BOOL)_innerPumpScanner {    
    BOOL didWork = NO;

    if (self.readyState >= SR_CLOSING) {
        [self _disconnect];  // <--- Added call to disconnect which releases _selfRetain
        return didWork;
    }

现在套接字关闭,实例被释放,内存泄漏也消失了。

我唯一不确定的是,当以这种方式结束时,是否应该调用委托。将会对此进行调查。

票数 4
EN

Stack Overflow用户

发布于 2013-08-17 22:31:03

一旦端点发送和接收了关闭控制帧,该端点就应该关闭7.1.1节中定义的WebSocket连接。(RFC 6455 7.1.2)

SRWebSocket实例在这里不会关闭,因为这会在客户端收到_disconnect控制帧响应之前关闭到服务器的连接。事实上,在客户端发送自己的Close帧到服务器之前,_disconnecting就会破坏TCP套接字,因为_disconnect最终会在closeWithCode:之前调用_pumpWriting。服务器可能会做出足够优雅的响应,但它是不一致的,而且在以这种方式设置时,您将无法发送情况唯一的关闭代码。

这在handleCloseWithData:中得到了正确的处理

代码语言:javascript
复制
if (self.readyState == SR_OPEN) {
    [self closeWithCode:1000 reason:nil];
}
dispatch_async(_workQueue, ^{
    [self _disconnect];
});

此块处理由客户端和服务器发起的关闭请求。如果服务器发送第一个Close帧,该方法将按照您布置的序列运行,最终通过closeWithCode:_pumpWriting中结束,客户端将使用自己的Close帧进行响应。然后,它继续断开与该_disconnect的连接。

当客户端首先发送帧时,由于_closeWhenFinishedWriting仍为false,因此closeWithCode:会运行一次,而不会关闭连接。这使服务器有时间使用自己的Close帧进行响应,这通常会导致再次运行closeWithCode:,但对于该方法顶部的以下块:

代码语言:javascript
复制
if (self.readyState == SR_CLOSING || self.readyState == SR_CLOSED) {
    return;
}

因为在closeWithCode:的第一次迭代中更改了readyState,所以这一次它根本不会运行。

然而,emp的错误修复是使其按预期工作所必需的:否则来自服务器的关闭框架不会做任何事情。连接仍将结束,但会弄脏,因为服务器(已发送和接收其帧)将在其一端中断套接字,而客户端将使用NSStreamEventEndEncountered:进行响应,这通常是为突然失去连接而导致的流错误保留的。一种更好的方法是确定为什么帧永远不会从_innerPumpScanner到达handleCloseWIthData:。另一个要记住的问题是,默认情况下,close只是使用RFC不一致代码-1调用closeWithCode:。这会在我的服务器上抛出错误,直到我将其更改为发送一个可接受的值。

:您的委托方法不起作用,因为您在调用close之后立即取消了委托的设置。close中的所有内容都在一个异步块中;当您调用didCloseWithCode:时,无论您在这里做了什么,都不会留下要调用的委托。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15530437

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档