Communication in Netty Nio java

末鹿安然 提交于 2019-12-04 02:33:40

If my memory is correct, ChannelHandlerContext is one per channel and it can have multiple ChannelHandlers in it's pipeline. Your channels variable is an instance variable of your handler class. And you create a new ProcessingHandler instance for each connection. Thus each will have one and only one connection in channels variable once initialized - the one it was created for.

See new ProcessingHandler() in initChannel function in the server code (NettyServer.java).

You can either make channels variable static so that it is shared between ProcessingHandler instances. Or you can create a single ProcessingHandler instance elsewhere (e.g. as a local variable in the run() function) and then pass that instance to addLast call instead of new ProcessingHandler().

Why the size of ChannelGroup channels is always one. Even if I connect more clients?

Because child ChannelInitializer is called for every new Channel (client). There you are creating new instance of ProcessingHandler, so every channel see its own instance of ChannelGroup.

Solution 1 - Channel Attribute

Use Attribute and associate it with Channel.

Create attribute somewhere (let's say inside Constants class):

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:

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

Update your child ChannelInitializer in NettyServer :

@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:

@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.

Solution 2 - static field

If you declare ChannelGroup as static, all class instances will see same ChannelGroup instance:

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

Solution 3 - propagate shared instance

Introduce parameter into constructor of 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:

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
  • You can access same ChannelGroup in other handlers
  • You can have multiple instances of server (running on different port, within same JVM)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!