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

Java 客户端服务器聊天应用程序-消息在fwd_all和add_用户方法中重复,但如何解决?,java,sockets,debugging,duplicates,client-server,Java,Sockets,Debugging,Duplicates,Client Server,如上所述,我的问题是在adduser和fwd这两个方法中,我做了一些调试,发现复制是由这两个方法引起的,我想知道我的源代码中是否还有其他地方导致了异常功能 下面是服务器类的源代码,我可以确认这是问题的根源: public class TCP_Server extends javax.swing.JFrame { ArrayList<String> C_Username; // Arraylist of users connected to the server A

如上所述,我的问题是在adduser和fwd这两个方法中,我做了一些调试,发现复制是由这两个方法引起的,我想知道我的源代码中是否还有其他地方导致了异常功能

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

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                   
}
感谢sudipn,我找到了解决方案,这是因为聊天记录附加在转发消息的while循环中:

Received: Bruce:Hello:Chat
Forwarding Message: Bruce:Hello:Chat
Received: Vincent:Greetings:Chat
Forwarding Message: Vincent:Greetings:Chat
以下是所做的更改:

/* 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\u User
  • 第二个是
    while(it.hasNext()){
    inside
    fwd\u All
从第一个循环调用第二个循环运行的
fwd_All

当数组中只有一个元素(用户Bruce)时,
Add\u user
中的
for
循环运行一次,在此运行中,它调用
fwd\u All
,其中
while
循环也运行一次

现在,当
C\u Username
数组中有两个元素(Bruce和Vincent)时,
Add\u User
方法中的
for
循环运行两次。在与用户Bruce的
for
循环的第一次运行中,它调用
fwd\u All
,现在在这个方法中while循环迭代两次(因为在
C_Outstream
)中有两个条目。因此您会看到重复的输出。while循环完成后,控件将移回
for
循环,在该循环中为第二个用户运行,然后第二个用户再次调用
fwd_All
方法,迭代器运行两次


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

提示:两个数组列表(
C_用户名
C_扩展
)相互关联。因此,快速修复方法是:

public void添加用户(字符串数据){
串味精;
字符串add=“::Connect”;
String done=“服务器::完成”;
字符串名称=数据;
C_用户名。添加(名称);
Chat_LogS.append(“新成员:“+name+”已加入\n”);
对于(int i=0;i

类似于
Remove\u User

我已经更新了我的答案,说明了您必须在哪里更改代码。是的,您是对的,这是访问器方法。很抱歉,我完全弄错了。请您撤消您的更改。我会再看一看嘿,sudipn,谢谢您建议我应该在哪里调试代码但是我仍然非常不确定我的源代码中是否还有其他地方导致我的程序功能异常。例如用于存储用户名和输出流的arraylist。对不起,我不同意你的观点,我上面的帖子专门回答了你在标题中问到的问题-为什么消息会重复。正如前面所说的,这是由于两个列表(用户名、outputstreams)中有两个循环,因此我已经仔细调试了一个ArrayList,如果我是正确的,那么这两个ArrayList“相互重叠”-我注意到索引0中的第一个是用户名,然后在索引1中是outputstream-使用可以处理重复数据的不同数据结构是否更好?这是真的,这是解决问题的一种方法,我找到了另一种解决方案,我发现这是因为聊天日志附加在while循环中-therefo我把它放在while循环之外,它解决了我的程序-检查更新的解决方案和结果。更新确实修复了服务器端的日志,但消息仍会多次发送到客户端。是吗?是的,这是因为发送消息的收件人收到了一条消息,而发送消息的收件人也收到了相同的消息转到所有其他连接的客户端。