基于Mina框架的 socket网络通信

来源:互联网 发布:淘宝必须交保证金吗 编辑:程序博客网 时间:2024/04/28 18:40

一  Mina框架简介

      1.1   Mina是apache基于socket nio的一套框架. 其因封装了socket网络通信 ,解耦了网络通信和具体的业务逻辑。让开发者

专注于具体的业务逻辑(数据)的处理.

        1.2 Mina 框架的通信模型(主要的接口类) 简要介绍:

               

          IoService接口: 连接网络的接口,对于服务器端IoAcceptor实现了这个接口。对于客户端IoConnector实现了这个接口

          IoFilter(过滤器):  主要用于对数据(消息)进行编码和解码以及日志处理 

          IoHandler(处理器):   在这里接受IoFilter发送过来的数据进行业务处理返回数据给客户端。继承IoHandlerAdapter这个类

                                                    重写里面进行业务处理的方法。

         1.3 Mina框架需要的jar包:

      这里给出下载资源:http://download.csdn.net/detail/android_hdh/9601796

二   一个基于Mima框架的小Demo:MinaDemo

       2.1  MinaDemo主要实现客户端发送消息给服务器端 服务器端打印客户端发来的消息。并统一回复消息“你好 Mina!”

       2.2  服务器端的代码

      2.2.1 IoHandler层代码:

package MinaService;import org.apache.mina.core.service.IoHandlerAdapter;import org.apache.mina.core.session.IdleStatus;import org.apache.mina.core.session.IoSession;/** * 服务端实现 * IoHandler 业务处理层 * @author XuHao * */public class ServerHandler extends IoHandlerAdapter {/** * 当接口中其他方法抛出异常未被捕获时触发此方法 */@Overridepublic void exceptionCaught(IoSession session, Throwable cause)throws Exception {System.out.println("通信出错!");cause.printStackTrace();}/** * 当接收到客户端的请求信息后触发此方法 */@Overridepublic void messageReceived(IoSession session, Object message)throws Exception {String remsg = message.toString();System.out.println("messageReceived:" + remsg);session.write("你好 Mina!");}/** * 当连接空闲时触发此方法 */@Overridepublic void sessionIdle(IoSession session, IdleStatus status)throws Exception {System.out.println("IDLE:" + session.getIdleCount(status));}}
2.2.2 IOFilter 层代码:

package MinaEncoding;import org.apache.mina.core.buffer.IoBuffer;import org.apache.mina.core.session.IoSession;import org.apache.mina.filter.codec.ProtocolEncoderOutput;import org.apache.mina.filter.codec.demux.MessageEncoder;/** * 编码类  (没有进行编码  直接进行了数据发送) * @author XuHao * */public class Encoder implements MessageEncoder<String>{public Encoder() {}/** *  没有编码  直接进行了数据的发送 */@Overridepublic void encode(IoSession session, String message, ProtocolEncoderOutput out)throws Exception {//    没有编码     模拟一下System.out.println("对数据进行编码");String value = (String)message;//  message长度的一个bufIoBuffer buf = IoBuffer.allocate(value.getBytes().length);if (value != null){buf.put(value.trim().getBytes()); //  将message转成字节数组写入缓存里面}buf.flip();   //  反转缓存区out.write(buf);out.flush();}}
package MinaDecoding;import java.util.ArrayList;import java.util.List;import org.apache.mina.core.buffer.IoBuffer;import org.apache.mina.core.session.IoSession;import org.apache.mina.filter.codec.ProtocolDecoderOutput;import org.apache.mina.filter.codec.demux.MessageDecoder;import org.apache.mina.filter.codec.demux.MessageDecoderResult;/** * 解码类 * @author XuHao * */public class Decoder implements MessageDecoder {private byte[] r_curPkg = null;     private int r_pos = -1;     //  计数器static private final int PKG_SIZE_BYTES = 2;  //  包长度public Decoder() {}/** * 判断接受的数据是否与协议相同 */@Overridepublic MessageDecoderResult decodable(IoSession session, IoBuffer in) {return MessageDecoderResult.OK;}/** * 解码接受到的数据包 */@Overridepublic MessageDecoderResult decode(IoSession session, IoBuffer in,ProtocolDecoderOutput out) throws Exception {List<String> list = new ArrayList<String>();while (in.remaining() >= PKG_SIZE_BYTES || (r_pos >= 0 && in.hasRemaining())) {// 循环接收包,4为一个整型,表示包长度b, 如果上一个包未接收完成时,继续接收              // 如果上个包已收完整,则创建新的包              if (r_pos == -1) {                  //得到下一个包的长度,长度不包括前两位,即包的长度=压缩位长度+数据位长度                  int pkgLen = in.getShort();                                     //如果包长度小于0,那么此包错误,解码失败,返回。                  if (pkgLen < 0) {                      return MessageDecoderResult.NOT_OK;                  }                  in.get();                   r_curPkg = new byte[pkgLen-1]; //数组长度为数据长度                  r_pos = 0;              }              int need = r_curPkg.length - r_pos; //需要读取的数据长度              int length = in.remaining();//缓冲区中可读的数据长度              if (length >= need) {// 可以把当前包读完整                  in.get(r_curPkg, r_pos, need); // 复制缓冲区中的数据到r_curPkg中                  // 处理接收到一个完整的包数据后,把包添加到池中,判断是否需要需要解压                  byte[] data = r_curPkg;                  String str = new String(data);                  list.add(str);                  r_curPkg = null;                  r_pos = -1;              } else {                  // 如果剩下的字节数,不够一个包则                  int remainBytes = in.remaining();                  in.get(r_curPkg, r_pos, remainBytes);                  r_pos += remainBytes;                  return MessageDecoderResult.NEED_DATA;              }          }          for (String protocol : list) {              out.write(protocol);          }          return MessageDecoderResult.OK;  }@Overridepublic void finishDecode(IoSession session, ProtocolDecoderOutput out)throws Exception {}}
package MinaCoding;import org.apache.mina.filter.codec.demux.DemuxingProtocolCodecFactory;import MinaDecoding.Decoder;import MinaEncoding.Encoder;/** * 编码工厂类 * @author XuHao * */public class CodecFactory  extends DemuxingProtocolCodecFactory{public CodecFactory() {super.addMessageEncoder(String.class, Encoder.class);super.addMessageDecoder(Decoder.class);}}
2.2.3 IoService层代码:

package MinaService;import java.io.IOException;import java.net.InetSocketAddress;import org.apache.mina.core.filterchain.IoFilter;import org.apache.mina.core.service.IoAcceptor;import org.apache.mina.core.session.IdleStatus;import org.apache.mina.filter.codec.ProtocolCodecFilter;import org.apache.mina.filter.logging.LoggingFilter;import org.apache.mina.transport.socket.nio.NioSocketAcceptor;import MinaCoding.CodecFactory;/** * IoService层   * @author XuHao * */                       public class MinaService {private static final int PORT = 9123;public static void main(String[] args) throws IOException {IoAcceptor acceptor = new NioSocketAcceptor();   //  IoService的一个扩展接口//  编写过滤器//  日志记录acceptor.getFilterChain().addLast("logger", new LoggingFilter());//   设置编解码acceptor.getFilterChain().addLast( "codec", (IoFilter) new ProtocolCodecFilter(new CodecFactory()));//   将IoHandler注册到IoServiceacceptor.setHandler(new ServerHandler());acceptor.getSessionConfig().setReadBufferSize( 3 );   //  设置读取数据的缓存区大小为3个字节acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );  //  设置读写通道均为10秒内无任何操作就进入空闲状态acceptor.bind( new InetSocketAddress(PORT) ); //  绑定socket端口地址}}

2.3 客户端代码:

package Client;import java.io.ByteArrayOutputStream;import java.net.InetSocketAddress;import java.nio.Buffer;import java.nio.ByteBuffer;import java.nio.channels.SocketChannel;/** * 客户端 * @author XuHao * */public class SocketClient {public static void main(String[] args) throws Exception {SocketChannel socketChannel = SocketChannel.open();socketChannel.connect(new InetSocketAddress("localhost",9123));byte[] bytes = "xuhao".getBytes();    //  这里就是发送的消息 为了简单明了  这里可以手动修改//  对数据包进行编码ByteBuffer buffer = ByteBuffer.allocate(bytes.length + 3);buffer.putShort((short)(bytes.length+1)); //包长度          buffer.put((byte)1);//闲置位          buffer.put(bytes);//数据          buffer.flip();          socketChannel.write(buffer);        socketChannel.socket().shutdownOutput();                String obj = receive(socketChannel);        System.out.println(obj);}/** * 接受服务器返回的消息 * @param socketChannel * @return * @throws Exception */private static String receive(SocketChannel socketChannel) throws Exception{ByteBuffer buffer = ByteBuffer.allocate(1024);ByteArrayOutputStream baos = new ByteArrayOutputStream();int size = 0;byte[] bytes = null;while((size = socketChannel.read(buffer))>=0){              buffer.flip();              bytes = new byte[size];              buffer.get(bytes);              baos.write(bytes);              buffer.clear();          }          bytes = baos.toByteArray();          baos.close();          return new String(bytes);}}

0 0