【问题标题】:Making a JButton print a "connecting.." message to a JTextArea whilst attempting to connect to a socket使 JButton 在尝试连接到套接字时向 JTextArea 打印“正在连接..”消息
【发布时间】:2021-03-02 13:56:09
【问题描述】:

我目前正在制作一个简单的聊天服务器程序,该程序可以同时处理多个连接的客户端(以创建一个基本的聊天室),我遇到了一个相当小但令人讨厌的问题,我似乎无法解决破解。

我有一个 JButton,当单击它时,它会检索用户在各自 JTextField 中输入的 IP 地址、端口号和名称。然后将这些值传递给 Socket 构造函数以尝试打开连接(名称被发送到其他地方并且仅标识服务器上的用户)。我试图做的是在文本区域中显示“正在连接...”消息,然后是成功的连接报告或错误(如果抛出)。但是,在实践中——如果我故意输入错误的 IP(强制抛出错误)——在抛出错误之前不会将文本附加到文本区域;即所有内容一次附加。

以下是单击按钮时运行的代码:

private void submitBtnClicked() {
        String hostName = ipJtf.getText();
        int port = Integer.parseInt(portJtf.getText());
        String name = nameJtf.getText();

        if (!name.trim().equals("")) {

            // This is where I want the code to print "Connecting..."
            jta.setText("");
            jta.append("Connecting...\n");

            try {

                socket = new Socket(hostName, port);
                jta.append("Connected to server on: " + hostName + " : " + port + "\n");

                writer = new PrintWriter(socket.getOutputStream(), true);
                writer.println(name);

                new ReadThread(socket, this).start();
                new WriteThread(socket, this).start();

            }
            catch (UnknownHostException ex) {
                jta.append("Server not found: " + ex.getMessage() + "\n");
            }
            catch (IOException e) {
                jta.append("I/O Error: " + e.getMessage() + "\n");
            }
        }
        else {
            jta.setText("");
            jta.append("Please enter a user name before connecting.\n");
        }
    }

所以当代码运行时,它当前会追加:

Connecting...
I/O Error: Connection refused: connect

所有同时,而不是先打印“正在连接...”,然后再打印错误,当它不成功时。

我是编程方案的初学者,所以我确信这只是我不了解或在我的学习中还没有遇到的一些基本知识的结果,但我到现在还真的没找到解决办法。我怀疑这更多是因为我无法弄清楚要问什么正确的问题,因为我确信之前一定有人遇到过这个问题并且在 stackoverflow 上得到了很好的答案;如果是这样的话,我将非常感谢您指出它。

在此先感谢您在此问题上提供的所有帮助。

P.S:我知道代码在当前状态下有点混乱 - 这主要是因为我在构建应用程序时测试了各种东西。

用于重现问题的附加代码

客户端.java

package ChatServerV2.Client;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.*;

public class Client extends JFrame {

    private JTextField ipJtf = new JTextField(13);
    private JTextField portJtf = new JTextField(4);
    private JTextField nameJtf = new JTextField(15);
    private JTextField messageJtf = new JTextField();
    private JTextArea jta = new JTextArea();
    private Socket socket;

    private String hostName;
    private int port;
    private String userName;

    // IO streams will go here
    private PrintWriter writer;

    public static void main(String[] args) {
        new Client();
    }

    public Client() {
        initUi();



    }

    // Builds the client UI and sets it as visible
    private void initUi() {
        JPanel p = new JPanel();
        p.setLayout(new BorderLayout());
        p.add(new JLabel("IP and Port: "), BorderLayout.WEST);
        p.add(ipJtf, BorderLayout.CENTER);
        p.add(portJtf, BorderLayout.EAST);
        // Add jtf listeners

        JPanel p2 = new JPanel();
        p2.setLayout(new BorderLayout());
        p2.add(new JLabel("Name: "), BorderLayout.WEST);
        p2.add(nameJtf, BorderLayout.CENTER);
        // Add jtf listener

        JPanel p5 = new JPanel();
        p5.setLayout(new BorderLayout());
        JButton btn = new JButton("Submit");
        btn.addActionListener((ActionEvent e) -> {
            submitBtnClicked();
        });
        p5.add(btn, BorderLayout.EAST);

        JPanel p4 = new JPanel();
        p4.setLayout(new BorderLayout());
        p4.add(p, BorderLayout.NORTH);
        p4.add(p2, BorderLayout.SOUTH);

        JPanel p6 = new JPanel(new BorderLayout());
        // p6.setLayout(new BorderLayout());
        p6.add(p4, BorderLayout.CENTER);
        p6.add(p5, BorderLayout.EAST);

        JPanel p3 = new JPanel();
        p3.setLayout(new BorderLayout());
        p3.add(new JLabel("Message: "), BorderLayout.WEST);
        messageJtf.addActionListener((ActionEvent e) -> {
            sendingAMessage();
        });
        p3.add(messageJtf, BorderLayout.CENTER);
        // Add jtf listener

        setLayout(new BorderLayout());
        add(p6, BorderLayout.NORTH);
        add(new JScrollPane(jta), BorderLayout.CENTER);
        jta.setEditable(false);
        add(p3, BorderLayout.SOUTH);

        setTitle("Client");
        setSize(500, 300);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);

    }

    /** Writes a message locally to the client's window */
    public void clientWrite(String message) {
        jta.append(message + "\n");
    }

    private void submitBtnClicked() {
        String hostName = ipJtf.getText();
        int port = Integer.parseInt(portJtf.getText());
        String name = nameJtf.getText();

        if (!name.trim().equals("")) {

            jta.setText("");
            jta.append(javax.swing.SwingUtilities.isEventDispatchThread() + "\n");
            jta.append("Connecting...\n");
            try {

                socket = new Socket(hostName, port);
                jta.append("Connected to server on: " + hostName + " : " + port + "\n");

                writer = new PrintWriter(socket.getOutputStream(), true);
                writer.println(name);

                new ReadThread(socket, this).start();
                new WriteThread(socket, this).start();

            }
            catch (UnknownHostException ex) {
                jta.append("Server not found: " + ex.getMessage() + "\n");
            }
            catch (IOException e) {
                jta.append("I/O Error: " + e.getMessage() + "\n");
            }
        }
        else {
            jta.setText("");
            jta.append("Please enter a user name before connecting.\n");
        }
    }

    private void sendingAMessage() {

    }
}

