无法更新文本区域

时间:2018-05-17 17:31:04

标签: java multithreading swing network-programming

这是我关于StackOverflow的第一个问题,所以我希望我的问题不是太模糊或格式错误。

我正在尝试使用Netbeans Swing GUI构建器使用Swing内置的GUI构建Java桌面聊天应用程序。有一个托管聊天的服务器,而多个客户端可以连接并离开聊天。对于连接到服务器的每个客户端,我创建一个新线程来处理与相应客户端的所有通信。当客户端发送消息时,它首先进入线程读取它的服务器,更新聊天记录(textarea),然后通过调用write()将消息写入所有其他客户端。

以下是服务器代码:

import java.io.*;
import java.net.*;

public class server extends javax.swing.JFrame implements Runnable
{

    static Socket sock[]=new Socket[100];                       //Array of sockets, 1 for each accept
    static server obj[]=new server[100];                        //Array of server objects, 1 for each thread
    static Thread threads[]=new Thread[100];                    //Array of threads, 1 for each client
    static boolean conn_state[]=new boolean[100];               //Array of boolean values depicting whether connection is closed or not
    static DataOutputStream dos[]=new DataOutputStream[100];    //Array of DataOutputStreams, 1 for each client
    static InetAddress ipaddr[]=new InetAddress[100];           //Array to hold InetAddress objects of each client
    static int port[]=new int[100];                             //Array to hold port numbers
    static int i=0;                                             //No. of clients
    static int except=0;                                        //Marks client no. who sent last message
    static int count=0;                                         //Total no. of open connections at any time
    static boolean flag=false;                                  //Currently unused
    static String str;                                          //To hold message sent by a client
    public server() {
        initComponents();
        setLocationRelativeTo(null);
    }

