I have been using Sockets often, mostly with Selectors, and though not a Network OSI expert, from my understanding, calling shutdownOutput()
on a Socket actually sends something on the network (FIN) that wakes up my Selector on the other side (same behaviour in C language). Here you have detection: actually detecting a read operation that will fail when you try it.
In the code you give, closing the socket will shutdown both input and output streams, without possibilities of reading the data that might be available, therefore loosing them. The Java Socket.close()
method performs a "graceful" disconnection (opposite as what I initially thought) in that the data left in the output stream will be sent followed by a FIN to signal its close. The FIN will be ACK'd by the other side, as any regular packet would1.
If you need to wait for the other side to close its socket, you need to wait for its FIN. And to achieve that, you have to detect Socket.getInputStream().read() < 0
, which means you should not close your socket, as it would close its InputStream
.
From what I did in C, and now in Java, achieving such a synchronized close should be done like this:
- Shutdown socket output (sends FIN on the other end, this is the last thing that will ever be sent by this socket). Input is still open so you can
read()
and detect the remote close()
- Read the socket
InputStream
until we receive the reply-FIN from the other end (as it will detect the FIN, it will go through the same graceful diconnection process). This is important on some OS as they don't actually close the socket as long as one of its buffer still contains data. They're called "ghost" socket and use up descriptor numbers in the OS (that might not be an issue anymore with modern OS)
- Close the socket (by either calling
Socket.close()
or closing its InputStream
or OutputStream
)
As shown in the following Java snippet:
public void synchronizedClose(Socket sok) {
InputStream is = sok.getInputStream();
sok.shutdownOutput(); // Sends the 'FIN' on the network
while (is.read() > 0) ; // "read()" returns '-1' when the 'FIN' is reached
sok.close(); // or is.close(); Now we can close the Socket
}
Of course both sides have to use the same way of closing, or the sending part might always be sending enough data to keep the while
loop busy (e.g. if the sending part is only sending data and never reading to detect connection termination. Which is clumsy, but you might not have control on that).
As @WarrenDew pointed out in his comment, discarding the data in the program (application layer) induces a non-graceful disconnection at application layer: though all data were received at TCP layer (the while
loop), they are discarded.
1: From "Fundamental Networking in Java": see fig. 3.3 p.45, and the whole ยง3.7, pp 43-48
Your confusion lies in thinking that a socket is identified by Server IP : Server Port. When in actuality, sockets are uniquely identified by a quartet of information:
Client IP : Client Port
and Server IP : Server Port
So while the Server IP and Server Port are constant in all accepted connections, the client side information is what allows it to keep track of where everything is going.
Example to clarify things:
Say we have a server at 192.168.1.1:80
and two clients, 10.0.0.1
and 10.0.0.2
.
10.0.0.1
opens a connection on local port 1234
and connects to the server. Now the server has one socket identified as follows:
10.0.0.1:1234 - 192.168.1.1:80
Now 10.0.0.2
opens a connection on local port 5678
and connects to the server. Now the server has two sockets identified as follows:
10.0.0.1:1234 - 192.168.1.1:80
10.0.0.2:5678 - 192.168.1.1:80
Best Answer
The only way you can detect that a socket is connected is by writing to it.
Getting a error on
read()/recv()
will indicate that the connection is broken, but not getting an error when reading doesn't mean that the connection is up.You may be interested in reading this: http://lkml.indiana.edu/hypermail/linux/kernel/0106.1/1154.html
In addition, using TCP Keep Alive may help distinguish between inactive and broken connections (by sending something at regular intervals even if there's no data to be sent by the application).
(EDIT: Removed incorrect sentence as pointed out by @Damon, thanks.)