处理另一个线程中的传入套接字

时间:2013-05-07 20:50:25

标签: java multithreading sockets java-7

我正在尝试做一些可能很愚蠢的事情,但我认为这是一个好主意,所以请耐心等待。我试图实现它,但是我遇到了一个棘手的问题,在线程之间关闭套接字 - 所以我想要对案例有一些新的看法。

方案

我想通过套接字从ClientServer写一个对象。可能会有多个Client同时与Server进行通信。

对象MessageServer通过其处理机制处理。建议不是Server的主线程寻找新的传入连接,而是建立Listener线程。一旦发现传入连接,它就会向Server发出警报,将套接字存储在队列中而不接收数据,因此它可以快速恢复监听。

在它自己的时间内,Server选择等待套接字,生成一个新线程,读取Message,然后关闭套接字。

代码

以下是我应该如何实施的第一个想法。它有一个根本性的缺陷,我将在下面解释。

忽略公共字段的使用 - 我只是想让你们的代码缩短

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();
        }
    }
}

这有什么问题?

此:

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)

这是因为我使用的Java 7尝试块在完成后关闭套接字。那我为什么不手动这样做呢?试试你自己 - 你最后会发出警告说你只会在空对象上调用close()!

那么,如何避免在Server线程接收之前关闭传入套接字的整个问题?或者这是一个坏主意,我应该做点什么呢?

1 个答案:

答案 0 :(得分:1)

您在Listener

中的陈述
try (Socket client = rxSock.accept()) { ...

客户端套接字的try-with-resources。只要将其添加到队列并退出try块,套接字就会自动关闭。