设为首页 收藏本站
查看: 1966|回复: 0

[经验分享] 【Apache Mina2.0开发之一】搭建Apache Mina框架并实现Server与Client端消息传递

[复制链接]

尚未签到

发表于 2018-11-25 14:15:45 | 显示全部楼层 |阅读模式
  
本站文章均为
李华明Himi
原创,转载务必在明显处注明:


转载自
【黑米GameDev街区】
原文链接:
http://www.himigame.com/apache-mina/831.html

          ☞ 点击订阅 ☜
本博客最新动态!及时将最新博文通知您!

Hibernate系列学习阶段到此结束了,那么紧接着进入Apache Mina的开发学习,很多童鞋在微薄和QQ中疑问Himi为什么突然脱离游戏开发了,嘿嘿,其实可能更多的童鞋已经看出来了,Himi在偏向服务器Server端开发了,Hibernate、MySQL等都是为了Server端Mina开发而做的铺垫,当前的Apache Mina才是Himi真正的目的。哈哈。Himi的技术目标是“一个人能做出一个网游~”,OK.不多说其他的了,开始Himi的Apache mina开发之旅吧。
对于Apache Mina不太连接的童鞋,请移步到如下百度百科连接进行学习了解:
http://baike.baidu.com/view/2668084.htm
首先建立一个new project(Server端),这里Himi使用IDE是 eclipse;
OK,首先我们这里先配置下环境:对于Mina的日志输出使用的是slf4j,对于slf4j在开发Hibernate的时候已经很熟悉了,不需要再介绍了。另外一方面就是加入mina的core核心jar包;
1. mina-core.jar         2. slf4j-api.jar         3.slf4j-simple.jar
然后我们首先创建两个类:
HimiObject.java



  • /**
  • * @author Himi
  • */

  • import java.io.Serializable;

  • public class HimiObject implements Serializable{

  •     public HimiObject(int id,String name){
  •         this.id=id;
  •         this.name=name;
  •     }

  •     private int id;

  •     private String name;

  •     public int getId() {
  •         return id;
  •     }
  •     public void setId(int id) {
  •         this.id = id;
  •     }
  •     public String getName() {
  •         return name;
  •     }
  •     public void setName(String name) {
  •         this.name = name;
  •     }

  • }

这个类是个消息Object,它用于server与client端的交互的数据,它需要序列化,所以我们使用Serializable接口;至于在mina框架中起到什么作用这个后续来说;

ClientMinaServerHanlder.java



  • /**
  • * @author Himi
  • */

  • import org.apache.mina.core.service.IoHandlerAdapter;
  • import org.apache.mina.core.session.IdleStatus;
  • import org.apache.mina.core.session.IoSession;

  • public class ClientMinaServerHanlder extends IoHandlerAdapter {

  •     private int count = 0;

  •     // 当一个新客户端连接后触发此方法.
  •     public void sessionCreated(IoSession session) {
  •         System.out.println("新客户端连接");
  •     }

  •     // 当一个客端端连结进入时 @Override
  •     public void sessionOpened(IoSession session) throws Exception {
  •         count++;
  •         System.out.println("第 " + count + " 个 client 登陆!address: : "
  •                 + session.getRemoteAddress());

  •     }

  •     // 当客户端发送的消息到达时:
  •     @Override
  •     public void messageReceived(IoSession session, Object message)
  •             throws Exception {
  •         // // 我们己设定了服务器解析消息的规则是一行一行读取,这里就可转为String:
  •         // String s = (String) message;
  •         // // Write the received data back to remote peer
  •         // System.out.println("收到客户机发来的消息: " + s);
  •         // // 测试将消息回送给客户端 session.write(s+count); count++;

  •         HimiObject ho = (HimiObject) message;
  •         System.out.println(ho.getName());

  •         ho.setName("serverHimi");
  •         session.write(ho);

  •     }

  •     // 当信息已经传送给客户端后触发此方法.
  •     @Override
  •     public void messageSent(IoSession session, Object message) {
  •         System.out.println("信息已经传送给客户端");

  •     }

  •     // 当一个客户端关闭时
  •     @Override
  •     public void sessionClosed(IoSession session) {
  •         System.out.println("one Clinet Disconnect !");
  •     }

  •     // 当连接空闲时触发此方法.
  •     @Override
  •     public void sessionIdle(IoSession session, IdleStatus status) {
  •         System.out.println("连接空闲");
  •     }

  •     // 当接口中其他方法抛出异常未被捕获时触发此方法
  •     @Override
  •     public void exceptionCaught(IoSession session, Throwable cause) {
  •         System.out.println("其他方法抛出异常");
  •     }

  • }

