Java Netty-如何在客户机中获取服务器响应

Java Netty-如何在客户机中获取服务器响应,java,networking,client-server,netty,Java,Networking,Client Server,Netty,我大部分时间都和Netty在一起,但有一个概念仍然在暗指我,我在教程中找不到任何东西等等。首先,我确实理解Netty是异步的,但是必须有一种方法让客户机调用服务器,并且能够在处理程序之外获得响应。让我再解释一下 我有一个客户,如下图所示。请注意,我知道它是自举的,每次调用都会建立一个新的连接,这只是为了使示例更小、更简洁。请忽略这个事实 Client.java // ServerResponse is a result from the server, in this case // a li

我大部分时间都和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();
}
// 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);
}
现在我了解了如何在服务器上获取数据,并返回结果。唯一的问题是我如何在客户端处理它?是的,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 (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();
}
// 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。如果您有一个GUI,您将如何将请求的结果传递给GUI?我从来没有见过这样的例子。

您必须使用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;
        }
    });

创建管道时,将在新连接上添加处理程序。只需将允许其通信的一个或多个对象传递回UI或控制器。

Jestan是正确的。在我的案例中,我有一个客户需要处理报价数据。我使用Antlr进行解析。我在解析器中触发事件,但在我的例子中,我的协议是基于字符串的。下面是一个没有Antlr的示例,我在您的案例中传递字符串消息,它可能是用户

//----------------- 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{
私有字符串消息;
公共DataChangeEvent(字符串消息){
this.message=消息;
}
公共字符串getMessage(){
返回消息;
}
}
//-----------------听众--------------
公共接口DataChangeListener{
公共作废dataChangeEvent(dataChangeEvent事件);
}
//-----------------激发dataChange事件的事件处理程序--------------
//该类需要是静态的,因为您需要注册所有希望收到数据更改事件通知的类
公共类DataChangedHandler{
私有静态列表侦听器=新的ArrayList();
公共静态无效注册表DataChangeListener(DataChangeListener侦听器){
添加(侦听器);
}
公共静态无效fireDataChange(DataChangeEvent DataChangeEvent){
for(DataChangeListener侦听器:侦听器){
Listener.dataChangeEvent(dataChangeEvent);
}
}
}
//-----------------实现侦听器并为事件注册自身的示例类--------------
公共类ProcessMessage实现DataChangeListener{
公共处理消息(){
DataChangedHandler.registerDataChangeListener(此);
}
公共无效dataChangeEvent(dataChangeEvent事件){
//根据您的协议,我使用Antlr解析消息
System.out.println(event.getMessage());
}
}
//----------------内蒂处理程序-----------
公共类TelnetClientHandler扩展了SimpleChannelHandler{
私有静态最终记录器Logger=Logger.getLogger(TelnetClientHandler.class.getName());
@凌驾
收到公共无效消息(ChannelHandlerContext ctx,MessageEvent e){
字符串消息=(字符串)e.getMessage();
DataChangedHandler.fireDataChange(消息);
}
}

例如,在http客户端示例中,结果被转储到System.out。如果您有一个必须将结果传递给的GUI,该怎么办?例如,它必须被传递到处理程序不知道的JTextArea,或者可能是JDialog,等等。换句话说,按下JButton,事件通过客户端调用服务器。基本上如何将结果从服务器传递回JButton的调用方法。另外,由于JButton处理程序可能会在JTextArea中显示结果,因此可能是JDialog等。您希望使用回调(侦听器)来处理响应,如hotpottao asych模式(也使用Netty),然后你必须在messageReceived()中处理这一点,我认为默认情况下不会提供这种回调,因为它们更特定于协议/应用程序的功能?用代码修改了我的答案,试图解释我在原始答案中的意思。我会有一个控制器,可以更新我的UI,并将其传递给处理程序。我不会将单个UI元素直接传递给处理程序,因为它们之间的耦合太多了