ReadThread.java

package ChatServerV2.Client;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;


public class ReadThread extends Thread {
    private BufferedReader reader;
    private Socket socket;
    private Client client;

    public ReadThread(Socket socket, Client client) {
        this.socket = socket;
        this.client = client;

        try {
            InputStream input = socket.getInputStream();
            reader = new BufferedReader(new InputStreamReader(input));
        }
        catch (IOException e) {
            client.clientWrite("Error getting input stream: " + e.getMessage());
        }
    }

    public void run() {
        while (true) {
            try {
                String response = reader.readLine();
                client.clientWrite(response);
            }
            catch (IOException e) {
                client.clientWrite("Error reading from server: " + e.getMessage());
                break;
            }
        }
    }
}

WriteThread.java

package ChatServerV2.Client;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;


public class WriteThread extends Thread {
    private PrintWriter writer;
    private Socket socket;
    private Client client;

    public WriteThread(Socket socket, Client client) {
        this.socket = socket;
        this.client = client;

        try {
            OutputStream output = socket.getOutputStream();
            writer = new PrintWriter(output);
        }
        catch (IOException e) {
            client.clientWrite("Error getting output stream: " + e.getMessage());
        }
    }

    public void run() {

    }
}

如前所述,这是一个初学者正在进行的工作,所以如果它看起来很糟糕,那是因为它是。

【问题讨论】:

  • 我猜你在 EDT 上运行了长连接过程。应该避免这种情况。见Concurrency in Swing。如需更多帮助,请发帖minimal reproducible example,这样我们就不必猜测了。
  • @c0der 我将添加使客户端工作所需的所有三个类文件——我只是没有这样做,因为对于一个非常具体的问题来说感觉太混乱了。我知道没有它就很难诊断,所以我会做出改变。我还通过使用 javax.swing.SwingUtilities.isEventDispatchThread() 确认它正在 EDT 上运行。
  • 请注意 MRE 的 M。 Mre 并不意味着 post-all-code,而是重现问题所需的最低限度。还有什么是重现错误的输入?
  • @c0der 例如,在 IP 字段中输入 127.0.0.1,在端口字段中输入 4444,然后在名称字段中输入 James - 只要失败(即没有成功连接到套接字)。老实说,就算成功了也应该有同样的问题,但是失败的时候比较明显,因为有停顿。

标签: java swing actionlistener


【解决方案1】:

为了更好地演示查看问题(和解决方案),请向ReadThread 添加延迟:

    try {
        TimeUnit.SECONDS.wait(1); //todo remove after testing 
        InputStream input = socket.getInputStream();
        reader = new BufferedReader(new InputStreamReader(input));
    }
    catch (IOException | InterruptedException e) {
        client.clientWrite("Error getting input stream: " + e.getMessage());
    }  

运行Client 并查看问题是否变得明显。 要修复它,您需要对EDT 进行任何长时间的处理。
一种简单的方法是在不同的线程上运行长进程。

以下是submitBtnClicked修改为这样做:

  private void submitBtnClicked() {
        String hostName = ipJtf.getText();
        int port = Integer.parseInt(portJtf.getText());
        String name = nameJtf.getText();

        if (!name.trim().equals("")) {

            jta.setText("");
            jta.append(javax.swing.SwingUtilities.isEventDispatchThread() + "\n");
            jta.append("Connecting...\n");
            new Thread(()->{//take long process off the EDT 
                  try {

                      socket = new Socket(hostName, port);
                      //Modifying a Swing component must be done by the EDT 
                      SwingUtilities.invokeLater(()-> jta.append("Connected to server on: " + hostName + " : " + port + "\n"));

                      writer = new PrintWriter(socket.getOutputStream(), true);
                      writer.println(name);

                      new ReadThread(socket, this).start();
                      new WriteThread(socket, this).start();

                  }
                  catch (UnknownHostException ex) {
                       SwingUtilities.invokeLater(()-> jta.append("Server not found: " + ex.getMessage() + "\n"));
                  }
                  catch (IOException e) {
                      SwingUtilities.invokeLater(()-> jta.append("I/O Error: " + e.getMessage() + "\n"));
                  }
            }).start();
        }
        else {
            jta.setText("");
            jta.append("Please enter a user name before connecting.\n");
        }
    }

(在线运行here)

【讨论】:

  • 我会使用SwingWorker,可能与PropertyChangeListener结合使用。
  • @Abra 我同意SwingWorker 是这项工作的工具。为了清晰和简单,这里使用了一个简单的线程。发布基于SwingWorker 的解决方案,我的支持是有保证的。
  • 这项工作做得非常出色——感谢@c0der 的所有努力和帮助;我很抱歉让它变得比需要的更难。我也有兴趣看到SwingWorker 解决方案,因为它似乎是解决它的“正确”方式。
  • 是的。但我认为使用简单的线程(这是一个合法的解决方案)更容易理解问题和解决方案。 SwingWorker 使用起来有点复杂。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-10
  • 1970-01-01
  • 2017-02-20
  • 2018-07-11
  • 1970-01-01
相关资源
最近更新 更多