2014-09-28 29 views
-1

我已经很难调试这个问题两天了。我在/ stackoverflow外搜索了很多内容后找不到答案。网络响应缓冲区重写压力?

我正在为定制的键/值服务器编写客户端。协议很简单。 如果客户端发送

"GET 1 12\r\nkey1\r\nkey2\r\n" 

服务器可以重放

"0 1 16\r\nvalue1\r\nvalue2\r\n" 

在响应中,第一行表示主体的长度是16个字节,并为下一个16个字节,它包含了值两个键。他们被“\ r \ n”分隔。

问题是,在压力测试中,有时我从客户端看到响应消息看起来不正确。它看起来像缓冲区被覆盖。

例如发送

"GET 1 12\r\nkey1\r\nkey2\r\n" 

为10,000次, 在响应缓冲区我可能会看到

"0 1 16\r\nvalue1\r\nval0 1 16" 

它看起来像值2这里由一个响应部分覆盖。

服务器在那里工作了很长时间,我认为它运行良好。我也使用tcpdump并证明它是正确的。所以错误应该在客户端。

我遵循Netty Telnet示例here,并做了小的修改。

在DelimiterBasedFrameDecoder之后,一个处理程序逐个解析这些行并组装响应。

我以为它可能与多线程有关。但即使我把线程号设置为1,问题重演。

那么,我是否以错误的方式使用Netty?

====================

更新: 更多的调查之后,我发现它不与Netty的关系。即使使用简单的java NIO程序,它也可以重新生成。它似乎与缓冲区溢出有关。

从tcpdump,我可以看到来自远程服务器的包是正确的。

因此,我捕获每个ByteBuffer,并在发生错误时将其打印出来。

protected void onRead(ByteBuffer buf) throws Exception { 
     buf.mark(); 
     int l = buf.limit(); 
     int p = buf.position(); 
     byte[] bytes = new byte[l - p]; 
     buf.get(bytes, p, l - p); 
     String v = new String(bytes, Charset.forName("UTF-8")); 
     buffers.addFirst(v); 
     if (buffers.size() > 30) { 
      buffers.removeLast(); 
     } 
     buf.reset(); 
     //... 
     // process one line of buf 

}; 

以下是最后三个缓冲器捕获,因为我处理它逐行:与此代码 - (1K我故意将缓冲区大小设置为小的数目。)。看起来头部“0 0 1040”错误放置了一个截断线“20”

************************************* 
20131101/booking.com.png^M 
20131101/booking.com.png^M 
20131101/booking.com.png^M 
20131101/booking.com.png^M 
0 0 1040^M 
20131101/booking.com.png^M 
20131101/booking.com.png^M 
20131101/booking.com.png^M 
20131101/booking.com.png^M 
200 0 1040^M 
20131101/booking.com.png^M 

************************************* 
20131101/booking.com.png^M 
20131101/booking.com.png^M 
20131101/booking.com.png^M 
20131101/booking.com.png^M 
20131101/booking.com.png^M 
0 0 1040^M 
20131101/booking.com.png^M 
20131101/booking.com.png^M 
20131101/booking.com.png^M 
20131101/booking.com.png^M 
20 
************************************* 
20131101/booking.com.png^M 
20131101/booking.com.png^M 
20131101/booking.com.png^M 
20131101/booking.com.png^M 
20131101/booking.com.png^M 
20131101/booking.com.png^M 
0 0 1040^M 
20131101/booking.com.png^M 
20131101/booking.com.png^M 
20131101/booking.com.png^M 
20131101/boo 
************************************* 

我没有找到根本原因。一旦我得到答案,我会回复它。

=====================

这是我原来的代码片段, 初始化

@Override 
    public void initChannel(SocketChannel ch) { 
     ChannelPipeline pipeline = ch.pipeline(); 

     pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); 
     pipeline.addLast(DECODER); 
     pipeline.addLast(ENCODER); 

     pipeline.addLast(new NettyClientHandler()); 
    } 

和处理程序:

//@Sharable 
public class NettyClientHandler extends SimpleChannelInboundHandler<String> { 
    boolean head = true; 
    int len = -1; 
    ArrayList<String> vals = new ArrayList<>(); 

    @Override 
    public void channelRead0(ChannelHandlerContext ctx, String request) throws InterruptedException { 
     if (head) { 
      vals.clear(); 
      String[] splits = request.split(" "); 
      len = -1; 
      try { 
       len = Integer.parseInt(splits[2]); 
      } catch (NumberFormatException ex) { 
       ex.printStackTrace(); 
      } 
      if (len == -1) { 
       return; 
      } 
      head = false; 
     } else { 
      vals.add(request); 
      len -= (request.length() + 2); 
      if (len == 0) { 
//    System.err.print("["); 
//    for (int i = 0; i < vals.size(); i++) { 
//     System.err.print(vals.get(i) + ","); 
//    } 
//    System.err.println("]"); 
       head = true; 
      } 
     } 
     //System.err.println(request); 
    } 

    @Override 
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 
     cause.printStackTrace(); 
     ctx.close(); 
    } 
} 

回答

-1

经过调查,结果有点令人失望。服务器有一个错误。

回想起来,我在这里学到了一些东西。

  • TCP不能错。我正在考虑缓冲区溢出或其他什么, 但TCP拥塞控制,所以这不会发生;
  • 我可以使用一个简单的程序来测试服务器,例如一个简单的 双线程旧IO程序。
  • NCAT是为了验证这个easist办法,那就是,准备大 包,并使用NCAT获得响应〜
0

你假设有这样的事情在TCP的消息。没有。这是一个字节流。它可以完全根据它的奇想提供零个,一个或多个字节。如果您希望获得特定的消息长度,则取决于您是否要循环,直到获得它,如果更少,或者将您已收到的内容分开,如果更多。