Chris Watts
Chris Watts

Reputation: 6715

Handle incoming sockets in another thread

I'm trying to do something potentially stupid, but I reckon it's a good idea, so bear with me. I tried to implement it, but I hit an awkward issue with sockets closing between threads - so I want some fresh eyes on the case.

Scenario

I want to write an object from a Client to a Server via sockets. There may be more than one Client communicating with the Server concurrently.

The object, a Message, is handled by the Server through its handling mechanisms. It is proposed that instead of the Server's main thread looking out for new incoming connections, a Listener thread is set up. Once it spots an incoming connection, it alerts the Server, storing the socket in a queue without receiving the data, so it can go back to listening quickly.

In its own time, the Server picks up the waiting socket, spawns a new thread, reads the Message, and closes the socket.

The code

Here's my first thoughts on how this should be implemented. There is a fundamental flaw in it which I will explain below.

Ignore the use of public fields - I'm just trying to make the code short for you guys

public class Server {
    public boolean messageWaiting = false;

    public static void main(String[] args) {
        new Server().run();
    }

    public void run() {
        Listener l = new Listener();
        l.listen(this);
        try {
            while (true) {
                System.out.println("I'm happily doing my business!");
                Thread.sleep(1000);
                if (messageWaiting) {
                    acceptMessages(l);
                }
            }
        } catch (InterruptedException die) {}
    }

    private void acceptMessages(Listener l) {
        while (!l.waiting.isEmpty()) {
            try (
                Socket client = l.waiting.poll();
                ObjectInputStream ois = new ObjectInputStream(client.getInputStream())
            ) {
                // Handle messages in new threads! (or a thread pool)
                new Thread() {
                    public void run() {
                        try {
                            System.out.println(ois.readObject());
                        } catch (Exception ex) {
                            ex.printStackTrace();
                        }
                    }
                }.start();
            } catch (Exception ex) {
                // Oh no! The socket has already been closed!
                ex.printStackTrace();
            }
        }
    }
}

public class Listener {
    public ConcurrentLinkedQueue<Socket> waiting = new ConcurrentLinkedQueue<>();

    public void listen(final Server callback) {
        new Thread() {
            public void run() {
                try (ServerSocket rxSock = new ServerSocket(7500)) {
                    while (!isInterrupted()) {
                        try (Socket client = rxSock.accept()) {
                            // Once a new socket arrives, add it to the waiting queue
                            waiting.add(client);
                            // Alert the server
                            callback.messageWaiting = true;
                        } catch (IOException ex) {
                            ex.printStackTrace();
                        }
                    }
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }.start();
    }
}

public class Client {
    public static void main(String[] args) {
        try (
            Socket txSock = new Socket(InetAddress.getLoopbackAddress(), 7500);
            ObjectOutputStream oos = new ObjectOutputStream(txSock.getOutputStream())
        ) {

            oos.writeObject("This is a Message, trust me.");
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

What's wrong with this?

This:

I'm happily doing my business!
I'm happily doing my business!
java.net.SocketException: Socket is closed
    at java.net.Socket.getInputStream(Unknown Source)
    at Server.acceptMessages(Server.java:30)
    at Server.run(Server.java:20)
    at Server.main(Server.java:9)

This is because the Java 7 try blocks I'm using close the sockets once they're finished. So why don't I do this manually? Try yourself - you end up with a warning saying you're only ever going to call close() on a null object!

So, how do I avoid the whole issue of my incoming socket being closed before the Server thread picks up on it? Or is this a bad idea anyway and I should do something else?

Upvotes: 2

Views: 1655

Answers (1)

Jim Garrison
Jim Garrison

Reputation: 86754

Your statement in Listener

try (Socket client = rxSock.accept()) { ...

Is a try-with-resources for the client socket. As soon as you add it to the queue and exit the try block, the socket gets auto-closed.

Upvotes: 1

Related Questions