简体   繁体   中英

Wait Until Condition become true

I got three classes in, which are TCPClient.java, TCPProtocol.java, TCPServer.java .

*Update, i paste my full TCPServer.java as well

first, user will input the text in TCPProtocol GUI Window (by using JTextarea), and click button "Send". TCPClient will get the input from TCPProtocol if user click "send", and last will send to TCPServer. So the problem is, my TCPClient never get the input from TCPProtocol.

TCPServer.java

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

public class TCPServer {

public static void main(String[] args) throws IOException {

    //TCP component
    ServerSocket serverSocket = null;
    Socket clientSocket = null;
    int port = 8081;
    TCPProtocol nis = new TCPProtocol();//create an object to call method in protocol class

    try {
        serverSocket = new ServerSocket(port);
        System.out.println("Waiting for connection...");
        System.out.println();
    } catch (IOException e) {
        System.out.println(e.toString() + " :Could not listen on port: " + port);
        System.exit(1);
    }

    try {
        clientSocket = serverSocket.accept();
        System.out.println("TCP Session established.");
        nis.frame.setVisible(true);
        nis.frame.setAlwaysOnTop(true);
        System.out.println();

    } catch (IOException e) {
        System.out.println(e.toString() + " :Accept failed.");
        System.exit(1);
    }

    PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
    BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

    String inputLine, outputLine;
    outputLine = nis.processAnswer(null);//get Learn Learn English line
    out.println(outputLine);//and send the line out to client
    nis.Jservertxt.setText(outputLine);

    while ((inputLine = in.readLine()) != null) {
         outputLine = nis.processAnswer(inputLine);
         out.println(outputLine);
         nis.Jservertxt.setText(outputLine);
         if (outputLine.equalsIgnoreCase("Don't miss me! Bye Bye!"))
            break;
         else if (outputLine.equalsIgnoreCase("Thank For Your Guessing. Have a Nice Day =)"))
            break;
    }

    //End connection
    out.flush();
    out.close();
    in.close();
    nis.frame.setVisible(false);
    if (!clientSocket.isClosed())
        clientSocket.close();
        nis.frame.setVisible(false);
    if (!serverSocket.isClosed())
        serverSocket.close();
        nis.frame.setVisible(false);

    System.out.println("TCP session closed.");
}
}

Here is my code for TCPProtocol.java

public volatile boolean getPressed;
Jsend.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {

            String Data = Jusertxt.getText();
            Jusertxt.setText(null);
            System.out.println(Data);
            getPressed=true;
    }
    });
    }

    public boolean getPressed()
    {
        return getPressed;
    }

i din't paste the full code here because i just show the part where do got problem.

and here is my TCPClient Code

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

public class TCPClient{

public static void main(String[] args)  throws IOException, InterruptedException{

    //TCP component
    int port = 8081;
    String host = "localhost";
    Socket nisSocket = null;
    PrintWriter out = null;
    BufferedReader in = null;
    TCPProtocol pro = new TCPProtocol();

    try {
        nisSocket = new Socket(host, port);
        out = new PrintWriter(nisSocket.getOutputStream(), true);
        in = new BufferedReader(new InputStreamReader(nisSocket.getInputStream()));
    } catch (UnknownHostException e) {
        System.out.println(e.toString() + " :Don't know about " + host);
        System.exit(1);
    } catch (IOException e) {
        System.out.println(e.toString() + " :Couldn't get I/O for the connection to " + host);
        System.exit(1);
    }

    BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
    String fromServer, fromUser;

    while ((fromServer = in.readLine()) != null) {
            System.out.println("Server: " + fromServer);
        if (fromServer.equalsIgnoreCase("Don't miss me! Bye Bye!"))
            break;
        if (fromServer.equalsIgnoreCase("Thank For Your Guessing. Have a Nice Day =)"))
            break;

        if (pro.getPressed())
        {
            fromUser = pro.Jusertxt.getText();
            if (fromUser != null) {
                    System.out.println("\nClient: " + fromUser);
                    out.println(fromUser);
                    }
        }
            System.out.println("fail liao");

    }
    System.out.println("try again");
    //End connection
    out.flush();
    out.close();        
    in.close(); 
    stdIn.close();

    //if (!nisSocket.isClosed())
        nisSocket.close();
    System.out.println("TCP session closed.");
}
}

