首先,对于mina的基本使用这里就不多说了,之前已经转载了一篇很详细的说明。这次想分享的是使用mina框架自定义编解码器,实现发送纯文本和非纯文本消息,带PBE加密。
首先定义要发送的信息包,之前一直想用mina进行图片语音,短视频的传送,把多媒体信息封装成一个类,纯文本文件封装成一个类。然后使用多路分离解码器,虽然这种方法可行,但是发送和接收都直接跳过了handler这层。到最后处理业务逻辑很不方便。
然后尝试值使用一种格式的信息类,消息类型只有是纯文本或非纯文本的标识。一个String类型,真实发送时是发送JSON数据。然后一个byte数组。当是纯文本信息是,标识是‘+‘,byte数组为null,这里不会造成new 一个信息类就会分配多余的内存。当时非纯文本信息时,标识是‘-‘。byte数组存放多媒体信息。
接下来重要的环节就是编写编码器和解码器,思想很简单,根据发送的信息标识进行相应的编码,根据标识进行相应的解码。然后就是加密,这里用到的是PBE对称加密。加密时机是编码器编码时。解密就是在解码器解码时。加密速度我测试了一下。4.1M 加密:191ms 解密:198ms 15M 加密: 600ms 解密:570ms,应该还可以了。
接下来附上代码:
1.信息类
/**
* symbol = ‘+‘ : 纯本文件
* symbol = ‘-‘ : 非纯本文件
* @author Administrator
*/
public class iMoMoMsg {
public char symbol;//判断是否是纯文本文件
public String msgJson;//包含图片详情的解释
public byte[] msgBytes;//图片
}
2.编码类
package com.imomo_codecfactory;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import org.apache.commons.codec.binary.Base64;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
import com.imomo_msg.iMoMoMsg;
import com.security.PBE;
public class iMoMoEncoder extends ProtocolEncoderAdapter {
CharsetEncoder encoder = Charset.forName("utf-8").newEncoder();
// 纯文本发送格式:头标记(char) + 信息长度(int) +文本信信息(加密byte[])
// 非纯文本发送格式:头标记(char) + 文字信息长度(int) + 文字信息(加密byte[]) + 多媒体信息长度(int) + 多媒体信息(加密byte[])
@Override
public void encode(IoSession session, Object msg, ProtocolEncoderOutput out)
throws Exception {
iMoMoMsg moMsg = (iMoMoMsg) msg;
char symbol = moMsg.symbol;
try {
if (symbol == ‘+‘) {
byte[] msgJsonByte = PBE.enCrypt(moMsg.msgJson.getBytes());//加密
System.out.println("--发送文本信息 = " + moMsg.msgJson);
int msgLen = msgJsonByte.length;
// 2 :最开始的头部symbol,这里是‘-+ ,4表示长度位
IoBuffer io = IoBuffer.allocate(2 + 4 + msgLen).setAutoExpand(
true);
io.putChar(symbol);
io.putInt(msgLen);
io.put(msgJsonByte);
io.flip();
out.write(io);
} else if (symbol == ‘-‘) {
byte[] msgJsonByte = PBE.enCrypt(moMsg.msgJson.getBytes());//加密
byte[] msgBytes = PBE.enCrypt(moMsg.msgBytes);
System.out.println("--发送多媒体信息 = " + moMsg.msgJson
+ " msgBytesLen = " + msgBytes.length);
int textLen = msgJsonByte.length;// 文字信息长度
int msgBytesLen = msgBytes.length;// 图片长度
IoBuffer io = IoBuffer.allocate(
2 + 4 + 4 + textLen + msgBytesLen).setAutoExpand(true);
io.putChar(symbol);
io.putInt(textLen);
io.put(msgJsonByte);
io.putInt(msgBytesLen);
io.put(msgBytes);
io.flip();
out.write(io);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.解码类
package com.imomo_codecfactory;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Date;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.apache.mina.filter.codec.demux.MessageDecoder;
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
import com.imomo_msg.iMoMoMsg;
import com.security.PBE;
public class iMoMoDecoder extends CumulativeProtocolDecoder {
CharsetDecoder decoder = Charset.forName("utf-8").newDecoder();
@Override
protected boolean doDecode(IoSession session, IoBuffer in,
ProtocolDecoderOutput out) throws Exception {
iMoMoMsg moMsg = new iMoMoMsg();
int pos = in.position();
int remaining = in.remaining();
try {
// 判断长度,开头symbol 是char型
if (remaining < 2) {
in.position(pos);
return false;
}
char symbol = in.getChar();// 相当于消费掉字节流
moMsg.symbol = symbol;
if (symbol == ‘+‘) {
int msgJsonLen = 0;// msgJson长度
// 判断是否够解析出的长度
msgJsonLen = in.getInt();
if (remaining - 2 < msgJsonLen || msgJsonLen < 0) {
in.position(pos);
return false;
}
byte[] temp = new byte[msgJsonLen];
in.get(temp);//得到加密后byte数组
moMsg.msgJson = new String(PBE.deCiphering(temp));
out.write(moMsg);
} else if (symbol == ‘-‘) {
// 接收文本信息
int msgJsonLen = 0;// msgJson长度
int msgBytesLen = 0;// msgBytes长度
msgJsonLen = in.getInt();
if (remaining - 2 < msgJsonLen || msgJsonLen < 0) {
in.position(pos);
return false;
}
byte[] temp1 = new byte[msgJsonLen];
in.get(temp1);//得到加密后byte数组
moMsg.msgJson = new String(PBE.deCiphering(temp1));
// 接收图片信息
msgBytesLen = in.getInt();
if (remaining - 2 - 4 - 4 - msgJsonLen < msgBytesLen
|| msgBytesLen < 0) {
in.position(pos);
return false;
}
byte[] temp2 = new byte[msgBytesLen];
in.get(temp2);//得到加密后byte数组
moMsg.msgBytes = PBE.deCiphering(temp2);
out.write(moMsg);
}
} catch (Exception e) {
e.printStackTrace();
in.position(pos);
return false;
}
return true;
}
}
如果对编解码不熟的可以参考mina开发手册。
测试 发送15m的视频:
client handler类里:
@Override
public void sessionOpened(IoSession session) throws Exception {
iMoMoMsg moMoMsg = new iMoMoMsg();
JSONObject textJson = new JSONObject();
textJson.put("type", "login");
textJson.put("id", "201513");
textJson.put("pwd", "1234");
moMoMsg.msgJson = textJson.toJSONString();
moMoMsg.symbol = ‘+‘;
session.write(moMoMsg);
// 发送图片信息
JSONObject multiJson = new JSONObject();
multiJson.put("type", "pic");
multiJson.put("sender", "001");
multiJson.put("getter", "002");
FileInputStream fileInputStream = new FileInputStream("f:\\moshou.avi");
FileChannel channel = fileInputStream.getChannel();
ByteBuffer bytebuffer = ByteBuffer.allocate((int) channel.size());
bytebuffer.clear();
channel.read(bytebuffer);
iMoMoMsg moMoMsg2 = new iMoMoMsg();
moMoMsg2.msgJson = multiJson.toJSONString();
moMoMsg2.msgBytes = bytebuffer.array();
moMoMsg2.symbol = ‘-‘;
session.write(moMoMsg2);
fileInputStream.close();
channel.close();
}
server handler里:
public void messageReceived(IoSession session, Object message)
throws Exception {
super.messageReceived(session, message);
iMoMoMsg moMoMsg = (iMoMoMsg) message;
if(moMoMsg.symbol == ‘+‘){
String msgJson = moMoMsg.msgJson;
System.out.println("服务器 收到纯文本信息 : " + msgJson);
} else if(moMoMsg.symbol == ‘-‘){
String msgJson = moMoMsg.msgJson;
System.out.println("服务器 收到多媒体本信息 : " + msgJson + "多媒体信息长度" + moMoMsg.msgBytes.length);
FileOutputStream fileout = new FileOutputStream("F:\\"
+ new Date().getMinutes() + "-.avi");
FileChannel fc = fileout.getChannel();
fileout.write(moMoMsg.msgBytes);
fc.close();
fileout.close();
}
}
控制台打印:
客户端:
本次加密用时 : 774
本次加密用时 : 651
--发送多媒体信息 = {"getter":"002","sender":"001","type":"pic"} msgBytesLen = 16174624
服务器:
本次解密用时 : 177
本次解密用时 : 597
服务器 收到多媒体本信息 : {"getter":"002","sender":"001","type":"pic"}多媒体信息长度16174616
从上可以看到:加密不管是对文本信息加密还是对多媒体文件加密,都比较耗时间,解密就相对较快。
具体工程:http://download.csdn.net/detail/u011102153/8615791