8

ハンドラーに 2048 バイトを書き込む場合、すべてのデータを受信するには messageRevieved メソッドを 2 回呼び出す必要があります... 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.
    }
}
4

6 に答える 6

6

一度に書き込むバイト数はいつでも決定できますが、いつ、何バイト受信するかは絶対にわかりません (これが NIO が理にかなっている理由です)。必要な固定バイト数を受信するには、独自のバッファーを処理する必要があります。そのために、この目的のために設計されたFrameDecoderを使用できます。

さらに、 tcpNoDelayを true に設定することで、データが送信側ソケット バッファに長く留まらないようにすることができます。これにより、現在の「フレーム」が特定の重要なサイズに達するのを待たずに、データを物理的に送信できます。

よく分かると、例えば2048バイトと書いているのに、一方でmessagedReceivedイベントで全てのデータが受信されていない?次の一般的な問題を確認してください。

  • アプリケーションの終了が早すぎて、データがまだ到着していない
  • チャネルを閉じておらず、 tcpNoDelayオプションが true に設定されていなかったため、データは「送信者」のソケット バッファにスタックされています。これにより、ソケットはパケットを送信する前に追加のバイトを待機します。
  • ChannelBuffer 内のすべてのデータを読み取ったわけではありませんが、理由により、readerIndexがさらに位置に設定されています

あなたのコードの一部を見せてください。物事が簡単になるはずです...

2012 年 4 月 17 日追加

送信者から受信者に文字列のバイト配列コーディングを渡そうとしていることがわかれば。少しリファクタリングした後のコードは次のとおりです。

----------------------------コード ---------------------- -------手書き: 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);

---------------------------- ハンドを受け取る: 2 回受け取る必要があり、println() は 2 回実行されます

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.stringのStringEncoderおよびStringDecoderを直接使用する必要があります。フレーミングの問題を処理します。それでもコードをデバッグしたい場合は、Netty が提供するLoggingHandlerを使用してください。また、このオプションを本当に設定しましたか:

bootstrap.setOption("tcpNoDelay", true);

両側のブートストラップで?

于 2012-04-16T12:30:12.263 に答える
3

の channelbuffer の代わりに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();

        }
    }

}       
于 2012-07-07T08:07:28.850 に答える
2

まず、クライアントの場合、bootstrap オプションは「child」で開始しないでください。

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

また、クライアントとサーバーで同じポートを使用しないでください!!

第二に、あなたには「クローズ」戦略がありません.クライアントはいつ仕事が終わったことを知っていると思いますか? スレッドが途中で終了するのをどのように防ぎますか? これを行う必要があります

サーバーハンドラー

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

クライアントのブートストラップ

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パッケージ内にあります。

于 2012-04-18T09:53:35.337 に答える
1

同じ問題が発生しました。NioではなくOioを使用してみてください。(「nio」を「oio」に、「Nio」を「Oio」に変更するだけです。

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

于 2013-01-15T11:59:38.000 に答える
1

RenaudBlue@ は良い点を挙げています。さらに、Netty4 に切り替えることをお勧めします。これにより、すべての ByteBuf が動的になり、チャンク化された読み取り/書き込みの管理が容易になります。「クライアントの移植」を参照してください。

例えば、

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がはるかに使いやすくなります。

于 2013-03-31T17:35:18.887 に答える