简体   繁体   English

Netty Nio java中的通信

[英]Communication in Netty Nio java

I want to create a communication system with two clients and a server in Netty nio. 我想在Netty nio中创建一个具有两个客户端和一个服务器的通信系统。 More specifically, firstly, I want when two clients are connected with the server to send a message from the server and after that to be able to exchnage data between the two clients. 更具体地说,首先,我希望当两个客户端与服务器连接以从服务器发送消息时,之后能够在两个客户端之间传递数据。 I am using the code provided from this example . 我正在使用此示例提供代码 My modifications in the code can be found here: link 我可以在这里找到我对代码的修改: 链接

It seems that the channelRead in the serverHandler works when the first client is connceted so it always return 1 but when a second client is connected does not change to 2. How can I check properly from the server when both clients are connected to the server? 似乎serverHandler中的channelRead在第一个客户端被连接时工作,因此它总是返回1但是当连接第二个客户端时不会更改为2.当两个客户端连接到服务器时,如何从服务器正确检查? How can I read this value dynamically from my main function of the Client? 如何从客户端的主要功能中动态读取此值? Then which is the best way to let both clients communicate? 那么这是让两个客户沟通的最佳方式?

EDIT1: Apparently it seems that the client service is running and close directly so every time that I am running a new NettyClient is connected but the connection is closed after that. EDIT1:显然似乎客户端服务正在运行并直接关闭,因此每次运行新的NettyClient时都会连接,但之后关闭连接。 So the counter is always chnages from zero to one. 所以计数器总是从0到1。 As I was advised in the below comments I tested it using telnet in the same port and the counter seems to increasing normally, however, with the NettyClient service no. 正如我在下面的评论中所建议的那样,我在相同的端口使用telnet测试它,并且计数器似乎正常增加,但是,NettyClient服务没有。

EDIT2: It seems that the issue I got was from future.addListener(ChannelFutureListener.CLOSE); EDIT2:看来我得到的问题来自future.addListener(ChannelFutureListener.CLOSE); which was in channelRead in the ProcessingHandler class . 它位于ProcessingHandler class channelRead中。 When I commented it that out it seems that the code works. 当我评论它似乎代码工作。 However, am not sure what are the consequences of commented that out. 但是,我不确定评论的后果是什么。 Moreover, I want from my main function of the client to check when the return message is specific two. 此外,我希望从客户端的主要功能检查返回消息何时是特定的两个。 How, could I create a method that waits for a specific message from server and meanwhile it blocks the main functionality. 如何,我可以创建一个等待来自服务器的特定消息的方法,同时它阻止主要功能。

 static EventLoopGroup workerGroup = new NioEventLoopGroup();
 static Promise<Object> promise = workerGroup.next().newPromise(); 
 public static void callClient() 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();
    } finally {
        //workerGroup.shutdownGracefully();
    }
}

I want inside the main function to call the method and return the result and when it is 2 to continue with the main functionality. 我想在main函数内部调用方法并返回结果,当它为2时继续使用主要功能。 However, I cannot call callClient inside the while since it will run multiple times the same client. 但是,我无法在内部调用callClient,因为它将在同一个客户端运行多次。

   callBack();
    while (true) {
        Object msg = promise.get();
        System.out.println("Case1: the connected clients is not two");
        int ret = Integer.parseInt(msg.toString());
        if (ret == 2){
            break;
        }
    }
   System.out.println("Case2: the connected clients is two");
   // proceed with the main functionality

How can I update the promise variable for the first client. 如何更新第一个客户端的promise变量。 When I run two clients, for the first client I always received the message : 当我运行两个客户端时,对于第一个客户端,我总是收到消息:

Case1: the connected clients is not two 案例1:连接的客户端不是两个

seems that the promise is not updated normally, while for the second client I always received the: 似乎承诺没有正常更新,而对于第二个客户我总是收到:

Case2: the connected clients is two 案例2:连接的客户端是两个

If my memory is correct, ChannelHandlerContext is one per channel and it can have multiple ChannelHandlers in it's pipeline. 如果我的内存是正确的,ChannelHandlerContext是每个通道一个,它可以在它的管道中有多个ChannelHandler。 Your channels variable is an instance variable of your handler class. 您的channels变量是处理程序类的实例变量。 And you create a new ProcessingHandler instance for each connection. 并为每个连接创建一个新的 ProcessingHandler实例。 Thus each will have one and only one connection in channels variable once initialized - the one it was created for. 因此,一旦初始化,每个将在channels变量中具有一个且仅一个连接 - 它是为其创建的。

