2013-03-20 35 views
14

tôi sử dụng thư viện SocketRocket cho Objective-C để kết nối với một WebSocket:WebSocket kết nối không được đóng bằng SocketRocket

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

Mở kết nối làm việc hoàn toàn tốt. Đại biểu được gọi sau khi kết nối được thiết lập.

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

NSLog(@"WebSocket is open"); 

} 

Nhưng khi tôi muốn đóng kết nối, không có gì xảy ra.

-(void)close { 

if(!self.webSocket) 
    return; 

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

} 

Đại biểu đóng thành công kết nối không được gọi. Bất cứ ai có thể cho tôi biết lý do tại sao điều này xảy ra?

Cảm ơn bạn đã đọc câu hỏi của tôi.

+0

điều kiện này trả về "if (! Self.webSocket)" là gì? –

+0

có nghĩa là websocket sẽ không được đóng nếu nó là 0, vì nó không mở anyways –

+0

Tôi luôn gặp lỗi này và Test to Speech bị ngắt âm thanh, WebSocket đóng với lý do [1000]: Ngắt kết nối thủ công kết nối Bất kỳ đề xuất? –

Trả lời

4

Tôi đã tìm ra rằng đại biểu không bao giờ được gọi, bởi vì websocket không bao giờ thực sự bị đóng. Việc đóng cửa các WebSocket trong SRWebSocket xảy ra trong phương pháp pumpWriting như thế này:

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 "); 
} 

Tất cả các con suối và một đối tượng để giữ lại WebSocket được đóng cửa hoặc bị xóa đó. Miễn là chúng vẫn mở, ổ cắm sẽ không được đóng một cách thích hợp. Nhưng kết thúc không bao giờ xảy ra trong chương trình của tôi, bởi vì khi tôi cố gắng đóng websocket, _closeWhenFinishedWriting luôn là KHÔNG.

Boolean này chỉ được đặt một lần trong phương thức ngắt kết nối.

- (void)_disconnect; 
{ 

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

} 

Nhưng khi gọi closeWithCode phương pháp trong SRWebSocket, ngắt kết nối chỉ được gọi trong một trường hợp và có nghĩa là, nếu WebSocket ở trạng thái kết nối.

BOOL wasConnecting = self.readyState == SR_CONNECTING; 

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

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

Điều này có nghĩa, nếu ổ cắm ở trạng thái khác, websocket sẽ không bao giờ thực sự đóng. Một giải pháp khác là luôn gọi phương thức ngắt kết nối. Ít nhất nó đã làm việc cho tôi và mọi thứ dường như không sao cả.

Nếu có ai có ý tưởng, tại sao SRWebSocket được thực hiện như vậy, vui lòng để lại nhận xét cho câu trả lời này và giúp tôi.

+0

Tôi không tìm thấy phương pháp ngắt kết nối ở bất kỳ đâu trong toàn bộ mã SRWebSocket –

+0

Tôi luôn gặp lỗi này và Kiểm tra giọng nói bị gián đoạn âm thanh, WebSocket đóng với lý do [1000]: Kết nối socket kết nối thủ công Bất kỳ đề xuất nào? –

4

Tôi nghĩ đây là lỗi.
Khi gọi gần, máy chủ sẽ trả lại thông báo 'đóng'.
Nó nhận được bởi SRWebSocket, tuy nhiên _selfRetain không bao giờ được đặt thành 0 và ổ cắm vẫn mở (các luồng không bị đóng) và chúng tôi bị rò rỉ bộ nhớ.
Tôi đã kiểm tra và quan sát điều này trong ứng dụng trò chuyện thử nghiệm.
tôi thực hiện thay đổi sau đây:

