Netty - Как получить ответ сервера в клиенте - PullRequest
10 голосов
/ 29 января 2012

Я в основном там с Нетти, но одна концепция все еще намекает на меня, и я не могу найти ничего в уроках и так далее. Во-первых, я понимаю, что Netty асинхронный, но у клиента должен быть способ вызвать сервер и получить ответ за пределами обработчика. Позвольте мне объяснить больше.

У меня есть клиент, как показано ниже. И, пожалуйста, обратите внимание, что я понимаю, что он загружается, и при каждом вызове устанавливается новое соединение, это просто для того, чтобы сделать пример меньше и более кратким. Пожалуйста, игнорируйте этот факт.

Client.java

// ServerResponse is a result from the server, in this case 
// a list of users of the system (ignore that each time it's all bootstrapped).

public User[] callServerForInformationFromGUIWidget()
{
    ClientBootstrap bootstrap = new ClientBootstrap(...);
    bootstrap.setPipelineFactory(...);

    ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));
    Channel channel = future.awaitUninterruptibly().getChannel();

    // Where request is a POJO sent to the server, 
    // with a request such as get me a list of users
    RequestPojo request = new RequestPojo(requestUserListCommand);

    ChannelFuture lastWriteFuture = channel.write(request);

    if(lastWriteFuture != null)
        lastWriteFuture.awaitUninterruptibly();
}

Теперь я понимаю, как получить данные на сервере и запустить результат. Единственное, как мне справиться с этим на стороне клиента? Да, класс clientHandler может делать что-то вроде следующего:

ClientHandler.java

@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) 
{
    User[] users = (User[])e.getMessage();
}

Проблема в том, как клиентский код на самом деле получает такой результат? Все примеры похожи на службу чата, где событие запускает что-то еще на клиенте, который не ожидает ответа. Даже в примере с http-клиентом этого нет. В целом документация действительно хорошая, но в ней нет информации о том, как выполнять обратные вызовы. В любом случае, в этом случае мне нужен клиент, чтобы получить ответ от сервера, и на основании результатов он будет делать то, что ему нужно.

Другими словами, как мне написать клиенту сделать что-то вроде этого:

IdealClient.java

// ServerResponse is a result from the server, in this case 
// a list of users of the system.

public User[] callServerForInformationFromGUIWidget()
{
    ...
    RequestPojo request = new RequestPojo(requestUserListCommand);
    ChannelFuture lastWriteFuture = channel.write(request);

    if(lastWriteFuture != null)
        lastWriteFuture.awaitUninterruptibly();

    User[] users = resultFromCallToServer();

    performSomeAction(users);
}

Потому что обработчик не знает, кто ищет ответ или кто задал вопрос. А если это сделано в обработчике, то как?

Возвращаясь к моим комментариям о примерах, примеры http-клиента (и обработчика) просто выводят результат в System.out. Если бы у вас был графический интерфейс, как бы вы передавали результат из вашего запроса в графический интерфейс? Я никогда не видел примеров для этого.

Ответы [ 2 ]

5 голосов
/ 29 января 2012

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

//----------------- Event --------------
public class DataChangeEvent {
    private String message;

    public DataChangeEvent(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }


}

//----------------- Listener --------------
public interface DataChangeListenter {
    public void dataChangeEvent(DataChangeEvent event);
}

//----------------- Event Handler that fires the dataChange events --------------
// This class needs to be static since you need to register all your classes that want to be notified of data change events
public class DataChangedHandler {
    private static List<DataChangeListenter> listeners = new ArrayList<DataChangeListenter>();

    public static void registerDataChangeListener(DataChangeListenter listener) {
        listeners.add(listener);
    }

    public static void fireDataChange(DataChangeEvent dataChangeEvent) {
        for(DataChangeListenter listenter : listeners) {
            listenter.dataChangeEvent(dataChangeEvent);
        }
    }
}

//----------------- Example class that implements the listener and registers itself for events --------------
public class ProcessMessage implements DataChangeListenter {

    public ProcessMessage() {
        DataChangedHandler.registerDataChangeListener(this);
    }

    public void dataChangeEvent(DataChangeEvent event) {
        //Depending on your protocal, I use Antlr to parse my message
        System.out.println(event.getMessage());
    }


}

//---------------- Netty Handler -----------
public class TelnetClientHandler extends SimpleChannelHandler {

    private static final Logger logger = Logger.getLogger(TelnetClientHandler.class.getName());

    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
        String message = (String) e.getMessage();
        DataChangedHandler.fireDataChange(message);
    }
}
1 голос
/ 29 января 2012

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

Это было почти то, что я пытался сказать. Ваш обработчик создается в PipelineFactory, который легко передает параметры в обработчик оттуда:

    bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
        public ChannelPipeline getPipeline() throws Exception {
            ChannelPipeline pipeline = Channels.pipeline();

            pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.nulDelimiter()));
            pipeline.addLast("decoder", new XMLDecoder() );
            pipeline.addLast("encoder", new XMLEncoder() );
            // notice here I'm passing two objects to the Handler so it can 
            // call the UI.
            pipeline.addLast("handler", new MyHandler(param1, param2)); 

            return pipeline;
        }
    });

Когда вы создаете свой конвейер, вы добавляете ваш обработчик при новом соединении. Просто передайте один или несколько объектов, которые позволяют ему связаться с пользовательским интерфейсом или контроллером.

...