使用過Mina做Socket長鏈接的同學應該都遇到到過,在解碼時少包、多包的問題!
1.文本內容發送端需要等緩沖區滿才發送出去,造成粘包
2.接收方不及時接收緩沖區的包,造成多個包接收
解決方法就是使用CumulativeProtocolDecoder 解碼器,確保在編碼的時候要把前4位設成標志位,標志消息內容的長度。
數據包編碼和解碼一定要單獨實現:
編碼器 class RequestEncoder implements ProtocolEncoder
解碼器 class RequestDecoder extends CumulativeProtocolDecoder
public class RequestDecoder extends CumulativeProtocolDecoder {
private String TAG = this.getClass().getName();
/**
* 返回值的解釋:
* 1、false, 繼續接收下一批數據,有兩種情形,如緩沖區數據剛剛就是一個完整消息,或不夠一條消息時。
* 如果不夠一條消息,那么會將下一批數據和剩余消息進行合并
* 2、true, 當緩沖區的消息多于一條消息時,剩余消息會再會推送至doDecode
*/
protected boolean doDecode(IoSession session, IoBuffer in,
ProtocolDecoderOutput out) throws Exception {
if (in.remaining() < 4) {
in.reset();
return false;//繼續接收數據,以待數據完整
}
in.mark();// 標記當前位置,以便reset
int size = in.getInt();// 讀取4字節判斷消息長度
if (in.remaining() < size) {// 如果消息內容不夠,則重置,相當于不讀取size
in.reset();
return false;// 接收新數據,以拼湊成完整數據
} else {
decodePacket(session, in, out, size);
}
if (in.remaining() > 0) {
in.mark();
return true;// 如果讀取內容后還粘了包,就讓父類再給俺 一次,進行下一次解析
} else {
return false;
}
}
private void decodePacket(IoSession session, IoBuffer in,
ProtocolDecoderOutput out, int size) {
try {
byte[] sizeBytes = new byte[size];
in.get(sizeBytes);
ResponseData data = ResponseData.parseFrom(sizeBytes);
Response response = new Response(data.getResId(),
data.getResCode(), data.getResData().toByteArray());
response.setSession(session);
response.setCallBack(data.getCallback());
Log.d(TAG, " >> " + response);
out.write(response);
} catch (Exception e) {
Log.d(TAG, "RequestEncoder encode:" + e);
}
}
}
public class RequestEncoder implements ProtocolEncoder {
private String TAG = this.getClass().getName();
@Override
public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {
Request request = (Request) message;
Log.d(TAG, " << " + request);
RequestData.Builder builder = RequestData.newBuilder();
if(request.getByteData() != null) {
builder.setByteData(ByteString.copyFrom(request.getByteData()));
}
builder.setReqData(request.getRequestData().toString());
builder.setReqId(request.getCommandId());
if (request.getCallBack() != null) {
builder.setCallback(request.getCallBack());
}
byte[] data = builder.build().toByteArray();
int dataLength = data.length;//int是4個字節32位
IoBuffer buffer = IoBuffer.allocate(4 + dataLength, false);//數據包長度信息+原始數據大小
buffer.putInt(dataLength);//先寫入int大小的長度信息
buffer.put(data);//再寫入要發送的請求數據
buffer.flip();
out.write(buffer);
buffer.free();
}
@Override
public void dispose(IoSession session) throws Exception {
}
}