本类主要是继承IoHandlerAdapter并且重写其类的一些函数,至于每个函数的作用Himi都已经在代码中加以注视;本类的作用:
此类是用以处理消息的也可说是个消息处理器,当客户端有消息传给server端的时候,或者server端传递给Client端的时候(Client端也会有个消息处理器)都会通过消息处理器进行处理。
OK,下面我们来书写server端的main函数类:



  • /**
  • * @author Himi
  • */

  • import java.io.IOException;
  • import java.net.InetSocketAddress;

  • import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
  • import org.apache.mina.filter.codec.ProtocolCodecFilter;
  • import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;
  • import org.apache.mina.transport.socket.SocketAcceptor;
  • import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

  • public class MinaServer {

  •     /**
  •      * @param args
  •      */

  •     public static void main(String[] args) {
  •         //创建一个非阻塞的server端Socket ,用NIO
  •         SocketAcceptor acceptor = new NioSocketAcceptor();

  •         /*---------接收字符串---------*/
  • //      //创建一个接收数据过滤器
  • //      DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
  • //      //设定过滤器一行行(/r/n)的读取数据
  • //      chain.addLast("mychin", new ProtocolCodecFilter(new TextLineCodecFactory()   ));
  •         /*---------接收对象---------*/
  •         //创建接收数据的过滤器
  •         DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
  •         //设定这个过滤器将以对象为单位读取数据
  •         ProtocolCodecFilter filter= new ProtocolCodecFilter(new ObjectSerializationCodecFactory());
  •         chain.addLast("objectFilter",filter);

  •         //设定服务器消息处理器
  •         acceptor.setHandler(new ClientMinaServerHanlder());
  •         //服务器绑定的端口
  •         int bindPort = 9988;
  •         //绑定端口,启动服务器
  •         try {
  •             acceptor.bind(new InetSocketAddress(bindPort));
  •         } catch (IOException e) {
  •             System.out.println("Mina Server start for error!"+bindPort);
  •             e.printStackTrace();
  •         }
  •         System.out.println("Mina Server run done! on port:"+bindPort);
  •     }
  • }

IoService 是负责底层通讯接入,而 IoHandler 是负责业务处理的。那么 MINA 架构图中的 IoFilter 作何用途呢?答案是你想作何用途都可以。但是有一个用途却是必须的,那就是作为 IoService 和 IoHandler 之间的桥梁。IoHandler 接口中最重要的一个方法是 messageReceived,这个方法的第二个参数是一个 Object 型的消息,总所周知,Object 是所有 Java 对象的基础,那到底谁来决定这个消息到底是什么类型呢?这个取决于我们后面设定的过滤器!  
对于在mina中建立一个server,步骤如下:
1. 建立一个SockerAcceptor ,除了启动server之外它还可以为我们可以生成过滤器DefaultIoFilterChainBuilder、设置消息处理器等功能;
        2.设置过滤器
        3. 设置消息处理器
其实很容易不是么? 哈哈;
OK,这里多说一些:
对于消息处理器 DefaultIoFilterChainBuilder,它的作用是用于设定收发的形式,例如:



  • //创建一个接收数据过滤器
  •         DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
  •         //设定过滤器一行行(/r/n)的读取数据
  •         chain.addLast("mychin", new ProtocolCodecFilter(new TextLineCodecFactory()   ));

