Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/312.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/hadoop/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Swing GUI客户端和服务器聊天应用程序文本区域未更新_Java_Swing_User Interface_Textarea - Fatal编程技术网

Java Swing GUI客户端和服务器聊天应用程序文本区域未更新

Java Swing GUI客户端和服务器聊天应用程序文本区域未更新,java,swing,user-interface,textarea,Java,Swing,User Interface,Textarea,我正在使用Java和Swing类为GUI开发一个聊天应用程序 ChatServer类将是从客户端接收消息并回显到所有客户端的服务器,但我只打算为2个客户端进行聊天 ChatClient类是两个客户端。它们在文本区域显示从服务器发送的内容。并将文本字段中的文本发送到服务器 聊天客户端类 package chatclient; import java.net.Socket; import java.io.BufferedReader; import java.io.DataInputStream;

我正在使用Java和Swing类为GUI开发一个聊天应用程序

ChatServer类将是从客户端接收消息并回显到所有客户端的服务器,但我只打算为2个客户端进行聊天

ChatClient类是两个客户端。它们在文本区域显示从服务器发送的内容。并将文本字段中的文本发送到服务器

聊天客户端类

package chatclient;

import java.net.Socket;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStreamReader;

public class ChatClient extends javax.swing.JFrame {


public ChatClient() {
    initComponents();
}

/**
 * This method is called from within the constructor to initialize the form.
 * WARNING: Do NOT modify this code. The content of this method is always
 * regenerated by the Form Editor.
 */
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">                          
private void initComponents() {

    scrollPane = new javax.swing.JScrollPane();
    textArea = new javax.swing.JTextArea();
    btnConnect = new javax.swing.JButton();
    btnDisconnect = new javax.swing.JButton();
    lblStatus = new javax.swing.JLabel();
    lblShowStatus = new javax.swing.JLabel();
    txtInput = new javax.swing.JTextField();

    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
    setTitle("Chat Client A");

    textArea.setEditable(false);
    textArea.setColumns(20);
    textArea.setRows(5);
    textArea.setText("Welcome to the Chat Server. Type '/close' or Click 'Disconnect' to close.");
    textArea.setWrapStyleWord(true);
    textArea.setCaretPosition(textArea.getDocument().getLength());
    scrollPane.setViewportView(textArea);

    btnConnect.setText("Connect");
    btnConnect.setActionCommand("btnConnect");
    btnConnect.addMouseListener(new java.awt.event.MouseAdapter() {
        public void mouseClicked(java.awt.event.MouseEvent evt) {
            btnConnectMouseClicked(evt);
        }
    });

    btnDisconnect.setText("Disconnect");
    btnDisconnect.setActionCommand("btnDisconnect");
    btnDisconnect.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnDisconnectActionPerformed(evt);
        }
    });

    lblStatus.setText("Status: ");

    lblShowStatus.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N
    lblShowStatus.setForeground(new java.awt.Color(255, 51, 51));
    lblShowStatus.setText("Disconnected");

    txtInput.setToolTipText("");
    txtInput.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            txtInputActionPerformed(evt);
        }
    });

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
    getContentPane().setLayout(layout);
    layout.setHorizontalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(layout.createSequentialGroup()
            .addContainerGap()
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(scrollPane)
                .addGroup(layout.createSequentialGroup()
                    .addComponent(btnConnect)
                    .addGap(18, 18, 18)
                    .addComponent(btnDisconnect)
                    .addGap(42, 42, 42)
                    .addComponent(lblStatus)
                    .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                    .addComponent(lblShowStatus)
                    .addGap(0, 42, Short.MAX_VALUE))
                .addComponent(txtInput))
            .addContainerGap())
    );
    layout.setVerticalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(layout.createSequentialGroup()
            .addContainerGap()
            .addComponent(scrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 213, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 11, Short.MAX_VALUE)
            .addComponent(txtInput, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                .addComponent(btnConnect)
                .addComponent(btnDisconnect)
                .addComponent(lblStatus)
                .addComponent(lblShowStatus))
            .addContainerGap())
    );

    pack();
}// </editor-fold>                        

