-1

I've been given this question and I've been trying since days but I'm not able to get the correct output. Any help would be appreciated.

Develop a simple chat application between two users using Send-Wait-Receive Protocol:

Once a user sends a message, he waits until a message is received from the other user. The users are "User1" and "User2".

At initial stage of application, User1 is in sending mode and User2 is in receiving mode. These two users are sending and receiving the messages alternatively. -Create a Chat class with two methods: sendMessage and recvMessage -Create two threads to represent two users,User1 and User2. -Use Interthread communication to exchange messages. -No need to maintain any chat history.

OUTPUT:

User1(User2):HI

User2(User1):HI

User2(User1):Hello

User1(User2):Hello

class Chat{
    Scanner sc=new Scanner(System.in);
    String message;
    ArrayList<String> user1 = new ArrayList<String>();
    ArrayList<String> user2 = new ArrayList<String>();
    boolean sendMode = true;
    String name = "";
    String otherName = "";
    
    synchronized void recvMessage(){
        name = Thread.currentThread().getName();
        while(sendMode) {
            try{
                wait();
            }catch(InterruptedException e){
                System.out.println("InterruptedException caught");
            }
        }
        System.out.println(name);
        if (name.contentEquals("User1")) {
            otherName="User2";
        }
        else {
            otherName="User1";
        }
        System.out.println(name+"("+otherName+"):" + message);
        sendMode=true;
        System.out.println("rcv");
        notify();
    }
    
    synchronized void sendMessage(){
        name = Thread.currentThread().getName();
        while(!sendMode) {
            try{
                wait();
            }catch(InterruptedException e){
                System.out.println("InterruptedException caught");
            }
        }
        System.out.println(name);
        if (name.contentEquals("User1")) {
            otherName="User2";
        }
        else {
            otherName="User1";
        }
        System.out.print(name+"("+otherName+"):");
        message=sc.next();
        if(name.contains("User1")) {
            user1.add(message);
        }
        else {
            user2.add(message);
        }
        System.out.println("send");
        sendMode=false;
        notify();
    }
}

class Person1 implements Runnable{
    Chat ex;
    
    public Person1(Chat ex) {
        this.ex = ex;
        Thread u2=new Thread(this, "User1");
        u2.start();
    }
    
    public void run() {
        while(true) {
            ex.sendMessage();
        }
    }
}

class Person2 implements Runnable{
    Chat ex;
    
    public Person2(Chat ex) {
        this.ex=ex;
        Thread u1=new Thread(this, "User2");
        u1.start();
    }
    
    public void run() {
        while(true) {
            ex.recvMessage();
        }
    }
    
}

class Main{
    public static void main(String args[])
    {
        Chat ex =new Chat();
        new Person1(ex);
        new Person2(ex);
    }
} 

the output I'm getting is: User1(User2):hi

User2(User1):hi

User2(User1):hello

User1(User2):hello

User2(User1):what's up

User1(User2):what's

User2(User1):User1(User2):up

User2(User1):

Basically the threads are not in doing their job in order. The first 4 lines of output are right. But after that, it's always user2 sending the message and user1 receiving it.

Please help me out.

dreamcrash
  • 36,542
  • 23
  • 64
  • 87

1 Answers1

1

There is two issues with your code first is that you are using:

 message=sc.next();

and instead you should be using:

 message=sc.nextLine();

Using next() will only return what comes before the delimiter (defaults to whitespace). nextLine() automatically moves the scanner down after returning the current line.

From this SO thread.

That is why for the first inputs without spaces it works fine, but as soon as you send a string with spaces:

what's up

you ran into problems.

The second part is that with your current design only one of the threads send and another receives. For having both communication with each other instead of a monologue, I would suggest to do something like:

class Chat{
    Scanner sc=new Scanner(System.in);
    String message;
    final Object rec = new Object();
    final Object send = new Object();
    boolean msg_send = false;
    boolean msg_recv = false;

    void recvMessage(){
        synchronized (send){
            while(!msg_send) {
                try{
                    send.wait();
                }catch(InterruptedException e){
                    System.out.println("InterruptedException caught");
                }
            }
            msg_send = false;
        }
        synchronized (rec) {
            String name = Thread.currentThread().getName();
            System.out.println(name);
            String otherName = name.contentEquals("User1") ? "User2" : "User1";
            System.out.println(name + "(" + otherName + "):" + message);
            System.out.println("rcv");
            msg_recv = true;
            rec.notify();
        }
    }

     void sendMessage(){
         synchronized (send) {
             String name = Thread.currentThread().getName();
             System.out.println(name);
             String otherName = name.contentEquals("User1") ? "User2" : "User1";
             System.out.print(name + "(" + otherName + "):");
             message = sc.nextLine();
             System.out.println("send");
             msg_send = true;
             send.notify();
         }
        synchronized (rec) {
            while (!msg_recv) {
                 try {
                     rec.wait();
                 } catch (InterruptedException e) {
                     System.out.println("InterruptedException caught");
                 }
             }
             msg_recv = false;
        }
    }
}

class Person1 implements Runnable{
    Chat ex;

    public Person1(Chat ex) {
        this.ex = ex;
        Thread u2=new Thread(this, "User1");
        u2.start();
    }

    public void run() {
        while(true) {
            ex.sendMessage();
            ex.recvMessage();
        }
    }
}

class Person2 implements Runnable{
    Chat ex;

    public Person2(Chat ex) {
        this.ex=ex;
        Thread u1=new Thread(this, "User2");
        u1.start();
    }

    public void run() {
        while(true) {
            ex.recvMessage();
            ex.sendMessage();
        }
    }

}

class Main{
    public static void main(String args[])
    {
        Chat ex =new Chat();
        new Person1(ex);
        new Person2(ex);
    }
} 

Waiting and notifying using two objects representing when the message was received and send:

   final Object rec = new Object();
   final Object send = new Object();

Then you need the reason about when should you synchronize on one or the other object.

dreamcrash
  • 36,542
  • 23
  • 64
  • 87
  • thank you. that was a problem too, but the main problem was that the threads were not alternatively sending and receiving the messages. as you can see in my output, first user1 sends the message to user2, then user2 recieves it. next the opposite happens. but the third time onwards, it's always user2 sending a message to user1 and user1 just receiving that message. user1 isn't able to send anymore messages after the first time. –  Dec 06 '20 at 12:19
  • nope. I'm not great at interthread communication but I do know it has something to do with the wait() and notify() methods. the logic of the code seems to be right. –  Dec 06 '20 at 12:29
  • okay so, user1 has to send a message to user2 and user2 should receive it (there are methods for sending and receiving). then user2 should send a message to user1 and user1 should receive it. the sending and receiving should keep happening alternatively between user1 and user2, one at a time. is the question a bit clearer now?? –  Dec 06 '20 at 12:37
  • yess it works thank you so much –  Dec 06 '20 at 14:20