2012-04-16 9 views
8

Al escribir 2048bytes en el controlador, el método messageRevieved se debe llamar dos veces para recibir los datos de todos ... ¿Cómo puedo recibir los datos 2048bytes enen netty, solo podemos escribir y recibir datos de menos de 1024bytes: ¿cómo podemos escribir o recibir más?

Código

Servidor:

public class Server{ 
    public static void main(String[] args){ 
     ChannelFactory factory=new NioServerSocketChannelFactory(
      Executors.newCachedThreadPool(), 
      Executors.newCachedThreadPool()); 
     ServerBootstrap bootstrap=new ServerBootstrap(factory); 
     bootstrap.setPipelineFactory(new CarPipelineFactory()); 

     bootstrap.setOption("child.tcpNoDelay", true); 
     bootstrap.setOption("child.keepAlive", true); 

     bootstrap.bind(new InetSocketAddress(8989)); 
    } 
} 

Handler servidor:

public class ServerHandler extends SimpleChannelHandler{ 

    public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e){ 
     byte[] resp=data.getBytes();//data is a String greater than 1024bytes; 
     ChannelBuffer buffer=ChannelBuffers.buffer(resp.length); 
     buffer.writerBytes(resp); 
     e.getChannel().write(buffer); 
     buffer.clear(); 
    } 
} 

Cliente:

public class Client{ 
    public static void main(String[] args){ 
     ChannelFactory channelFactory=new NioClientSocketChannelFactory(
      Executors.newCachedThreadPool(), 
      Executors.newCachedThreadPool()); 
     ClientBootstrap bootstrap=new ClientBootstrap(channelFactory); 
     bootstrap.getPipeline().addLast("handler", new PhoneClientHandler()); 

     bootstrap.setOption("child.tcpNoDelay", true); 
     bootstrap.setOption("child.keepAlive", true); 

     bootstrap.connect(new InetSocketAddress("127.0.0.1",8181)); 
    } 
} 

Handler Cliente:

public class ClientHandler extends SimpleChannelHandler{ 
    public void messageRecieved(ChannelHandlerContext ctx, ChannelStateEvent e){ 
     ChannelBuffer buffer=(ChannelBuffer)e.getMessage(); 
     int size=buffer.readableBytes(); 
     byte[] bytes=new byte[size]; 
     buffer.readBytes(bytes); 
     buffer.clear(); 
     System.out.println(new String(bytes));//if the data size>1024,the String will speprate into parts. 
    } 
} 
+0

lo siento, no entiendo la pregunta. ¿Puedes tratar de ser más específico? –

+0

lo siento, soy chino y mi inglés no es bueno. a continuación está mi pregunta: como estoy usando netty, cuando escribo 2048bytes (más de 1024bytes) datos de una mano a otra, la mano receptora debería recibir dos veces, cómo puedo revivir toda la información (más de 1024bytes) por una vez ? – Gofier

Respuesta

5

Bueno siempre se puede decidir el número de bytes a escribir a la vez, pero definitivamente nunca se sabe cuándo y cuántas se reciben bytes (esta es la razón por la cual NIO tiene sentido). Necesita manejar su propio búfer para recibir un número de corrección de bytes que desee. Para hacerlo, puede usar un FrameDecoder que está diseñado para este propósito.

Adicionalmente, puede asegurarse de que los datos básicos no permanece demasiado tiempo en el buffer del socket del remitente mediante el establecimiento de Tcpnodelay true, por lo que tendrá que esperar por el "marco" actual para alcanzar un cierto tamaño crítico antes de enviar físicamente los datos

Si entiendo bien, está escribiendo digamos 2048 Bytes en una mano, pero todos los datos no se reciben en el evento messagedReceived por otra parte? intenta comprobar estos problemas comunes:

  • que la aplicación termina demasiado pronto y los datos básicos aún no se llegaron
  • sus datos están Stucked en la memoria intermedia de separación del "emisor" debido a que no cerca la Channel y tcpNoDelay la opción no se estableció en verdadero. Esto hace que el socket espere algunos bytes adicionales antes de enviar el paquete.
  • usted no leyó todos los datos básicos dentro de la ChannelBuffer pero por una razón del readerIndex como se establece en una posición más

