之前做个一段时间的游戏开发,用到了mina通讯框架,怕久了忘记,这里整理下知识点。第一个就是搞清楚他的核心nio,但是讲这个就必须先搞清楚bio。jdk1.4之前用的是阻塞-BIO,jdk1.4之后引入NIO-非阻塞IO, jdk1.7后引入了 AIO-异步非阻塞IO。BIO相信大家入门时都写过:
while(true){
Socket s = socket.accept();
//操作s
//new Thread(s);
//s.read();
//s.write();
}
bio编程时需要一个线程去循环检测是否有新的socket连接进来,连接进来后socket就放入新的线程或者连接池。所以socket个数和线程个数M:N,M>N。bio是面相流的,建立链接,读取,写入都是同步阻塞的。这就意味着对方处理数据较慢或者网络传输数据较慢时,处理这个请求的线程只有阻塞等待。就算用了线程池,也会把线程吃完,后续处理不能正常进行。
nio socket编程
将通道和对应的事件会一起注册到selector上。事件分四种:服务器接收客户端连接,客户端连接服务器,读事件,写事件。所有的事件都可以共用一个selector,也可以分开。
while(true){
int n=selector.select();
if(n>0){
//取出就绪通道
//判断事件类型
//处理对应事件
}
}
selector选择器也需要一个线程去轮训是否有就绪的通道。原来程序要读取对方传来的数据时,需要阻塞等待对方的流数据在网卡组装好后,才能读取。就像你要下楼等到快递员给你送快递。而nio是当程序需要读取数据时,只需要将读取事件和通道一起注册到选择器上,选择器一直轮训你关心的数据准备好没有。当数据到达网卡且准备好时,就被轮训出来。 当有就绪通道时,就把通道里面的数据读到缓冲区里面。通道是否就绪,是由操作系统决定的。
mina通讯框架的粘包和断包问题
什么是粘包,断包问题?
TCP是面相字节流的,他的消息是无边界概念的,就好比文章没有标点符号,没有分段,全部粘在一起。那么发送方发送好几个包,接收方当作一个包一次性接收,就叫粘包。而当发送方发送的数据太大时,又不的不拆成几个包分开发送,就叫断包。那怎么解决呢?
1,常见的就是用特殊分隔符分割。比如\r\n之类的。
2,还有就是把消息的长度加到消息头部。
//封包
public void encode(IoSession session, Object message,
ProtocolEncoderOutput out) throws Exception {
Map retm = (Map) message;
//map转字节数组
byte[] bytes = gzipMsg(retm);
//消息长度
int len = bytes.length;
IoBuffer buf = IoBuffer.allocate(len+4);
buf.setAutoExpand(true);
//把长度加消息开头
writeInt(buf, len);
buf.put(bytes);
buf.flip();
out.write(buf);
}
//拆包
@Override
public MessageDecoderResult decodable(IoSession session, IoBuffer buf) {
return messageComplete(buf) ? MessageDecoderResult.OK
: MessageDecoderResult.NEED_DATA;
}
private boolean messageComplete(IoBuffer in) {
int remaining = in.remaining();
log.info("remaining:" + remaining);
if (remaining < 6)
return false;
//读取消息头
int len = readInt(in);
//看本次收到的消息完整吗
if (len > in.remaining())
return false;
return true;//不完成就继续接收
}
@Override
public MessageDecoderResult decode(IoSession session, IoBuffer buf,
ProtocolDecoderOutput out) throws Exception {
// Try to decode body
int length = readInt(buf);
if (length < 6)
return MessageDecoderResult.NEED_DATA;
if (length > 128 * 1024)
return MessageDecoderResult.NOT_OK;
byte[] message = new byte[length];
buf.get(message);
out.write(message);
message = null;
return MessageDecoderResult.OK;
}
未完待续...