这样设置一个过滤器作用是将来自客户端输入的信息转换成一行行的文本后传递给 IoHandler,因此我们可以在 messageReceived 中直接将 msg 对象强制转换成 String 对象。
ps.而如果我们不提供任何过滤器的话,那么在 messageReceived 方法中的第二个参数类型就是一个 byte 的缓冲区,对应的类是 org.apache.mina.common.ByteBuffer。虽然你也可以将解析客户端信息放在 IoHandler 中来做,但这并不是推荐的做法,使原来清晰的模型又模糊起来,变得 IoHandler 不只是业务处理,还得充当协议解析的任务。
mina自身带有一些常用的过滤器,例如LoggingFilter(日志记录)、BlackListFilter(黑名单过滤)、CompressionFilter(压缩)、SSLFilter(SSL加密)等。
当我们设置如下:



  • //创建接收数据的过滤器
  •         DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
  •         //设定这个过滤器将以对象为单位读取数据
  •         ProtocolCodecFilter filter= new ProtocolCodecFilter(new ObjectSerializationCodecFactory());
  •         chain.addLast("objectFilter",filter);

这样以来我们server可以收发Object对象啦;


acceptor.setHandler(new ClientMinaServerHanlder());
这里是设置消息处理器,绑定在我们的ClientMinaServerHanlder类上,其实mina对于收发处理已经完全交给开发者来进行处理,所以至于在消息处理器如何编写和处理就放任不会管了;
OK,现在我们可以run一下启动server端了;
当然我们现在也可以来测试了,当前我们还没有书写Client端的代码,所以我们使用terminal终端进行测试,OK,打开你的terminal
然后输入  telnet localhost 9988
观察服务器打印:
http://www.himigame.com/wp-content/uploads/2012/05/123.png
OK,没有任何问题;但是这时候大家不要在终端书写内容给server,因为我们server的消息处理器中的接受Client的函数中做了如下处理:



  • // 当客户端发送的消息到达时:
  •     @Override
  •     public void messageReceived(IoSession session, Object message)
  •             throws Exception {
  •         // // 我们己设定了服务器解析消息的规则是一行一行读取,这里就可转为String:
  •         // String s = (String) message;
  •         // // Write the received data back to remote peer
  •         // System.out.println("收到客户机发来的消息: " + s);
  •         // // 测试将消息回送给客户端 session.write(s+count); count++;

  •         HimiObject ho = (HimiObject) message;
  •         System.out.println(ho.getName());

  •         ho.setName("serverHimi");
  •         session.write(ho);

  •     }

Himi这里server处理client端发来的数据处理函数(如上代码)中,当Client发送数据过来的时候,将消息message强制转换成了一个HimiObject对象,然后改个name重写发给Client端,但是由于Client端是使用终端模拟登陆根本无法接受这个对象,所以会出异常;
但是到这里大家应该懂得,HimiObject类的作用了;哈哈
下面我们来书写Client客户端,对于建立一个Client端,其实步骤雷同,步骤如下:
        1 . 建立一个NioSocketConnector对象;
        2. 设定过滤器
        3. 设定消息处理器
