为了账号安全,请及时绑定邮箱和手机立即绑定

mina 客户端与服务器端之间的通信

标签:
Java Android

关于长连接

与Http连接相反,通过某种方式与服务器一直保持连接称之为长连接。底层都是基于TCP/IP协议,通过Socket、ServerSocket与服务期保持连接,服务端一般是通过ServerSocket建立监听,监听客户端与之连接,客户端通过Socket指定端口和地址与服务端进行连接。

长连接的意义

  • 长连接实现服务端主动向客户端推送消息
  • 减少对服务器的轮询,减小服务器的压力
  • 通信效率高于Http

mina 的优势

  • 非常适合C/S架构的通信框架
  • apache 的一个开源项目,比较信赖
  • 官网上有详细的学习资料供开发者学习
  • 使用起来比较简单,一定程度上降低了学习的成本

mina 的核心类

  1. IoService 及其相关类
  2. IoAcceptor 及其相关类
  3. IoConnector 及其相关类
  4. Filter 及其相关类,LoggingFilter 记录 mina 所有日志,ProtocolCodecFilter 数据转化过滤器,主要定义与服务器进行通信的数据类型,有利于数据传递;CompressionFilter 数据压缩过滤器,支持数据压缩,有利于提高数据传输效率;SSLFilter 数据加密过滤器。也可以通过 IoFilterAdapter 来实现自己的过滤器
  5. IoSession 类主要与通信有关,当与服务器建立连接之后,mina 返回一个 IoSession 对象,通过该对象就可以与服务器端进行读写操作,如果不想进行读写操作,可选择关闭。IoSession 的其他设置:设置接收数据缓存区的大小、设置数据发送缓存区的大小、设置状态恢复时间、设置写超时时间,其中设置缓存区的大小主要是防止内存溢出,设置状态恢复时间就是不进行读写操作多长时间之后自动进入空闲状态,可节省系统资源
  6. Handler 类主要对 SessionCreated、SessionOpen、SessionClosed 等事件进行监听,messageReceived、messageSend事件的监听,exceptionCaught异常的捕获等。

    mina 服务器的搭建

    主要步骤

    • 创建 IoAcceptor 监听对象
    • 设置 I0Acceptor 监听对象,通过 IoAcceptor 监听对象获得过滤链,然后设置日志过滤器、数据转化过滤器,然后设置一个事件处理handler
    • 创建一个类继承IoHandlerAdapter,主要负责 Session 对象的创建监听、消息发送和接收的监听
    • 通过 IoAcceptor 监听对象获得 SessionConfig 设置读取缓存区的大小、设置状态恢复时间
    • 通过 IoAcceptor 监听对象的 bind() 方法指定端口号进行监听

      集成 mina 框架

      首先 mina 官网下载 mina 的压缩包,然后解压导入对应的 jar 包,这里导入的 jar 包为 mina-core-2.0.16.jar 这是 mina 的核心包,而 mina 又依赖与 slf4j ,故需导入 slf4j-api-1.7.21.jar ,此时运行项目会出错,根据提示需导入
      slf4j-nop-1.8.0-alpha2.jar,此时运行项目正常,说明导入 mina 框架已经集成到我们的项目中了,如下图如下:

image

代码参考

/**
 * 使用Mina创建一个简单的通信服务器
 * @author jzman
 */
public class MinaServer {
    public static void main(String[] args) {
        //创建监听对象
        IoAcceptor acceptor = new NioSocketAcceptor();
        //为监听对象添加日志过滤器、数据转换过滤器
        acceptor.getFilterChain().addLast("logger", new LoggingFilter());
        acceptor.getFilterChain().addLast("protocal", 
                new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
        //设置事件处理的Handler
        acceptor.setHandler(new DataHandler());
        //设置读取数据缓存区的大小
        acceptor.getSessionConfig().setReadBufferSize(1024);
        //设置状态恢复时间
        acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);

        try {
            //指定端口号进行监听
            acceptor.bind(new InetSocketAddress(12345));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println(e.toString());
        }
        System.out.println("启动服务..."); 
    }

