网络响应是否会在压力下覆盖缓冲区?

时间:2014-09-28 07:10:05

标签: java networking netty

我几乎没有调试这个问题两天。在stackoverflow之外/之外进行了大量搜索后,我无法找到答案。

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

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

服务器可以重播

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

在响应中,第一行表示正文的长度是16个字节,而对于接下来的16个字节,它包含两个键的值。他们被“\ r \ n”分开。

问题在于,在压力测试中,有时我从客户端看到,响应消息看起来格格不入。看起来缓冲区被覆盖了。

e.g。发送

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

10k次, 在响应缓冲区中我可能会看到

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

看起来这里的value2被下一个响应部分覆盖。

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

我按照Netty Telnet示例here进行了小修改。

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

我认为它可能与多线程有关。但即使我将线程数设置为1,问题就是重新编写。

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

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

更新 经过更多的调查,我发现它与Netty无关。即使使用简单的Java NIO程序,它也可以重现。它似乎与缓冲区溢出有关。

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

所以我捕获每个ByteBuffer,并在bug发生时将其打印出来。 (我故意将缓冲区大小设置为一个小数字 - 1k。)使用以下代码:

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

};

以下是最后三个缓冲区捕获,因为我逐行处理它。似乎头部“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();
    }
}

2 个答案:

答案 0 :(得分:0)

你假设在TCP中存在消息这样的事情。没有。这是一个字节流。它可以完全根据其奇思妙想为您提供零个,一个或多个字节。如果你期望一个特定的消息长度,你需要循环,直到你得到它,如果它更少,或拆分你已经收到的,如果它更多。

答案 1 :(得分:-1)

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

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

  • TCP不能错。我在考虑缓冲区溢出或其他什么, 但TCP有拥塞控制,所以这不可能发生;
  • 我可以使用一个非常简单的程序来测试服务器,例如一个简单的 双线程旧IO程序。
  • ncat是验证这一点的简单方法,也就是说,准备大 包并使用ncat来获取响应〜