#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, и, возможно, обработаете ваше сообщение по-другому.