    /**
     * session对象的创建监听和消息的接收、发送的监听
     * @author lcdn
     *
     */
    private static class DataHandler extends IoHandlerAdapter{
        /**
         * session的创建
         */
        public void sessionCreated(IoSession session) throws Exception {
            super.sessionCreated(session);
        }
        /**
         * session的打开
         */
        public void sessionOpened(IoSession session) throws Exception {
            super.sessionOpened(session);
        }
        /**
         * 消息的接收
         */
        public void messageReceived(IoSession session, Object message) throws Exception {
            super.messageReceived(session, message);
            String info = message.toString();
            Date date = new Date(System.currentTimeMillis());
            SimpleDateFormat sdf = new  SimpleDateFormat("yy-MM-dd HH:mm:ss");
            String time = sdf.format(date);
            session.write(time);
            System.out.println("接收到的消息:"+info);
        }
        /**
         * 消息的发送
         */
        public void messageSent(IoSession session, Object message) throws Exception {
            super.messageSent(session, message);
        }
        /**
         * session的关闭
         */
        public void sessionClosed(IoSession session) throws Exception {
            super.sessionClosed(session);
        }
    }
}

mina 客户端的搭建

主要步骤

  1. 创建一个 Service 用来与远程服务器连接
  2. 封装一个 ConnectManager 类用来提供与服务器连接的方法
  3. 在 Service 中启动线程,调用 ConnectManager 里面的方法完成连接的创建

集成 mina 框架

打开 Android studio 创建工程,导入slf4j-api-1.7.21.jar 、
mina-core-2.0.16.jar 或 slf4j-android-1.6.1-RC1.jar , 编译不出错说明将 mina 框架导入到项目中了,如下图所示:

image

代码参考

ConnectConfig
/**
 * Created by lcdn on 2017/6/30 0030.
 * 使用构建者模式进行网络连接的一些配置信息
 */
public class ConnectConfig {

    private Context mContext;
    private String ip;
    private int port;
    private int readBufferSize;
    private long connectionTimeOut;

    public Context getmContext() {
        return mContext;
    }

    public void setmContext(Context mContext) {
        this.mContext = mContext;
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public int getReadBufferSize() {
        return readBufferSize;
    }

    public void setReadBufferSize(int readBufferSize) {
        this.readBufferSize = readBufferSize;
    }

    public long getConnectionTimeOut() {
        return connectionTimeOut;
    }

    public void setConnectionTimeOut(long connectionTimeOut) {
        this.connectionTimeOut = connectionTimeOut;
    }

    public static class Builder{
        private Context mContext;
        private String ip = "192.168.1.104";
        private int port = 1234;
        private int readBufferSize = 1024;
        private long connectionTimeOut = 10000;

        public Builder(Context context){
            this.mContext = context;
        }

        public Builder setIp(String ip){
            this.ip = ip;
            return this;
        }
        public Builder setPort(int port){
            this.port = port;
            return this;
        }
        public Builder setreadBufferSize(int readBufferSize){
            this.readBufferSize = readBufferSize;
            return this;
        }
        public Builder setConnectionTimeOut(long timeOut){
            this.connectionTimeOut = timeOut;
            return this;
        }

        private void applyConnectConfig(ConnectConfig config){
            config.mContext = this.mContext;
            config.ip = this.ip;
            config.port = this.port;
            config.readBufferSize = this.readBufferSize;
            config.connectionTimeOut = this.connectionTimeOut;
        }

        public ConnectConfig builder(){
            ConnectConfig connectConfig = new ConnectConfig();
            applyConnectConfig(connectConfig);
            return connectConfig;
        }
    }
}
ConnectManager

/**
 * Created by jzman on 2017/6/30 0030.
 * 封装与服务器连接相关的方法
 */
public class ConnectManager {

    public static final String BROADCAST_MINA_ACTION = "com.manu.mina.BROADCAST_MINA_ACTION";

    private ConnectConfig mConnectConfig;//网络连接的配置信息
    private WeakReference<Context> mContextWeakReference;
    private NioSocketConnector mNioSocketConnector;
    private InetSocketAddress mInetSocketAddress;
    private IoSession mIoSession;