See new ProcessingHandler() in initChannel function in the server code (NettyServer.java). 请参阅服务器代码(NettyServer.java)中initChannel函数中的new ProcessingHandler() )。

You can either make channels variable static so that it is shared between ProcessingHandler instances. 您可以将channels变量设置为静态,以便在ProcessingHandler实例之间共享它。 Or you can create a single ProcessingHandler instance elsewhere (eg as a local variable in the run() function) and then pass that instance to addLast call instead of new ProcessingHandler() . 或者您可以在其他地方创建单个ProcessingHandler实例(例如,作为run()函数中的局部变量),然后将该实例传递给addLast调用而不是new ProcessingHandler()

Why the size of ChannelGroup channels is always one. 为什么ChannelGroup频道的大小始终是一个。 Even if I connect more clients? 即使我连接更多客户?

Because child ChannelInitializer is called for every new Channel (client). 因为为每个新Channel (客户端)调用子ChannelInitializer There you are creating new instance of ProcessingHandler , so every channel see its own instance of ChannelGroup . 在那里,您正在创建ProcessingHandler新实例,因此每个通道都会看到自己的ChannelGroup实例。

Solution 1 - Channel Attribute 解决方案1 ​​ - 通道属性

Use Attribute and associate it with Channel . 使用属性并将其与Channel关联。

Create attribute somewhere (let's say inside Constants class): 在某处创建属性(让我们说在Constants类中):

public static final AttributeKey<ChannelGroup> CH_GRP_ATTR = 
       AttributeKey.valueOf(SomeClass.class.getName());

Now, create ChannelGroup which will be used by all instances of ProcessingHandler : 现在,创建将由ProcessingHandler的所有实例使用的ChannelGroup:

final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

Update your child ChannelInitializer in NettyServer : 在NettyServer中更新您的子ChannelInitializer

@Override
public void initChannel(SocketChannel ch) throws Exception {
    ch.pipeline().addLast(
        new RequestDecoder(), 
        new ResponseDataEncoder(), 
        new ProcessingHandler());

    ch.attr(Constants.CH_GRP_ATTR).set(channels);
}

Now you can access instance of ChannelGroup inside your handlers like this: 现在您可以在处理程序中访问ChannelGroup的实例,如下所示:

@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
    final ChannelGroup channels = ctx.channel().attr(Constants.CH_GRP_ATTR).get();
    channels.add(ctx.channel());

This will work, because every time new client connects, ChannelInitializer will be called with same reference to ChannelGroup . 这将起作用,因为每次新客户端连接时,将使用与ChannelGroup相同的引用调用ChannelInitializer。

Solution 2 - static field 解决方案2 - 静态字段

If you declare ChannelGroup as static, all class instances will see same ChannelGroup instance: 如果将ChannelGroup声明为static,则所有类实例都将看到相同的ChannelGroup实例:

private static final ChannelGroup channels =
     new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

Solution 3 - propagate shared instance 解决方案3 - 传播共享实例

Introduce parameter into constructor of ProcessingHandler : 将参数引入ProcessingHandler构造函数:

private final ChannelGroup channels;
public ProcessingHandler(ChannelGroup chg) {
    this.channels = chg;
}

Now, inside your NettyServer class create instance of ChannelGroup and propagate it to ProcessingHandler constructor: 现在,你的NettyServer类中创建的实例ChannelGroup ,并将其传播到ProcessingHandler构造函数:

final ChannelGroup channels = new 
      DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

@Override
public void initChannel(SocketChannel ch) throws Exception {
    ch.pipeline().addLast(
        new RequestDecoder(), 
        new ResponseDataEncoder(), 
        new ProcessingHandler(channels)); // <- here
}

Personally, I would choose first solution, because 就个人而言,我会选择第一个解决方案,因为

  • It clearly associate ChannelGroup with Channel context 它明确地将ChannelGroup与Channel上下文相关联
  • You can access same ChannelGroup in other handlers 您可以在其他处理程序中访问相同的ChannelGroup
  • You can have multiple instances of server (running on different port, within same JVM) 您可以拥有多个服务器实例(在同一JVM中的不同端口上运行)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM