2012-04-19 1 views
1

Когда вы создаете клиента, который будет подключаться к большому количеству серверов, например, сканера.Как передать данные приложения/пользователя в ChannelHandler Netty

Вы закодировать что-то вроде этого:

// 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, который вы видите в моем примере кода?

Я использую грязный хак, я использую карту < «ip: port», MyURL>, поэтому я могу получить связанные данные в channelConnected, потому что я знаю ip: порт, к которому я подключен.

Этот хак действительно грязный, он не будет работать, если вы одновременно подключаетесь к одному и тому же серверу (или вам придется привязываться к локальному порту и использовать такой ключ, как «localport: ip: remoteport», но это так грязно).

Итак, я ищу, какой хороший способ передать данные CrawlerHander?

Было бы здорово, если бы мы могли передавать эти данные с помощью метода connect() бутстрапа. Я знаю, что могу передать аргумент в моей ChannelPipelineFactory.getPipeline(), потому что он вызывается через connect(). Но теперь мы не можем, так вот еще один грязный хак я использую:

EDIT:

// 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()); 
     } 
+0

Каковы проблемы синхронизации, о которых вы говорите? Передайте его в ChannelPipelineFactory - это путь к атм. Я думаю, у нас была идея, прежде чем иметь возможность передать какое-то вложение в методе connect, которое затем автоматически передается методу ChannelPipeline.getPipeline(). Но я не смог найти письмо об этом atm. –

+0

Эй, Норман, вы на хорошем пути, вся моя проблема будет решена, если я смогу передать вложение в connect(), чтобы я мог передать ее консулатору ChannelHandler() через мою ChannelPipelineFactory.getPipeline (Object theNewExtraParamater); – Pierre

+0

Извините, что мои последние предложения не были проблемой синхронизации, потому что мой другой метод - грязный, посмотрите на отредактированный пост, я добавил пример кода – Pierre

ответ

0

Вы можете передать данные в обработчик вновь подключен канал/канал с помощью ChannelLocal или в ChannelHandlerContext (или в Channel it self in latest Netty 3.x), используя подключаемый будущий слушатель. В приведенном ниже примере используется 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()

  • Channel.setAttachment()/getAttachment () в последней версии 3.x Netty

, но оба подхода не поддерживают rt тип безопасности.

+1

Извините, но это нехорошее решение. В примере кода вы не можете быть уверены, что cf.addListener (new ChannelFutureListener() будет вызван до того, как будет выполнено соединение, если соединение будет выполнено сразу после connect(), функция channeledConnected будет вызываться до вашего cf.addListener (новая строка ChannelFutureListener() {"строка – Pierre

+0

Вы ошибаетесь :), на практике это невозможно, каналы, которые прослушивают будущие слушатели, всегда выполняются до запуска соответствующего события потоком NioWorker. Например подключиться к будущему слушателю будет выполняться перед каналомConnected() в обработчике. –

+0

Извините, чувак, вы ошибаетесь. Я не говорю о канале FutureListenner, я полностью согласен с вами, что он будет вызываться перед ассоциированным событием. как вы вызываете addListenner, вы вызываете addListenner после подключения(). Как вы можете быть уверены, что соединение не будет выполнено до того, как addLis tener? Вы не можете. – Pierre

 Смежные вопросы

  • Нет связанных вопросов^_^