    public ConnectManager(ConnectConfig config){
        this.mConnectConfig = config;
        this.mContextWeakReference = new WeakReference<>(config.getmContext());
        init();
    }

    private void init() {
        //根据主机名和端口号创建地址对象
        mInetSocketAddress = new InetSocketAddress(mConnectConfig.getIp(),mConnectConfig.getPort());
        //创建连接对象
        mNioSocketConnector = new NioSocketConnector();
        //设置读缓存大小
        mNioSocketConnector.getSessionConfig().setReadBufferSize(mConnectConfig.getReadBufferSize());
        //设日志过滤器、数据过滤器
        mNioSocketConnector.getFilterChain().addLast("logger",new LoggingFilter());
        mNioSocketConnector.getFilterChain().addLast("protocol",
                new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
        mNioSocketConnector.setHandler(new DataHandler(mContextWeakReference.get()));
        //为连接对象设置默认远程地址(必须)
        mNioSocketConnector.setDefaultRemoteAddress(mInetSocketAddress);
    }

    /**
     * 是否连接成功
     * @return
     */
    public boolean connect(){
        try{
            ConnectFuture future = mNioSocketConnector.connect();
            future.awaitUninterruptibly();
            mIoSession = future.getSession();
            //将获取到的 Session 保存在 SessionManager 中
            SessionManager.getInstance().setSession(mIoSession);
        }catch(Exception e){
            Log.i("mina:","连接失败");
            Log.i("mina:",e.toString());
            e.printStackTrace();
            return false;
        }
        return mIoSession != null;
    }

    /**
     * 关闭连接
     */
    public void disConnect(){
        mNioSocketConnector.dispose();
        mNioSocketConnector = null;
        mInetSocketAddress = null;
        mIoSession = null;
        mContextWeakReference = null;
    }

    private static class DataHandler extends IoHandlerAdapter {

        private Context mContext;
        public DataHandler(Context context) {
            this.mContext = context;
        }

        @Override
        public void sessionOpened(IoSession session) throws Exception {
            super.sessionOpened(session);
            //将session保存在session manager中,从而可以向服务器发送消息
            Log.i("sessionOpened:","");
            Log.i("sessionOpened:",session.toString());
        }

        @Override
        public void messageReceived(IoSession session, Object message) throws Exception {
            //接收消息
            if (mContext!=null){
                //将接受到的消息广播出去
                Intent intent = new Intent(BROADCAST_MINA_ACTION);
                intent.putExtra("message",message.toString());
                LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
            }
        }
    }
}
ConnectService
/**
 * Created by jzman on 2017/7/5 0005.
 * 用来连接的服务
 */
public class ConnectService extends Service {

    private ConnectRunnable runnable;

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("onCreate:","");
        runnable = new ConnectRunnable(getApplicationContext());
        new Thread(runnable).start();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        runnable.disConnect();
        runnable = null;
    }

    /**
     * 创建连接服务器的线程
     */
    class ConnectRunnable implements Runnable{
        private Context mContext;
        private boolean isConnect;
        private ConnectManager mManager;
        ConnectRunnable(Context context){
            this.mContext = context;
            ConnectConfig config = new ConnectConfig.Builder(mContext)
                    .setIp("192.168.1.104")
                    .setPort(12345)
                    .setreadBufferSize(1024)
                    .setConnectionTimeOut(10000)
                    .builder();
            mManager = new ConnectManager(config);
        }

        @Override
        public void run() {
            for( ; ; ){
                isConnect = mManager.connect();
                System.out.println("isConnect:"+isConnect);
                if (isConnect){
                    Log.i("mina:","连接成功");
                    break;
                }
                try{
                    Log.i("mina:","重新连接");
                    Thread.sleep(3000);
                }catch(Exception e){
                    Log.i("mina:","连接失败");
                    Log.i("mina:",e.toString());
                }
            }
        }
        //关闭连接
        public void disConnect(){
            mManager.disConnect();
        }
    }
}
SessionManager

/**
 * Created by jzman on 2017/7/5 0005.
 * Session 管理器
 */
public class SessionManager {
    private static SessionManager mInstance = null;
    private IoSession mSession;//客户端与服务器端之间的通信对象
    public static SessionManager getInstance(){
        if (mInstance == null){
            synchronized (SessionManager.class){
                if (mInstance == null){
                    mInstance = new SessionManager();
                }
            }
        }
        return mInstance;
    }

