Обеспечение правильного ответа на запрос клиента

#java #sockets

#java #сокеты

Вопрос:

Ищу предложения по решению следующей проблемы.

Я работаю над приложением (клиентом), которое использует один TCP-сокет для чтения и записи сообщений на сервер.

Сообщение является одним из нескольких предопределенных типов, которые будут проанализированы по прибытии.

Сервер может передать сообщение в любое время.

Клиент отправит сообщение на сервер и ожидает ответа. Однако (и здесь возникает мой вопрос), я не могу прочитать из сокета, чтобы получить это сообщение, поскольку я понятия не имею, когда оно может быть доставлено. В большинстве случаев ответное сообщение клиента будет доставлено сразу после запроса клиента. Однако иногда сначала отправляется другое широковещательное сообщение.

Канал чтения сокета помещается в очередь блокировки одним потоком-производителем. В отдельном потоке-потребителе любые сообщения удаляются из очереди и отправляются для дальнейшей обработки. Чтобы получить ожидаемые ответы клиента, должен ли я использовать идиому источника событий / прослушивателя, чтобы мои клиенты получали уведомления о поступлении их ответов (и если)?

Спасибо за любые предложения.

РЕДАКТИРОВАТЬ: Я думаю, что мой вопрос неясен, поскольку предложения пока не касаются рассматриваемой проблемы. В конце концов, я использовал идиому источника событий / прослушивателя, чтобы справиться с этим. Еще раз, спасибо за ошибку, но я считаю, что это закрыто. Модератор может даже захотеть удалить этот вопрос.

Ответ №1:

Это прекрасная возможность использовать механизмы сериализации Java. Вы можете сделать что-то вроде этого (предположим, вы перехватываете все соответствующие исключения, которые для краткости опущены)

 class ClientListeningThread {

    ObjectInputStream in;
    ObjectOutputStream out;

    ClientListeningThread(Socket s) {
        in = new ObjectInputStream(s.getInputStream());
        out = new ObjectOutputStream(s.getOututStream());
    }

    public void run() {
        while(true) {
            ClientMessage message = (ClientMessage)in.readObject();
            engine.addMessage(this,message); // add to the message queue, signifiying which listening thread to give the response to
        }
    }

    public void send(ServerMessage message) {
        out.writeObject(message);
    }

}
  

В ваших сообщениях могут даже содержаться обратные вызовы для

класс LoginMessage {

 public final String username;
public final String password;

public LoginMessage(String username, String password) {
    this.username = username;
    this.password = password;
}

public void callback(ClientListeningThread thread, ServerProcessor engine) {
    ServerMessage response = engine.attemptLogin(username,password);
    thread.send(response);
}
  

}

И в вашем движке

 while(!requests.isEmpty()) {
    ClientRequest request = requests.poll();
    ClientListeningThread = request.thread;
    ClientMessage message = request.message;
    request.callback(thread,this);
}
  

Комментарии:

1. Не понимаю, где здесь вступает в игру сериализация. Также, пожалуйста, посмотрите мою правку. В любом случае спасибо.

Ответ №2:

Вы можете реализовать это с помощью прослушивателя и пула кэшированных потоков. Таким образом, вы можете создать доступный для выполнения класс, который выполняет обработку сообщения. Затем создайте класс прослушивателя, который просто создает экземпляр сокета (или серверного сокета) и создает ваш пул потоков. В вашем классе listener создайте бесконечный цикл while, который прослушивает входящие запросы, и передайте socket.accept в конструктор вашего выполняемого объекта, чтобы он мог обрабатывать любые входные данные из сокета.

Код будет выглядеть примерно так:

 public class MessageHandler implements Runnable {

    String msg = "";
    Socket socket = null;
    BufferedReader in = null;
    PrintWriter out = null;

    public void MessageHandler(ServerSocket socket){
        this.socket = socket;
    }

    @Override
    public void run(){
        //Message read from socket
        in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        System.out.println("Message: "   in.readLine());

        //Reply send back through same socket
        out = new PrintWriter(socket.getOutputStream(), true);
        out.println("MESSAGE RECEIVED. THANKS.");
    }
}
  

И ваш класс прослушивания будет выглядеть примерно так:

 public class SocketListener {

    ServerSocket socket = null;
    ExecutorService threadExecutor = null;
    Runnable runnable = null;

    public static void main (String[] args){
        socket = new ServerSocket(8181);

        /* Socket will always be listening, when a request arrives a thread will handle
         * the incoming stream.
         */
        while(true) {
            threadExecutor = Executors.newCachedThreadPool();
            runnable = new MessageHandler(socket.accept);
            threadExecutor.execute(runnable);
        }
    }
}
  

Я не уверен, что этот код будет скомпилирован, но ваша серверная реализация может выглядеть очень похоже на это и будет масштабируемой и надежной.

Ваш клиент может быть почти идентичным, хотя вы будете использовать сокет, а не ServerSocket, и, возможно, обработаете ваше сообщение по-другому.