"java.net.BindException: Address already in use" despite setting SO_REUSEADDR - java

I have written this simple NIO server but when running multiple times, one right after another I get this exception:
Exception in thread "main" java.lang.IllegalStateException: java.net.BindException: Address already in use
at test.Server.start(Server.java:38)
at test.Server.main(Server.java:93)
I have set setReuseAddress(true) before a call to bind.
I have also tried to call setOption(StandardSocketOptions.SO_REUSEADDR, true) on ServerSocketChannel but it is still the same.
Can someone point out why it happens?
Here is the code:
package test;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class Server {
private ServerSocketChannel ssc;
private ServerSocket serverSocket;
private Selector accept;
private ExecutorService executor = Executors.newSingleThreadExecutor();
void start(final CountDownLatch cdl) {
try {
this.accept = Selector.open();
ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.setOption(StandardSocketOptions.SO_REUSEADDR, true);
InetSocketAddress isa = new InetSocketAddress("127.0.0.1", 9123);
serverSocket = ssc.socket();
serverSocket.setReuseAddress(true);
serverSocket.bind(isa);
ssc.register(accept, SelectionKey.OP_ACCEPT);
} catch (IOException e) {
throw new IllegalStateException(e);
}
executor.submit(new Runnable() {
#Override
public void run() {
try {
if (cdl != null) {
cdl.countDown();
}
while (true) {
accept.select();
if (Thread.currentThread().isInterrupted()) {
return;
}
Set<SelectionKey> readyKeys = accept.selectedKeys();
Iterator<SelectionKey> i = readyKeys.iterator();
while (i.hasNext()) {
SelectionKey sk = i.next();
if (sk.isValid() && sk.isAcceptable()) {
accept(sk);
}
i.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void accept(final SelectionKey sk) throws IOException {
ServerSocketChannel ssc = (ServerSocketChannel) sk.channel();
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
sc.register(accept, SelectionKey.OP_READ);
System.out.println("Connection accepted from: "
+ sc.getRemoteAddress());
}
});
}
void stop() {
try {
executor.shutdown();
executor.awaitTermination(10, TimeUnit.SECONDS);
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
Server s = new Server();
CountDownLatch cdl = new CountDownLatch(1);
s.start(cdl);
cdl.await();
Client.connect();
s.stop();
}
}
class Client {
static void connect() {
try {
new Socket("127.0.0.1", 9123);
} catch (IOException e) {
e.printStackTrace();
}
}
}

You cannot have two different invocations of your code listening on the same adapter and port number. This is the way the TCP/IP stack works. If you did, how would the stack know which process gets the connection? SO_REUSEADDR has nothing to do with this.
From What exactly does SO_REUSEADDR do?
This socket option tells the kernel that even if this port is busy
(in the TIME_WAIT state), go ahead and reuse it anyway. If it is
busy, but with another state, you will still get an address already
in use error. It is useful if your server has been shut down, and
then restarted right away while sockets are still active on its
port. You should be aware that if any unexpected data comes in, it
may confuse your server, but while this is possible, it is not
likely.
In other words, if you've closed the socket but it's still waiting for the connection to quiesce (receive the FIN/ACK or timeout) you can immediately grab it again. You can never have two processes connected to the same endpoint at the same time.

Related

Selector.select() not being unblocked even after I register a channel ready to read from another thread

I am creating a server to handle many different connections at the same time. I create two Selectors, on for the serverSocketChannel to accept and then the other for the connections to read data.
The one selector successfully gets past the blocking select() function to accept the new connection. Then the goal is to register that SocketChannel that was accepted with the other selector that is currently blocked by it's select() function so that I can read the data when I need to.
Everything seems to be set up correctly, but even after sending data, I am not getting past the other selector's select() function.
Here is my server file:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
public class Server implements Runnable
{
public Thread threadAccept;
public Thread threadRead;
protected ServerSocketChannel serverSocket;
protected Selector selectorAccept;
protected Selector selectorIO;
protected Set<Connection> connections;
private ByteBuffer buffer;
public Server()
{
try
{
selectorAccept = Selector.open();
selectorIO = Selector.open();
serverSocket = ServerSocketChannel.open();
serverSocket.configureBlocking(false);
InetSocketAddress hostAddress = new InetSocketAddress("localhost",4444);
serverSocket.bind(hostAddress);
int ops = serverSocket.validOps();
SelectionKey selectKey = serverSocket.register(selectorAccept, ops);
threadAccept = new Thread(this);
threadAccept.start();
threadRead = new Thread(this);
threadRead.start();
}
catch(Exception e)
{
System.out.println("Error "+e);
}
}
public void run()
{
if(Thread.currentThread() == threadAccept)
{
acceptNewConnections();
}
else if(Thread.currentThread() == threadRead)
{
readData();
}
}
private void acceptNewConnections()
{
int numberOfKeys = 0;
while(true)
{
try
{
numberOfKeys = selectorAccept.select();
Set<SelectionKey> keys = selectorAccept.selectedKeys();
Iterator<SelectionKey> itr = keys.iterator();
while(itr.hasNext())
{
SelectionKey key = itr.next();
if(key.isAcceptable())
{
SocketChannel client = serverSocket.accept();
client.configureBlocking(false);
client.register(selectorIO, SelectionKey.OP_READ);
System.out.println("New connection");
}
}
}
catch(Exception e)
{
System.out.println(e);
}
}
}
public void readData()
{
int numberOfKeys = 0;
ByteBuffer buffer = ByteBuffer.allocate(256);
while(true)
{
try
{
System.out.println("About to block on IO selector");
numberOfKeys = selectorIO.select();
System.out.println("I NEVER GET HERE");
Set<SelectionKey> keys = selectorIO.selectedKeys();
Iterator<SelectionKey> itr = keys.iterator();
while(itr.hasNext())
{
SelectionKey key = itr.next();
if(key.isReadable())
{
SocketChannel channel = (SocketChannel)key.channel();
channel.read(buffer);
String s = buffer.toString();
System.out.println(s);
}
}
}
catch(Exception e)
{
System.out.println(e);
}
}
}
}
And here is my main class to kick the server off and also create a client.
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
public class Main
{
public static void main(String[] args)
{
Server s = new Server();
Thread t = new Thread(new Runnable() {
public void run()
{
try
{
Socket s = new Socket("localhost", 4444);
byte[] data = "hello".getBytes();
s.getOutputStream().write(data);
s.getOutputStream().flush();
} catch (UnknownHostException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
t.start();
}
}
My guess is that a wakeup would be sufficient
client.register(selectorIO, SelectionKey.OP_READ);
selectorIO.wakeup();
Or just execute a task on the client thread where it registers the client on the same selectorIO it is waiting for. So the client thread should also check for a task queue as part of its loop (e.g. ConcurrentLinkedQueue) and call a selectorIO.wakup after placing the task in the task queue.

Why socket is being closed in run method?

Server:
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public final class Server {
public static void main(String[] args) {
new Server().start();
}
public void start() {
ExecutorService executorService = Executors.newFixedThreadPool(10);
try (ServerSocket serverSocket = new ServerSocket(1200)) {
while (true) {
try (Socket socket = serverSocket.accept()) {
executorService.submit(new SocketHandler(socket));
} catch (IOException e) {
System.out.println("Error accepting connections");
}
}
} catch (IOException e) {
System.out.println("Error starting server");
}
}
public final class SocketHandler implements Runnable {
private final Socket socket;
public SocketHandler(Socket connection) {
this.socket = connection;
System.out.println("Constructor: is socket closed? " + this.socket.isClosed());
}
#Override
public void run() {
System.out.println("Run method: is socket closed? " + this.socket.isClosed());
}
}
}
Client:
import java.io.IOException;
import java.net.Socket;
public final class Client{
public static void main(String[] args) {
try (Socket socket = new Socket("localhost", 1200)) {
} catch (IOException e) {}
}
Output:
Constructor: is socket closed? false
Run method: is socket closed? true
As you can see from output, when run method is invoked socket is closed, but in constructor it was opened.
Question: How to prevent socket being closed in run method, so that I can access its output stream?
Don't use try with resources with a Socket as the resource, since in this situation since the resource, here the socket, will be closed as soon as the try block exits.

java nio socket not detecting when machine goes to sleep or hibernates

Here are simplified versions of my socket server and client components.
The primary goal is for the client to detect when the server goes down and for the server to detect when the client goes down.
This works perfectly (on Windows) when either the client or the server are killed (getting IOException "An existing connection was forcibly closed by the remote host").
I would also like to detect when the machine where the client or server is running goes to sleep (or hibernates), eventually using the same mechanism.
Instead, the current behavior is that "the other machine going to sleep" event is not detected, and when the machine is woken up the connection is live again. At this time "the process going down" event is detected as before.
In the case where the client machine goes to sleep, the culprit seems to be "selector.selectedKeys()" not returning a key for the connection to the sleeping machine.
Is this functionality missing in the socket implementation on Windows?
Does anybody have any suggestion on how to fix / go around this issue?
package test;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class TestServer {
private ByteBuffer _inBuf;
private int _serverPort;
public static void main(String[] args) {
TestServer server = new TestServer(7071);
server.start();
}
public TestServer(int serverPort) {
_serverPort = serverPort;
}
public void start() {
_inBuf = ByteBuffer.allocate(512);
System.out.println("Server starting on port "+_serverPort);
new Thread() {
public void run() {
try {
Selector selector = Selector.open();
ServerSocketChannel server = ServerSocketChannel.open();
server.socket().bind(new InetSocketAddress(_serverPort));
server.configureBlocking(false);
SelectionKey serverKey = server.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
for (Iterator<SelectionKey> i = keys.iterator(); i.hasNext(); ) {
SelectionKey key = i.next();
i.remove();
if (key == serverKey) {
if (key.isAcceptable()) {
System.out.println("acceptable server key "+Integer.toHexString(key.hashCode()));
try {
SocketChannel client = server.accept();
client.configureBlocking(false);
SelectionKey clientKey = client.register(selector, SelectionKey.OP_READ);
System.out.println("registered client key "+Integer.toHexString(clientKey.hashCode()));
} catch (IOException x) {
x.printStackTrace();
}
}
} else {
if (!key.isReadable()) continue;
SocketChannel client = (SocketChannel) key.channel();
System.out.println("reading "+Integer.toHexString(key.hashCode()));
try {
int no = client.read(_inBuf);
if (no<0) throw new IOException("reached end-of-stream"+Integer.toHexString(key.hashCode()));
if (no>0) System.out.println("read "+no+" bytes from "+Integer.toHexString(key.hashCode()));
} catch (IOException x) {
System.out.println(x.getMessage()+" "+Integer.toHexString(key.hashCode()));
key.cancel();
try {
client.close();
} catch (IOException ignore) {
ignore.printStackTrace();
}
continue;
}
_inBuf.flip();
_inBuf.compact();
}
}
}
} catch (Exception x) {
x.printStackTrace();
}
}
}.start();
}
}
and
package test;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class TestClient {
private static final int _connectionTimeoutNanos = 10 * 1000000;
private String _serverHost;
private int _serverPort;
private SocketChannel _channel = null;
private ByteBuffer _inBuf;
public static void main(String[] args) {
TestClient client = new TestClient("192.168.1.180", 7071);
client.start();
}
public TestClient(String serverHost, int serverPort) {
_serverHost = serverHost;
_serverPort = serverPort;
}
public void start() {
_inBuf = ByteBuffer.allocate(512);
ClientThread thread = new ClientThread();
thread.start();
}
private class ClientThread extends Thread {
#Override
public void run() {
System.out.println("Client connecting to "+_serverHost+":"+_serverPort);
SocketAddress socketAddress = new InetSocketAddress(_serverHost, _serverPort);
while (true) {
boolean connected = false;
try {
_channel = SocketChannel.open();
_channel.configureBlocking(false);
try {
connected = _channel.connect(socketAddress);
} catch (IOException x) {
try {
_channel.close();
} catch (Throwable suppressed) {
x.addSuppressed(suppressed);
}
throw x;
}
long nanoStart = System.nanoTime();
while (!connected) {
connected = _channel.finishConnect();
if (!connected && (nanoStart+_connectionTimeoutNanos < System.nanoTime())) {
throw new IOException("Non blocking connect failed");
}
}
_channel.socket().setSoLinger(true, 10);
System.out.println("Connected to "+_serverHost+":"+_serverPort);
while (true) {
if (!readFromChannel()) break;
}
System.out.println("Disconnected from "+_serverHost+":"+_serverPort);
} catch (IOException x) {
if (connected) {
System.out.println("Disconnected from "+_serverHost+":"+_serverPort+" "+x.getMessage());
}
}
try {Thread.sleep(1000);} catch (InterruptedException x) {}
}
}
}
public boolean readFromChannel() throws IOException {
int no = _channel.read(_inBuf);
if (no<0) {
return false;
}
if (no>0) System.out.println("read "+no+" bytes from "+_serverHost+":"+_serverPort);
_inBuf.flip();
_inBuf.compact();
return true;
}
}
This behavior differs from system to system and even its configuration. Old versions of Windows used to shut down all the pending connections when computer became sleeping and even when temporarily lost network connectivity. This is often not what the user wanted, because in case of just temporary outages the user had to reopen all the connections again. So it had changed some time ago and now (by default, it's configurable) it behaves similarly to other systems (Linux, MacOs, ...). So the connection is kept until it timeouts.
To avoid long living dead connections the best option is to set SO_KEEPALIVE option on the socket. Both sides and their operating systems will then send dummy packets over the socket (not payload data so not visible to application layer) and unless receiving response in reasonable time, OS will kill the connection. In Java you can achieve this like following:
channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
Thanks Zbynek, that solved the issue :-).
Here is what I had to do:
1) in the TestServer code, after client.configureBlocking(false) at line 50 I added:
client.socket().setKeepAlive(true);
which is equivalent to your
client.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
2) in the TestClient code, after line 60:
_channel.socket().setSoLinger(true, 10);
I added:
_channel.socket().setKeepAlive(true);
3) using regedit, on both Windows machines, I added the following value under
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Tcpip/Parameters/
KeepAliveTime REG_DWORD 1000
Its default value is 2 hours and I reduced it to 1 second.
I left the KeepAliveInterval at its default value of 1 second and TcpMaxDataRetransmissions at its default value of 5.
Like with any Microsoft software, I had to restart both machines.
Note that one of my machines is Win10 and the other is Win7.
With these changes, whichever machine goes to sleep, the component on the other machine detects the disconnect event (within 5 seconds). As soon as the machine wakes up, the component on it detects that the connection is no longer there and then re-connects fresh. Exactly what I was trying to accomplish.
Thanks again,
Vladimir

Connections to SSL ports using NIO stay established after disconnect

In our application we need to check if certain ports of certain host are available for communication. At stage of this check we do not proceed with real communication - we need just to check if ports are open. As many ports has to be checked at once we originally used NIO approach (Selector + SocketChannel classes):
package test;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class TestNIO {
public static void main(final String... params) {
final List<String> portsToCheck = Arrays.asList(new String[] {"443", "5989"});
final List<String> openPorts = new ArrayList<String>();
final String host = "<SOME_IP>";
final int timeout = 5000;
Selector selector = null;
if (!portsToCheck.isEmpty()) {
try {
selector = Selector.open();
for (final String port : portsToCheck) {
final SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);
channel.connect(new InetSocketAddress(host, Integer.valueOf(port)));
channel.register(selector, SelectionKey.OP_CONNECT);
}
final int readyChannels = selector.select(timeout);
if (readyChannels != 0) {
final Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
final SelectionKey selKey = it.next();
try {
if (selKey.isValid() && selKey.isConnectable()) {
final SocketChannel channel = (SocketChannel) selKey.channel();
try {
if (channel.finishConnect()) {
openPorts.add(String.valueOf(channel.socket().getPort()));
}
} catch (final IOException ex) {
ex.printStackTrace();
} finally {
channel.close();
}
}
} catch (final Exception ex) {
ex.printStackTrace();
} finally {
selKey.cancel();
}
it.remove();
}
}
} catch (final IOException ex) {
ex.printStackTrace();
} finally {
try {
if (selector != null && selector.isOpen()) {
selector.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
System.out.print("Open ports: " + openPorts.toString());
}
}
}
}
This approach worked successfully for years at let's say hundreds of customers till at one of our customer this approach leaded to a problem. Namely, connections from client (where this check is running, it's Windows Server 2012 R2) to server (it's ESXi) at just one of SSL-ports stay established and never closed till restart of the server. This happens just with one of SSL-ports (standard 443), for example with another SSL-port - 5989 (it's HTTPS CIM server) this does not happen. Looks like this is because of some configuration on Windows side: 1. Happens just with one of several HTTPS ports; 2. Happens with any ESXi servers connected to this Windows client; 3. Does not happen with another Windows client connected to the same ESXi servers. Problem is that customer does not wish very much to cooperate with us finding the root cause and we have to guess it ourselves. We used another classic approach to check SSL-ports which works fine even in this problematic system. Here it is:
package test;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public class TestHttpUrlConnection {
public static void main(final String... params) {
final List<String> portsToCheck = Arrays.asList(new String[] {"443", "5989"});
final List<String> openPorts = new ArrayList<String>();
final String host = "<SOME_IP>";
final int timeout = 5000;
if (!portsToCheck.isEmpty()) {
trustAllHttpsCertificates();
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
public boolean verify(String urlHostName, SSLSession session) {
return true;
}
});
for (final String port : portsToCheck) {
HttpsURLConnection connection = null;
OutputStreamWriter out = null;
try {
connection = (HttpsURLConnection) new URL(
"https://" + host + ":" + Integer.valueOf(port)).openConnection();
connection.setDoOutput(true);
connection.setConnectTimeout(timeout);
final OutputStream os = connection.getOutputStream();
out = new OutputStreamWriter(os, "UTF8");
out.close();
openPorts.add(port);
} catch(final IOException ex) {
ex.printStackTrace();
} finally {
if (out != null) {
try {
out.close();
} catch (final IOException ex) {
ex.printStackTrace();
}
}
if (connection != null) {
connection.disconnect();
}
}
}
System.out.print("Open ports: " + openPorts.toString());
}
}
private static void trustAllHttpsCertificates() {
try {
final TrustManager[] trustAllCerts = new TrustManager[1];
trustAllCerts[0] = new TrustAllManager();
final SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (final Exception ex) {
ex.printStackTrace();
}
}
private static class TrustAllManager implements X509TrustManager {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkServerTrusted(final X509Certificate[] certs, final String authType) throws CertificateException {
// Empty
}
public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
// Empty
}
}
}
But customer wants us to tell him the reason why one approach works and another does not. Can anyone help?
UPDATE
I've found out that on that problematic system even following code leads to a situation that each connection stays ESTABLISHED and not released back to system. This is not NIO and explicit close() on socket invoked:
Socket sock = new Socket();
SocketAddress serverSocketAddress = new InetSocketAddress(host, port);
try {
sock.connect(serverSocketAddress, timeout);
if (sock.isConnected()) {
openPorts.add(port);
}
} catch (IOException e) {
ex.printStackTrace();
} finally {
if (sock != null) {
try {
sock.close();
} catch (IOException e) {
ex.printStackTrace();
}
}
}
Setting keepAlive to false does not change the situation.
UPDATE 2
Problem repeated also on nonSSL-port (135, it's Hyper-V virtualization). And what confuses me most is that after restart of guest OS with which connections were established and and after stopping software which opened these connections, they are still marked as established on client machine. I consider there is really something wrong with the system itself (and has nothing to do with our Java code), but what exactly is wrong...
UPDATE 3
The problem was caused by TrendMicro's Anti-Virus software "Virus Buster". It prevented connections to be closed normally.
Registered channels aren't completely closed until the next select() call. This is documented somewhere which I can never find when I look for it.
NB your trustAllCertificates() method does nothing useful except implement a radical insecurity, and calling it once per open socket that shouldn't be open at all appears to be completely pointless.
The problem was caused by TrendMicro's Anti-Virus software "Virus Buster". It prevented connections to be closed normally.

socket.getInputSteam.read() does not throw when I close the socket from the client

I am on windows 7 x64. I am writing a server which opens a thread for every incoming connection - the thread reads from the connection's input stream. The read() should block and throw an exception if the socket is closed(). It does not - just returns -1. If I do not close the connection from the client - just let the client terminate - I get a connection reset as excpected - but if I close() the connection from the client (or just the client's output stream for that matter) read() in the server thread does not throw - just returns -1. The docs are pretty clear on this :
public void close()
throws IOException
Closes this socket.
Any thread currently blocked in an I/O operation upon this socket will throw a SocketException.
Help
Working code :
Server :
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class CloseTest {
private int port;
public CloseTest(int port) {
this.port = port;
}
void base_station_activate() {
ServerSocket baseStationListeningSocket=null;
try {
baseStationListeningSocket = new ServerSocket(this.port, 1, InetAddress.getByName("127.0.0.1"));
} catch (IOException ex) {
}
main_server: while (true) {
try {
Socket clientSocket = baseStationListeningSocket.accept();
BaseStationClientHandler ch = new BaseStationClientHandler(clientSocket);
Thread myThread = new Thread(ch);
myThread.start();
} catch (IOException ex) {
System.exit(1);
} // main_server
finally {
// baseStationListeningSocket.close()
}
}
}
public static void main(String args[]){
CloseTest bs = new CloseTest(8082);
bs.base_station_activate();
}
public class BaseStationClientHandler implements Runnable {
private final Socket clientSocket;
private BaseStationClientHandler(Socket clientSocket) {
this.clientSocket = clientSocket;
}
public void run() {
String debug_message = null;
try {
InputStream in = clientSocket.getInputStream();
// read message and respond
String s = "";
char x;
int r;
server: while (true) {
try {
while ((r = in.read()) != (int) '%') {
if (r == -1) {
debug_message = "Stream/socket .closed() - exception not thrown (WHYYYYY ????) by client";
System.out.println(debug_message);
break server;
}
x = (char) r;
s += x;
}
System.out.println(s);
} catch (SocketException socketException) {
System.out.println(socketException.getLocalizedMessage()); // if connection reset (but not if Stream/socket .closed()) read throws !!!!!
debug_message = "socket_reset";
break server;
}
s = "";
} //server
} catch (IOException ex) {
System.out.println("IOexception in client handler - check if thrown by read");
} finally {
try {
clientSocket.close();
} catch (IOException ex) {
}
}
}
}
}
Client :
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Vector;
public class CloseTestClient {
public CloseTestClient(int port, String ipAddress){
Vector<Socket> connections = new Vector<Socket>();
try {
for(int i=0;i<20;i++){
Socket connection = new Socket(InetAddress.getByName(ipAddress), port);
connections.add(connection);
OutputStream out = connection.getOutputStream();
out.write( ("CONNECT#"+(i+1)+"#1%").getBytes());
System.out.println("[CloseTestClient SENT]:"+"CONNECT#"+(i+1)+"#1%");
Thread.sleep(1000); // to be sure the server threads are blocked in the read()
// connection.close(); // if I comment this out I see the connection reset message from the server when this main terminates
// commented it out finally and moved the closing at the end to be sure the server threads are blocked in read()
}
} catch (Exception ex) {
ex.printStackTrace();
}
finally{
// if I comment *for* out I see the "connection_reset" message from the server when this main terminates
for (Socket c : connections){
try{
c.close();
}catch(Exception ex){
}
}
}
}
public static void main(String args[]){
System.out.println("CloseTestClient run !");
new CloseTestClient(8082,"127.0.0.1");
}
}
The bit of documentation you're referring to applies to threads on that machine, not remote threads. If you have thread A read()'ing on socket X, and thread B of the same process closes socket X, then an exception will be thrown for thread A's read call.
When a socket is close()'d on the the local machine, the remote machine can determine that there will be no more data coming over the socket so it returns -1 (see the read() documentation for InputStream). This is what is happening when you explicitly close the connection on the client. The server knows no more data will be coming so read() happily returns -1. There are no exceptional circumstances.
I'm guessing that when you let the client terminate without calling close() on the socket, the JVM or OS is sending a TCP RST instead of closing the connection nicely (sending TCP FIN). This causes the read() call on the server to throw an exception.

Categories