-(BOOL)_innerPumpScanner {  
    BOOL didWork = NO; 

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

Bây giờ ổ cắm đóng cửa, các trường hợp được phát hành, và rò rỉ bộ nhớ đã biến mất.
Điều duy nhất mà tôi không chắc chắn là nếu đại biểu nên được gọi khi đóng theo cách này. Sẽ xem xét điều này.

+0

Đúng vậy. Như tôi đã đề cập trong câu trả lời của tôi, _disconnect phải được gọi để đặt BOOL _closeWhenFinishedWriting thành YES, sao cho _selfRetain có thể được đặt thành nil một cách thích hợp –

+0

Đúng. Có trường hợp máy chủ bị chết, người được ủy quyền sẽ không được thông báo và sẽ chỉ ngồi đó. Tôi đã thực hiện thay đổi để đại biểu được thông báo. Tôi đã tạo một [pull request] (https://github.com/square/SocketRocket/pull/106) nếu có ai muốn xem những thay đổi tôi đã thực hiện. – emp

+0

Sự cố này đã được giải quyết trên GitHub, mặc dù bản sửa lỗi chưa được hợp nhất. https://github.com/mediumbear/SocketRocket/commit/08afedbeb991edfda13aa675b5720c53ffaf7ef8 –

2

Khi một thiết bị đầu cuối đã cả gửi và nhận một khung kiểm soát Đóng, thiết bị đầu cuối mà NÊN Đóng WebSocket kết nối theo quy định tại Mục 7.1.1. (RFC 6455 7.1.2)

Các dụ SRWebSocket không _disconnect đây vì đó sẽ đóng kết nối TCP đến máy chủ trước khách hàng đã nhận được một khung kiểm soát Đóng trong phản ứng. Trong thực tế, _disconnect ing ở đây sẽ xé xuống ổ cắm TCP trước khi khách hàng thậm chí có thể gửi khung Đóng của riêng nó đến máy chủ, bởi vì _disconnect cuối cùng gọi _pumpWriting trước khi closeWithCode: có thể. Máy chủ có thể sẽ đáp ứng đủ một cách duyên dáng, nhưng nó không phù hợp và bạn sẽ không thể gửi mã đóng duy nhất cho tình huống trong khi mọi thứ được thiết lập theo cách này.

này được xử lý đúng cách trong handleCloseWithData:

if (self.readyState == SR_OPEN) { 
    [self closeWithCode:1000 reason:nil]; 
} 
dispatch_async(_workQueue, ^{ 
    [self _disconnect]; 
}); 

khối này xử lý Đóng yêu cầu khởi xướng bởi cả hai máy khách và máy chủ. Nếu máy chủ gửi khung Đóng đầu tiên, phương thức chạy theo chuỗi bạn đã đặt, cuối cùng kết thúc bằng _pumpWriting qua closeWithCode:, nơi khách hàng sẽ phản hồi với khung Đóng của riêng nó. Sau đó nó tiếp tục phá bỏ kết nối với số _disconnect.

Khi khách hàng gửi frame trước, closeWithCode: chạy một lần mà không đóng kết nối TCP vì _closeWhenFinishedWriting vẫn là sai. Điều này cho phép thời gian máy chủ để đáp ứng với khung Đóng riêng của mình, mà thông thường sẽ cho kết quả trong việc điều hành closeWithCode: một lần nữa, nhưng đối với các khối sau ở phía trên cùng của phương pháp đó:

if (self.readyState == SR_CLOSING || self.readyState == SR_CLOSED) { 
    return; 
} 

Vì readyState được thay đổi trên phiên đầu tiên của closeWithCode:, lần này nó sẽ không chạy.

Sửa lỗi của emp là cần thiết để thực hiện công việc này như dự định, tuy nhiên: nếu không khung Đóng từ máy chủ không làm gì cả. Kết nối sẽ vẫn kết thúc, nhưng bẩn, bởi vì máy chủ (có cả gửi và nhận khung của nó) sẽ phá vỡ ổ cắm ở cuối của nó và máy khách sẽ phản hồi với NSStreamEventEndEncountered:, thường được dành riêng cho các lỗi luồng gây ra bởi tổn thất đột ngột kết nối. Một cách tiếp cận tốt hơn là xác định lý do khung không bao giờ bị loại ra khỏi số _innerPumpScanner đến handleCloseWIthData:. Một vấn đề khác cần lưu ý là theo mặc định, close chỉ cần gọi số closeWithCode: với mã RFC không tương thích -1. Điều này đã ném lỗi trên máy chủ của tôi cho đến khi tôi thay đổi nó để gửi một trong các giá trị được chấp nhận.

Tất cả những gì đã nói: phương thức đại biểu của bạn không hoạt động vì bạn đang hủy ủy quyền ngay sau khi bạn gọi close. Mọi thứ trong close nằm bên trong một khối không đồng bộ; sẽ không có người được ủy quyền để gọi vào lúc bạn gọi didCloseWithCode: bất kể bạn làm gì khác ở đây.

+0

Tôi luôn gặp lỗi này và Kiểm tra giọng nói bị gián đoạn âm thanh, WebSocket đóng với lý do [1000]: Ngắt kết nối socket theo cách thủ công Bất kỳ đề xuất nào? –

Các vấn đề liên quan