tratan de mostrar una parte de su código, debe facilitar las cosas. ..

AÑADIDO 17/04/2012

Si entiendo que está intentando pasar una matriz de bytes que codifica una cadena desde el emisor al receptor. Aquí está su código después de un pequeño refactor:

---------------------------- código -------- -------------------- escribir mano: respuesta.size()> 1024bytes

byte[] datas = ((String)msg).getBytes("UTF-8"); //ALWAYS SPECIFY THE ENCODING 
ChannelBuffer buffer = ChannelBuffers.wrap(datas); //USE DIRECTLY THE ARRAY 
System.out.println(buffer); //buffer'size>1024 here 
channel.write(buffer); 

---------------------------- mano Recieve: deben recibir dos veces, println() ejecutaría en dos ocasiones

ChannelBuffer buffer = (ChannelBuffer) event.getMessage(); 
System.out.println(buffer) //buffer'size once 1024,once the remainder size 
byte[] datas =buffer.readBytes(buffer.readableBytes()).array() 
String msg=new String(datas , "UTF-8"); //BAD IDEA because the bytes sequence of the last UTF-8 char could be uncompleted there 
System.out.println(str); 

Ésta no es la manera de hacerlo, en su lugar debe utilizar directamente el StringEncoder y StringDecoder en el paquete org.jboss.netty.handler.codec.string. Manejará el problema de enmarcado para usted. Si aún desea depurar su código, use el LoggingHandler proporcionado por Netty. también hizo que realmente establece esta opción:

bootstrap.setOption("tcpNoDelay", true); 

en ambos lados sin ayuda de nadie?

+0

en primer lugar, gracias por responder mi pregunta, lo siento mucho mi inglés no es muy bueno. Sí, como dijiste, cuando escribo 2048 bytes en el controlador, el método messageRevieved debe llamarse dos veces para recibir todos los datos ... Mostraré mi código en este momento, gracias agian. – Gofier

+0

realmente gracias! NumRenaud, cambié mi código como sugirió, pero el problema continúa, estoy seguro de que configuro la opción bootstrap.setOption ("tcpNoDelay", true) en ambos lados, también probé "child.tcpNoDelay", y uso "SimpleChannelHandler" en ambos lados. – Gofier

+0

bootstrap.setOption ("tcpNoDelay", verdadero) en el cliente y bootstrap.setOption ("child.tcpNoDelay", verdadero) en el servidor – RenaudBlue

2

En primer lugar, para el cliente, la opción de arranque no debe comenzar con 'niño':

bootstrap.setOption("tcpNoDelay", true); 
bootstrap.setOption("keepAlive", true); 

también que no utilizan el mismo puerto en el cliente y el servidor !!

En segundo lugar, no tiene una estrategia "cercana": ¿cuándo debe su cliente saber que su trabajo está hecho? ¿Cómo se evita que el hilo termine prematuramente? Usted debe hacer esto

SERVIDOR MANIPULADOR

public class ServerHandler extends SimpleChannelHandler{ 

    public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e){ 
     byte[] resp=data.getBytes();//data is a String greater than 1024bytes; 
     ChannelBuffer buffer=ChannelBuffers.buffer(resp.length); 
     buffer.writerBytes(resp); 
     e.getChannel().write(buffer); 
     buffer.clear(); 
     e.getChannel.close(); 
    } 
} 

CLIENTE BOOTSTRAP

public class Client{ 
    public static void main(String[] args){ 
     ChannelFactory channelFactory=new NioClientSocketChannelFactory(
      Executors.newCachedThreadPool(), 
      Executors.newCachedThreadPool()); 
     ClientBootstrap bootstrap=new ClientBootstrap(channelFactory); 
     bootstrap.getPipeline().addLast("handler", new PhoneClientHandler()); 

     bootstrap.setOption("child.tcpNoDelay", true); 
     bootstrap.setOption("child.keepAlive", true); 

     // Start the connection attempt. 
     ChannelFuture future = bootstrap.connect(new InetSocketAddress("127.0.0.1",8181)); 

     // Wait until the connection is closed or the connection attempt fails. 
     future.getChannel().getCloseFuture().awaitUninterruptibly(); 

     // Shut down thread pools to exit. 
     bootstrap.releaseExternalResources(); 
    } 
} 

