Java 客户端-服务器和观察者

问题描述 投票:0回答:1

我正在为一项大学任务实现一个 Java 客户端-服务器应用程序,但我陷入了以下困境:我必须使用客户端-服务器,并且每当数据库中的数据发生变化时也必须更新视图。我所做的是,每当数据库发生更改时,我都会用“CHANGE IN DATA”消息通知所有客户端,然后客户端应该阅读并理解此消息,以便调用将更新其图形界面的方法。但是,或者我错误地读取了客户端的部分,或者由于某些错误,客户端不会读取“数据更改”消息,因此整个过程会卡在此时并且视图不会更新。

这里有一些相关代码! 服务器类别:

public class FinesPaymentServer implements Runnable {

private Database database;
private UserGateway userGateway;
private FineGateway fineGateway;
private DriverGateway driverGateway;
private Socket connection;
private int ID;
static ArrayList<Socket> clientsConnected;

/**
 * Constructor of the class connecting to the database and initializing the socket
 * @param database the database used
 * @param connection the socket for the server
 * @param ID the id
 */
private FinesPaymentServer(Database database, UserGateway userGateway, FineGateway fineGateway, DriverGateway driverGateway, Socket connection, int ID) {
    this.connection = connection;
    this.userGateway = userGateway;
    this.fineGateway = fineGateway;
    this.driverGateway = driverGateway;
    this.database = database;
    this.ID = ID;
}

/**
 * Run method of the threads for each socket on the server
 */
public void run() {
    try {
        while(true)
            readFromClient(connection);
    } catch (IOException | SQLException e) {
    System.out.println(e);
    }
}

 /**
 * Read method from the client
 * @param client the client socket from where to read
 * @throws IOException
 * @throws SQLException 
 */
public void readFromClient(Socket client) throws IOException, SQLException {
    BufferedInputStream is = new BufferedInputStream(client.getInputStream());
    InputStreamReader reader = new InputStreamReader(is);
    StringBuffer process = new StringBuffer();
    int character;
    while((character = reader.read()) != 13) {
        process.append((char)character);
    }
    System.out.println("[SERVER READ]: "+process);
    String[] words = process.toString().split("\\s+");
    switch (process.charAt(0)) {
        case 'a' : 
        {
            int type = database.verifyLogin(words[1], words[2]);
            sendMessage(client, ""+type + " ");
            break;
        }
        case 'b' :
        {
            String rs = userGateway.getUsers();
            sendMessage(client, rs);
            break;
        }
        case 'c' :
        {
            userGateway.createUser(words[1], words[2], words[3]);
            notifyClients();
            break;
        }
        case 'd' :
        {
            userGateway.updateUser(words[1], words[2], words[3]);
            notifyClients();
            break;
        }
        case 'e' :
        {
            userGateway.deleteUser(words[1]);
            notifyClients();
            break;
        }
    }
    try {
    Thread.sleep(1000);
    } catch (Exception e){}
    String time_stamp = new java.util.Date().toString();
    String returnCode = "Single Socket Server responded at " + time_stamp + (char) 13;
    sendMessage(client, returnCode);
}

/**
 * Method for sending messages from the server to the client
 * @param client the client socket where to send the message
 * @param message the message itself to be sent
 * @throws IOException 
 */
private void sendMessage(Socket client, String message) throws IOException {
    BufferedWriter writer = new BufferedWriter(
    new OutputStreamWriter(client.getOutputStream()));
    writer.write(message);
    System.out.println("[SERVER WRITE]: "+message);
    writer.flush();
}

public void notifyClients() throws IOException
{
    for(Socket s : clientsConnected)
    {
        sendMessage(s, "CHANGE IN DATA ");
    }
}

/**
 * @param args the command line arguments
 * @throws java.sql.SQLException
 */
public static void main(String[] args) throws SQLException {
    Database database = new Database();
    UserGateway userGateway = new UserGateway();
    FineGateway fineGateway = new FineGateway();
    DriverGateway driverGateway = new DriverGateway();
    clientsConnected = new ArrayList<>();
    // Setting a default port number.
    int portNumber = 2015;
    int count = 0;
    System.out.println("Starting the multiple socket server at port: " + portNumber);
    try {
    ServerSocket serverSocket = new ServerSocket(portNumber);
    System.out.println("Multiple Socket Server Initialized");
    //Listen for clients
    while(true) {
    Socket client = serverSocket.accept();
    clientsConnected.add(client);
    Runnable runnable = new FinesPaymentServer(database, userGateway, fineGateway, driverGateway, client, ++count);
    Thread thread = new Thread(runnable);
    thread.start();
    }
    } catch (Exception e) {}
}
}

