首页 > java > mina read方法出现BufferUnderflowException异常的解决办法

mina read方法出现BufferUnderflowException异常的解决办法

2010年1月9日
88 views 评论 发表评论

现象:

先连续发几十个很小很小的包(<10 byte)

再突然发一个大小64byte的包

这时你会发现mina就会出现以下错误
java.nio.BufferUnderflowException
at java.nio.HeapByteBuffer.get(Unknown Source)
at org.apache.mina.core.buffer.AbstractIoBuffer.get(AbstractIoBuffer.java:419)
at org.apache.mina.core.buffer.AbstractIoBuffer.get(AbstractIoBuffer.java:827)
at com.labox.common.net.ProtocolHandler.messageReceived(ProtocolHandler.java:81)
at org.apache.mina.core.filterchain.DefaultIoFilterChain$TailFilter.messageReceived(DefaultIoFilterChain.java:752)
at org.apache.mina.core.filterchain.DefaultIoFilterChain.callNextMessageReceived(DefaultIoFilterChain.java:414)
at org.apache.mina.core.filterchain.DefaultIoFilterChain.access$5(DefaultIoFilterChain.java:411)
at org.apache.mina.core.filterchain.DefaultIoFilterChain$EntryImpl$1.messageReceived(DefaultIoFilterChain.java:832)
at org.apache.mina.core.filterchain.DefaultIoFilterChain$HeadFilter.messageReceived(DefaultIoFilterChain.java:616)
at org.apache.mina.core.filterchain.DefaultIoFilterChain.callNextMessageReceived(DefaultIoFilterChain.java:414)
at org.apache.mina.core.filterchain.DefaultIoFilterChain.fireMessageReceived(DefaultIoFilterChain.java:408)
at org.apache.mina.core.polling.AbstractPollingIoProcessor.read(AbstractPollingIoProcessor.java:582)
at org.apache.mina.core.polling.AbstractPollingIoProcessor.process(AbstractPollingIoProcessor.java:542)
at org.apache.mina.core.polling.AbstractPollingIoProcessor.process(AbstractPollingIoProcessor.java:534)
at org.apache.mina.core.polling.AbstractPollingIoProcessor.access$7(AbstractPollingIoProcessor.java:532)
at org.apache.mina.core.polling.AbstractPollingIoProcessor$Worker.run(AbstractPollingIoProcessor.java:861)

经过对mina的分析,这是由对包长度不对做成的(即,我们发的包长是大于64byte的,但他的byteBuffer大小只有64byte,当我们尝试读取第65个byte就会出现这个错误)

mina怎会出现这种错误的呢??是不是有什么配置可以调整

找到mina读取byte的方法AbstractPollingIoProcessor类的read(T session)

此方法源代码如下

private void read(T session) {
IoSessionConfig config = session.getConfig();

System.out.println(“cap buffer size”+config.getReadBufferSize());//这句我自己加的
IoBuffer buf = IoBuffer.allocate(config.getReadBufferSize());

final boolean hasFragmentation =
session.getTransportMetadata().hasFragmentation();

try {
int readBytes = 0;
int ret;

try {
if (hasFragmentation) {
while ((ret = read(session, buf)) > 0) {
readBytes += ret;
if (!buf.hasRemaining()) {
break;
}
}
} else {
ret = read(session, buf);
if (ret > 0) {
readBytes = ret;
}
}
} finally {
buf.flip();
}

if (readBytes > 0) {
session.getFilterChain().fireMessageReceived(buf);
buf = null;

if (hasFragmentation) {
if (readBytes << 1 < config.getReadBufferSize()) {
session.decreaseReadBufferSize();
} else if (readBytes == config.getReadBufferSize()) {
session.increaseReadBufferSize();
}
}
}
if (ret < 0) {
scheduleRemove(session);
}
} catch (Throwable e) {
if (e instanceof IOException) {
scheduleRemove(session);
}
session.getFilterChain().fireExceptionCaught(e);
}
}

经过对这段代码的分析终于发现问题所在了

大家注意if (readBytes > 0) 这个块下的代码

你不难发现

if (hasFragmentation) {
if (readBytes << 1 < config.getReadBufferSize()) {
session.decreaseReadBufferSize();
} else if (readBytes == config.getReadBufferSize()) {
session.increaseReadBufferSize();
}
}

意思是if hasFragmentation==true

if 当前配置初始化ByteBuffer大小 > 当前读取包的平方 为 true 就把配置中初始化byteBuffer大小减半

else if 当前已读取字节==配置包初始化大小  为true时 把配置中初始化byteBuffer大小加倍

接下来结合我出错的现象看看

当我接连发几十个小于10byte的包时,这时配置中的初始化ByteBuffer大小就为取小,默认最小为64byte

当我再发一个大于64byte的包,但整个ByteBuffer只有64byte,那就出错了。

接下来我们来修正这个问题

方法一:不要改变默认初始化byteBuffer大小,要修改mina的源码

找到org.apache.mina.transport.socket.nio.NioSocketSession 这个类的METADATA变量

把 new DefaultTransportMetadata()的第四个参数改成false就ok了

方法二:自己写read()方法中得到byteBuffer实例的方法

从read()方法看出,他得到byteBuffer实例是每次去请求的,如果我们在这里做一个cache,每次从cache中得到,自然byteBuffer的大小也是固定的,只要按自己业务最大包大小去开就可以了。

每个线程用一个自己的ByteBuffer实例,这样就不会有同步问题.

找到org.apache.mina.core.polling.AbstractPollingIoProcessor类中的read(T session)方法改成

static ThreadLocal readCache=new ThreadLocal();//这个是放ByteBuffer实例的cache

private void read(T session) {

IoBuffer buf=readCache.get();
if(buf==null){
buf=IoBuffer.allocate(512);//512为包默认大小
readCache.set(buf);
}else{
buf.clear();
}

try {
int readBytes = 0;
int ret;

try {
ret = read(session, buf);
if (ret > 0) {
readBytes = ret;
}
} finally {
buf.flip();
}

if (readBytes > 0) {
session.getFilterChain().fireMessageReceived(buf);
}
if (ret < 0) {
scheduleRemove(session);
}
} catch (Throwable e) {
if (e instanceof IOException) {
scheduleRemove(session);
}
session.getFilterChain().fireExceptionCaught(e);
}

}

搞定了

ps:不知这个是不是mina的bug,是不是还有别的方法配置的呢???

请教那位兄弟有更好的解决方法.

qq:85529766

纯净水 java

  1. 2011年12月11日13:12 | #1

    There will likely be several completely different portions about the LA Weight reduction eating strategy and 1 is actually crucial. Begin stage is your truly truly of these extra load. weight loss 863012

  1. 2011年12月11日13:42 | #1
  • 粤ICP备09032914号