should the disconnect delegates get called?

138 views
Skip to first unread message

Ruug

unread,
Dec 7, 2010, 10:43:54 AM12/7/10
to CocoaAsyncSocket
1. [asyncSocket connectToHost:onPort:error:] to connect to a host
2. after 4-5 seconds the host sends a TCP FIN to the client because it
wants to close the connection
3. client isn't aware that the socket was closing (closed?) until it
attempts to use it

This might be a newbie question so forgive me if i am missing
something obvious...but I was expecting the onSockedDidDisconnect:
(and maybe onSocket:willDisconnectWithError:) delegate to be called
when the socket was closed by the remote host.

This is all being done in the iPhone environment.

I don't see a version number in the class files I downloaded, but they
are the read-only files from a few wks ago.

The delegates do get called if I disable wireless on the client...so I
know I have them hooked up correctly.

PS- I added this as an issue, but just realized this is a better place
for it...sorry for the duplication

Ruug

unread,
Dec 8, 2010, 11:26:30 AM12/8/10
to CocoaAsyncSocket
It appears the read stream is getting a callback event type
kSocketHasBytesAvailable when the remote closes the connection but
doBytesAvailable ignores it because there is no current read. It seems
like it should tell the client that there were some bytes to read but
there aren't any read requests pending. Then the client can device
what to do. I added the delegate, read the stream and found that I get
a kCFStreamEventEndEncountered event.

It looks like kCFStreamEventEndEncountered is being treated as an
error (the same as kCFStreamEventErrorOccurred). I read here:
http://lists.apple.com/archives/macnetworkprog/2008/Jul/msg00051.html
that it means the peer closed the connection. Is that true? and how is
the AsyncSocket class intended to support this case.

Am I going down the wrong path here?

Robbie Hanson

unread,
Dec 9, 2010, 3:11:03 PM12/9/10
to cocoaasy...@googlegroups.com
Hi Ruug,

This is very much NOT a newbie question, so don't be alarmed. In fact, this is one of the more confusing aspects of socket programming.

(By the way, the fact that you know what a TCP FIN is means you're not a newbie. :) )

Before I launch into this discussion I would like to preface it by saying that the following refers to AsyncSocket only. It does NOT refer to GCDAsyncSocket, which I will mention at the end.

>> 1. [asyncSocket connectToHost:onPort:error:] to connect to a host
>> 2. after 4-5 seconds the host sends a TCP FIN to the client because it
>> wants to close the connection
>> 3. client isn't aware that the socket was closing (closed?) until it
>> attempts to use it


This is "normal" for most socket libraries, but is rarely seen in practice because nearly every protocol is always either reading or writing.

Consider HTTP for example. The server is either reading a request or sending a response. And the client is either sending a request, or reading the response. So, since each side is always actively using the socket, it is quickly notified if the socket is closed. In the case of reading, the socket would read an EOF (end of file). In the case of writing, it would eventually get an error.

(So the short answer is: Do something with your socket, and all will be as expected.)

I'm guessing you're the inquisitive type, so you're probably wondering why this is the way it is.

When thinking about your application's interface to the socket, it helps to visualize it as a simple API into the OS networking stack. When you create a socket, the OS networking stack creates all of it's buffers and what-not, and handles all the intricate details of TCP for us. When our application reads data from the socket, it is simply copying data out of the OS networking stack's read buffer. And when our application writes data to the socket, it is simply copying data into the OS networking stack's write buffer.

So let us imagine a situation in which the remote host sends us exactly 5 bytes of data, and then closes its socket. Our OS networking stack receives those 5 bytes, followed by a TCP FIN.

At this point, even though a TCP FIN has been received, the socket doesn't get closed. Because there is still data available for us to read. So if our app then requests to read exactly 5 bytes from the socket, the read will succeed, and everything will still look good.

Now we know if we execute a read at this point, we will read an EOF. However, what if we poll the socket? The result of the poll will be POLLOUT. And what exactly does this mean?? It means the application can read from the socket without blocking. Well, of course, because the read is immediately going to return an EOF.

Since most asynchronous networking libraries are based on poll/select, this is how it works. And Apple's CFNetworking libraries are no different - as you pointed out, Apple sends the kSocketHasBytesAvailable after a TCP FIN. Only after a read do we discover the truth. This is a result of their API being originally based on poll/select technology.

One last lingering question: If AsyncSocket is being notified of "kSocketHasBytesAvailable", why doesn't it just go ahead and read the bytes? Regardless of whether there is a pending read operation or not. And the problem with this is that if the socket does this, it would break proper TLS support. If the available data was the beginning of a SSL/TLS handshake, and we remove the data from the socket, then later trying to start SSL/TLS would fail because the underlying CFSocket wouldn't see the handshake on the socket.

You can see your description of the problem by using the EchoServer in the RunLoop folder.
Simply comment out the readDataToData calls (line 163 and/or 170 of AppDelegate.m). Then connect using telnet, and then disconnect. You'll notice the EchServer doesn't notice the disconnection.

----- HOWEVER -----

The newer kqueues technology solves this little dilemma. Using a kqueue (instead of poll/select), one can register to receive events related to the read stream. If the socket receives a TCP FIN, the kqueue would fire, and it would report both "POLLOUT" and the number of bytes available to be read. In the scenario above it would report 5 the first time, and zero the second time. This would tell us about the TCP FIN without us having to read EOF from the socket!

GCDAsyncSocket is based on GCD and kqueues. So we're actually able to solve this problem.

You can see this by using the EchoServer in the GCD folder.
Again, just comment out the readDataToData calls in AppDelegate.m, and notice that the EchoServer still detects when the client disconnects.


I hope my answer made sense. Let me know if you have any other questions.

-Robbie Hanson

> --
> You received this message because you are subscribed to the Google Groups "CocoaAsyncSocket" group.
> To post to this group, send email to cocoaasy...@googlegroups.com.
> To unsubscribe from this group, send email to cocoaasyncsock...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/cocoaasyncsocket?hl=en.
>

Daniel Dickison

unread,
Dec 10, 2010, 11:35:20 AM12/10/10
to CocoaAsyncSocket
Thank you, that was extremely edifying :)
Reply all
Reply to author
Forward
0 new messages