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.
>