So how can i only let the TCPClient.java process if only getPressed() method was true.

I try using volatile boolean variable but not working as well. Or should i use synchronized?


Solve after change to call GUI code from server to client

while ((fromServer = in.readLine()) != null) {
            System.out.println("Server: " + fromServer);
        if (fromServer.equalsIgnoreCase("Don't miss me! Bye Bye!"))
            break;
        if (fromServer.equalsIgnoreCase("Thank For Your Guessing. Have a Nice Day =)"))
            break;

        pro.frame.setVisible(true);
        pro.frame.setAlwaysOnTop(true);
        pro.Jservertxt.setText(fromServer);
        if(pro.getPressed()){
            fromUser = pro.getMessage();
                if (fromUser != null) {
                        System.out.println("\nClient: " + fromUser);
                        out.println(fromUser);
                }
        }
    }

Here is a little example of a main thread (think of it as your TCPClient) that waits until user clicks button (think of this part as your TCPProtocol):

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextArea;

public class Test extends JFrame {

    public Object       lock    = new Object();
    private JTextArea   area    = new JTextArea();

    public Test() {
        super("test");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JButton send = new JButton("send");
        send.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                synchronized (lock) {
                    lock.notify();
                }

            }
        });
        add(area);
        add(send, BorderLayout.SOUTH);
    }

    public String pullText() {
        String result = area.getText();
        area.setText("");
        return result;

    }

    public static void main(String[] args) {
        Test t = new Test();
        t.setSize(200, 200);
        t.setVisible(true);

        while (true) {
            synchronized (t.lock) {
                try {
                    t.lock.wait();
                    System.out.println(t.pullText());
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }

    }

}

Maybe you can apply a similar approach to your system.

Warning: this is just a quick and dirty example to give you the idea, please double check it. In general you should be very careful when dealing with synchronization.

EDIT: As a starting point you can modify your TCPProtocol class (the part you posted) like this:

public volatile boolean getPressed;

private Object lock = new Object(); // added lock for wait/notify coordination
private String Data; //changed scope to make it accessible in getMessage()

Jsend.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {

        synchronized(lock){
            Data = Jusertxt.getText(); // no more a local variable, it's an instance attribute now, see declaration above
            Jusertxt.setText(null);
            System.out.println(Data);
            getPressed=true;
            lock.notify(); // notify client that user performed action
        }
    });
}

public boolean getPressed(){
    synchronized (lock) {
        try {
            lock.wait(); // wait for user to perform action...
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    return getPressed; // ...then return value (I guess getPressed will always be true at this point)
}

public String getMessage(){ // call this in TCPClient instead of pro.Jusertxt.getText()
    return Data;
}

I added a lock object to enable wait/notify mechanism and applied that to your actionPerformed and getPressed methods. I also changed the scope of Data variable and I added a public method to get its value (getMessage()). If you doublecheck your code you'll see that you will always get null from pro.Jusertxt.getText() in TCPClient.java because you set it to null in actionPerformed. So change pro.Jusertxt.getText() to pro.getMessage() in TCPClient.java.

// [...]
fromUser = pro.getMessage(); // used to be fromUser = pro.Jusertxt.getText();
// [...]

If you get this raw version to work then you can refine and refactor it to make it more clean and consistent. (for instance, I suspect you don't actually need getPressed variable as it's actually always true)

I confess I didn't test this code so you might have to fix or adjust it a little, but I hope it can clarify how I meant you could apply the code I posted to your system.

Hope it helps.

Good luck :-)

You can try this:

while (!pro.getPressed()) {
                    System.out.println("still not true");
                    Thread.sleep(1000);
                }
System.out.println("getPressed is true");

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM