我目前正在尝试编写一个非常简单的聊天应用程序来向自己介绍 java 套接字编程和多线程。它由 2 个模块组成,一个伪服务器和一个伪客户端,但是我的设计让我相信我正在尝试实现一个不可能的概念。
服务器
服务器在 localhost 端口 4000 上等待连接,当它收到一个连接时,它启动 2 个线程,一个侦听器线程和一个扬声器线程。扬声器线程不断等待用户对控制台的输入,并在收到所述输入时将其发送给客户端。对于客户端发送的任何消息,侦听器线程阻塞到套接字的 ObjectInputStream,然后将消息打印到控制台。
客户端
客户端将用户连接到服务器的 4000 端口,然后启动 2 个线程,一个监听器和一个扬声器。这些线程具有与服务器线程相同的功能,但出于显而易见的原因,它们以相反的方式处理输入/输出。
第一个问题
我遇到的问题是,为了结束聊天,用户必须输入“Bye”。现在,由于我的线程已被循环以阻止输入:
while(connected()){
//block for input
//do something with this input
//determine if the connection still exists (was the message "Bye"?)
}
然后,当尝试退出应用程序时,它变成了一个非常有趣的场景。如果客户端键入“Bye”,则它返回发送线程,服务器上侦听“Bye”的线程也返回。这给我们留下了客户端监听器和服务器端扬声器不知道“Bye”已被键入的问题,因此继续执行。
我通过创建一个包含两个线程以同步方式访问的布尔变量的类 Synchronizer 解决了这个问题:
public class Synchronizer {
boolean chatting;
public Synchronizer(){
chatting = true;
onChatStatusChanged();
}
synchronized void stopChatting(){
chatting = false;
onChatStatusChanged();
}
synchronized boolean chatting(){
return chatting;
}
public void onChatStatusChanged(){
System.out.println("Chat status changed!: " + chatting);
}
}
然后,我将这个类的同一个实例传递到创建它的线程中。不过还有一个问题。
第二个问题
这就是我推断我正在尝试做的事情是不可能使用我目前使用的方法的地方。鉴于一个用户必须键入“Bye”才能退出聊天,其他 2 个未使用的线程仍继续通过连接检查并开始阻塞 I/O。在阻塞时,原来的 2 个线程意识到连接已经终止,但是即使他们改变了布尔值,其他 2 个线程已经通过了检查,并且已经阻塞了 I/O。
这意味着即使您将在循环的下一次迭代中终止线程,您仍将尝试从已正确终止的其他线程接收输入。这使我得出了最后的结论和问题。
我的问题
是否可以以我尝试的方式异步接收和发送数据?(每个客户端/服务器两个线程都阻塞 I/O)或者我必须在服务器和请求任何新数据的客户端之间每隔几毫秒来回发送一个心跳,并使用这个心跳来确定断开连接吗?
问题似乎在于我的线程在意识到伙伴线程已断开连接之前就阻塞了 I/O。这导致了主要问题,您将如何异步停止 I/O 线程阻塞?
我觉得这似乎是应该能够做到的事情,因为这种行为在整个社交媒体中都可以看到。
任何澄清或建议将不胜感激!