【发布时间】:2013-05-07 20:50:25
【问题描述】:
我正在尝试做一些可能很愚蠢的事情,但我认为这是个好主意,所以请耐心等待。我尝试实现它,但遇到了一个尴尬的问题,即线程之间的套接字关闭 - 所以我想对这个案例有一些新的看法。
场景
我想通过套接字将对象从Client 写入Server。可能有多个Client同时与Server通信。
Message 对象通过其处理机制由Server 处理。建议不使用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 try 块一旦完成就会关闭套接字。那我为什么不手动做呢?试试自己 - 你最终会得到一个警告,说你只会在 null 对象上调用 close()!
那么,我如何避免在Server 线程启动之前关闭传入套接字的整个问题?或者这是一个坏主意,我应该做点别的?
【问题讨论】:
-
为什么不在连接打开后立即将套接字传递给线程?
-
我正在这样做,只是我希望服务器处理消息接收而不是侦听器。我为什么要这样做?因为我的规范需要我为客户端和服务器机器创建静态方法、sendMessage() 和 receiveMessage()。所以服务器必须知道何时调用 receiveMessage() 来获取它的数据。
-
你在没有使用线程的情况下测试过这段代码吗?
-
是的(除了监听器由于明显的原因在不同的线程上运行)
-
您的规范当然不应该下降到那种详细程度,但即使它这样做了,也没有什么能阻止您使用另一个线程。您在对象流方面犯了一个大错误。在套接字的生命周期内,您必须在两端使用相同的对象输入和输出流。
标签: java multithreading sockets java-7