【问题标题】:Netty - How to get server response in the clientNetty - 如何在客户端获取服务器响应
【发布时间】:2012-02-21 08:26:23
【问题描述】:

我大部分时间都在使用 Netty,但一个概念仍在暗示我,我在教程等中找不到任何东西。首先,我确实了解 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);
}

因为处理程序不知道谁在寻找答案,也不知道是谁提出了问题。如果它是在处理程序中完成的,那么如何?

回到我关于示例的 cmets,http 客户端(和处理程序)示例只是将结果转储到 System.out。如果您有一个 GUI,您将如何将请求的结果传递给 GUI?我从来没有见过任何例子。

【问题讨论】:

    标签: java networking client-server netty


    【解决方案1】:

    你必须在 Handler 中使用 messageReceived() 来处理它。我不确定你的问题到底是什么。我的猜测是您对请求的响应会根据提出的请求而变化?也许是对您正在执行的响应的具体描述,该响应必须知道它来自什么请求。您可能能够做的一件事是将一个长期存在的对象传递给知道未完成请求的处理程序,并且它可以在收到响应时匹配响应。管道工厂方法可以将管理器类型对象的引用传递给 Handler。

    这几乎就是我想说的。您的 Handler 是在 PipelineFactory 中创建的,很容易从那里将参数传递给 Handler:

        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;
            }
        });
    

    创建管道时,您将在新连接上添加处理程序。只需传递一个或多个对象,使其能够与 UI 或控制器进行通信。

    【讨论】:

    • 例如,在 http 客户端示例中,结果只是转储到 System.out。如果您有一个必须将结果传递给的 GUI,该怎么办?例如,它必须被传递到处理程序不知道的 JTextArea,或者可能是 JDialog 等。换句话说,按下 JButton 并且事件通过客户端调用服务器。你基本上如何将服务器的结果传回给 JButton 的调用方法。
    • 添加,因为JButton handler可能会在JTextArea中显示结果,可能会是一个JDialog等
    • 你想使用回调(监听器)来处理响应,比如hotpottao asych模式hotpotato.biasedbit.com,(这也是使用Netty),那么你必须在messageReceived中处理它:),我想默认情况下不提供这种回调,因为它们更特定于协议/应用程序做什么?
    • 用代码修改了我的答案,以尝试解释我在原始答案中的意思。我有一个控制器,它可以更新我的 UI 是否合适,并将其传递给处理程序。我不会将单个 UI 元素直接传递给 Handler,因为我的低级网络和我选择使用的 UI 组件之间的耦合太大。
    • @chubbard 这就是我缺少的部分。我不明白你的意思:“你可能能够做的一件事是将一个长期存在的对象传递给知道未完成请求的处理程序,它可以在收到响应时匹配响应。管道工厂方法可以将对管理器类型对象的引用传递给处理程序。”现在回想起来,这很有意义,但是在没有看到代码或仍在学习框架的情况下尝试获得它,不幸的是它太难破译了。
    【解决方案2】:

    杰斯坦是正确的。就我而言,我有一个需要处理价格变动数据的客户。我使用 Antlr 进行解析。我在解析器中触发我的事件,但在我的情况下,我的协议是基于字符串的。下面是一个没有 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);
        }
    }
    

    【讨论】:

    • 我正在尝试按照您的指南注册侦听器,但我得到一个“不兼容的类型:字符串无法转换为 DataChangeEvent”......怎么了?谢谢。
    猜你喜欢
    • 2016-12-03
    • 2014-06-01
    • 2019-05-18
    • 1970-01-01
    • 2018-12-02
    • 2019-12-18
    • 2019-02-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多