Java 如何停止网络I/O的线程阻塞?
我目前正试图编写一个非常简单的聊天应用程序来介绍java套接字编程和多线程。它由两个模块组成,一个psuedo服务器和一个psuedo客户端,然而我的设计让我相信我正在尝试实现一个不可能的概念 服务器 服务器在localhost端口4000上等待连接,当收到连接时,它启动两个线程,一个侦听器线程和一个扬声器线程。扬声器线程不断等待用户输入到控制台,并在接收到所述输入时将其发送到客户端。侦听器线程阻塞套接字的ObjectInputStream以获取客户端发送的任何消息,然后将消息打印到控制台 客户 客户端将用户连接到端口4000上的服务器,然后启动两个线程,一个侦听器和一个扬声器。这些线程具有与服务器线程相同的功能,但是,出于明显的原因,它们以相反的方式处理输入/输出 第一个问题 我遇到的问题是,为了结束聊天,用户必须键入“Bye”。现在,由于我的线程已循环到块以供输入:Java 如何停止网络I/O的线程阻塞?,java,multithreading,Java,Multithreading,我目前正试图编写一个非常简单的聊天应用程序来介绍java套接字编程和多线程。它由两个模块组成,一个psuedo服务器和一个psuedo客户端,然而我的设计让我相信我正在尝试实现一个不可能的概念 服务器 服务器在localhost端口4000上等待连接,当收到连接时,它启动两个线程,一个侦听器线程和一个扬声器线程。扬声器线程不断等待用户输入到控制台,并在接收到所述输入时将其发送到客户端。侦听器线程阻塞套接字的ObjectInputStream以获取客户端发送的任何消息,然后将消息打印到控制台 客户
while(connected()){
//block for input
//do something with this input
//determine if the connection still exists (was the message "Bye"?)
}
然后,当尝试退出应用程序时,它会成为一个非常有趣的场景。如果客户端键入“Bye”,那么它将返回发送线程,并且在服务器上侦听“Bye”的线程也将返回。这给我们留下了一个问题,即客户端侦听器和服务器端说话者不知道已经键入了“Bye”,因此继续执行
我通过创建一个类同步器解决了这个问题,该类同步器包含一个布尔变量,两个线程以同步方式访问该变量:
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”才能退出聊天,其他两个未被使用的线程仍会继续通过连接检查并开始阻塞I/O。在阻塞时,最初的两个线程意识到连接已终止,但即使它们更改了布尔值,其他两个线程已通过检查,并且已阻塞I/O
这意味着,即使您将在循环的下一次迭代中终止线程,您仍将尝试从已正确终止的其他线程接收输入。这就引出了我的最终结论和问题
我的问题
是否可以按照我尝试的方式异步接收和发送数据?(每个客户端/服务器都有2个线程,它们都阻止I/O)或者我必须在请求任何新数据的服务器和客户端之间每隔几毫秒发送一次心跳信号,并使用此心跳信号确定断开连接
问题似乎在于,我的线程在意识到伙伴线程已断开连接之前正在阻塞I/O。这就引出了一个主要问题,即如何异步停止I/O线程阻塞
我觉得这应该是可以做到的,因为这种行为在社交媒体上随处可见
如有任何澄清或建议,将不胜感激 我不知道Java,但是如果它有线程、在线程上调用函数的能力以及杀死线程的能力,那么即使它没有任务,也可以添加任务,这就是开始构建自己的异步接口所需的全部内容 因此,如果可以杀死线程,那么退出的线程就可以杀死其他线程 此外,在窗口关闭且连接打开的任何情况下,都应该发送一个“Bye”(或其他代码)——如果Java有事件,并且您正在使用的窗口有一个关闭事件,那么就应该将其放置在该位置 或者,您可以测试有效/打开的窗口,如果窗口无效/关闭,则发送“再见”。把它想象成穷人的事件处理程序 此外,请确保您知道如何(并且有权)手动向网络防火墙添加例外
此外,请始终通过实时网络进行测试。仅仅因为它在环回中工作,并不意味着它将在网络上工作。尽管你可能已经知道了。为了向将来可能偶然发现这篇文章的人澄清,我最终通过稍微调整线程的语法来解决这个问题。首先,我必须删除我的旧线程,并分别用AsyncSender和AsyncReader替换它们。这些线程不断地发送和接收,而不考虑用户的输入。当没有用户输入时,它只发送/接收一个空白字符串,只有当它不是空白字符串时才打印到控制台 解决办法 在接收方线程的这个迭代中,它不会阻塞输入,而是测试读取的对象是否为null(流中没有对象)。在发送者线程中也会执行同样的操作 这成功地绕过了必须停止阻塞I/O的线程的问题
请注意,还有其他解决此问题的方法,例如使用。是的,这是可能的。例如,IRC是如何工作的?(当前标题很糟糕。)您是否尝试关闭客户端的套接字(可能在发送“再见”后)?关闭套接字将通过异常中断服务器端的阻塞读取,还应通过异常中断客户端的读取线程。另外还有异步IO(基于事件的思考)。在这里,您不需要在套接字上进行阻塞读取,只需抓取任何可用的内容并立即返回(可能没有任何内容或部分消息)。@Pyranja感谢您的建议。我试着简单地关闭
try{
if((obj = in.readObject()) != null){
if(obj instanceof String)
output = (String) obj;
if(output.equalsIgnoreCase("Bye"))
s.stop();
}
}
catch(ClassNotFoundException e){
e.printStackTrace();
}
catch(IOException e){
e.printStackTrace();
}