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.
Related
so today I've tried to install Citadel Email server on a raspberry pi which went ok but unfortunantly I cannot reach it from outside LAN.
I've tried to pinpoint the problem and use scanner tools that look for open ports like these :https://www.whatsmyip.org/port-scanner/
I've verified that my public IP adress is the same as my domain returns. Which indeed it is.
I've checked port forwarding severel times.
Last but not least I've wrote this java code to have a really simple example:
package main;
import java.io.IOException;
public class Main {
public static void main(String... args){
try {
Server server = new Server(8080);
Client client = new Client(8080);
} catch (IOException e) {
e.printStackTrace();
}
}
}
package main;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
private boolean stop = false;
public Server(int port) throws IOException {
ServerSocket serverSocket = new ServerSocket(port);
Runnable runnable = new Runnable() {
#Override
public void run() {
while(stop == false) {
try {
Socket socket = serverSocket.accept();
DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream());
outputStream.writeUTF("Hello World!");
DataInputStream inputStream = new DataInputStream(socket.getInputStream());
String input = inputStream.readUTF();
System.out.println("Client wrote: " + input);
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runnable, "server executor");
thread.start();
}
public void stop(){
this.stop = true;
}
}
package main;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
public class Client {
public static final String LOCALHOST = "localhost";
public static final String KIRAZUKE = "---";
public static final String PUBLIC_IP_4 = "---";
public Client(int port) {
try{
doTest(LOCALHOST, port);
} catch (IOException e) {
e.printStackTrace();
}
try{
Thread.sleep(2500);
} catch (InterruptedException e) {
e.printStackTrace();
}
try{
doTest(KIRAZUKE, port);
} catch (IOException e) {
e.printStackTrace();
}
try{
Thread.sleep(2500);
} catch (InterruptedException e) {
e.printStackTrace();
}
try{
doTest(PUBLIC_IP_4, port);
} catch (IOException e) {
e.printStackTrace();
}
}
private void doTest(String host, int port) throws IOException {
System.out.println("Opening to: " + host);
Socket socket = new Socket(host, port);
DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream());
outputStream.writeUTF("Hello dear Server ... calling from " + host + " ... over.");
DataInputStream inputStream = new DataInputStream(socket.getInputStream());
String string = inputStream.readUTF();
System.out.println("Response from server after " + host + " call: " + string);
}
}
So I've replaced the domain name and my public ip with dashes for privacy reasons. But what happens is that when using the localhost connection everything works fine the server prints the text sent by the client and vise versa. While using either the public IP or Domain name it fails due to time out.
What could be reasons that any incoming traffic is blocked altough port forwarding is enabled for the 8080 port (and other ports that I tried) ?
Note: I've also called my ISP, according to them they "don't block anything". Additionally I tried port forwarding port 3389 and tried remote desktop to my pi but that also failed.
Thanks in advance :)
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
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.
My final project for a class is to put together a game, including multiplayer. So I started trying to figure out java networking, and I'm kind of stuck.
Each of the two game clients needs to be able to send and receive messages to and from the other client.
The way I figured I would handle this is that I have a NetworkServer and NetworkClient objects that runs in their own threads.
I was thinking that I would just start them from my main game application, but I wanted to do some testing first, so I set this project up:
NetworkClient:
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Timer;
import java.util.TimerTask;
public class NetworkClient extends Thread {
Socket server;
ObjectOutputStream out;
Timer timer;
public NetworkClient(String hostname, int port) throws IOException
{
server = new Socket(hostname, port);
out = new ObjectOutputStream(server.getOutputStream());
timer = new Timer();
timer.schedule(new SendTask(), 0, 1*1000);
}
public void sendData(Integer b) throws IOException {
out.writeObject(b);
}
class SendTask extends TimerTask {
Integer i = new Integer(1);
public void run() {
System.out.println("Client: Sending Integer: " + i.toString());
try {
sendData(i);
i = new Integer(i.intValue()+1);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} // run()
} // class SendTask
}
NetworkServer:
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
public class NetworkServer extends Thread {
private ServerSocket serverSocket;
private Socket client;
private Integer i;
private ObjectInputStream in;
public NetworkServer(int port) throws IOException
{
serverSocket = new ServerSocket(port);
//serverSocket.setSoTimeout(10000);
}
public void run()
{
try {
System.out.println("Waiting for client on port " + serverSocket.getLocalPort() + "...");
client = serverSocket.accept();
System.out.println("Just connected to " + client.getRemoteSocketAddress());
in = new ObjectInputStream(client.getInputStream());
} catch (IOException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
while(true)
{
try
{
i = (Integer) in.readObject();
System.out.println("Server: Received the integer: " + i.toString());
}
catch(SocketTimeoutException s)
{
System.out.println("Socket timed out!");
break;
}
catch(IOException e)
{
e.printStackTrace();
try { client.close();} catch (IOException e1) {}
break;
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Network (The thing I'm using to try and test this):
import java.io.IOException;
public class Network {
NetworkClient client;
NetworkServer server;
public Network() throws IOException {
server = new NetworkServer(6066);
server.start();
client = new NetworkClient("192.168.1.196", 6066);
client.start();
}
public static void main(String [] args)
{
try
{
Network n = new Network();
}catch(IOException e)
{
e.printStackTrace();
}
}
}
I have the timer in there to facilitate sending data from the client to the server, which would normally be done by my game, but since I'm testing I had to have it send somehow.
When I run this where the client and server are talking to each other, I get both the Sent and Received messages.
When I put it on my laptop (and change the IP in NetworkClient to match my desktop, and vice versa on my desktop) and run it in both places, the client on the desktop sends to the server on the laptop, but the client on the laptop does not send to the server on the desktop.
And at some point during the running, I get an exception about that client's connection being reset by peer, though the working client/server connection continue.
So, I guess my question is, Does anyone know why it works in one direction but not bidirectionally?
FIXED!!
Edit: Gah, I figured it out. It had to do with the timing on starting the two servers.
I changed Network to:
import java.io.IOException;
public class Network {
NetworkClient client;
NetworkServer server;
public Network() throws IOException {
startServer();
startClient();
}
private void startServer() throws IOException {
server = new NetworkServer(6066);
server.start();
}
private void startClient(){
boolean isConnected = false;
while (!isConnected) {
try {
client = new NetworkClient("192.168.1.196", 6066);
} catch (IOException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
continue;
}
isConnected = true;
}
client.start();
}
public static void main(String [] args)
{
try
{
Network n = new Network();
}catch(IOException e)
{
e.printStackTrace();
}
}
}
Edit your Firewall. Make sure that java.exe has inbound and outbound traffic enabled. Also, add a rule to your Firewall for port 6066.
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.