在mina的ProtocolDecoder合并 未完整发送的数据包。
由于Socket方面的基础不扎实,对nio的理解不深入,
这几天看了Apache MINA 2.0.0-M1 里的相关代码,才明白了nio的处理和我先前理解的不一样
虽然在 http://618119.com/archives/2008/03/07/73.html
考虑到了客户端把多个PDU数据包一次发送的情况,却没有考虑到PDU数据包被分片发送的情况,
于是以前写的ProtocolDecoder在网络环境恶劣或客户端没有整包发送数据的情况下,就会出现问题,
在ProtocolDecoder增加了合并PDU数据包的处理,经过测试,PDU数据包分片发送的时候,服务端也能够正常解析了。
客户端往Socket发送数据包的代码:
[code]
byte bo[] = bao.toByteArray();
sc.getOutputStream().write(bo, 0, 4);//先发四个字节
Thread.sleep(500);
sc.getOutputStream().write(bo, 4, 8);//再发送8个字节
Thread.sleep(500);
sc.getOutputStream().write(bo, 12, bo.length - 12);//再把剩余的数据发过去
[/code]
服务端改进后的代码:
[code]
package com.lizongbo.smpp.server.codec;
import org.apache.mina.common.AttributeKey;
import org.apache.mina.common.IoBuffer;
import org.apache.mina.common.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import com.lizongbo.smpp.BadCommandIDException;
import com.lizongbo.smpp.message.SMPPPacket;
import com.lizongbo.smpp.util.SMPPIO;
import com.lizongbo.smpp.util.PacketFactory;
public class SMPPProtocolDecoder implements ProtocolDecoder {
private static final AttributeKey BUF_BYTE = new AttributeKey(
SMPPProtocolDecoder.class, "bufb");
public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out)
throws Exception {
try {
IoBuffer bufTmp = null;
byte[] buf = (byte[]) session.getAttribute(BUF_BYTE);
if (buf == null) {
// System.out.println("没有尚未处理的数据");
bufTmp = in;
} else {
// System.out.println("合并尚未处理的数据");
bufTmp = IoBuffer.allocate(buf.length + in.remaining());
bufTmp.setAutoExpand(true);
bufTmp.put(buf);
bufTmp.put(in);
bufTmp.flip();
}
while (bufTmp.remaining() >= 4
&& bufTmp.remaining() >= bufTmp.getInt(bufTmp.position())) {// 循环处理数据包
int dataLen = bufTmp.getInt(bufTmp.position());
byte[] b = new byte[dataLen];
bufTmp.get(b);
SMPPPacket pak = null;
int id = -1;
id = SMPPIO.bytesToInt(b, 4, 4);
pak = PacketFactory.newInstance(id);
if (pak != null) {
pak.readFrom(b, 0);
out.write(pak);
}
}
if (bufTmp.hasRemaining()) {// 如果有剩余的数据,则放入Session中
byte[] tmpb = new byte[bufTmp.remaining()];
bufTmp.get(tmpb);
session.setAttribute(BUF_BYTE, tmpb);
}
}
catch (BadCommandIDException ex) {
ex.printStackTrace();
}
}
public void dispose(IoSession session) throws Exception {
}
public void finishDecode(IoSession session, ProtocolDecoderOutput out)
throws Exception {
}
}
[/code]
调试日志如下:
(一个数据包被分为三段发送,服务端收到之后重新组包解析的日志)
Listening on port a 5435
sessionCreated==(0×013E8D89: nio socket, server, /127.0.0.1:1188 => /127.0.0.1:5435)
run decode!!!
in.remaining() == 4
没有不完整数据
数据包长69 bufTmp.remaining() ==4
数据还不完整
剩余数据保存到Session!!!4字节 00:00:00:45
run decode!!!
in.remaining() == 8
尚有不完整数据:00:00:00:45
数据包长69 bufTmp.remaining() ==12
数据还不完整
剩余数据保存到Session!!!12字节 00:00:00:45:80:00:00:00:00:00:00:00
run decode!!!
in.remaining() == 57
尚有不完整数据:00:00:00:45:80:00:00:00:00:00:00:00
数据包长69 bufTmp.remaining() ==69
hexdata:00:00:00:45:80:00:00:00:00:00:00:00:00:00:00:7a
00:00:00:1d:68:74:74:70:3a:2f:2f:31:32:37:2e:30
2e:30:2e:31:3a:35:34:33:35:2f:58:64:70:70:59:61
6e:00:00:00:10:63:02:00:6d:00:04:65:63:68:6f:04
79:79:79:79:7a
(上面是 合并之后得到的数据包)
ps: 也可以使用Apache mina 提供的statemachine来处理类似的问题。