mina消息过长

  MINA在处理网络编程问题的时候不失为一个好助手,但使用过程中总会碰到大大小小的问题,最近在使用时便碰到了如此的一个小问题,出现问题的时候查阅源码是个好习惯。

问题

  用MINA发送字符串消息的时候,出现了消息过长的问题。

1
org.apache.mina.common.BufferDataException: Line is too long

  消息编码和解码的时候用到了TextLineCodecFactory,其实消息过长的问题出现在了解码的时候。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
* Decode a line using the default delimiter on the current system
*/
private void (Context ctx, IoSession session, IoBuffer in, ProtocolDecoderOutput out)
throws CharacterCodingException, ProtocolDecoderException {
int matchCount = ctx.getMatchCount();
// Try to find a match
int oldPos = in.position();
int oldLimit = in.limit();
while (in.hasRemaining()) {
byte b = in.get();
boolean matched = false;
switch (b) {
case 'r':
// Might be Mac, but we don't auto-detect Mac EOL
// to avoid confusion.
matchCount++;
break;
case 'n':
// UNIX
matchCount++;
matched = true;
break;
default:
matchCount = 0;
}
if (matched) {
// Found a match.
int pos = in.position();
in.limit(pos);
in.position(oldPos);
ctx.append(in);
in.limit(oldLimit);
in.position(pos);
if (ctx.getOverflowPosition() == 0) {
IoBuffer buf = ctx.getBuffer();
buf.flip();
buf.limit(buf.limit() - matchCount);
try {
byte[] data = new byte[buf.limit()];
buf.get(data);
CharsetDecoder decoder = ctx.getDecoder();
CharBuffer buffer = decoder.decode(ByteBuffer.wrap(data));
String str = buffer.toString();
writeText(session, str, out);
} finally {
buf.clear();
}
} else {
int overflowPosition = ctx.getOverflowPosition();
ctx.reset();
throw new RecoverableProtocolDecoderException("Line is too long: " + overflowPosition);
}
oldPos = pos;
matchCount = 0;
}
}
// Put remainder to buf.
in.position(oldPos);
ctx.append(in);
ctx.setMatchCount(matchCount);
}

当读取到消息结束标识后,剩余的空间不够用,所以抛出异常。

办法

1
2
3
4
TextLineCodecFactory lineCodec=new TextLineCodecFactory();
lineCodec.setDecoderMaxLineLength(1024*1024); //1M
filterChain.addLast("codec", new ProtocolCodecFilter(lineCodec));

给decode中每一行的读取长度做了定义,默认是1K,可以按照自己的需要来改动。

多看看

  • TextLineCodecFactory中设置decoder的长度
1
2
3
4
5
6
7
8
9
10
11
12
13
* Sets the allowed maximum size of the line to be decoded.
* If the size of the line to be decoded exceeds this value, the
* decoder will throw a {@link BufferDataException}. The default
* value is <tt>1024</tt> (1KB).
* <p>
* This method does the same job with {@link TextLineDecoder#setMaxLineLength(int)}.
*
* @param maxLineLength the maximum decoded line length
*/
public void setDecoderMaxLineLength(int maxLineLength) {
decoder.setMaxLineLength(maxLineLength);
}

做了这样的设置之后,每次再读取一行数据的时候就会按照1M的数据量去读。

  • MINA中的ProtocolCodecFactory自己有4个实现类,来进行编码处理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
* Provides {@link ProtocolEncoder} and {@link ProtocolDecoder} which translates
* binary or protocol specific data into message object and vice versa.
* <p>
* Please refer to
* <a href="../../../../../xref-examples/org/apache/mina/examples/reverser/ReverseProtocolProvider.html"><code>ReverserProtocolProvider</code></a>
* example.
*
* @author <a href="http://mina.apache.org">Apache MINA Project</a>
*/
public interface ProtocolCodecFactory {
* Returns a new (or reusable) instance of {@link ProtocolEncoder} which
* encodes message objects into binary or protocol-specific data.
*
* @param session The current session
* @return The encoder instance
* @throws Exception If an error occurred while retrieving the encoder
*/
ProtocolEncoder getEncoder(IoSession session) throws Exception;
* Returns a new (or reusable) instance of {@link ProtocolDecoder} which
* decodes binary or protocol-specific data into message objects.
*
* @param session The current session
* @return The decoder instance
* @throws Exception If an error occurred while retrieving the decoder
*/
ProtocolDecoder getDecoder(IoSession session) throws Exception;
}
类名 作用 备注
TextLineCodecFactory 文本消息,按行处理,识别回车换行符
DemuxingProtocolCodecFactory 转送消息到对应CodeFactory
PrefixedStringCodecFactory 处理有固定前缀长度的字符串
ObjectSerializationCodecFactory Java对象的序列化 传输对象必须实现序列化接口,且服务端和客户端包路径必须一致

总结

  MINA是一个优秀的网络编程辅助工具,但我们不一定能使用到它里面的所有功能,因此应该有选择性的进行学习,以及碰到问题的时候进行适当的源码查阅可以加深我们对于MINA的理解。

  其实,不只是针对MINA应该查阅源码,对于很多其他的开源框架或类库,多查阅源码和API总会有好处的。