private void btnConnectMouseClicked(java.awt.event.MouseEvent evt) {                                        
    // TODO add your handling code here:
    lblShowStatus.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N
    lblShowStatus.setForeground(new java.awt.Color(0, 204, 51));
    lblShowStatus.setText("Connected");

    // ADD CODES FOR CONNECTING TO CHAT SERVER

}                                       

private void btnDisconnectActionPerformed(java.awt.event.ActionEvent evt) {                                              
    // TODO add your handling code here:
    lblShowStatus.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N
    lblShowStatus.setForeground(new java.awt.Color(255, 51, 51));
    lblShowStatus.setText("Disconnected");

    // ADD CODES FOR DISCONNECTING FROM CHAT SERVER

}                                             

private void txtInputActionPerformed(java.awt.event.ActionEvent evt) {                                         
    // TODO add your handling code here:
}                                        

/**
 * @param args the command line arguments
 */
public static void main(String args[]) {
    /* Set the Nimbus look and feel */
    //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
    /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
     * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
     */
    try {
        for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
            if ("Nimbus".equals(info.getName())) {
                javax.swing.UIManager.setLookAndFeel(info.getClassName());
                break;
            }
        }
    } catch (ClassNotFoundException ex) {
        java.util.logging.Logger.getLogger(ChatClient.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (InstantiationException ex) {
        java.util.logging.Logger.getLogger(ChatClient.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (IllegalAccessException ex) {
        java.util.logging.Logger.getLogger(ChatClient.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (javax.swing.UnsupportedLookAndFeelException ex) {
        java.util.logging.Logger.getLogger(ChatClient.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    }
    //</editor-fold>      

    /* Create and display the form */
    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
            new ChatClient().setVisible(true);
        }
    });

    String input = "", serverInput = "";
    String host = "localhost";
    int port = 1337;
    Socket client;

    // updateTextArea("TEST UPDATE");

    try {
        client = new Socket(host, port);
        System.out.println("Connected to Server!");

        DataInputStream in = new DataInputStream(client.getInputStream());
        DataOutputStream out = new DataOutputStream(client.getOutputStream());

        System.out.println("Before setting text area");
        updateTextArea("trying to update");

        do {
            // HANDLE INPUT PART HERE
            serverInput = in.readUTF();


            if(serverInput != null) {
                System.out.println("Reached here");
                System.out.println(serverInput);
                updateTextArea(serverInput); 
            }

        } while(!input.equals("/close"));
        System.out.println("Program closed");
    }    

    catch(Exception exc) {
        System.err.println(exc.getMessage());
    }


}

private static void updateTextArea(String temp) {
    textArea.setText(textArea.getText() + "\n" + temp + "\n");
    textArea.setCaretPosition(textArea.getDocument().getLength());
}

// Variables declaration - do not modify                     
private javax.swing.JButton btnConnect;
private javax.swing.JButton btnDisconnect;
private javax.swing.JLabel lblShowStatus;
private javax.swing.JLabel lblStatus;
private javax.swing.JScrollPane scrollPane;
private static javax.swing.JTextArea textArea;
private javax.swing.JTextField txtInput;
// End of variables declaration                   
}
package chatserver;

import java.io.BufferedReader;
import java.io.InputStreamReader;
// for testing
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 *
 * @author wacats
 */
public class ChatServer {
public static void main(String args[]) {
    int port = 1337;

    try {
        ServerSocket server = new ServerSocket(port);
        String inMessage = "";

        while(true) {
            Socket clientA = server.accept();
            DataInputStream inA = new DataInputStream(clientA.getInputStream());
            DataOutputStream outA = new DataOutputStream(clientA.getOutputStream());
            // outA.writeUTF("Welcome to the Chat Server. Type '/close' or Click 'Disconnect' to close.");

            // for testing
            // BufferedReader user = new BufferedReader(new InputStreamReader(System.in));

            do {
                inMessage = inA.readUTF();

                outA.writeUTF("testing");

                if(inMessage != null) {
                   outA.writeUTF(inMessage); 
                }

            } while(!inMessage.equals("/close"));
            clientA.close();
        }



    }

    catch(Exception ex) {
        ex.printStackTrace();
    }
}
}
我对这个项目的过程的想法是:

  • 启动聊天服务器
  • 启动聊天客户端
  • 当两个客户端都连接到服务器时,他们就可以开始聊天了
  • 按“回车”键将文本字段中的文本发送到服务器
  • 服务器将向两个客户端广播文本
  • 客户端将更新附加从服务器接收的文本的文本区域

  • 基本问题是,
    textArea
    在您尝试调用
    updateTextArea
    时是
    null
    ,这是因为
    invokeLater
    调用尚未执行并构建您的UI,您基本上有竞争条件

    处理Swing中的
    Socket
    s的正常方法是使用
    SwingWorker

    查看和了解更多详细信息

    有很多方法可以解决你的问题。您可以使用
    SwingWorker
    从套接字读取文本并生成更新通知(通过
    publish
    /
    过程
    )方法,这通常被称为“观察者模式”。这很好,因为它将代码解耦,并生成一个更可重用的解决方案

    SocketReader
    这个类所做的就是从
    Socket
    读取文本,并从文本生成
    ActionEvent
    s,很简单

    public class SocketReader extends SwingWorker<Void, String> {
    
        private List<ActionListener> actionListeners;
    
        public SocketReader() {
            actionListeners = new ArrayList<>(25);
        }
    
        public void addActionListener(ActionListener listener) {
            actionListeners.add(listener);
        }
    
        public void removeActionListener(ActionListener listener) {
            actionListeners.remove(listener);
        }
    
        @Override
        protected Void doInBackground() throws Exception {
            System.out.println("Connected to Server!");
    
            try (DataInputStream in = new DataInputStream(SocketManager.INSTACNE.getInputStream())) {
    
                System.out.println("Before setting text area");
    
                String serverInput = null;
                do {
                    // HANDLE INPUT PART HERE
                    serverInput = in.readUTF();
    
                    if (serverInput != null) {
                        System.out.println("Read " + serverInput);
                        publish(serverInput);
                    }
    
                } while (!serverInput.equals("/close"));
                System.out.println("Program closed");
            }
            return null;
        }
    
        @Override
        protected void process(List<String> chunks) {
            for (String text : chunks) {
                ActionEvent evt = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, text);
                for (ActionListener listener : actionListeners) {
                    listener.actionPerformed(evt);
                }
            }
        }
    
    }
    
    SocketManager
    好吧,这对我来说有点过分了,但是我想要一个用于
    套接字的中央控制器,你不必使用单例,你可以简单地将它变成一个简单的类,并将它的引用传递给你的
    聊天客户端
    ,然后向下传递给
    SocketReader/Writer
    ,但是它很晚了,我很懒

    public enum SocketManager {
        INSTACNE;
    
        private String host = "localhost";
        private int port = 1337;
        private Socket socket;
    
        public Socket open() throws IOException {
            if (socket != null) {
                close();
            }
            socket = new Socket(host, port);
            return socket;
        }
    
        public void close() throws IOException {
            if (socket == null) {
                return;
            }
            socket.close();
        }
    
        public boolean isOpen() {
            return socket != null
                && socket.isConnected()
                && !socket.isClosed()
                && !socket.isInputShutdown()
                && !socket.isOutputShutdown();
        }
    
        public InputStream getInputStream() throws IOException {
            Objects.requireNonNull(socket, "Socket is not open");
            return socket.getInputStream();
        }
    
        public OutputStream getOutputStream() throws IOException {
            Objects.requireNonNull(socket, "Socket is not open");
            return socket.getOutputStream();
        }
    }
    
    ChatClient
    这很好,非常棒,但是你打算怎么用呢

    基本上,您将在
    ChatClient
    中创建
    SocketReader
    SocketWriter
    的实例,将
    ActionListener
    附加到读取器,并在触发时更新
    JTextArea
    ,并将您想要发送的文本发送到
    SocketWriter
    ,例如

    public class ChatClient extends javax.swing.JFrame {
    
        public ChatClient() {
            initComponents();
            socketReader = new SocketReader();
            socketReader.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    String text = e.getActionCommand();
                    textArea.append(text);
                    textArea.append("\n");
                    textArea.setCaretPosition(textArea.getDocument().getLength());
                }
            });
            socketReader.execute();
    
            socketWriter = new SocketWriter();
            socketWriter.execute();
        }
    
        /**
         * This method is called from within the constructor to initialize the form.
         * WARNING: Do NOT modify this code. The content of this method is always
         * regenerated by the Form Editor.
         */
        @SuppressWarnings("unchecked")
        // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
        private void initComponents() {
    
            scrollPane = new javax.swing.JScrollPane();
            textArea = new javax.swing.JTextArea();
            btnConnect = new javax.swing.JButton();
            btnDisconnect = new javax.swing.JButton();
            lblStatus = new javax.swing.JLabel();
            lblShowStatus = new javax.swing.JLabel();
            txtInput = new javax.swing.JTextField();
    
            setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
            setTitle("Chat Client A");
    
            textArea.setEditable(false);
            textArea.setColumns(20);
            textArea.setRows(5);
            textArea.setText("Welcome to the Chat Server. Type '/close' or Click 'Disconnect' to close.");
            textArea.setWrapStyleWord(true);
            textArea.setCaretPosition(textArea.getDocument().getLength());
            scrollPane.setViewportView(textArea);
    
            btnConnect.setText("Connect");
            btnConnect.setActionCommand("btnConnect");
            btnConnect.addMouseListener(new java.awt.event.MouseAdapter() {
                public void mouseClicked(java.awt.event.MouseEvent evt) {
                    btnConnectMouseClicked(evt);
                }
            });
    
            btnDisconnect.setText("Disconnect");
            btnDisconnect.setActionCommand("btnDisconnect");
            btnDisconnect.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    btnDisconnectActionPerformed(evt);
                }
            });
    
            lblStatus.setText("Status: ");
    
            lblShowStatus.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N
            lblShowStatus.setForeground(new java.awt.Color(255, 51, 51));
            lblShowStatus.setText("Disconnected");
    
            txtInput.setToolTipText("");
            txtInput.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    txtInputActionPerformed(evt);
                }
            });
    
            javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
            getContentPane().setLayout(layout);
            layout.setHorizontalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup()
                    .addContainerGap()
                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                        .addComponent(scrollPane)
                        .addGroup(layout.createSequentialGroup()
                            .addComponent(btnConnect)
                            .addGap(18, 18, 18)
                            .addComponent(btnDisconnect)
                            .addGap(42, 42, 42)
                            .addComponent(lblStatus)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(lblShowStatus)
                            .addGap(0, 42, Short.MAX_VALUE))
                        .addComponent(txtInput))
                    .addContainerGap())
            );
            layout.setVerticalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup()
                    .addContainerGap()
                    .addComponent(scrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 213, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 11, Short.MAX_VALUE)
                    .addComponent(txtInput, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                        .addComponent(btnConnect)
                        .addComponent(btnDisconnect)
                        .addComponent(lblStatus)
                        .addComponent(lblShowStatus))
                    .addContainerGap())
            );
    
            pack();
        }// </editor-fold>                        
    
        private void btnConnectMouseClicked(java.awt.event.MouseEvent evt) {
            // TODO add your handling code here:
            lblShowStatus.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N
            lblShowStatus.setForeground(new java.awt.Color(0, 204, 51));
            lblShowStatus.setText("Connected");
    
            // ADD CODES FOR CONNECTING TO CHAT SERVER
        }
    
        private void btnDisconnectActionPerformed(java.awt.event.ActionEvent evt) {
            // TODO add your handling code here:
            lblShowStatus.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N
            lblShowStatus.setForeground(new java.awt.Color(255, 51, 51));
            lblShowStatus.setText("Disconnected");
    
            // ADD CODES FOR DISCONNECTING FROM CHAT SERVER
        }
    
        private void txtInputActionPerformed(java.awt.event.ActionEvent evt) {
            if (SocketManager.INSTACNE.isOpen()) {
                socketWriter.write(txtInput.getText());
            } else {
                System.out.println("!! Not open");
            }
        }
    
        /**
         * @param args the command line arguments
         */
        public static void main(String args[]) {
    
            try {
                /* Set the Nimbus look and feel */
                //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
                /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
                * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
                 */
                try {
                    for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                        if ("Nimbus".equals(info.getName())) {
                            javax.swing.UIManager.setLookAndFeel(info.getClassName());
                            break;
                        }
                    }
                } catch (ClassNotFoundException ex) {
                    java.util.logging.Logger.getLogger(ChatClient.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
                } catch (InstantiationException ex) {
                    java.util.logging.Logger.getLogger(ChatClient.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
                } catch (IllegalAccessException ex) {
                    java.util.logging.Logger.getLogger(ChatClient.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
                } catch (javax.swing.UnsupportedLookAndFeelException ex) {
                    java.util.logging.Logger.getLogger(ChatClient.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
                }
                //</editor-fold>
    
                SocketManager.INSTACNE.open();
    
                /* Create and display the form */
                java.awt.EventQueue.invokeLater(new Runnable() {
                    public void run() {
                        new ChatClient().setVisible(true);
                    }
                });
    
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            //</editor-fold>      
    
        }
    
        private SocketWriter socketWriter;
        private SocketReader socketReader;
    
        // Variables declaration - do not modify                     
        private javax.swing.JButton btnConnect;
        private javax.swing.JButton btnDisconnect;
        private javax.swing.JLabel lblShowStatus;
        private javax.swing.JLabel lblStatus;
        private javax.swing.JScrollPane scrollPane;
        private javax.swing.JTextArea textArea;
        private javax.swing.JTextField txtInput;
        // End of variables declaration                   
    }
    
    通常,当客户端连接时,您会启动一个新的
    线程
    ,并让它处理客户端
    套接字
    ,但这不是我的重点


    因此,基于所有这些,你有很多阅读需要跟上,包括和

    基本问题是,
    textArea
    null
    当你尝试调用
    updateTextArea
    ,这是因为
    invokeLater
    调用还没有执行并构建你的UI,你基本上有一个竞争条件

    处理Swing中的
    Socket
    s的正常方法是使用
    SwingWorker

    查看和了解更多详细信息

    有很多方法可以解决你的问题。您可以使用
    SwingWorker
    从套接字读取文本并生成更新通知(通过
    publish
    /
    过程
    )方法,这通常被称为“观察者模式”。这很好,因为它将代码解耦,并生成一个更可重用的解决方案

    SocketReader
    这个类所做的就是从
    Socket
    读取文本,并从文本生成
    ActionEvent
    s,很简单

    public class SocketReader extends SwingWorker<Void, String> {
    
        private List<ActionListener> actionListeners;
    
        public SocketReader() {
            actionListeners = new ArrayList<>(25);
        }
    
        public void addActionListener(ActionListener listener) {
            actionListeners.add(listener);
        }
    
        public void removeActionListener(ActionListener listener) {
            actionListeners.remove(listener);
        }
    
        @Override
        protected Void doInBackground() throws Exception {
            System.out.println("Connected to Server!");
    
            try (DataInputStream in = new DataInputStream(SocketManager.INSTACNE.getInputStream())) {
    
                System.out.println("Before setting text area");
    
                String serverInput = null;
                do {
                    // HANDLE INPUT PART HERE
                    serverInput = in.readUTF();
    
                    if (serverInput != null) {
                        System.out.println("Read " + serverInput);
                        publish(serverInput);
                    }
    
                } while (!serverInput.equals("/close"));
                System.out.println("Program closed");
            }
            return null;
        }
    
        @Override
        protected void process(List<String> chunks) {
            for (String text : chunks) {
                ActionEvent evt = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, text);
                for (ActionListener listener : actionListeners) {
                    listener.actionPerformed(evt);
                }
            }
        }
    
    }
    
    SocketManager
    好吧,这对我来说有点过分了,但是我想要一个用于
    套接字的中央控制器,你不必使用单例,你可以简单地将它变成一个简单的类,并将它的引用传递给你的
    聊天客户端
    ,然后向下传递给
    SocketReader/Writer
    ,但是它很晚了,我很懒

    public enum SocketManager {
        INSTACNE;
    
        private String host = "localhost";
        private int port = 1337;
        private Socket socket;
    
        public Socket open() throws IOException {
            if (socket != null) {
                close();
            }
            socket = new Socket(host, port);
            return socket;
        }
    
        public void close() throws IOException {
            if (socket == null) {
                return;
            }
            socket.close();
        }
    
        public boolean isOpen() {
            return socket != null
                && socket.isConnected()
                && !socket.isClosed()
                && !socket.isInputShutdown()
                && !socket.isOutputShutdown();
        }
    
        public InputStream getInputStream() throws IOException {
            Objects.requireNonNull(socket, "Socket is not open");
            return socket.getInputStream();
        }
    
        public OutputStream getOutputStream() throws IOException {
            Objects.requireNonNull(socket, "Socket is not open");
            return socket.getOutputStream();
        }
    }
    
    ChatClient
    这很好,非常棒,但是你打算怎么用呢

    基本上,您将在
    ChatClient
    中创建
    SocketReader
    SocketWriter
    的实例,将
    ActionListener
    附加到读取器,并在触发时更新
    JTextArea
    ,并将您想要发送的文本发送到
    SocketWriter
    ,例如

    public class ChatClient extends javax.swing.JFrame {
    
        public ChatClient() {
            initComponents();
            socketReader = new SocketReader();
            socketReader.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    String text = e.getActionCommand();
                    textArea.append(text);
                    textArea.append("\n");
                    textArea.setCaretPosition(textArea.getDocument().getLength());
                }
            });
            socketReader.execute();
    
            socketWriter = new SocketWriter();
            socketWriter.execute();
        }
    
        /**
         * This method is called from within the constructor to initialize the form.
         * WARNING: Do NOT modify this code. The content of this method is always
         * regenerated by the Form Editor.
         */
        @SuppressWarnings("unchecked")
        // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
        private void initComponents() {
    
            scrollPane = new javax.swing.JScrollPane();
            textArea = new javax.swing.JTextArea();
            btnConnect = new javax.swing.JButton();
            btnDisconnect = new javax.swing.JButton();
            lblStatus = new javax.swing.JLabel();
            lblShowStatus = new javax.swing.JLabel();
            txtInput = new javax.swing.JTextField();
    
            setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
            setTitle("Chat Client A");
    
            textArea.setEditable(false);
            textArea.setColumns(20);
            textArea.setRows(5);
            textArea.setText("Welcome to the Chat Server. Type '/close' or Click 'Disconnect' to close.");
            textArea.setWrapStyleWord(true);
            textArea.setCaretPosition(textArea.getDocument().getLength());
            scrollPane.setViewportView(textArea);
    
            btnConnect.setText("Connect");
            btnConnect.setActionCommand("btnConnect");
            btnConnect.addMouseListener(new java.awt.event.MouseAdapter() {
                public void mouseClicked(java.awt.event.MouseEvent evt) {
                    btnConnectMouseClicked(evt);
                }
            });
    
            btnDisconnect.setText("Disconnect");
            btnDisconnect.setActionCommand("btnDisconnect");
            btnDisconnect.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    btnDisconnectActionPerformed(evt);
                }
            });
    
            lblStatus.setText("Status: ");
    
            lblShowStatus.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N
            lblShowStatus.setForeground(new java.awt.Color(255, 51, 51));
            lblShowStatus.setText("Disconnected");
    
            txtInput.setToolTipText("");
            txtInput.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    txtInputActionPerformed(evt);
                }
            });
    
            javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
            getContentPane().setLayout(layout);
            layout.setHorizontalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup()
                    .addContainerGap()
                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                        .addComponent(scrollPane)
                        .addGroup(layout.createSequentialGroup()
                            .addComponent(btnConnect)
                            .addGap(18, 18, 18)
                            .addComponent(btnDisconnect)
                            .addGap(42, 42, 42)
                            .addComponent(lblStatus)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(lblShowStatus)
                            .addGap(0, 42, Short.MAX_VALUE))
                        .addComponent(txtInput))
                    .addContainerGap())
            );
            layout.setVerticalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup()
                    .addContainerGap()
                    .addComponent(scrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 213, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 11, Short.MAX_VALUE)
                    .addComponent(txtInput, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                        .addComponent(btnConnect)
                        .addComponent(btnDisconnect)
                        .addComponent(lblStatus)
                        .addComponent(lblShowStatus))
                    .addContainerGap())
            );
    
            pack();
        }// </editor-fold>                        
    
        private void btnConnectMouseClicked(java.awt.event.MouseEvent evt) {
            // TODO add your handling code here:
            lblShowStatus.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N
            lblShowStatus.setForeground(new java.awt.Color(0, 204, 51));
            lblShowStatus.setText("Connected");
    
            // ADD CODES FOR CONNECTING TO CHAT SERVER
        }
    
        private void btnDisconnectActionPerformed(java.awt.event.ActionEvent evt) {
            // TODO add your handling code here:
            lblShowStatus.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N
            lblShowStatus.setForeground(new java.awt.Color(255, 51, 51));
            lblShowStatus.setText("Disconnected");
    
            // ADD CODES FOR DISCONNECTING FROM CHAT SERVER
        }
    
        private void txtInputActionPerformed(java.awt.event.ActionEvent evt) {
            if (SocketManager.INSTACNE.isOpen()) {
                socketWriter.write(txtInput.getText());
            } else {
                System.out.println("!! Not open");
            }
        }
    
        /**
         * @param args the command line arguments
         */
        public static void main(String args[]) {
    
            try {
                /* Set the Nimbus look and feel */
                //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
                /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
                * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
                 */
                try {
                    for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                        if ("Nimbus".equals(info.getName())) {
                            javax.swing.UIManager.setLookAndFeel(info.getClassName());
                            break;
                        }
                    }
                } catch (ClassNotFoundException ex) {
                    java.util.logging.Logger.getLogger(ChatClient.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
                } catch (InstantiationException ex) {
                    java.util.logging.Logger.getLogger(ChatClient.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
                } catch (IllegalAccessException ex) {
                    java.util.logging.Logger.getLogger(ChatClient.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
                } catch (javax.swing.UnsupportedLookAndFeelException ex) {
                    java.util.logging.Logger.getLogger(ChatClient.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
                }
                //</editor-fold>
    
                SocketManager.INSTACNE.open();
    
                /* Create and display the form */
                java.awt.EventQueue.invokeLater(new Runnable() {
                    public void run() {
                        new ChatClient().setVisible(true);
                    }
                });
    
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            //</editor-fold>      
    
        }
    
        private SocketWriter socketWriter;
        private SocketReader socketReader;
    
        // Variables declaration - do not modify                     
        private javax.swing.JButton btnConnect;
        private javax.swing.JButton btnDisconnect;
        private javax.swing.JLabel lblShowStatus;
        private javax.swing.JLabel lblStatus;
        private javax.swing.JScrollPane scrollPane;
        private javax.swing.JTextArea textArea;
        private javax.swing.JTextField txtInput;
        // End of variables declaration                   
    }
    
    通常,当客户端连接时,您会启动一个新的
    线程
    ,并让它处理客户端
    套接字
    ,但这不是我的重点


    因此,基于所有这些,您有很多阅读需要跟上,包括和

    您需要更新Swing线程中的文本区域。您可能需要调用由方法
    SwingUtilities.invokeLater
    包装的方法
    updateextarea
    和void
    static
    引用;使用
    append
    over
    setText
    您的
    updateextarea
    中有
    NullPointerException
    ,因为在UI建立之前,允许从服务器读取/写入内容的循环运行change
    System.err.println(exc.getMessage())
    exc.printStackTrace()您将看到why@MadProgrammer如何在不使用static的情况下访问文本区域?您需要在Swing线程中更新文本区域。您可能需要调用由方法
    SwingUtilities.invokeLater
    包装的方法
    updateextarea
    和void
    static
    引用;使用
    append
    over
    setText
    您的
    updateextarea
    中有
    NullPointerException
    ,因为在UI建立之前允许从服务器读/写内容的循环运行change
    System.err.println(exc.getMessage())