zoukankan      html  css  js  c++  java
  • 使用Mina框架开发 QQ Android 客户端

    Apache MINA是一个网络应用程序框架,用来帮助用户简单地开发高性能和高可靠性的网络应用程序。它提供了一个通过Java NIO在不同的传输例如TCP/IP和UDP/IP上抽象的事件驱动的异步API。

      Apache MINA 也称为:
      ● NIO 框架库
      ● 客户端服务器框架库
      ● 一个网络套接字库
      MINA虽然简单但是仍然提供了全功能的网络应用程序框架:
      ● 为不同的传输类型提供了统一的API:
      ○ 通过Java NIO提供TCP/IP 和 UDP/IP支持
      ○ 通过RXTX提供串口通讯(RS232)
      ○ In-VM管道通讯
      ○ 你能实现你自己的API!
      ● 过滤器作为一个扩展特性; 类似Servlet过滤器
      ● 低级和高级的API:
      ○ 低级: 使用字节缓存(ByteBuffers)
      ○ 高级: 使用用户定义的消息对象(objects)和编码(codecs)
      ● 高度定制化线程模型:
      ○ 单线程
      ○ 一个线程池
      ○ 一个以上的线程池(也就是SEDA)
      ● 使用Java 5 SSL引擎提供沙盒(Out-of-the-box) SSL · TLS · StartTLS支持
      ● 超载保护和传输流量控制
      ● 利用模拟对象进行单元测试
      ● JMX管理能力
      ● 通过StreamIoHandler提供基于流的I/O支持
      ● 和知名的容器(例如PicoContainer、Spring)集成
      ● 从Netty平滑的迁移到MINA, Netty是MINA的前辈。

    MINA 基本类的描述 : 
    IoAccepter 相当于网络应用程序中的服务器端 
    IoConnector 相当于客户端 
    IoSession 当前客户端到服务器端的一个连接实例 
    IoHandler 业务处理逻辑 
    IoFilter 过滤器用于悬接通讯层接口与业务层接口

    要编写和运行一个基于Apache MINA 2.0的程序,需要JDK 5.0以上版本,

    下面看一个domo:

    准备工作:

    mina-core-2.0.0-M6.jar
    slf4j-api-1.5.2.jar


    在官网下载到mina,找出这几个jar,添加到项目,

    服务端的代码:

    1. public class MainFrame { 
    2.     private static final int PORT=5469; 
    3.      
    4.     public static void main(String[] args) throws Exception{ 
    5.         IoAcceptor acceptor=new NioSocketAcceptor(); 
    6.         IoFilter filter=new ProtocolCodecFilter(new TextLineCodecFactory()); 
    7.         acceptor.getFilterChain().addLast("vestigge", filter); 
    8.         acceptor.setHandler(new ServerHandler()); 
    9.         acceptor.bind(new InetSocketAddress(PORT)); 
    10.          
    11.         System.out.println( "服务器正在监听端口" + PORT +"..."); 
    12.     } 

    其中new ServerHandler()传入的是实现了IoHandler接口的类,代码如下:

    1. public class ServerHandler extends IoHandlerAdapter { 
    2.  
    3.     @Override 
    4.     public void messageReceived(IoSession session, Object message) 
    5.             throws Exception { 
    6.         System.out.println("收到客户端消息:" + message.toString()); 
    7.     } 
    8.      
    9.     @Override 
    10.     public void exceptionCaught(IoSession session, Throwable cause) 
    11.             throws Exception { 
    12.         System.out.println("服务器出现异常:" +cause); 
    13.     } 
    14.  

    重写了父类中的messageReceived()和exceptionCaught()

    一般在messageReceived()中对客户端的请求进行业务,逻辑处理

    下面在命令行用telnet测试一下,

    如果是win 7没有telnet,找到“打开或关闭Windows功能” ,找到telnet客户端和telnet服务端,勾选即可,

    在命令行下输入telnet 127.0.0.1 5469

    然后再telnet窗口中输入几个字符回车,在控制台可以看到服务器成功收到了消息:

    上节中通过一个简单的例子,对Mina框架有了大体的了解,在上节的基础上,看看 怎样实现客户端与服务端的通信,

    废话不多说了,直接看代码:

    1. public class Test { 
    2.  
    3.     public static void main(String[] args) throws Exception{ 
    4.         SocketConnector connector = new NioSocketConnector(); 
    5.         IoFilter filter = new ProtocolCodecFilter(new TextLineCodecFactory()); 
    6.         connector.getFilterChain().addLast("vestigge", filter); 
    7.         SocketAddress soketAddress = new InetSocketAddress("127.0.0.1", 5469); 
    8.         connector.setHandler(new ClientHandler()); 
    9.         ConnectFuture future= connector.connect(soketAddress); 
    10.         future.join(); 
    11.         if (!future.isConnected()) { 
    12.             System.out.println("连接服务器失败"); 
    13.             return
    14.         } 
    15.         future.getSession().write("hello"); 
    16.     } 

    可以看到代码与服务器端的代码很像,也是非常的简单,这就是框架的好处,不用再重复发明轮子,省了不少事,

    1. public class ClientHandler extends IoHandlerAdapter { 
    2.      
    3.     public void messageReceived(IoSession arg0, Object message) throws Exception { 
    4.         System.out.println("收到服务器消息:" + message.toString()); 
    5.     } 
    6.  
    7.     public void exceptionCaught(IoSession arg0, Throwable arg1) 
    8.             throws Exception { 
    9.  
    10.     } 

    效果演示:

    登陆界面的文章 http://www.linuxidc.com/Linux/2012-11/73437.htm

    就不在重复了,直接看登陆的代码,

    用Mina传递字符串上节已经看过了,要实现传递对象,也非常简单,只需要修改一下过滤器:

    chain.addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory())); 

    Android客户端,登陆的Activity中:

    1. public class LoginActivity extends Activity{ 
    2.     private EditText accountEditText; 
    3.     private EditText passwordEditText; 
    4.     private CheckBox remeberCheckBox; 
    5.      
    6.      public void onCreate(Bundle savedInstanceState) { 
    7.             super.onCreate(savedInstanceState); 
    8.             requestWindowFeature(Window.FEATURE_NO_TITLE); 
    9.             setContentView(R.layout.activity_login); 
    10.             accountEditText=(EditText) findViewById(R.id.login_account); 
    11.             passwordEditText=(EditText) findViewById(R.id.login_password); 
    12.             remeberCheckBox=(CheckBox) findViewById(R.id.login_remember); 
    13.              
    14.             findViewById(R.id.login_login).setOnClickListener(new OnClickListener(){ 
    15.                 public void onClick(View v) { 
    16.                     if(accountEditText.getText().toString().equals("") ||   
    17.                             passwordEditText.getText().toString().equals("")){ 
    18.                         Toast.makeText(LoginActivity.this"账号或密码不能为空!", Toast.LENGTH_SHORT).show(); 
    19.                     }else
    20.                         User user=new User(); 
    21.                         user.setAccount(Integer.parseInt(accountEditText.getText().toString())); 
    22.                         user.setPassword(passwordEditText.getText().toString()); 
    23.                         user.setOperation(VQMessageType.LOGIN); 
    24.                         boolean b=new VQClient().sendLoginInfo(user); 
    25.                         //如果登录成功  
    26.                         if(b){ 
    27.                             Toast.makeText(LoginActivity.this"登陆成功!", Toast.LENGTH_SHORT).show(); 
    28.                             startActivity(new Intent(LoginActivity.this,MainActivity.class)); 
    29.                         }else
    30.                             Toast.makeText(LoginActivity.this"连接超时,登陆失败!", Toast.LENGTH_SHORT).show(); 
    31.                         } 
    32.                     } 
    33.                 } 
    34.             }); 
    35.      } 

    可以看到会调用VQClient这个类中的方法去发送登陆请求,VQClient的实现:

    1. public class VQClient { 
    2.     private static int PORT=5469; 
    3.     public boolean sendLoginInfo(User u){ 
    4.         boolean b=false
    5.         System.setProperty("java.net.preferIPv6Addresses", "false");//2.2会有ipv6的问题,  
    6.         SocketConnector connector = new NioSocketConnector(); 
    7.         connector.setConnectTimeoutMillis(300000); 
    8.         DefaultIoFilterChainBuilder chain=connector.getFilterChain(); 
    9.         chain.addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory())); 
    10.         SocketSessionConfig cfg = connector.getSessionConfig(); 
    11.         cfg.setUseReadOperation(true);   
    12.         IoSession session = connector.connect(new InetSocketAddress("10.0.2.2", PORT))   
    13.                 .awaitUninterruptibly().getSession(); 
    14.         //发送并等待完成  
    15.         session.write(u).awaitUninterruptibly(); 
    16.         //接收  
    17.         ReadFuture readFuture = session.read(); 
    18.         //接收超时  
    19.         if (readFuture.awaitUninterruptibly(30000,TimeUnit.SECONDS)) {   
    20.             VQMessage message = (VQMessage) readFuture.getMessage(); 
    21.             //如果是登陆成功的信息  
    22.             if(message.getType().equals(VQMessageType.SUCCESS)){ 
    23.                 b= true
    24.             } 
    25.         } else { 
    26.             b= false
    27.         }   
    28.         //断开连接  
    29.         session.close(true);   
    30.         session.getService().dispose();   
    31.         return b; 
    32.     } 

    然后服务器端变化不大,在收到登陆请求后,会查询数据库,返回一个信息,来表明登陆成功或者失败。

    1. public void messageReceived(IoSession session, Object message) 
    2.             throws Exception { 
    3.         User user=(User) message; 
    4.         if(user.getOperation().equals(VQMessageType.LOGIN)){ 
    5.             //操作数据库  
    6.             boolean b=new UserDao().login(user.getAccount(), user.getPassword()); 
    7.             if(b){ 
    8.                 System.out.println(">> ["+user.getAccount()+"] 上线了"); 
    9.                 //告诉客户端登陆成功  
    10.                 VQMessage m=new VQMessage(); 
    11.                 m.setType(VQMessageType.SUCCESS); 
    12.                 session.write(m); 
    13.             } 
    14.         } 
    15.     } 

    最后附上测试效果:

  • 相关阅读:
    从B树、B+树、B*树谈到R 树
    The Log-Structured Merge-Tree(译)
    Leveldb源码分析--2
    Leveldb源码分析--1
    little-endian And big-endian
    Fixed数据类型
    Varint数值压缩存储方法
    JavaEE开发之SpringBoot工程的创建、运行与配置
    Javaee基本框架(Struts2,Spring,MyBatista)之间的关系
    XLM解析技术概述
  • 原文地址:https://www.cnblogs.com/dongweiq/p/3993343.html
Copyright © 2011-2022 走看看