Java 网络响应在压力下被缓冲区覆盖?
我两天来一直在调试这个问题。在stackoverflow内外进行了大量搜索之后,我找不到答案 我正在为定制的密钥/值服务器编写一个客户端。协议很简单。 如果客户端发送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”分隔 问题在于,在压力测试中,有时我从我的客户那里看到,响应消息的格式似乎
"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获取响应~
//@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();
}
}