代码如下:



  • /**
  • * @author Himi
  • */
  • import java.net.InetSocketAddress;

  • import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
  • import org.apache.mina.core.future.ConnectFuture;
  • import org.apache.mina.filter.codec.ProtocolCodecFilter;
  • import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;
  • import org.apache.mina.transport.socket.nio.NioSocketConnector;

  • public class MainClient {
  •     public static void main(String[] args) {
  •         // 创建一个tcp/ip 连接
  •         NioSocketConnector connector = new NioSocketConnector();

  •         /*---------接收字符串---------*/
  •         // //创建接收数据的过滤器
  •         // DefaultIoFilterChainBuilder chain = connector.getFilterChain();
  •         // // 设定这个过滤器将一行一行(/r/n)的读取数据
  •         // chain.addLast("myChin", new ProtocolCodecFilter(
  •         // new TextLineCodecFactory()));
  •         /*---------接收对象---------*/
  •         // 创建接收数据的过滤器
  •         DefaultIoFilterChainBuilder chain = connector.getFilterChain();
  •         // 设定这个过滤器将以对象为单位读取数据
  •         ProtocolCodecFilter filter = new ProtocolCodecFilter(
  •                 new ObjectSerializationCodecFactory());
  •         // 设定服务器端的消息处理器:一个SamplMinaServerHandler对象,
  •         chain.addLast("objectFilter",filter);

  •         // 设定服务器端的消息处理器:一个 SamplMinaServerHandler 对象,
  •         connector.setHandler(new ClientMinaServerHanlder());
  •         // Set connect timeout.
  •         connector.setConnectTimeoutCheckInterval(30);
  •         // 连结到服务器:
  •         ConnectFuture cf = connector.connect(new InetSocketAddress("localhost",
  •                 9988));
  •         // Wait for the connection attempt to be finished.
  •         cf.awaitUninterruptibly();
  •         cf.getSession().getCloseFuture().awaitUninterruptibly();
  •         connector.dispose();

  •     }
  • }

不多说了,很eazy:那么我们继续看Clent端的消息处理器以及HimiObject:
ClentMinaServerHanlder:



  • /**
  • * @author Himi
  • */

  • import org.apache.mina.core.service.IoHandlerAdapter;
  • import org.apache.mina.core.session.IoSession;

  • public class ClientMinaServerHanlder extends IoHandlerAdapter {
  •     // 当一个客端端连结到服务器后
  •     @Override
  •     public void sessionOpened(IoSession session) throws Exception {
  • //      session.write("我来啦........");
  •         HimiObject ho = new HimiObject(1,"Himi");
  •         session.write(ho);
  •     }

  •     // 当一个客户端关闭时
  •     @Override
  •     public void sessionClosed(IoSession session) {
  •         System.out.println("I'm Client &&  I closed!");
  •     }

  •     // 当服务器端发送的消息到达时:
  •     @Override
  •     public void messageReceived(IoSession session, Object message)
  •             throws Exception {
  • //      // 我们己设定了服务器解析消息的规则是一行一行读取,这里就可转为 String:
  • //      String s = (String) message;
  • //      // Write the received data back to remote peer
  • //      System.out.println("服务器发来的收到消息: " + s);
  • //      // 测试将消息回送给客户端 session.write(s);

  •         HimiObject ho = (HimiObject) message;
  •         System.out.println(ho.getName());

  •     }
  • }

Client的HimiObject与服务器Server的HimiObejct类一模一样!
可能有童鞋不理解为什么server端与client的HimiObject一模一样,这里Himi说下,通过Client端的消息处理器可以看出,当我们Client端连接到服务器后,首先会写给Server端一个HimiObject对象!那么服务器之前说过了,接受到Client端的消息后首先将消息强制转换成HimiObject对象然后处理;
既然Client端发的是Object,那么当然我们的服务器也要有对应的此Object对象才可以,否则如何获取这个Object?  大家应该很容易理解;
OK,不多说直接运行Client端,观察结果:
http://www.himigame.com/wp-content/uploads/2012/05/321.png

OK,结果正常。
    Client与Server消息逻辑如下:
    1. Client->传递HimiObject给Server
     2. Server端接受message强制转换HimiObject,并且设置其name为serverHimi,然后再传递给Client
    3. 客户端接受message强制转换HimiObject,然后获取此类的name打印出来!





运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-639466-1-1.html 上篇帖子: 服务器证书安装配置(Apache for Linux) 下篇帖子: 【Java Servlet 开发系列之一】在mac系统安装Apache Tomcat的详细步骤
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表