我的客户端有一个异步方法,它向服务器发送一个udp请求并返回 Promise 。我需要以某种方式将此Promise传递给我的一个入站处理程序,以便它能够将其标记为" done"然后通过 Promise.setSuccess(结果)发回响应。
你是怎么做到的?一旦入站处理程序收到响应,您如何将请求实例与处理程序的响应相关联?
本网站建议的一些方法对我来说也没有用处:
我的代码:
客户端:
private final EventLoopGroup group;
private final Bootstrap bootstrap;
private Channel channel;
private BlockingQueue<GameQuery> requestQueue;
public SourceServerQueryClient() {
group = new NioEventLoopGroup();
bootstrap = new Bootstrap();
configureBootstrap(bootstrap);
try {
channel = bootstrap.bind(0).sync().channel();
} catch (InterruptedException e) {
log.error("InterruptedException", e);
}
}
public void configureBootstrap(Bootstrap bootstrap) {
//Contains our request queue
requestQueue = new ArrayBlockingQueue<>(50);
//Configure our bootstrap
bootstrap.group(group).channel(NioDatagramChannel.class)
.handler(new ChannelInitializer<NioDatagramChannel>() {
@Override
protected void initChannel(NioDatagramChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new ErrorHandler());
pipeline.addLast(new SourcePacketVerifierHandler());
pipeline.addLast(new SourceQueryEncoder());
pipeline.addLast(new MasterServerDecoder());
pipeline.addLast(new SourceServerInfoDecoder());
pipeline.addLast(new QueryResponseHandler(requestQueue));
}
});
}
public Promise<SourceServer> getServerDetails(InetSocketAddress address, QueryCallback<SourceServer> callback) {
Promise<SourceServer> p = sendQuery(new ServerInfoQuery(address));
p.addListener(future -> {
if (future.isSuccess())
callback.onReceive(p.get());
});
return p;
}
private Promise sendQuery(GameQuery query) {
Promise promise = channel.eventLoop().newPromise();
query.setPromise(promise);
ChannelFuture f = null;
try {
requestQueue.put(query);
f = channel.writeAndFlush(query).sync();
} catch (InterruptedException e) {
if (f != null)
promise.setFailure(f.cause());
}
return promise;
}
public static void main(String[] args) throws InterruptedException {
try (SourceServerQueryClient client = new SourceServerQueryClient()) {
Promise query1 = client.getServerDetails(new InetSocketAddress("169.38.68.44", 27015), msg -> log.info("REPLY FROM SERVER: {}, EXPECTED: 169.38.68.44:27015", msg.toString()));
Promise query2 = client.getServerDetails(new InetSocketAddress("112.211.234.23", 27016), msg -> log.info("REPLY FROM SERVER: {}, EXPECTED: 112.211.234.23:27016", msg.toString()));
query2.awaitUninterruptibly();
log.info("Done");
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
入站处理程序:
public class QueryResponseHandler<T extends GameQuery> extends SimpleChannelInboundHandler<Object> {
private static final Logger log = LoggerFactory.getLogger(QueryResponseHandler.class);
private BlockingQueue<T> requestQueue;
public QueryResponseHandler(BlockingQueue<T> requestQueue) {
this.requestQueue = requestQueue;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
log.debug("From QueryResponseHandler: {}", msg);
T request = requestQueue.poll();
Promise p = request.getPromise();
if (request != null) {
p.setSuccess(msg);
} else
p.setFailure(new BufferUnderflowException());
}
}
在我的测试中,我同时运行了两个请求。第一个不应该工作,因为它是一个死的服务器。第二个电话应该发回一个回复。
输出
REPLY FROM SERVER: 112.211.234.23:27016, EXPECTED: 169.38.68.44:27015
正如您所看到的,由于设计的原因,它没有像预期的那样成功。第一个查询收到了针对第二个查询的响应。
我已经没有想法如何正确设计,所以任何输入都将非常感谢!感谢。
答案 0 :(得分:1)
可能会添加一个&#34; id&#34;请求,当你&#34;民意调查&#34;它,你确实可以得到正确的一个(所以通过一种队列地图,而不仅仅是一个队列)?
这个&#34; id&#34;可以基于你显示的输出信息(也许不是?)。
WDYT?
答案 1 :(得分:0)
由于UDP 无状态,您需要提供一条消息ID,以便能够关联并跟进对请求的回复。
虽然如果你想进行有状态的沟通,为什么不简单地使用TCP?