客户类别:

public class FinesPaymentClient implements Runnable {

private String hostname = "localhost";
private int port = 2015;
Socket socketClient;
AdministratorModel adminModel;
PoliceModel policeModel;
PostModel postModel;

/**
 * Constructor of the class
 * @param hostname the host name of the connection
 * @param port the port of the connection
 * @throws UnknownHostException
 * @throws IOException 
 */
public FinesPaymentClient(String hostname, int port, AdministratorModel adminModel, PoliceModel policeModel, PostModel postModel) throws UnknownHostException, IOException
{
    this.hostname = hostname;
    this.port = port;
    this.adminModel = adminModel;
    this.policeModel = policeModel;
    this.postModel = postModel;
    connect();
}

/**
 * Method for connecting to the host by a socket
 * @throws UnknownHostException
 * @throws IOException 
 */
public void connect() throws UnknownHostException, IOException {
    System.out.println("Attempting to connect to " + hostname + ":" + port);
    socketClient = new Socket(hostname, port);
    System.out.println("Connection Established");
}

/**
 * Method for reading response from the server
 * @return the string read from the server
 * @throws IOException 
 */
public String readResponse() throws IOException {
    String userInput;
    BufferedReader stdIn = new BufferedReader(
    new InputStreamReader(socketClient.getInputStream()));
    System.out.println("[CLIENT READ]:");
    while ((userInput = stdIn.readLine()) != null) {
        System.out.println(userInput);
        return userInput;
    }
    return userInput;
}

/**
 * Method for closing connection between client and server
 * @throws IOException 
 */
public void closeConnection() throws IOException {
    socketClient.close();
}

/**
 * Method for writing messages to the server
 * @param message the message to be sent
 * @throws IOException 
 */
public void writeMessage(String message) throws IOException {
    String time_stamp = new java.util.Date().toString();
    // Please note that we placed a char(13) at the end of process...
    // we use this to let the server know we are at the end
    // of the data we are sending
    String process = message + (char) 13;
    BufferedWriter stdOut = new BufferedWriter(
    new OutputStreamWriter(socketClient.getOutputStream()));
    stdOut.write(process);
    System.out.println("[CLIENT WRITE]: "+process);
    // We need to flush the buffer to ensure that the data will be written
    // across the socket in a timely manner
    stdOut.flush();
}

@Override
public void run() {
    try {
        String response;
        while(true)
        {
            response = readResponse();
            System.out.println("HERE"+response.substring(0, 13));
            if(response.substring(0, 13).equals("CHANGE IN DATA"))
            {
                adminModel.setChange();
            }
        }
    } catch (IOException e) {
        System.out.println(e);
    }
}

/**
 * Main method of the application
 * @param arg the parameters given as arguments
 * @throws SQLException
 * @throws UnknownHostException
 * @throws IOException 
 */
public static void main(String arg[]) throws SQLException, UnknownHostException, IOException {
    AdministratorModel adminModel = new AdministratorModel();
    PoliceModel policeModel = new PoliceModel();
    PostModel postModel = new PostModel();
    FinesPaymentClient client = new FinesPaymentClient("localhost", 2015, adminModel, policeModel, postModel);
    Runnable client2 = new FinesPaymentClient("localhost", 2015, adminModel, policeModel, postModel);
    Thread thread = new Thread(client2);
    thread.start();
    Login login = new Login();
    ClientSide clientSide = new ClientSide(login, client, adminModel, policeModel, postModel);
}
}

客户端类:

public class ClientSide {
private final Login login;
private FinesPaymentClient client;
AdministratorModel adminModel;
PoliceModel policeModel;
PostModel postModel;

/**
 * Constructor instantiating needed classes
 * @param login an instance of the login class
 * @param client the client needing the control logic
 * @param adminModel
 * @param policeModel
 * @param postModel
 * @throws SQLException using classes connecting to a database sql exceptions can occur
 */
public ClientSide(Login login, FinesPaymentClient client, AdministratorModel adminModel, PoliceModel policeModel, PostModel postModel) throws SQLException
{
    this.login = login;
    this.client = client;
    this.adminModel = adminModel;
    this.policeModel = policeModel;
    this.postModel = postModel;
    login.addButtonListener(new ButtonListener());
}

/**
 * Listener for the login button. Reads, verifies and provides the interface according to logged in user type.
 */
class ButtonListener implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent e) {
        try 
        {
            client.writeMessage("a " + login.field1.getText()+ " " + login.field2.getText());
            String response = client.readResponse();
            if(response.charAt(0) == '1')
            {   
                login.setVisible(false);
                AdministratorGUI administratorGUI = new AdministratorGUI(adminModel, client);
                AdministratorController adminController = new AdministratorController(client, administratorGUI, adminModel);              
            }
            //if user is post office employee
            else if(response.charAt(0) == '2')
            {
                login.setVisible(false);
                PostGUI postGUI = new PostGUI();
                PostController postController = new PostController(client, postGUI, postModel);
            }
            //if user is police employee
            else if(response.charAt(0) == '3')
            {
                login.setVisible(false);
                PoliceGUI policeGUI = new PoliceGUI();
                PoliceController policeController = new PoliceController(client, policeGUI, policeModel);              
            }
            else
            {
                JOptionPane.showMessageDialog(null,"Login failed! Please try again!");
            }
        } 
        catch (IOException ex) 
        {
            Logger.getLogger(ClientSide.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}
}

我 99% 确信错误发生在客户端读取从服务器发送的消息作为通知时,但我根本不知道如何检索该消息。现在我在客户端线程运行方法中尝试过,但不起作用。其他类和其他功能工作得很好,这是我唯一的问题。您知道错误可能是什么吗?我将不胜感激任何帮助。

java client-server observer-pattern
1个回答
0
投票

对于我的解决方案,我需要服务器端所有已连接客户端的列表:

public class ClientListModel {
    private List<ClientHandler> liste = new LinkedList<>();
    private ClientListView clientView;

    public List<ClientHandler> getListe() {
        return liste;
    }

    public void setListe(List<ClientHandler> liste) {
        this.liste = liste;
    }

    public ClientListView getClientView() {
        return clientView;
    }

    public void setClientView(ClientListView clientView) {
        this.clientView = clientView;
    }

    public void update() {
        clientView.update();
    }

    public void add(ClientHandler echoClientHandler) {
        liste.add(echoClientHandler);
        update();
    }

    public void remove(ClientHandler echoClientHandler) {
        liste.remove(echoClientHandler);
        update();
    }
}

视图类将信息打印到控制台:

public class ClientListView {
    ClientListModel clientModel;

    public ClientListView() {
        super();
    }

    public void update() {
        System.out.println("*** connected clients ***");
        for (ClientHandler client : clientModel.getListe()) {
            System.out.println(client.getClientSocket().getInetAddress());
        }
    }

    public ClientListModel getClientModel() {
        return clientModel;
    }

    public void setClientModel(ClientListModel clientModel) {
        this.clientModel = clientModel;
    }
}

实际服务器拥有这两个类。它可以向所有注册的客户端处理程序发送更新触发器。

public class Server implements Runnable {
    private Thread t;
    private int port;
    private ServerSocket serverSocket;
    private ClientListModel clientListModel = new ClientListModel();
    private ClientListView clientListView = new ClientListView();
    private PostgresMock postgres;

    public Server(int port) {
        this.port = port;
        this.postgres = new PostgresMock("me", "pass123");

        // initiate mvc
        getClientListModel().setClientView(clientListView);
        clientListView.setClientModel(getClientListModel());

        t = new Thread(this);
        t.start();
    }

    @Override
    public void run() {
        try {
            serverSocket = new ServerSocket(port);
            System.out.println("listening on port " + port);
            while (true) {
                new ClientHandler(serverSocket.accept(), this);
            }
        } catch (IOException e) {
            System.out.println(e);
        }

    }

    public void updateClients() {
        for (ClientHandler client : getClientListModel().getListe()) {
            client.sendMsg("trigger update");
        }
    }

    public void close() throws IOException {
        this.serverSocket.close();
    }

    public ClientListModel getClientListModel() {
        return clientListModel;
    }

    public PostgresMock getPostgres() {
        return postgres;
    }

