Java nettynio中承诺的异步更新
我有一个交换信息的服务器和客户端架构。我想从服务器返回连接通道的数量。我想使用promise将服务器的消息返回给客户端。我的代码是:Java nettynio中承诺的异步更新,java,sockets,web,netty,nio,Java,Sockets,Web,Netty,Nio,我有一个交换信息的服务器和客户端架构。我想从服务器返回连接通道的数量。我想使用promise将服务器的消息返回给客户端。我的代码是: public static void callBack () throws Exception{ String host = "localhost"; int port = 8080; try { Bootstrap b = new Bootstrap(); b.group(workerGroup);
public static void callBack () throws Exception{
String host = "localhost";
int port = 8080;
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new RequestDataEncoder(), new ResponseDataDecoder(), new ClientHandler(promise));
}
});
ChannelFuture f = b.connect(host, port).sync();
//f.channel().closeFuture().sync();
}
finally {
//workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
callBack();
while (true) {
Object msg = promise.get();
System.out.println("The number if the connected clients is not two");
int ret = Integer.parseInt(msg.toString());
if (ret == 2){
break;
}
}
System.out.println("The number if the connected clients is two");
}
客户机处理程序中存储从服务器接收到的消息的代码。在Netty框架中,a和a是一次写入对象,这一原则使它们更易于在多线程环境中使用 由于承诺不能满足您的要求,我们需要看看其他技术是否适合您的条件,您的条件基本上可以归结为:
- 从多个线程读取
- 仅从单个线程写入(在Netty通道内,读取方法只能由1个线程同时执行,除非通道标记为可共享)
private static volatile int connectedClients = 0;
public static void callBack () throws Exception{
//....
ch.pipeline().addLast(new RequestDataEncoder(), new ResponseDataDecoder(),
new ClientHandler(i -> {connectedClients = i;});
//....
}
public static void main(String[] args) throws Exception {
callBack();
while (true) {
System.out.println("The number if the connected clients is not two");
int ret = connectedClients;
if (ret == 2){
break;
}
}
System.out.println("The number if the connected clients is two");
}
public class ClientHandler extends ChannelInboundHandlerAdapter {
public final IntConsumer update;
public ClientHandler(IntConsumer update) {
this.update = update;
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
RequestData msg = new RequestData();
msg.setIntValue(123);
msg.setStringValue("all work and no play makes jack a dull boy");
ctx.writeAndFlush(msg);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println(msg);
update.accept(Integer.parseInt(msg));
}
}
虽然上面的方法应该可以工作,但我们很快就会看到主类中的While循环占用了大量的CPU时间,这可能会影响本地客户端系统的其他部分,幸运的是,如果我们向系统中添加其他部分,即同步,这个问题也是可以解决的。通过将connectedClients
的初始读取放在同步块之外,我们仍然可以从“真”情况下的快速读取中获益,而“假”情况下,我们可以确保可以在系统其他部分使用的重要CPU周期的安全
为了解决这个问题,我们在阅读时使用以下步骤:
connectedClients
的值存储在单独的变量中private static volatile int connectedClients = 0;
private static final Object lock = new Object();
public static void callBack () throws Exception{
//....
ch.pipeline().addLast(new RequestDataEncoder(), new ResponseDataDecoder(),
new ClientHandler(i -> {
synchronized (lock) {
connectedClients = i;
lock.notifyAll();
}
});
//....
}
public static void main(String[] args) throws Exception {
callBack();
int connected = connectedClients;
if (connected != 2) {
System.out.println("The number if the connected clients is not two before locking");
synchronized (lock) {
while (true) {
connected = connectedClients;
if (connected == 2)
break;
System.out.println("The number if the connected clients is not two");
lock.wait();
}
}
}
System.out.println("The number if the connected clients is two: " + connected );
}
服务器端更改
然而,并不是所有的问题都与客户端有关
由于您向github存储库发布了一个链接,因此当有新用户加入时,您永远不会从服务器向旧客户端发送请求。因为没有这样做,所以客户端永远不会收到有关更改的通知,请确保也这样做。当您说promise时,您的意思是非阻塞吗?我的意思是这个对象msg=promise.get();,此值包含服务器的重新调谐消息。您可以根据您的问题按照我的答案回答不同的问题@konstantin,您的意思是:
promise.get()
返回客户端(通道)的数量已连接到您的服务器??在客户端处理程序中,我正在存储从客户端读取的消息。当我尝试将ConnectedClient添加到InitChannel时(新建ClientHandler(I->{connectedClients=I;})我接收到不兼容的类型,它在接收lambda参数时需要int。@konstantin您注意到我调整了ClientHandler
,我将参数从Promise
更改为IntConsumer
。如果您有一个类型为IntConsumer
的参数,您可以使用接受1的lamba块int作为输入,这是我用于将结果传播到另一个类的方法。好的,我正确编写了代码。我运行了两个客户端,但在这两个客户端中我都收到了以下消息:2017年11月2日11:43:09 AM io.netty.channel.DefaultChannelPipeline onUnhandledInboundException警告:exceptionCaught()事件被触发,并到达管道的尾部。这通常意味着管道中的最后一个处理程序没有处理异常。java.lang.ClassCastException:chat.ResponseData无法转换为java.lang.Integer更新的代码可以在这里找到,这可能是我在CLientHandler内部的原始代码中遗漏的内容
,而不是update.accept(Integer.parseInt(msg));
,而是update.accept(((RequestData)msg.getIntValue());
,它直接从请求数据对象提取大小
private static volatile int connectedClients = 0;
private static final Object lock = new Object();
public static void callBack () throws Exception{
//....
ch.pipeline().addLast(new RequestDataEncoder(), new ResponseDataDecoder(),
new ClientHandler(i -> {
synchronized (lock) {
connectedClients = i;
lock.notifyAll();
}
});
//....
}
public static void main(String[] args) throws Exception {
callBack();
int connected = connectedClients;
if (connected != 2) {
System.out.println("The number if the connected clients is not two before locking");
synchronized (lock) {
while (true) {
connected = connectedClients;
if (connected == 2)
break;
System.out.println("The number if the connected clients is not two");
lock.wait();
}
}
}
System.out.println("The number if the connected clients is two: " + connected );
}