在netty中,我们只能写入和接收小于1024字节的数据:我们如何写或接收更多?

时间:2012-04-16 08:05:28

标签: netty

在处理程序中写入2048字节时,应该调用messageRevieved方法两次以接收所有数据...如何在

中接收2048字节数据

代码

服务器

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));
    }
}

服务器处理程序:

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

客户端:

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));
    }
}

客户端处理程序:

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

6 个答案:

答案 0 :(得分:6)

你总是可以决定一次写入多少字节,但你绝对不知道接收的时间和字节数(这就是NIO有意义的原因)。您需要处理自己的缓冲区以接收所需的固定字节数。为此,您可以使用为此目的而设计的 FrameDecoder

另外,通过将 tcpNoDelay 设置为true,可以确保数据在发送方套接字缓冲区中不会停留太长时间,因此它不会等待当前“帧”达到某个关键字物理发送数据之前的大小。

如果我理解得很好,你写的是一手拿2048字节,但另一方面在messagedReceived事件中没有收到所有数据? 尝试检查这些常见问题:

  • 您的应用程序过早终止且数据尚未到达
  • 您的数据被卡在“发件人”的套接字缓冲区中,因为您没有关闭频道, tcpNoDelay 选项未设置为true。这导致套接字在发送数据包之前等待一些额外的字节。
  • 您没有读取ChannelBuffer中的所有数据,但有一个原因 readerIndex 被设置为更远的位置

尝试向我们展示您的代码的一部分,它应该让事情变得更容易......

ADDED 17/04/2012

如果我理解你正在尝试将字符串的字节数组编码从发送方传递给接收方。以下是小型重构后的代码:

---------------------------- code ------------------ ----------写手:response.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);

----------------------------收到手:应该收到两次,println()会执行两次

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);

这不是这样做的方法,您应该直接使用包 org.jboss.netty.handler.codec中的 StringEncoder StringDecoder 。 .string 即可。它将为您处理框架问题。 如果您仍想调试代码,请使用Netty提供的 LoggingHandler 。 你也真的设置了这个选项:

bootstrap.setOption("tcpNoDelay", true);

在双方的bootstraps?

答案 1 :(得分:3)

TruncatedChannelBuffer中尝试使用BigEndianHeapChannelBufferClientHandler代替频道缓冲。我认为它会起作用..或者如果它不起作用,请发布生成异常的堆栈跟踪。我在我的代码中试过这个并且它有效。我希望这对你有所帮助。

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();

        }
    }

}       

答案 2 :(得分:2)

首先,对于客户端,bootstrap选项不应以'child'开头:

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

此外,您不要在客户端和服务器上使用相同的端口!!

其次,你没有“关闭”策略:你的客户什么时候想知道它的工作已经完成了?你如何防止线程以prematura结束? 你应该这样做

SERVER HANDLER

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

CLIENT 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();
    }
}

最后,您需要通过阅读大量示例来更好地了解您的工作。它们可以在主捆绑下载中的 org.jboss.netty.example 包中找到。

答案 3 :(得分:1)

我有同样的问题,尝试使用Oio而不是Nio! (只需将“nio”改为“oio”,将“Nio”改为“Oio”。

http://lists.jboss.org/pipermail/netty-users/2009-June/000891.html

答案 4 :(得分:1)

RenaudBlue @提出了很好的观点。此外,我建议切换到Netty4,这会使所有ByteBufs动态化,这使得分块读/写更容易管理。请参阅"Porting the client"

例如,

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还具有通道选项配置的类型安全性,这可以防止"child.tcpNoDelay"错误。

但Netty4的最大胜利是定义明确的线程模型,这使得Netty 很多更容易使用。

答案 5 :(得分:0)

您需要在FixedRecvByteBufAllocator中设置SocketChannel childHandler(),如下所示:

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());
                ...
            }
        });