Por último, es necesario entender mejor lo que está haciendo mediante la lectura de un montón de ejemplos. Se pueden encontrar dentro del paquete org.jboss.netty.example en la descarga del paquete principal.

+0

muchas gracias, ¡lo descubriré! gracias ! – Gofier

3

Pruebe con TruncatedChannelBuffer o BigEndianHeapChannelBuffer en lugar de channelbuffer en su ClientHandler. Creo que funcionará ... o si no funciona, amablemente publique stacktrace de la excepción generada. Intenté esto en mi código y funcionó. Espero que esto te ayude.

public void messageReceived(ChannelHandlerContext channelHandlerContext,MessageEvent messageEvent) throws Exception { 

    Object messageObject = messageEvent.getMessage(); 

    // if size of message < 1024 then TruncatedChannelBuffer is returned. 

    if (messageObject instanceof TruncatedChannelBuffer) { 

     try { 

      TruncatedChannelBuffer truncatedChannelBuffer = (TruncatedChannelBuffer) messageObject; 

      byte[] byteArray = new byte[truncatedChannelBuffer.readableBytes()]; 

      truncatedChannelBuffer.readBytes(byteArray); 

      System.out.print(" Message = "+new String(byteArray)); 

      truncatedChannelBuffer.clear(); 

     } catch (Exception e) { 

      System.out.println("Exception in MessageReceived..."); 

      e.printStackTrace(); 


     } 
    } 
    // if size of message > 1024 then BigEndianHeapChannelBuffer is returned. 

    if (messageObject instanceof BigEndianHeapChannelBuffer) { 

     try { 

      BigEndianHeapChannelBuffer bigEndianHeapChannelBuffer = (BigEndianHeapChannelBuffer) messageObject; 

      byte[] byteArray = new byte[bigEndianHeapChannelBuffer.readableBytes()]; 

      bigEndianHeapChannelBuffer.readBytes(byteArray); 

      System.out.print(" Message = "+new String(byteArray)); 

      bigEndianHeapChannelBuffer.clear(); 


     } catch (Exception e) { 

      System.out.println("Exception in MessageReceived..."); 

      e.printStackTrace(); 

     } 
    } 

}  
1

RenaudBlue @ hace buenos puntos. Además, sugiero cambiar a Netty4, lo que hace que todos los ByteBufs dinámica y lo que hace fragmentada lecturas/escrituras más fácil de manejar. Ver "Porting the client".

por ejemplo,

private void sendNumbers() { 
    // Do not send more than 4096 numbers. 
    boolean finished = false; 
    MessageBuf<Object> out = ctx.nextOutboundMessageBuffer(); 
    while (out.size() < 4096) { 
     if (i <= count) { 
      out.add(Integer.valueOf(i)); 
      i ++; 
     } else { 
      finished = true; 
      break; 
     } 
    } 

    ChannelFuture f = ctx.flush(); 
    if (!finished) { 
     f.addListener(numberSender); 
    } 
} 

private final ChannelFutureListener numberSender = new ChannelFutureListener() { 
    @Override 
    public void operationComplete(ChannelFuture future) throws Exception { 
     if (future.isSuccess()) { 
      sendNumbers(); 
     } 
    } 
}; 

Netty4 también tiene seguridad de tipos de configuración de opciones de canal, lo que habría impedido el erro "child.tcpNoDelay" r.

Pero la gran victoria para Netty4 es el modelo de rosca bien definido, que hace que Netty mucho sea más fácil de usar.

0

necesita configurar FixedRecvByteBufAllocator de SocketChannel en childHandler() de la siguiente manera:

bootstrap.childHandler(new ChannelInitializer<SocketChannel>() { 
      @Override 
      protected void initChannel(SocketChannel ch) throws Exception { 
       ch.config().setRecvByteBufAllocator(new FixedRecvByteBufAllocator(2 * 1024)); 
       ChannelPipeline pipeline = ch.pipeline(); 
       pipeline.addLast("idleStateHandler", new IdleStateHandler(0, 0, 5)); 
       pipeline.addLast(new StringEncoder()); 
       pipeline.addLast(new StringDecoder()); 
       ... 
      } 
     }); 
Cuestiones relacionadas