让客户端等待输入,客户端 - 服务器

make client wait for input, client - server

我正在尝试编写一个非常简单的客户端-服务器程序。

基本上,一旦客户端连接到服务器,他就会看到一个登录屏幕,他必须在其中输入用户名密码和文件名。

我的问题是:

  1. 客户端继续代码流而不实际等待输入。
  2. 服务器端似乎卡在了某条线上,我重置了连接。

这是客户端的代码:

public static void main(java.lang.String[] args) throws JsonIOException, JsonSyntaxException, ClassNotFoundException, IOException
{
    try
    {
        Socket socket = new Socket(InetAddress.getLocalHost(), 12345);
        LoginScreen login = new LoginScreen();
        login.open();
        String name = login.getUsername();
        String pass = login.getPassword();
        String log = login.getLogname();
        System.out.println(name + " " + pass + " " + log);
    }

这是登录界面

@SuppressWarnings("serial")
public class LoginScreen extends JFrame implements ActionListener
{
    private JTextField userText;
    private JTextField passText;
    private JTextField logText;
    private JLabel userLabel;
    private JLabel passLabel;
    private JLabel logLabel;
    private JButton loginButton;

    private String username;
    private String password;
    private String logname;

    public String getUsername()
    {
        return username;
    }

    public String getPassword()
    {
        return password;
    }

    public String getLogname()
    {
        return logname;
    }

    public LoginScreen()
    {
        super("Login");

        userLabel = new JLabel("username: ");
        passLabel = new JLabel("password: ");
        logLabel = new JLabel("File name: ");

        Dimension preferredSize = new Dimension(80,20);
        userText = new JTextField("");
        userText.setPreferredSize(preferredSize);
        passText = new JTextField("");
        passText.setPreferredSize(preferredSize);
        logText = new JTextField("");
        logText.setPreferredSize(preferredSize);

        loginButton = new JButton("Login");
        loginButton.addActionListener(this);
    }
    public void open()
    {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(500,500);
        setLayout(new BorderLayout());

        JPanel userPanel = new JPanel();
        userPanel.setLayout(new FlowLayout());
        add(userPanel,BorderLayout.NORTH);

        JPanel passPanel = new JPanel();
        passPanel.setLayout(new FlowLayout());
        add(passPanel,BorderLayout.CENTER);

        JPanel logPanel = new JPanel();
        logPanel.setLayout(new FlowLayout());
        add(logPanel,BorderLayout.SOUTH);

        JPanel loginPanel = new JPanel();
        loginPanel.setLayout(new FlowLayout());
        add(loginPanel,BorderLayout.EAST);

        userPanel.add(userLabel);
        userPanel.add(userText);
        passPanel.add(passLabel);
        passPanel.add(passText);
        logPanel.add(logLabel);
        logPanel.add(logText);
        loginPanel.add(loginButton);

        pack();

        this.setVisible(true);
    }
    @Override
    public void actionPerformed(ActionEvent arg0) 
    {
        username = new String(userText.getText());
        password = new String(passText.getText());
        logname = new String(logText.getText());
    }
}

这是服务器

try 
{
    Socket client = socket.accept();
    ObjectOutputStream oos = new ObjectOutputStream(client.getOutputStream());
    ObjectInputStream ois = new ObjectInputStream(client.getInputStream());
    String userPassFile = (String)ois.readObject();
    String[] parts = userPassFile.split("");
    if(AuthenticationManager.Authenticate(parts[0], parts[1]))
    {
        new MMULogService(client,parts[2]);
    }
    else
    {
        oos.writeObject("Incorrect Username / Password");
    }
    client.close();
} 

需要注意的是,在执行客户端之前,我打开了服务器端。 当前输出是 null null null IE - 在我点击登录屏幕上的 "login" 之前,客户端到达 login.getUsername 方法。

关闭登录屏幕后,出现错误 -

java.net.SocketException: 连接在服务器 ObjectInputStream ois = new ObjectInputStream(client.getInputStream()); 行重置。

如何让客户端在执行 get 方法之前等待我实际点击登录按钮?我该如何修复连接重置错误?

这是解决第一个问题的方法(第二个问题我没有看到您的代码,所以我无能为力),您需要一种同步主线程和 UI 的方法。

所以首先让 AWT event dispatching thread 启动您的 UI 作为下一个:

SwingUtilities.invokeLater(new Runnable() {
    @Override
    public void run() {
        login.open();
    }
});

或者如果您使用 Java 8,只需:

SwingUtilities.invokeLater(() -> login.open());

然后让你的主线程在 get 方法之前等待,方法是让它像这样调用一个新方法:

login.waitForInputs();
String name = login.getUsername();
...

然后在您的 class LoginScreen 中,新方法将通过执行以下操作简单地让调用线程等待:

public void waitForInputs() throws InterruptedException {
    synchronized (this) {
        // Make the current thread waits until a notify or an interrupt
        wait();
    }
}

最后你需要修改方法 actionPerformed 以便在主线程正常时释放主线程,方法如下:

public void actionPerformed(ActionEvent arg0)
{
    username = userText.getText();
    password = passText.getText();
    logname = logText.getText();
    // Here you should test if the input values are OK if it is KO do a   
    // return to prevent calling the following code
    synchronized (this) {
        // Release the waiting threads
        notifyAll();
    }
}

我们已经实现了一个简单的 wait/notify 机制来在调用 get 方法之前从 UI 获取输入值。