    /**
     * 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() {

        jLabel1 = new javax.swing.JLabel();
        jButton1 = new javax.swing.JButton();
        jButton2 = new javax.swing.JButton();
        jScrollPane1 = new javax.swing.JScrollPane();
        jTextArea1 = new javax.swing.JTextArea();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setResizable(false);

        jLabel1.setText("Chat Log :");

        jButton1.setBackground(new java.awt.Color(204, 204, 255));
        jButton1.setText("Stop Server");
        jButton1.setToolTipText("Stop the server hosting the chat.");
        jButton1.setPreferredSize(new java.awt.Dimension(120, 25));

        jButton2.setText("Print Chat Log");
        jButton2.setPreferredSize(new java.awt.Dimension(120, 25));

        jScrollPane1.setAutoscrolls(true);

        jTextArea1.setColumns(20);
        jTextArea1.setRows(5);
        jScrollPane1.setViewportView(jTextArea1);

        javax.swing.GroupLayout layout = new 
 javax.swing.GroupLayout(getContentPane());
    getContentPane().setLayout(layout);
    layout.setHorizontalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(layout.createSequentialGroup()
            .addGap(52, 52, 52)
            .addComponent(jButton1, javax.swing.GroupLayout.PREFERRED_SIZE, 120, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addComponent(jButton2, javax.swing.GroupLayout.PREFERRED_SIZE, 120, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addGap(52, 52, 52))
        .addGroup(layout.createSequentialGroup()
            .addGap(22, 22, 22)
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 356, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 118, javax.swing.GroupLayout.PREFERRED_SIZE))
            .addContainerGap(22, Short.MAX_VALUE))
    );
    layout.setVerticalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(layout.createSequentialGroup()
            .addContainerGap()
            .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
            .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 179, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 34, Short.MAX_VALUE)
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                .addComponent(jButton1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addComponent(jButton2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
            .addGap(34, 34, 34))
    );

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

    public static void serve()
    {
        System.out.println("Server Program Running");
        try
        {
            ServerSocket ss =new ServerSocket(50000);
            while(true)
            {
                sock[i]=ss.accept();        
                System.out.println("Client with IP Address :- "+sock[i].getInetAddress().getHostAddress()+":"+sock[i].getPort()+" connected.");
                obj[i]=new server();
                threads[i]=new Thread(obj[i]);
                conn_state[i]=true;
                dos[i]=new DataOutputStream(sock[i].getOutputStream());
                ipaddr[i]=sock[i].getInetAddress();
                port[i]=sock[i].getPort();
                count++;
                i++;
                threads[i-1].start();   //A new thread is created for each client to read all incoming messages and write it out to every other client
            }
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }
    }
    @Override
    public void run()
    {
        try
        {
            int k=i-1;  //Marks client no.

            //TextArea does not get updated by the following lines
            jTextArea1.append("Client with IP Address :- "+sock[k].getInetAddress().getHostAddress()+":"+sock[k].getPort()+" connected.");
            jTextArea1.update(jTextArea1.getGraphics());

            DataInputStream dis = new DataInputStream(sock[k].getInputStream());
            while(true)
            {
                //System.out.println("k="+k);
                str=dis.readUTF();
                if(str.equalsIgnoreCase("exit"))
                {
                    //If a client types exit, the server closes all socket and streams of that client and writes out thst the client has left
                    dis.close();
                    conn_state[k]=false;
                    sock[k].close();
                    count--;
                    str="Client with ip ="+(ipaddr[k].getHostAddress())+" and port no. = "+port[k]+" left the chat.";
                    flag=true;
                    write();
                    if(count!=0)       //When open connections = 0, the server shuts down
                    return;
                    else
                    System.exit(0);
                }
                except=k;
                str=ipaddr[k].getHostAddress()+":"+port[k]+" :- "+str;
                System.out.println(str);

                //The text area does not get updated
                jTextArea1.setText(jTextArea1.getText()+str);
                jTextArea1.repaint();

                write();    //Function to write out "str" to all other clients
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }
    public void write()
    {
        int c;
        for(c=0;c<i;c++)
        {
            //System.out.println(c);
            if(c==except||conn_state[c]==false)     //If c=current sender's client no. or connection is closed then do not write to it
                continue;
            try
            {
                /*if(!flag)
                    str="From IP Address :- "+ipaddr[except].getHostAddress()+":"+port[except]+"\nMessage : "+str;*/
                dos[c].writeUTF(str);           //Writing "str" to every client other than except
                dos[c].flush();
                //dos.close();
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }
    }
    /**
    * @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(server.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (InstantiationException ex) {
        java.util.logging.Logger.getLogger(server.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (IllegalAccessException ex) {
        java.util.logging.Logger.getLogger(server.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (javax.swing.UnsupportedLookAndFeelException ex) {
        java.util.logging.Logger.getLogger(server.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 server().setVisible(true);
        }
    });
    serve();    //Main job done by server
}

// Variables declaration - do not modify                     
private javax.swing.JButton jButton1;
private javax.swing.JButton jButton2;
private javax.swing.JLabel jLabel1;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTextArea jTextArea1;
// End of variables declaration                   

}

现在,当我作为客户端连接到服务器时,“客户端已连接”消息,客户端发送的所有消息都正确输出到控制台,但文本区域中没有任何内容。客户端代码只包括连接到服务器,然后运行2个线程,一个用于读取,一个用于写入。

当我在C中实现了这么多客户端 - 服务器模型时,没有任何GUI,它运行良好。当所有输出都打印到控制台而不是GUI时,我甚至设法用Java做同样的工作。当我尝试包含GUI时,GUI上没有任何内容。谷歌搜索我的问题引发了关于StackOverflow的问题,我从中了解到Swing组件只能被1个名为EDT的线程改变,而其他任何线程都无法改变它们。他们建议使用

SwingUtilities.InvokeLater(new Runnable() {
    public void run() {
    JTextArea1.setText(jTextArea1.getText()+str);
    }
});

但是我已经尝试了这个但它仍然不起作用。

如果您愿意调查此事,我将非常感激。如果代码过于集中,不洁或充满不良做法,我感到非常抱歉。我仍然是编码甚至是Java的新手,我欢迎任何反馈。

1 个答案:

答案 0 :(得分:1)

对不起,我的第一次分析还没有完成。

您看到的JFrame是在main方法中创建的server。然而,没有任何线程可以将某些东西写入它的textarea,因为你没有保留对框架或textarea的引用。

您创建的每个JFrame实例都会在JTextAreasetVisible(true);上创建,但这些实例永远不会在屏幕上显示(您永远不会调用ServerCommunication在这些框架上)。

要解决您的问题,最好创建两个类:

  • 一个用于与客户端通信(可以为每个连接的客户端创建此类的实例,可以称之为ServerGUI
  • 一个用于应用程序的GUI(您将只有一个此类的实例将其引用传递给每个通信类实例,可以调用此SwingUtilities.invokeLater(new Runnable() { public void run() { jTextArea1.setText(jTextArea1.getText()+str); } });

更新textarea的正确方法来自您提供的片段:

SwingUtilities.invokeLater(() -> {
    jTextArea1.setText(jTextArea1.getText()+str);
});

或者,使用Java 8及更高版本的lambdas:

jTextArea1.repaint();

请不要尝试拨打jTextArea1.update(jTextArea1.getGraphics());import pandas as pd df = pd.DataFrame({'Group': ['A','A','A','B','B','B','B'], 'count': [1.1,11.2,1.1,3.3,3.40,3.3,100.0]}) print(pd.DataFrame(df.groupby('Group').quantile(.01)['count'])) - 在这种情况下,这些电话是无意义的,完全没必要。