Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<blockquote> <p>Once an endpoint has both sent and received a Close control frame, that endpoint SHOULD <em>Close the WebSocket Connection</em> as defined in Section 7.1.1. (<a href="http://tools.ietf.org/html/rfc6455#section-7.1.2" rel="nofollow">RFC 6455 7.1.2</a>)</p> </blockquote> <p>The SRWebSocket instance doesn't <code>_disconnect</code> here because that would close the TCP connection to the server <em>before</em> the client has received a Close control frame in response. In fact, <code>_disconnect</code>ing here will tear down the TCP socket before the client can even send its own Close frame to the server, because <code>_disconnect</code> ultimately calls <code>_pumpWriting</code> before <code>closeWithCode:</code> can. The server will probably respond gracefully enough, but it's nonconforming, and you won't be able to send situation-unique close codes while things are set up this way.</p> <p>This is properly dealt with in <code>handleCloseWithData:</code></p> <pre><code>if (self.readyState == SR_OPEN) { [self closeWithCode:1000 reason:nil]; } dispatch_async(_workQueue, ^{ [self _disconnect]; }); </code></pre> <p>This block handles Close requests initiated by both the client and the server. If the server sends the first Close frame, the method runs as per the sequence you laid out, ultimately ending up in <code>_pumpWriting</code> via <code>closeWithCode:</code>, where the client will respond with its own Close frame. It then goes on to tear down the connection with that <code>_disconnect</code>.</p> <p>When the client sends the frame first, <code>closeWithCode:</code> runs once without closing the TCP connection because <code>_closeWhenFinishedWriting</code> is still false. This allows the server time to respond with its own Close frame, which would normally result in running <code>closeWithCode:</code> again, but for the following block at the top of that method:</p> <pre><code>if (self.readyState == SR_CLOSING || self.readyState == SR_CLOSED) { return; } </code></pre> <p>Because the readyState is changed on the first iteration of <code>closeWithCode:</code>, this time it simply won't run.</p> <p>emp's bug fix is necessary to make this work as intended, however: otherwise the Close frame from the server doesn't do anything. The connection will still end, but dirtily, because the server (having both sent and received its frames) will break down the socket on its end, and the client will respond with an <code>NSStreamEventEndEncountered:</code>, which is normally reserved for stream errors caused by sudden losses of connectivity. A better approach would be to determine why the frame never gets out of <code>_innerPumpScanner</code> to <code>handleCloseWIthData:</code>. Another issue to keep in mind is that by default, <code>close</code> just calls <code>closeWithCode:</code> with an RFC-nonconforming code of -1. This threw errors on my server until I changed it to send one of the accepted values.</p> <p><strong>All that said</strong>: your delegate method doesn't work because you're unsetting the delegate right after you call <code>close</code>. Everything in <code>close</code> is inside an async block; there won't be a delegate left to call by the time you invoke <code>didCloseWithCode:</code> regardless of what else you do here.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload