0

我有一个关于使用服务器-客户端模型在网络中等待消息的问题。

您可以将收到的消息区分为两种不同的类型:

  1. Ad-hoc 消息,例如“用户已登录”,与客户端中的任何其他内容无关。

  2. 响应消息,例如当您自己尝试登录时。

我的问题是关于后者,即我使用这个方案:

  1. 向服务器发送登录请求(以“登录用户名密码加密”的形式)

  2. (客户端)等到服务器响应或发生五秒超时。

  3. (服务器)以“loginconfirmed”或“logindenied”响应。

所以我想知道最好的编码方式是什么,我这里有一些旧代码,但我认为这里的耦合太高了,我已经围绕这段代码重写了部分框架(它也驻留在图形中类,这根本没有意义,我不应该这样做):

public void initLoginPanel() {
    setLoginStatus(LoginStatus.TIMEOUT);    //standard value
    LoginPanelOld loginPanel = new LoginPanelOld();
    int result = JOptionPane.showConfirmDialog(this, loginPanel, "Please log in", JOptionPane.OK_CANCEL_OPTION);
    if (result == JOptionPane.OK_OPTION) {
        String login = loginPanel.getLoginValue();
        if (!login.isEmpty() && login.split(" ").length == 1) {
            Network.getInstance().waitFor("loginconfirmed");
            Network.getInstance().waitFor("logindenied");
            Network.getInstance().send("login " + login);
            initSimpleTextPanel("Logging in...");
            try {
                //TODO synchronize on some "login waiter object"
                synchronized(this) {
                    wait();
                }
                switch (loginStatus) {
                    case CONFIRMED:
                        JOptionPane.showMessageDialog(this, "Login succesful.");
                        break;
                    case DENIED:
                        JOptionPane.showMessageDialog(this, "Login denied.");
                        initLoginPanel();
                        break;
                    case TIMEOUT:
                        JOptionPane.showMessageDialog(this, "Could not connect to the server.");
                        initLoginPanel();
                        break;
                    default:
                        JOptionPane.showMessageDialog(this, "An unexpected error has occured.");
                        initLoginPanel();
                        break;
                }
                removeAll();
                revalidate();
                repaint();
            } catch (InterruptedException ex) {
                Logger.getLogger(MainPanelOld.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        else {
            JOptionPane.showMessageDialog(this, "You have entered invalid details.");
            initLoginPanel();
        }
    }
    else if (result == JOptionPane.CANCEL_OPTION || result == JOptionPane.CLOSED_OPTION) {
        initLoginPanel();
    }
}

我使用同步,但我不太喜欢它,所以如果可能的话,我想避免它。然而问题在于网络在不同的线程中运行,并且响应可以随时出现,但是登录过程只有五秒钟需要响应。

无需联网的重写代码:

public class LoginProcedure implements Procedure {
    private final GUI gui;

    private LoginPanel loginPanel;

    public LoginProcedure(final GUI gui) {
        this.gui = gui;
    }

    @Override
    public void start() {
        loginPanel = new LoginPanel(this);
        gui.setPanel(loginPanel);
    }

    @Override
    public void finish() {
        Controller.getInstance().finishProcedure();
    }

    public void tryLogin(final String username, final char[] password) {
        if (username.isEmpty()) {
            loginPanel.setErrorMessage("Please enter your username.");
            return;
        }
        if (password.length == 0) {
            loginPanel.setErrorMessage("Please enter your password.");
            return;
        }
        gui.setPanel(new LoadingPanel());
    }
}

在新代码中,它应该从gui.setPanel(new LoadingPanel());.

因此,再次重复主要问题:如何使 Networking 类(从服务器读取响应并处理它们的类)仅在给定时刻的给定超时内对“loginconfirmed”和“logindenied”做出反应,并重定向给定 LoginProcedure 对象的事件?

4

1 回答 1

0

我会以启用同步/异步消息传递系统的方式设计它。

  • 每个可交换消息都必须有一个 UUID 和一个“originID”属性。
  • 异步消息只是从发射器发送到接收器。
  • 同步消息将其内容发送到接收方,并进入“等待回复”队列。调用者线程暂停,或不时对队列执行 ping 操作。
  • 接收方解析原始消息,准备回复并使用原始消息的 ID 填充 originID 属性。
  • 回复消息被发送到发射器。
  • 发射器解析它,识别出它是一个回复,并发出“等待回复”队列的信号。
  • 原始线程被允许继续进行,而答复在手。
  • 如果没有回复到达,则可能会引发 TimeOutException。
于 2013-05-03T20:47:36.240 回答