基于mina的socket server——数据接收

本文详细介绍了使用MINA框架搭建Socket服务器的过程,包括开发环境配置、消息对象定义、解码器实现、会话句柄创建及服务器启动。

本文转自: http://blog.sina.com.cn/s/blog_46e73e770100aj34.html


关于mina的开发文档和说明网上应该可以找到很多,但比较适合初学者的那种比较详细的说明,还不不多的。由于在实际工作中用到了socket server方面的功能,所以,简单的研究了一下mina,也用mina做了一个socket server。下面就是我用mina做socket server的步骤和一些示例代码。

开发环境:
       工具:eclise3.3+jdk1.6.0
       需要的库:
                         backport-util-concurrent.jar
                         commons-logging.jar
                         log4j-1.2.14.jar
                         mina-core-2.0.0-M1.jar
                         mina-core-2.0.0-M1-sources.jar
                         mina-filter-compression-2.0.0-M1.jar
                         mina-filter-compression-2.0.0-M1-sources.jar
                         slf4j-api-1.5.2.jar
                         slf4j-jdk14-1.5.2.jar
       至于这些库的下载地址,网上应该很容易找到。
开发步骤:
1、首先在eclipse中新建一个java工程,简单类型的即可
2、然后在工程的目录下新建lib目录,把前面提到的所需要的库 都拷贝到lib下
3、在工程的目录下新建log目录,把log4j的配置文件放到该log目录下。至于log4j的配置文件,网上也很容易找到。我的网络硬盘上也有,可以直接去下载。
这是工程的目录结构看起来如下图所示:
基于mina的socket <wbr>server鈥斺斒萁邮
4、接下来就可以创建socket消息对象了,也就是通过socket接收、发送的对象。例如:
public class TestMessage implements Serializable{
   private byte version;
   private short protocolType;
   private int msgLen;
   private String msgBody;
   public TestMessage(){
   }
   ...//get or set method
}
在此,假设通讯的socket消息结构如下:一个字节的版本号+2个字节的协议类型 +4个字节的消息体长度+字 符串格式的消息体。所以,对应的消息对象定义就如上定义。在解析socket接收到的内容时,就需要根据该协议格式进行解析。如果有很多消息类型的话,最好通过抽象类、接口等模式进行设计。
5、创建真正用来解析socket server接收到内容的解码器,也就是新建一个类,要实现mina中的MessageDecoder接口以及方法。需要注意的是, 解码器必须是没有参数的构造函数 。如:
public class TestMessageDecoder implements MessageDecoder{
   private byte version;
   private short protocolType;
   //通过版本号与协议类型区分要用该解码器解析socket数据
   public TestMessageDecoder (){
       this.version = 1;
       this.protocolType = 2;
   }
   public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
        //7=1字节的version+2字节的protocolType+4字节的body长度,也就是协议头字节长度
        if(in.remaining() < 7){
            return MessageDecoderResult.NEED_DATA;
        }
        //get version
        byte ver = in.get();       
        //get protocol type
        short type = in.getShort();
        //get message length
        int len = in.getInt();
        //如果数据未接收完,继续接收
        if(in.remaining() < len)
            return MessageDecoderResult.NEED_DATA;  
        //如果版本号与协议类型都匹配,就可以进行解析   
        if(ver == this.version && type == this.protocolType ){
            return MessageDecoderResult.OK;
        }
        return MessageDecoderResult.NOT_OK;
    }
    //真正解析socket数据
    public MessageDecoderResult decode(IoSession session, IoBuffer in,
            ProtocolDecoderOutput out) throws Exception {
        TestMessage tMsg = new TestMessage();
        tMsg.setVersion(in.get());
        tMsg.setProtocolType(in.getShort());
        int msgLen = in.getInt();
        //字符串解码格式
        Charset charset = Charset.forName("GBK");
        CharsetDecoder decoder = charset.newDecoder();
        String msgBody = in.getString(msgLen,decoder);
        tMsg.setMsgLen(msgLen);
        tMsg.setMsgBody(msgBody);
        out.write(tMsg);
        return MessageDecoderResult.OK;
    }

    public void finishDecode(IoSession session, ProtocolDecoderOutput out)
            throws Exception {
    }
}

