之前网上查了些资料,有些blog说mina做服务端,socket做客户端,没法用DemuxingProtocolCodecFactory,只能用TextLineCodecFactory协议解析,但要是传文件,这个东东根本就没用,事实上,服务器很大可能是要传文件的,mina做服务端,客户端可能是不同的。比如用mina的客户端写,用nio自己写,用socket自己写,用C语言自己写。
本次测试例子:使用socket传送较长字符串,如果传送文件,也是一样的过程,先将字符串或者文件转换成byte数组
其实如果理解mina的协议解析的话就会非常简单处理这些解码:
大体思想:协议一般有头信息 告诉程序需要读取多少字节,程序就可以不断读取了,知道在哪个字节时候就不用再往后读了。
IoBuffer读取的时候就像一个管道,读取了这部分数据,这部分数据就没有,其实IO流就是这样
Socket客户端:
package com.test;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
public class client {
public static void main(String[] args) throws Exception{
Socket socket = new Socket("127.0.0.1",9123);
OutputStream os = socket.getOutputStream();
String str = "";
for(int i=0;i<100;i++){
str = str + "我们都是中国人啊!";
}
byte[] bytes = str.getBytes("utf-8");
System.out.println("数据byte长度:"+bytes.length);
os.write(i2b(bytes.length));
os.write(bytes);
os.flush();
InputStream is = null;
is = socket.getInputStream();
byte[] intByte = new byte[4];
is.read(intByte);
int msgLength = b2i(intByte);
System.out.println("int:"+msgLength);
int readPosition = 0;
byte[] byteArray = new byte[msgLength];
while(readPosition < msgLength){
int value = is.read(byteArray, readPosition, msgLength-readPosition);
if(value == -1){
break;
}
readPosition += value;
}
System.out.println(new String(byteArray,"utf-8"));
System.out.println("byteArray Length:"+byteArray.length+" string Length:"+new String(byteArray,"utf-8").length());
if(os != null){
os.close();
}
if(is != null){
is.close();
}
}
// 网上抄来的,将 int 转成字节
public static byte[] i2b(int i) {
return new byte[] { (byte) ((i >> 24) & 0xFF),
(byte) ((i >> 16) & 0xFF), (byte) ((i >> 8) & 0xFF),
(byte) (i & 0xFF) };
}
public static int b2i(byte[] b) {
int value = 0;
for (int i = 0; i < 4; i++) {
int shift = (4 - 1 - i) * 8;
value += (b[i] & 0x000000FF) << shift;
}
return value;
}
}
socket先发送测试数据,然后接收服务端返回的数据
package com.test;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.charset.Charset;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.AttributeKey;
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;
public class BaseMessageDecoder implements MessageDecoder {
AttributeKey CONTEXT = new AttributeKey(getClass(),"context");
/**
* 是否适合解码
* */
public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
// TODO Auto-generated method stub
return MessageDecoderResult.OK;
}
/**
* 数据解码
* */
public MessageDecoderResult decode(IoSession session, IoBuffer in,
ProtocolDecoderOutput outPut) throws Exception {
// TODO Auto-generated method stub
Context context = (Context) session.getAttribute(CONTEXT);
// TODO Auto-generated method stub
IoBuffer buffer = in;
if(context == null || !context.init){
IoBuffer b = buffer.get(new byte[4]);
byte[] intByte = b.array();
int num = b2i(intByte);
System.out.println("长度:"+num);
context = new Context();
context.number = num;
context.position = 0;
context.byteArray = new byte[num];
context.init = true;
while(buffer.hasRemaining()){
byte c = buffer.get();
context.byteArray[context.position] = c;
context.position++;
}
}else{
while(buffer.hasRemaining()){
byte c = buffer.get();
context.byteArray[context.position] = c;
if( context.position == context.number-1){
System.out.println(new String(context.byteArray,"utf-8"));
IoBuffer buff = IoBuffer.allocate(1024).setAutoExpand(true);
buff.put(i2b(context.number));
buff.put(context.byteArray);
buff.flip();
outPut.write(buff);
return MessageDecoderResult.OK;
}
context.position++;
}
}
session.setAttribute(CONTEXT,context);
return MessageDecoderResult.NEED_DATA;
}
/**
* 解码完成
* */
public void finishDecode(IoSession session, ProtocolDecoderOutput outPut)
throws Exception {
// TODO Auto-generated method stub
}
public static int b2i(byte[] b) {
int value = 0;
for (int i = 0; i < 4; i++) {
int shift = (4 - 1 - i) * 8;
value += (b[i] & 0x000000FF) << shift;
}
return value;
}
public static byte[] i2b(int i) {
return new byte[] { (byte) ((i >> 24) & 0xFF),
(byte) ((i >> 16) & 0xFF), (byte) ((i >> 8) & 0xFF),
(byte) (i & 0xFF) };
}
class Context {
public Integer number;
public byte[] byteArray;
public Integer position;
public boolean init;
}
}
因传送字符串较长,执行多次调用decode方法,需要中间变量记住之前的部分字段值。
IoBuffer buff = IoBuffer.allocate(1024).setAutoExpand(true);
buff.put(i2b(context.number));
buff.put(context.byteArray);
buff.flip();
outPut.write(buff);
这段代码本应该写在BaseMessageEncoder的encode方法中,但出于我并没有在服务端需要这些字符串值,所以直接将数据方法IoBuffer,服务端直接写回客户端
测试数据OK。