TCP采用停止等待协议来避免数据在传输过程中丢失,一般的
停止等待协议要求发送方在收到之前发送的数据后再发送剩余数据,为了提高数据发送的效率,TCP采用了一种累积确认的方式,这个协议为滑动窗口协议。由于网络双方的网速不尽相同,比如快的发送跟慢的接受,将导致接受服务器的接受缓冲区一直被填满,使得接受服务器拒绝接受数据,造成发送方不停重发送数据甚至连接被断开。所以滑动窗口协议的窗口大小是随着网络情况而动态变化的,无法保证一个应用上完整包被一次发送,难免出现断包现象。而延时确认以及重发机制,又难免会出现粘包的现象。
解决断包与粘包问题必须为制定一个包规范,比如包的起始标志,包长度等。有了包规范后,解决起来就很简单了。在读取一个包数据后,根据规范分析这个包,如果数据不完整,只需将不完整的数据放入连接上下文即可。因为TCP是面向连接的,所以很容易为每个连接创建了Session。
例子:1F1F + len(1byte) + string + EFEF。len=length(string) utf-8。
-
public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out)
-
throws Exception {
-
-
// 取得上次未处理的数据包
-
IoBuffer lastBuf = (IoBuffer) session.getAttribute("buffer");
-
if (lastBuf == null)
-
lastBuf = IoBuffer.allocate(100).setAutoExpand(true);
-
// 跟本次数据合并
-
lastBuf.put(in);
-
lastBuf.flip(); // 转为读模式
-
-
final int head = 4 + 1; //head + len
-
String shead = "";
-
-
while(lastBuf.hasRemaining()){
-
if(lastBuf.remaining() < head){ //断包
-
lastBuf.compact();
-
session.setAttribute("buffer", lastBuf);
-
break;
-
}
-
shead = "" + (char)lastBuf.get()+ (char)lastBuf.get()+ (char)lastBuf.get()
-
+ (char)lastBuf.get();
-
if(!"1F1F".equals(shead))
-
break;
-
int l = Integer.parseInt((char)lastBuf.get()+"", 10);
-
if(lastBuf.remaining() < l + 4){ //断包
-
int pos = lastBuf.limit();
-
lastBuf.clear();
-
lastBuf.position(pos);
-
session.setAttribute("buffer", lastBuf);
-
break;
-
}
-
byte bytes[] = new byte[l];
-
lastBuf.get(bytes);
-
-
String recvString = new String(bytes, "UTF-8");
-
-
out.write(recvString);
-
-
//EFEF
-
lastBuf.get();
-
lastBuf.get();
-
lastBuf.get();
-
lastBuf.get();
-
//去掉已读的完整包,如还有数据,则为粘包,继续处理
-
lastBuf.compact();
-
lastBuf.flip();
-
}
-
}
阅读(1531) | 评论(0) | 转发(0) |