我为一个项目构建了一个简单的客户端和服务器,使用一个非常基本的文本界面进行测试和开发(代码演示如下)。
客户端库有一个 acceptMessage() 方法和一个 getMessage() 方法,它们基本上将消息推入或拉出阻塞队列以分别用于输入和输出(在客户端中,这是通过 put() 和 take() 调用实现的) . 一个线程阻塞 System.in 并通过 acceptMessage 向客户端发送读取行,而客户端的 getMessage() 方法上的其他线程阻塞并在消息到达时将消息回显到 System.out。一切都非常基本,但它工作正常。
现在我已经让我的客户端库工作了,我正试图弄清楚如何将它集成到使用 Swing GUI 的应用程序中。到目前为止,除了在 Netbeans 的界面构建工具中构建一个带有文本输入框和标签的简单表单之外,我还没有做太多。这个想法是让文本输入框代替从 system.in 中读取的内容,并让标签显示本来会写入 system.out 的内容。到那时,我将在 Swing 中复制我的简单测试应用程序。
据我了解,与 Swing GUI 直接交互的所有内容都必须在 Swing 线程中运行,但客户端被构建为作为其自己的线程运行。我不认为从 GUI 向 acceptMessage() 发送消息会非常困难(我认为这涉及设置一个 ActionPerformed 方法,该方法将读取输入框的内容并在客户端调用 acceptMessage(),尽管我'我仍在试图弄清楚),但我不知道如何去获得回复。我知道由于线程安全问题,我不能让客户端调用 GUI 线程中的功能,而且客户端库的编写方式不知道其消费类的任何信息。客户端实例被简单地传递给消费类,然后使用 acceptMessage() 和 getMessage() 发送和接收消息。
这种架构是否可以轻松地将客户端集成到 GUI 中?如果是这样,处理来自客户端的输入的正确方法是什么?(就像我说的,我认为一旦我弄清楚了 Swing 的那个方面,向客户端输出就不会特别困难)。
我使用 Swing 的主要动机是 a)它似乎是 Java 文档中最好的 GUI 库,b)Netbeans 已经有使用 Swing 的工具,c)我有一个严格的截止日期,没有时间切换 GUI 库并从头开始(这是针对大学项目)。如果我有更多时间,我可能会研究其他图书馆,但我想他们也会有自己的怪癖。
import java.io.*;
import java.util.logging.*;
public class TestApp implements Runnable {
private Client <String> client = null;
private UpstreamChannel upstream = null;
private DownstreamChannel downstream = null;
private Thread upstreamThread = null;
private Thread downstreamThread = null;
private boolean ending = false;
private class UpstreamChannel implements Runnable {
private TestApp outer = null;
@Override
public void run () {
Thread.currentThread ().setName ("TestApp.UpstreamChannel");
try (BufferedReader inReader = new BufferedReader (new InputStreamReader (System.in))) {
while (!this.outer.ending) {
this.outer.client.acceptMessage (inReader.readLine ());
}
} catch (IOException ex) {
Logger.getLogger (this.getClass ().getName ()).log (Level.SEVERE, ex.getMessage (), ex);
} finally {
this.outer.ending = true;
this.outer.downstreamThread.interrupt ();
Thread.currentThread ().interrupt ();
return;
}
}
public UpstreamChannel (TestApp app) {
this.outer = app;
}
}
private class DownstreamChannel implements Runnable {
private TestApp outer = null;
@Override
public void run () {
Thread.currentThread ().setName ("TestApp.DownstreamChannel");
try {
while (!this.outer.ending) {
System.out.println (this.outer.client.getMessage ());
}
} catch (InterruptedException ex) {
Logger.getLogger (this.getClass ().getName ()).log (Level.INFO, ex.getMessage (), ex);
} finally {
this.outer.ending = true;
this.outer.upstreamThread.interrupt ();
Thread.currentThread ().interrupt ();
return;
}
}
public DownstreamChannel (TestApp app) {
this.outer = app;
}
}
@Override
public void run () {
if ((null == this.upstreamThread)
&& (null == this.downstreamThread)) {
this.upstreamThread = new Thread (this.upstream);
this.downstreamThread = new Thread (this.downstream);
this.upstreamThread.start ();
this.downstreamThread.start ();
try {
this.upstreamThread.join ();
this.downstreamThread.join ();
} catch (InterruptedException ex) {
Logger.getLogger (this.getClass ().getName ()).log (Level.INFO, ex.getMessage (), ex);
} finally {
this.upstreamThread.interrupt ();
this.downstreamThread.interrupt ();
System.out.println ("Sayonara");
}
}
}
public TestApp (Client <String> client) {
this.upstream = new UpstreamChannel (this);
this.downstream = new DownstreamChannel (this);
this.client = client;
Logger.getLogger (this.getClass ().getName ()).log (Level.INFO, "Class instantiated");
}
}
启动客户端应用的代码如下:
public static void main (String[] args) throws UnknownHostException, IOException, InterruptedException {
Client <String> clientInstance = new Client ("localhost", 8113);
TestApp app = new TestApp (clientInstance);
Thread clientThread = new Thread (clientInstance);
Thread appThread = new Thread (app);
clientThread.start ();
appThread.start ();
clientThread.join ();
appThread.interrupt ();
System.exit (0);
}
}
编辑:我认为轮询 getMessage (因此阻塞直到消息到达)并使用 publish() 使其可用的工作线程将是一个解决方案,但我认为如果两个消息有“掉在地上”的风险消息接二连三地到达。
SwingWorker reader = new SwingWorker () {
@Override
protected Object doInBackground () throws Exception {
while (!this.isCancelled ()) {
publish (clientInstance.getMessage ());
}
return null;
}
};