6、创建协议解析工厂,也就是要把上面创建的decoder添加到解析队列中。代码如:
public class TestMessageCodecFactory extends DemuxingProtocolCodecFactory{
    public TestMessageCodecFactory (){
        super.addMessageDecoder(TestMessageDecoder .class);
    }
}

7、创建socket会话句柄。扩展IoHandlerAdapter 类并重载其中的方法即可。示例代码如下:
public class TestServerSessionHandler extends IoHandlerAdapter {

    private final Logger log = Logger.getLogger(TestServerSessionHandler .class);
    private final int IDLE = 300;
  
    public void exceptionCaught(IoSession session, Throwable cause)
            throws Exception {
        session.close();
        log.error("session occured exception, so close it.");
    }

    public void messageReceived(IoSession session, Object message)
            throws Exception {
        if(message instanceof TestMessage){
            TestMessage msg = (TestMessage )message;
            //在此得到的msg就是通过socket接收到的且已经安装协议解析好的对象了。在此仅打印出而已
            System.out.println("received message content:"+msg.getMsgBody());
        }
    }

    public void messageSent(IoSession session, Object message) throws Exception {
        super.messageSent(session, message);
    }

    public void sessionCreated(IoSession session) throws Exception {
        log.info("remote client ["+session.getRemoteAddress().toString()+"] connected.");
    }

    public void sessionIdle(IoSession session, IdleStatus status)
            throws Exception {
        log.info("session idle, so disconnecting......");
        session.close();
        log.info("disconnected.");
    }

    public void sessionOpened(IoSession session) throws Exception {
        //设置socket空闲时间
        session.getConfig().setIdleTime(IdleStatus.BOTH_IDLE, IDLE);
        //其他配置也在此设置
    }
}

8、准备工作都已完成,接下来就可以创建socket server了。如:
public class TestSocketServer{
    private static final int DEFAULT_SERVER_PORT = 1234;
    private int port;
    public TestSocketServer(int port){
         this.port = port;
    }
    public void startServer()throws Exception{
        if(this.port < 0){
            this.port = DEFAULT_SERVER_PORT;
        }
        NioSocketAcceptor acceptor = new NioSocketAcceptor();
        //set self codecFactory
        acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TestMessageCodecFactory ()));
        acceptor.getFilterChain().addLast("logger", new LoggingFilter());
        //set self handler
        acceptor.setHandler(new TestServerSessionHandler ());
        acceptor.bind(new InetSocketAddress(this.port));
        System.out.println("socket server started,waitting for connection......");      
    }

    public static void main(String[] args) {
        PropertyConfigurator.configure("log/log4j.properties");
        ItvSocketServer server = new ItvSocketServer(1234);
        try {
            server.startServer();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

至此,一个基于mina的socket server就全部完成了,可以接收符合协议的socket数据,且接收到的内容已经封装成符合协议的对象。如果要根据接收到的内容做进一步处理,在TestServerSessionHandle中的messageReceived方法中进行即可。
对socket server的数据处理流程,可以这样理解:
1、当有socket数据过来时,首先查找已经注册了的解码器中是否有匹配的解码器,如果没有,直接报错返回;否则就进入对应的解码器。
2、进入对应的解码器后,首先进入过滤的是解码器中的decodable方法,判断是否具备解码的条件。只有具备了解码的条件后,才真正由解码器中的decoder方法按照协议进行解码,并把socket内容解析成对应的对象。
3、当一个对应的对象解析完成后,就会自动被socket的回话句柄TestServerSessionHandler中的messageReceived方法拦截,对接收到底消息进行响应和处理。

通常,socket server在接收到消息后,会根据解析情况以及处理结果返回给client一些信息,这就需要socket server要具有发送消息的功能。发送消息就需要把要发送的内容编码后通过socket的会话写出去,具体的实现步骤和方法,待续。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值