    public static void main(String[] args) {
        Server server = new Server(5555);
    }

}

这是客户端处理程序:

public class ClientHandler implements Runnable {
    private Thread t;
    private Socket clientSocket;
    private PrintWriter out;
    private BufferedReader in;
    private Server server;

    public ClientHandler(Socket socket, Server server) {
        this.clientSocket = socket;
        this.server = server;
        server.getClientListModel().add(this);

        t = new Thread(this);
        t.start();
    }

    @Override
    public void run() {

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

            String inputLine;
            while ((inputLine = in.readLine()) != null) {
                
                // IN
                System.out.println("in: " + inputLine);
                
                JSONArray data = server.getPostgres().execute(inputLine);
                
                if (!isSelectStatement(inputLine)) {
                    server.updateClients();
                }
                if (data == null) {
                    sendMsg("nil");
                    continue;
                }
                
                sendMsg(data.toString());
                
                // OUT
                System.out.println("out: " + data.toString());
                
            }
            server.getClientListModel().remove(this);
        } catch (IOException e) {
            System.err.println(e);
            server.getClientListModel().remove(this);
        } 
    }

    public Socket getClientSocket() {
        return clientSocket;
    }

    private boolean isSelectStatement(String sql) {
        return sql.toUpperCase().startsWith("SELECT") || sql.toUpperCase().startsWith("WITH");
    }

    public void sendMsg(String msg) {
        out.println(msg);
    }

}

它检测是否要执行 select 语句或其他语句。如果不是 select 语句,服务器将触发更新。

Postgres 类只是一个模拟类。

public class PostgresMock {

    public PostgresMock(String nutzer, String passwort) {

    }

    public JSONArray execute(String sqlStatement) {
        return new JSONArray();
    }
}

最后是客户端类。它做了很多事情,但最重要的是它触发了实际的更新方法。

public class Client implements Runnable {
    private volatile String msgOut = null;
    private volatile String msgIn = null;
    private volatile boolean done = true;
    private int queryTimeout = 1000;

    private Socket clientSocket;
    private BufferedReader in;
    private PrintWriter out;

    private String ip;
    private int port;

    private Thread t;

    private static volatile boolean active = true;

    private static int clockRate = 1;

    public Client(String ip, int port) {
        this.ip = ip;
        this.port = port;
        t = new Thread(this);
        t.start();
    }

    @Override
    public void run() {

        try {
            connect();

            String response;

            while (active) {

                if (getMsgOut() != null) {
                    out.println(getMsgOut());
                    setMsgOut(null);
                }

                response = readResponse();

                if (response != null) {
                    System.out.println(java.time.LocalDateTime.now() + ": in: " + response);
                }

                pollResponse(response);

                // slow down loop
                Thread.sleep(clockRate);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void connect() {
        while (clientSocket == null && active) {
            try {
                clientSocket = new Socket(ip, port);
                out = new PrintWriter(clientSocket.getOutputStream(), true);
                in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            } catch (UnknownHostException e) {
                System.out.println(e);
            } catch (IOException e) {
                System.out.println(e);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

    public String readResponse() throws IOException {
        String userInput = null;
        while (in.ready() && (userInput = in.readLine()) != null) {
            return userInput;
        }
        return userInput;
    }

    private void pollResponse(String response) {
        if (response == null) {
            return;
        }
        if (response.equals("trigger update")) {
            System.out.println("do update stuff here ..."); // trigger your update event here
        } else {
            setMsgIn(response);
        }
    }

    public String query(String sql, long timeout) {
        while (!done) {} // I don't know if this is actually necessary

        done = false;

        long start = System.currentTimeMillis();

        setMsgIn(null);
        setMsgOut(sql);

        while ((System.currentTimeMillis() - start < timeout) && getMsgIn() == null) {}
        
        setMsgOut(null);

        done = true;

        return getMsgIn();
    }

    public String query(String sql) {
        return query(sql, queryTimeout);
    }

    String getMsgOut() {
        return msgOut;
    }

    void setMsgOut(String msgOut) {
        this.msgOut = msgOut;
    }

    String getMsgIn() {
        return msgIn;
    }

    void setMsgIn(String msgIn) {
        this.msgIn = msgIn;
    }
    
    public static void main(String[] args) {
        Client c = new Client("127.0.0.1", 5555);
        c.query("select * from atable;");
        c.query("insert into atable (avalue) values ('anything');");
    }

}

我没有对代码做很多优化,但希望它有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.