Search code examples
javamultithreadingclient-serverawtjava-threads

Java: Using AWT button to disconnect from chat server


I am programming a simple multi-threaded client/server chat system. Project requirements specify: "Connection only happens when the connect button is clicked. The disconnect button should disconnect the connection. A user should be able to connect, disconnect, re-connect at will." Basically, I have the connect button hooked-up and running. However, when I attempt to disconnect I get stuck in an infinite loop where the client side (on command line) infinitely prints "Sock closed", while the server side infinitely prints "Message read: null". This has lead me to look into all of my for(;;) loops to somehow close the connections within them, however I cannot figure out how to close the connection within those loops. Please help, this is my first socket programming project and I am super stumped on this one! Thanks all.

Client:

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.util.ArrayList;

public class ClientFrame extends Frame{
    public ClientFrame(){
        setSize(500,500);
        setTitle("Chat Client");
        addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent We){
                System.exit(0);
            }
        });
        add(new ClientPanel(), BorderLayout.CENTER);        
        setVisible(true);
    }
    public static void main(String[] args){
        new ClientFrame();
    }

} // end ClientFrame

class ClientPanel extends Panel implements ActionListener, Runnable{
    TextField tf;
    TextArea ta;
    List list;
    Button connect, disconnect;
    Socket socketToServer;
    PrintWriter pw;
    BufferedReader br;
    Thread t;
    String userName;

    public ClientPanel(){
        setLayout(new BorderLayout());
        tf = new TextField();
        ta = new TextArea();
        list = new List();
        connect = new Button("Connect");
        disconnect = new Button("Disconnect");
        Panel bPanel = new Panel();
        bPanel.add(connect);
        disconnect.setEnabled(false);
        bPanel.add(disconnect);
        
        tf.addActionListener(this);
        add(tf, BorderLayout.NORTH);
        add(ta, BorderLayout.CENTER);
        add(list, BorderLayout.EAST);
        add(bPanel, BorderLayout.SOUTH);
        
        connect.addActionListener(this);
        disconnect.addActionListener(this);
    
    } // end ClientPanel constructor


    public void actionPerformed(ActionEvent ae){
        if (ae.getSource() == tf){
            String temp = tf.getText();
            pw.println(userName+": "+temp);
            tf.setText("");
        } else if (ae.getSource() == connect){
            if(tf.getText() == null || tf.getText().equals("")){
                    ta.append("Must enter a name to connect\n");
                }else { 
                    userName = tf.getText();
                    connect.setEnabled(false);
                    disconnect.setEnabled(true);
                    tf.setText("");     
                    try{
                        socketToServer = new Socket("127.0.0.1", 3000);
                        pw = new PrintWriter(new OutputStreamWriter
                                (socketToServer.getOutputStream()), true);
                        br = new BufferedReader(new InputStreamReader
                                (socketToServer.getInputStream()));
                    }catch(UnknownHostException uhe){
                        System.out.println(uhe.getMessage());
                    }catch(IOException ioe){
                        System.out.println(ioe.getMessage());
                    } 
                }

                    t = new Thread(this);
                    t.start();
                    pw.println(userName);
                    pw.println(userName +" has entered the chat.");
        }else if (ae.getSource()== disconnect){
            try{
                t.interrupt();
                socketToServer.close();
            }catch(IOException ioe){
                System.out.println(ioe.getMessage());
            }
        }
    } // end actionPerformed

    public void run(){
            for(;;){
                try{
                    String temp = br.readLine();
                    ta.append(temp + "\n");
                }catch(IOException ioe){
                    System.out.println(ioe.getMessage());
                } 
            }
    } // end run

} // end ClientPanel

Server:

import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.awt.*;

public class ThreadedServerWithPresence{  
    public static void main(String[] args){  
        ArrayList<ThreadedHandlerWithPresence> handlers;
            try{    
            handlers = new ArrayList<ThreadedHandlerWithPresence>();
            ServerSocket s = new ServerSocket(3000);
                for(;;){  
                Socket incoming = s.accept( );
                new ThreadedHandlerWithPresence(incoming, 
                                    handlers).start();

            }   
            }catch (Exception e){  
                System.out.println(e);
            }
    } 
}

class ThreadedHandlerWithPresence extends Thread{  

    Socket incoming;
    ArrayList<ThreadedHandlerWithPresence> handlers;
    PrintWriter pw;
    BufferedReader br;
    String userName;

    public ThreadedHandlerWithPresence(Socket i,
        ArrayList<ThreadedHandlerWithPresence> handlers){ 
        incoming = i;
        this.handlers = handlers;
        handlers.add(this);
    }

    public void setUserName(String userName){
        this.userName = userName;
    }

    public String getUserName(){
        return userName;
    }
   
    public void run(){  
        try{    
            br = new BufferedReader(new InputStreamReader
                            (incoming.getInputStream()));

            pw = new PrintWriter(new OutputStreamWriter
                            (incoming.getOutputStream()),true);
           
            String firstLine = br.readLine();
            setUserName(firstLine);

            for(;;){
                
                String temp = br.readLine();

                System.out.println("Message read: " + temp);
        
                for(int i = 0; i < handlers.size(); i++){
                    handlers.get(i).pw.println(temp);
                }
            }           
            }catch (Exception e){  
            System.out.println(e);
            }finally{
                handlers.remove(this); 
            } 
    }
}

Solution

  • Client

    Your run method does not handle interruptions so the for loop does not end and it continues trying to receive messages.

    You must add an action that can be interrupted and throws InterruptedException, Thread.sleep would be a good candidate in this case, also reducing CPU usage (You do not need to check for new messages every single moment).

        try {
            for (; ; ) {
                try {
                    String temp = br.readLine();
                    ta.append(temp + "\n");
    
                } catch (IOException ioe) {
                    System.out.println(ioe.getMessage());
    
                }
                Thread.sleep(10);
            }
        } catch (InterruptedException e) {
            System.out.println("Disconnected.");
        }
    

    Server

    When br.readLine() returns null, that indicates the connection is closed by the client and you should stop receiving messages.

            String temp = br.readLine();
    
            if(temp == null)
                break;