    private SessionManager(){
    }

    public void setSession(IoSession session){
        this.mSession = session;
    }

    /**
     * 将对象写到服务器
     * @param msg
     */
    public void writeToServer(Object msg){
        Log.i("writeToServer:",msg.toString());
        if (mSession!=null){
            Log.i("writeToServer:","msg");
            mSession.write(msg);
        }
    }

    /**
     * 关闭session
     */
    public void closeSession(){
        if (mSession!=null){
            mSession.closeOnFlush();
        }
    }

    /**
     * 移除session
     */
    public void removeSession(){
        mSession = null;
    }
}
布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.manu.minademo.MainActivity">
    <TextView
        android:id="@+id/tv_startConnect"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:layout_marginTop="20dp"
        android:layout_marginLeft="12dp"
        android:layout_marginRight="12dp"
        android:text="启动mina服务连接服务器端"
        android:padding="10dp"
        android:background="#60ed24"
        android:textSize="18sp"/>
    <TextView
        android:id="@+id/tv_sendMessage"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:layout_marginTop="20dp"
        android:layout_marginLeft="12dp"
        android:layout_marginRight="12dp"
        android:text="向mina服务端发送消息"
        android:padding="10dp"
        android:background="#60ed24"
        android:textSize="18sp"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_marginLeft="12dp"
        android:layout_marginRight="12dp"
        android:orientation="horizontal">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="from nina server:"
            android:padding="10dp"
            android:background="#60ed24"
            android:textSize="18sp"/>
        <TextView
            android:id="@+id/tv_receiveMessage"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:padding="10dp"
            android:background="#60ed24"
            android:textSize="18sp"
            android:textColor="#ffffff"/>
    </LinearLayout>
</LinearLayout>
MainActivity

public class MainActivity extends AppCompatActivity implements
        View.OnClickListener{

    private TextView tv_startConnect;
    private TextView tv_sendMessage;
    private TextView tv_receiveMessage;
    private MinaBroadcastReceiver receiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        tv_startConnect.setOnClickListener(this);
        tv_sendMessage.setOnClickListener(this);
        receiver = new MinaBroadcastReceiver();
        registerReceiver();
    }

    private void initView() {
        tv_startConnect = (TextView) findViewById(R.id.tv_startConnect);
        tv_sendMessage = (TextView) findViewById(R.id.tv_sendMessage);
        tv_receiveMessage = (TextView) findViewById(R.id.tv_receiveMessage);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        stopService(new Intent(this,ConnectService.class));
        unRegisterReceiver();
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.tv_startConnect:
                Intent intent = new Intent(this,ConnectService.class);
                startService(intent);
                break;
            case R.id.tv_sendMessage:
                SessionManager.getInstance().writeToServer("这是客户端发送的消息...");
                break;
        }
    }

    /**
     * 注册广播接收器
     */
    private void registerReceiver(){
        IntentFilter filter = new IntentFilter();
        filter.addAction("com.manu.mina.BROADCAST_MINA_ACTION");
        LocalBroadcastManager.getInstance(this).registerReceiver(receiver,filter);
    }

    /**
     * 解除广播
     */
    private void unRegisterReceiver(){
        LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
    }

    /**
     * 定义广播接收器
     */
    private class MinaBroadcastReceiver extends BroadcastReceiver{
        public MinaBroadcastReceiver(){
        }
        @Override
        public void onReceive(Context context, Intent intent) {
            tv_receiveMessage.setText(intent.getStringExtra("message"));
        }
    }
}

测试效果

image

<完>

点击查看更多内容
7人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
移动开发工程师
手记
粉丝
9
获赞与收藏
48

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消