1

当您设计一个将连接到许多服务器的客户端时,例如爬虫。

您将编写类似的代码:

// the pipeline
public class CrawlerPipelineFactory  implements ChannelPipelineFactory {
    public ChannelPipeline getPipeline() throws Exception {
        return Channels.pipeline(new CrawlerHandler());
    }
}

// the channel handler
public class CrawlerHandler extends SimpleChannelHandler {
    @Override
    public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        // ...
    }
}


// the main :
public static void main(){
    ChannelFactory factory = new NioClientSocketChannelFactory(Executors.newCachedThreadPool(),Executors.newCachedThreadPool());

    ClientBootstrap scannerBootstrap = new ClientBootstrap(factory);
    scannerBootstrap.setPipelineFactory(new CrawlerPipelineFactory());

    while(true){
        MyURL url = stack.pop();
        ChannelFuture connect = scannerBootstrap.connect(url.getSocketAddress()); 
    }
}

现在,当您在 ApplicationHandler 中时,实现您的 SimpleChannelHandler 或 WhatEverStreamHandler 的东西(在示例中为 CrawlerHander)您获得的唯一信息是您正在连接的 socketAdress,您可以在“public void channelConnected()”函数中恢复.

好的,但是如果我想恢复一些用户数据,比如您在我的代码示例中看到的 MyURL 对象,该怎么办?

我使用了一个肮脏的黑客,我使用了 Map<"ip:port",MyURL> 所以我可以在 channelConnected 中检索相关数据,因为我知道我连接的 ip:port 。

这个 hack 真的很脏,如果你同时连接到同一个服务器,它就行不通(或者你必须绑定到本地端口并使用像“localport:ip:remoteport”这样的键,但它太脏了)。

所以我正在寻找将数据传递给 CrawlerHander 的好方法是什么?

如果我们可以通过引导程序的 connect() 方法传递这些数据,那就太酷了。我知道我可以在我的 ChannelPipelineFactory.getPipeline() 中传递参数,因为它是通过 connect() 调用的。但现在我们不能,所以这是我使用的另一个肮脏的黑客:

编辑:

// the main
while(!targets.isEmpty()){
    client.connect("localhost",111); // we will never connect to localhost, it's a hack
}

// the pipleline
public ChannelPipeline getPipeline() throws Exception {
    return Channels.pipeline(
        new CrawlerHandler(targets.pop()) // I specify each new host to connect here
    );
}

// in my channel handler
// Now I have the data I want in the constructor, so I m sure I get them before everything is called
public class CrawlerHandler extends SimpleChannelHandler {
    ExtraParameter target;
    public CrawlerHandler(ExtraParameter target) {
        this.target =  target;

// but, and it's the most dirty part, I have to abort the connection to localhost, and reinit a new connection to the real target
    boolean bFirstConnect=true;
    @Override
    public void connectRequested(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        if(bFirstConnect){
            bFirstConnect = false;
            ctx.getChannel().connect(target.getSocketAddr());
        }
4

2 回答 2

1

您可以通过 Bootstrap 将变量传递给 Channel。

Netty.io 4.1 & SO - 在创建之前向通道添加属性

很晚才更新到这个答案。

于 2019-03-26T15:14:29.483 回答
0

您可以使用 ChannelLocal 或在 ChannelHandlerContext 中(或在最新的 Netty 3.x 中的自己的 Channel 中)使用连接未来侦听器将数据传递给新连接的通道/通道处理程序。在下面的示例中,使用了 ChannelLocal。

public class ChannelDataHolder {
  public final static ChannelLocal<String> CHANNEL_URL = new ChannelLocal<String>(true);
}

  // for each url in bootstrap

  MyURL url = ....; 
  ChannelFuture cf = scannerBootstrap.connect(url.getSocketAddress());

  final String urlString = url.getUrl();

  cf.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                ChannelDataHolder.CHANNEL_URL.set(future.getChannel(), urlString);
            }
   });



  //In the handler

 public class CrawlerHandler extends SimpleChannelHandler {
   @Override
   public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
      String urlString = ChannelDataHolder.CHANNEL_URL.get(ctx.getChannel()); 
      // ...use the data here
   }
 }

注意:代替 ChannelLocal,您可以使用设置和获取数据

  • ChannelHandlerContext.setAttachment()/getAttachment()

  • 最新 3.x 版 Netty 中的 Channel.setAttachment()/getAttachment()

但是这两种方法都不支持类型安全。

于 2012-04-19T19:33:46.613 回答