客户端-服务器聊天应用程序 - 消息在 fwd_all 和 add_user 方法中重复,但我该如何解决?

Client-Server Chat App - Messages are duplicating within the fwd_all and add_user methods but how do I resolve?

如上所述,我的问题是在 add user 和 fwd all 这两个方法中,我已经进行了一些调试,发现重复是在那里引起的,我想知道我的源代码中是否还有其他地方导致功能异常?

下面是服务器的源代码 class 我可以确认这是问题的根源:

public class TCP_Server extends javax.swing.JFrame {

    ArrayList<String> C_Username; // Arraylist of users connected to the server

    ArrayList C_OutStream; // Preparing Arraylist of output streams to client

    /**
     * Constructor to initialise the jFrame via 'generated code'.
     */
    public TCP_Server() {
        initComponents();
        Chat_LogS.setEditable(false); // textarea not interactable
    }

    /**
     * Client handler uses the interface 'Runnable' for multi-threading to
     * provide responsiveness in the snippet of code below that connect or
     * disconnect clients while processes operate 'concurrently'.
     */
    public class ClientHandler implements Runnable {

        BufferedReader from_Client; // Data buffer from client
        Socket connection; // Part of communication channel
        PrintWriter client; // format data as text (UTF-8)

        public ClientHandler(Socket clientSocket, PrintWriter user) {
            client = user;
            try {
                /* Opens the communication channel */
                connection = clientSocket;

                /* Prepares the input streams */
                InputStreamReader in_Client = new InputStreamReader(connection.getInputStream());
                from_Client = new BufferedReader(in_Client);
            } catch (Exception ex) {
                /* If communication isn't successful */
                Chat_LogS.append("Unexpected exception occurerd \n");
            }
        }

        /**
         * Method of Runnable interface for 'thread safe operation' of connect
         * or disconnect users from the chat.
         */
        @Override
        public void run() {
            /* Correspond to dialogue provided in the if statement */
            String msg;
            String connect = "Connect";
            String disconnect = "Disconnect";
            String chat = "Chat";
            String[] data;

            /* Enclosed in try catch block, server is to reply to client */
            try {
                while ((msg = from_Client.readLine()) != null) {
                    /* Response of server in textarea */
                    Chat_LogS.append("Received: " + msg + "\n");

                    /* Split array of string to substrings via ':' */
                    data = msg.split(":");

                    for (String token : data) {
                        Chat_LogS.append(token + "\n");
                    }

                    /* If statement for corresponding words declared above */
                    // Here are squiggly lines due to absent long data type...
                    if (data[2].equals(connect)) {
                        fwd_All((data[0] + ":" + data[1] + ":" + chat));
                        Add_User(data[0]);
                    } else if (data[2].equals(disconnect)) {
                        fwd_All((data[0] + ":has left the chat" + ":" + chat));
                        Remove_User(data[0]);
                    } else if (data[2].equals(chat)) {
                        fwd_All(msg);
                    } else {
                        Chat_LogS.append("No condition is met \n");
                    }
                }
            } catch (Exception ex) {
                Chat_LogS.append("Connection for a user is lost \n");
                C_OutStream.remove(client);
            }
        }
    }

    /* Auto generated code can be modified in JFrame */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        jScrollPane1 = new javax.swing.JScrollPane();
        Chat_LogS = new javax.swing.JTextArea();
        Start_Btn = new javax.swing.JButton();
        Online_Btn = new javax.swing.JButton();
        Clear_Btn = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Server Frame");
        setName("server"); // NOI18N
        setResizable(false);

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

        Start_Btn.setText("Start Server");
        Start_Btn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                Start_BtnActionPerformed(evt);
            }
        });

        Online_Btn.setText("Online Users");
        Online_Btn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                Online_BtnActionPerformed(evt);
            }
        });

        Clear_Btn.setText("Clean Page");
        Clear_Btn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                Clear_BtnActionPerformed(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)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(Start_Btn)
                        .addGap(18, 18, 18)
                        .addComponent(Online_Btn, javax.swing.GroupLayout.PREFERRED_SIZE, 138, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(18, 18, 18)
                        .addComponent(Clear_Btn, javax.swing.GroupLayout.PREFERRED_SIZE, 103, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addComponent(jScrollPane1))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 265, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(Clear_Btn)
                    .addComponent(Online_Btn)
                    .addComponent(Start_Btn))
                .addContainerGap())
        );

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

    /* Available to open communication channel and accept client(s) request */
    private void Start_BtnActionPerformed(java.awt.event.ActionEvent evt) {                                          
        Thread starter = new Thread(new ServerStart());
        starter.start();
        Chat_LogS.append("*Server is Activated* \n");
    }                                         

    /* Print in the server chat log: list of users connected to chat server */
    private void Online_BtnActionPerformed(java.awt.event.ActionEvent evt) {                                           
        Chat_LogS.append("Online users: \n");
        for (String current_user : C_Username) {
            Chat_LogS.append(current_user + "\n");
        }
    }                                          

    /* Clean 'slate' of the server chat log */
    private void Clear_BtnActionPerformed(java.awt.event.ActionEvent evt) {                                          
        Chat_LogS.setText("");
    }                                         

    /* Main method to execute the server class UI */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new TCP_Server().setVisible(true);
            }
        });
    }

    // Did I do anything wrong in here? 
    public class ServerStart implements Runnable {

        @Override
        public void run() {
            C_OutStream = new ArrayList();
            C_Username = new ArrayList();

            long id = 1L;

            try {             
                /* Opens the communication channel */
                ServerSocket serverSock = new ServerSocket(1183);

                while (true) {
                    /* Accepting client request */
                    Socket clientSock = serverSock.accept();

                    /* Sending response to client */
                    PrintWriter wr = new PrintWriter(clientSock.getOutputStream());
                    C_OutStream.add(wr);

                    /* Communicates with client handler class - accepting connections*/                                
                    Thread listener = new Thread(new ClientHandler(clientSock, wr));
                    listener.start();
                    Chat_LogS.append("Connection Successful \n");
                }
            } catch (Exception ex) {
                Chat_LogS.append("Experiencing Connectivity Issues \n");
            }
        }
    }

    /* Server approves new client to join the chatroom */
        // Made changes here for the ID
    public void Add_User(String data) {
        String msg;
        String add = ": :Connect";
        String done = "Server: :Done";
        String name = data;

        C_Username.add(name);

        Chat_LogS.append("New member: " + name + " has joined \n");
        String[] tempList = new String[(C_Username.size())];
        C_Username.toArray(tempList);

        for (String tkn : tempList) {
            msg = (tkn + add);
            fwd_All(msg);
        }
        fwd_All(done);
    }

    /* Sever approves of client request to be removed from chat room */
    // Made changes here for the ID
    public void Remove_User(String data) {
        String msg;
        String add = ": :Connect";
        String done = "Server: :Done";
        String name = data;

        C_Username.remove(name);
        String[] tempList = new String[(C_Username.size())];
        C_Username.toArray(tempList);

        for (String token : tempList) {
            msg = (token + add);
            fwd_All(msg);
        }
        fwd_All(done);
    }

    /* Iterates to all clients connected to the server */
    // This is a problem; it sends same dialogue twice!!!

        // I dont know if the implementation is done correct.
    public void fwd_All(String msg) {
        Iterator it = C_OutStream.iterator();

        while (it.hasNext()) {
            try {
                PrintWriter wr = (PrintWriter) it.next();
                wr.println(msg); // Recipient of message
                Chat_LogS.append("Sending to: " + msg + "\n");
                wr.flush();
                //Chat_LogS.setCaretPosition(Chat_LogS.getDocument().getLength());
            } catch (Exception ex) {
                Chat_LogS.append("Error forwarding message \n");
            }
        }
    }

    // Variables declaration - do not modify                     
    private javax.swing.JTextArea Chat_LogS;
    private javax.swing.JButton Clear_Btn;
    private javax.swing.JButton Online_Btn;
    private javax.swing.JButton Start_Btn;
    private javax.swing.JScrollPane jScrollPane1;
    // End of variables declaration                   
}

以下是在聊天室中连接新用户后服务器日志中产生的输出:

// When second user connects
Connection Successful 
Received: Vincent:has connected:Connect
Vincent
has connected
Connect
Sending to: Vincent:has connected:Chat
Sending to: Vincent:has connected:Chat
New member: Vincent has joined 
Sending to: Bruce: :Connect
Sending to: Bruce: :Connect
Sending to: Vincent: :Connect
Sending to: Vincent: :Connect
Sending to: Server: :Done
Sending to: Server: :Done

感谢 sudipn,我找到了解决方案,这是因为 chatlogS 附加在转发消息的 while 循环内:

这里是所做的更改:

/* Iterates to all clients connected to the server */
public void fwd_All(String msg) {
    Iterator it = C_OutStream.iterator();

    Chat_LogS.append("Forwarding Message: " + msg + "\n"); // NEW
    Chat_LogS.setCaretPosition(Chat_LogS.getDocument().getLength()); //NEW

    while (it.hasNext()) {
        try {
            PrintWriter wr = (PrintWriter) it.next();
            wr.println(msg); // Recipient of message
            wr.flush();
        } catch (Exception ex) {
            Chat_LogS.append("Error forwarding message \n");
        }
    }
}

这是现在的输出:

Connection Successful 
Received: Bruce:has connected:Connect
Forwarding Message: Bruce:has connected:Chat
New member: Bruce has joined 
Forwarding Message: Vincent: :Connect
Forwarding Message: Bruce: :Connect
Forwarding Message: Server: :Done

即使发送消息:

Received: Bruce:Hello:Chat
Forwarding Message: Bruce:Hello:Chat
Received: Vincent:Greetings:Chat
Forwarding Message: Vincent:Greetings:Chat

您的错误的原因是两个循环:

  • Add_User中的第一个for (String tkn : tempList) {
  • fwd_All里面的第二个while (it.hasNext()) {

从第一个循环开始,调用 fwd_All,第二个循环 运行。

当数组只有一个元素时(用户Bruce),forAdd_User中循环一次运行在此 运行 中,它调用 fwd_All,其中 while 循环也 运行 一次。

现在当 C_Username 数组中有两个元素(BruceVincent)时,forAdd_User 方法 运行s 中循环两次。在用户 Brucefor 循环的第一个 运行 中,它调用 fwd_All 并且现在在此方法中 while 循环迭代两次(因为C_Outstream 中有两个条目)。因此,您会看到重复的输出。 while 循环结束后,控件移回 for 循环,第二个用户 运行s 然后再次调用 fwd_All 方法和迭代器 运行s两次。

调试您的代码以获得更好的理解。我希望这可以解释为什么您的日志数量不断增加。

提示:两个数组列表(C_UsernameC_OutStream)是相互关联的。所以一个快速的解决办法是:

public void Add_User(String data) {
    String msg;
    String add = ": :Connect";
    String done = "Server: :Done";
    String name = data;

    C_Username.add(name);
    Chat_LogS.append("New member: " + name + " has joined \n");
    for (int i = 0; i < C_Username.size(); i++) {
        PrintWriter wr = C_OutStream[i]; 
        msg = C_Username[i] + add;
        fwd(msg, wr);
        fwd(done, wr);
    }
}

private void fwd(String msg, PrintWriter wr) {
    try {
        wr.println(msg); // Recipient of message
        Chat_LogS.append("Sending to: " + msg + "\n");
        wr.flush();
    } catch (Exception ex) {
        Chat_LogS.append("Error forwarding message \n");
    }
}

Remove_User 相似。