Java 网络响应在压力下被缓冲区覆盖?

Java 网络响应在压力下被缓冲区覆盖?,java,networking,netty,Java,Networking,Netty,我两天来一直在调试这个问题。在stackoverflow内外进行了大量搜索之后,我找不到答案 我正在为定制的密钥/值服务器编写一个客户端。协议很简单。 如果客户端发送 "GET 1 12\r\nkey1\r\nkey2\r\n" 服务器可能会重播 "0 1 16\r\nvalue1\r\nvalue2\r\n" 在响应中,第一行表示body的长度是16字节,对于接下来的16字节,它包含两个键的to值。它们由“\r\n”分隔 问题在于,在压力测试中,有时我从我的客户那里看到,响应消息的格式似乎

我两天来一直在调试这个问题。在stackoverflow内外进行了大量搜索之后,我找不到答案

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

"GET 1 12\r\nkey1\r\nkey2\r\n"
服务器可能会重播

"0 1 16\r\nvalue1\r\nvalue2\r\n"
在响应中,第一行表示body的长度是16字节,对于接下来的16字节,它包含两个键的to值。它们由“\r\n”分隔

问题在于,在压力测试中,有时我从我的客户那里看到,响应消息的格式似乎不正确。缓冲区似乎被覆盖。

e、 g.发送

"GET 1 12\r\nkey1\r\nkey2\r\n"
10万次, 在响应缓冲区中,我可能会看到

"0 1 16\r\nvalue1\r\nval0 1 16"
这里的值2似乎被下一个响应部分覆盖

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

我遵循Netty Telnet的例子,做了一些小的修改

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

我认为这可能与多线程有关。但即使我将线程数设置为1,问题仍然存在

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

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

更新: 经过更多的调查,我发现这与内蒂无关。即使使用一个简单的JavaNIO程序,它也可以重新编程。这似乎与缓冲区溢出有关

从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”

我还没有找到根本原因。我一得到答案就答复

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

这是我的原始代码片段, 初始值设定项

    @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();
    }
}
/@Sharable
公共类NetCyclientHandler扩展了SimpleChannelInboundHandler{
布尔头=真;
int len=-1;
ArrayList VAL=新的ArrayList();
@凌驾
public void channelRead0(ChannelHandlerContext ctx,字符串请求)引发InterruptedException{
若有(总目){
vals.clear();
String[]splits=request.split(“”);
len=-1;
试一试{
len=Integer.parseInt(拆分[2]);
}捕获(NumberFormatException ex){
例如printStackTrace();
}
如果(len==-1){
返回;
}
头=假;
}否则{
VAL.add(请求);
len-=(request.length()+2);
如果(len==0){
//系统错误打印(“[”);
//对于(int i=0;i
经过调查,结果有点令人失望。服务器有一个bug

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

  • TCP不可能是错误的。我在考虑缓冲区溢出之类的问题, 但是TCP有拥塞控制,所以这不可能发生
  • 我可以使用一个非常简单的程序来测试服务器,例如一个简单的 双线程旧IO程序
  • ncat是验证这一点最简单的方法,即准备大型 打包并使用ncat获取响应~

您假设TCP中存在消息这样的东西。没有。这是一个字节流。它可以完全根据自己的突发奇想为您提供零、一个或多个字节。如果您希望获得特定的消息长度,那么您可以自行循环直到获得它(如果它较短),或者分割您已经收到的消息(如果它较长)

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