mina框架实现长连接与android客户端通信

2023-05-16

这篇文章也是通过学习mina框架视频来的,网上也有很多类似的代码,这里通过自己敲一遍代码,熟悉mina框架的使用以及安卓编程。mina框架作为一个网络异步编程框架,它和netty一样,底层实现了nio。

核心类:

IoAcceptor:服务端接收器,负责创建socket服务,并监听客户端连接。

IoSession:连接会话,可以通过write方法向外发送消息。

IoHandlerAdapter:连接处理器,负责消息收发。

mina实现长连接,是构建基于tcp/ip的socket网络连接,他与http连接的本质区别是,客户端可以维持和服务端的长连接,因为连接一直存在,减少了由于轮询导致的服务器压力,适合做消息推送系统(push),服务器可以主动给客户端推送消息。

服务端代码:(很简单,就一个类)

MinaServer.java,这里采用的是mina-core-2.0.19版本,使用maven构建工程,可以考虑将group:org.apache.mina,artifact:mina-core,version:2.0.19加入到pom.xml文件中。

package com.xxx.mina;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Date;

import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MinaServer {
	private static final Logger log = LoggerFactory.getLogger(MinaServer.class);

	public static void main(String[] args) {
		IoAcceptor acceptor = new NioSocketAcceptor();
		acceptor.getFilterChain().addLast("logger", new LoggingFilter());
		acceptor.getFilterChain().addLast("codec", 
				new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
		acceptor.setHandler(new ServerHandler());
		acceptor.getSessionConfig().setReadBufferSize(2048);
		acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
		try {
			acceptor.bind(new InetSocketAddress(9123));
			log.info("mina server listen at port 9123.");
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
    private static class ServerHandler extends IoHandlerAdapter{
    	@Override
    	public void sessionCreated(IoSession session) throws Exception {
    		super.sessionCreated(session);
    	}
    	
    	@Override
    	public void sessionOpened(IoSession session) throws Exception {
    		super.sessionOpened(session);
    		log.info("one client connected.");
    	}
    	
    	@Override
    	public void messageReceived(IoSession session, Object message)
    			throws Exception {
    		super.messageReceived(session, message);
    		String res = message.toString();
    		Date date = new Date();
    		session.write(date.toString());
    		log.info("server receive message : "+res);
    	}
    	
    	@Override
    	public void messageSent(IoSession session, Object message)
    			throws Exception {
    		super.messageSent(session, message);
    	}
    }
}

客户端代码:(稍微复杂一点,毕竟涉及到UI,还需要考虑与服务端的连接)

客户端工程需要的jar包:

AndroidMainfest.xml,这里关键就是将<uses-permission android:name="android.permission.INTERNET">加入进来

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.xxx.mina"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="22"
        android:targetSdkVersion="27" />

    <uses-permission android:name="android.permission.INTERNET"/>
    
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        
        <service android:name=".common.MinaService"></service>
    </application>

</manifest>

activity_main.xml

<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.xxx.mina.MainActivity" >

    <TextView 
        android:id="@+id/datetxt"
        android:layout_width="wrap_content"   
        android:layout_height="wrap_content"
        android:text="@string/showdate"
        />
    <Button
        android:id="@+id/connectBtn"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_marginTop="40dp"
        android:text="@string/connect" />
    
    <Button 
        android:id="@+id/sendBtn"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_marginTop="100dp"
        android:text="@string/send"/>

</RelativeLayout>

ConnectionConfig.java

package com.xxx.mina.common;

import android.content.Context;

public class ConnectionConfig {
	private Context context;
	private String ip;
	private int port;
	private int readBufferSize;
	private long connectionTimeout;
	public Context getContext() {
		return context;
	}
	public void setContext(Context context) {
		this.context = context;
	}
	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 context;
		private String ip="123.207.70.98";
		private int port=9123;
		private int readBufferSize=10240;
		private long connectionTimeout=10000;
		
		public Builder(Context context){
			this.context = context;
		}
		
		public Builder setPort(int port){
			this.port = port;
			return this;
		}
		public Builder setIp(String ip){
			this.ip = ip;
			return this;
		}
		public Builder setReadBufferSize(int readBufferSize){
			this.readBufferSize = readBufferSize;
			return this;
		}
		public Builder setConnectionTimeout(int connectionTimeout){
			this.connectionTimeout = connectionTimeout;
			return this;
		}
		
		private void applyConfig(ConnectionConfig config){
			config.context = this.context;
			config.ip = this.ip;
			config.port = this.port;
			config.connectionTimeout = this.connectionTimeout;
			config.readBufferSize = this.readBufferSize;
		}
		
		public ConnectionConfig builder(){
			ConnectionConfig config = new ConnectionConfig();
			applyConfig(config);
			return config;
		}
	}

}

ConnectionManager.java

package com.xxx.mina.common;

import java.lang.ref.WeakReference;
import java.net.InetSocketAddress;

import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketConnector;

import android.content.Context;
import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

public class ConnectionManager {
	public static final String BROADCAST_ACTION="com.xxx.mina.common";
	public static final String MESSAGE = "message";
	private ConnectionConfig config;
	private WeakReference<Context> context;
	private NioSocketConnector connection;
	private IoSession session;
	private InetSocketAddress address;
	
	public ConnectionManager(ConnectionConfig config){
		this.config = config;
		this.context = new WeakReference<Context>(config.getContext());
		init();
	}
	
	private void init(){
		address = new InetSocketAddress(config.getIp(),config.getPort());
		connection = new NioSocketConnector();
		connection.setDefaultRemoteAddress(address);
		connection.getSessionConfig().setReadBufferSize(config.getReadBufferSize());
		connection.getFilterChain().addLast("logger", new LoggingFilter());
		connection.getFilterChain().addLast("codec", new ProtocolCodecFilter(
				new ObjectSerializationCodecFactory()));
		connection.setHandler(new DefaultHandler(context.get()));
	}
	
	public boolean connect(){
		try {
			ConnectFuture future = connection.connect();
			future.awaitUninterruptibly();
			session = future.getSession();
		} catch (Exception e) {
			e.printStackTrace();
			Log.e("tag",e.getMessage());
			return false;
		}
		return session == null ? false:true;
	}
	
	public void disConnect(){
		connection.dispose();
		connection = null;
		session = null;
		address = null;
		context = null;
	}
	private static class DefaultHandler extends IoHandlerAdapter{
		private Context context;
		public DefaultHandler(Context context) {
			this.context = context;
		}
		
		@Override
		public void sessionOpened(IoSession session) throws Exception {
			//put session to sessionmanager for send message
			SessionManager.getInstance().setSession(session);
		}
		
		@Override
		public void messageReceived(IoSession session, Object message)
				throws Exception {
			if(context != null){
				Intent intent = new Intent(BROADCAST_ACTION);
				intent.putExtra(MESSAGE, message.toString());
				LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
			}
		}
	}
}

MinaService.java

package com.xxx.mina.common;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.HandlerThread;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;

public class MinaService extends Service {

	private ConnectionThread thread;
	@Override
	public void onCreate() {
		super.onCreate();
		thread = new ConnectionThread("mina", getApplicationContext());
		thread.start();
		Log.e("tag", "start thread to connect");
	}

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

	@Override
	public void onDestroy() {
		super.onDestroy();
		thread.disConnect();
		thread = null;
	}
	@Nullable
	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}

	class ConnectionThread extends HandlerThread {
		boolean isConnected;
		ConnectionManager manager;

		public ConnectionThread(String name, Context context) {
			super(name);
			ConnectionConfig config = new ConnectionConfig.Builder(context)
					.setIp("123.207.70.98").setPort(9123)
					.setReadBufferSize(10240).setConnectionTimeout(10000)
					.builder();
			manager = new ConnectionManager(config);
		}

		@Override
		protected void onLooperPrepared() {
			while(true) {
				isConnected = manager.connect();
				if (isConnected){
					Log.e("tag", "connect successfully.");
					break;
				}
				try {
					Log.e("tag", "connect fail.");
					Thread.sleep(3000);
				} catch (Exception e) {
					Log.e("tag", "fail with error");
				}
			}
		}
		
		public void disConnect(){
			manager.disConnect();
		}
	}
}

SessionManager.java

package com.xxx.mina.common;

import org.apache.mina.core.session.IoSession;

import android.util.Log;

public class SessionManager {
	private static SessionManager instance = null;
	
	private IoSession session;
	public static SessionManager getInstance(){
		if(instance==null){
			synchronized (SessionManager.class) {
				if(instance==null){
					instance = new SessionManager();
				}
			}
		}
		return instance;
	}
	private SessionManager(){}
	public void setSession(IoSession session){this.session = session;}
	public void writeToServer(Object msg){
		if(session != null){
			Log.e("tag", "mina client ready to send message:"+msg);
			session.write(msg);
		}
	}
	
	public void closeSession(){
		if(session != null){
			session.closeOnFlush();
		}
	}
	
	public void removeSession(){
		this.session  = null;
	}
}

MainActivity.java

package com.xxx.mina;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

import com.xxx.mina.common.ConnectionManager;
import com.xxx.mina.common.MinaService;
import com.xxx.mina.common.SessionManager;

public class MainActivity extends Activity implements OnClickListener{
	
	protected TextView dateView;
	protected Button connectBtn;
	protected Button sendBtn;
	
	MessageBroadcastReceiver receiver = new MessageBroadcastReceiver();

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		initView();
		registerBroadcast();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		int id = item.getItemId();
		if (id == R.id.action_settings) {
			return true;
		}
		return super.onOptionsItemSelected(item);
	}
	
	@Override
	protected void onDestroy() {
		super.onDestroy();
		stopService(new Intent(this,MinaService.class));
		unregisterBroadcast();
	}
	
	//receive message and update ui
	private class MessageBroadcastReceiver extends BroadcastReceiver{

		@Override
		public void onReceive(Context context, Intent intent) {
			dateView.setText(intent.getStringExtra(ConnectionManager.MESSAGE));
		}
		
	}
	
	public void registerBroadcast(){
		IntentFilter filter = new IntentFilter(ConnectionManager.BROADCAST_ACTION);
		LocalBroadcastManager.getInstance(this).registerReceiver(receiver, filter);
	}
	
	public void unregisterBroadcast(){
		LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
	}
	
	public void initView(){
		connectBtn = (Button)findViewById(R.id.connectBtn);
		sendBtn = (Button)findViewById(R.id.sendBtn);
		dateView = (TextView)findViewById(R.id.datetxt);
		connectBtn.setOnClickListener(this);
		sendBtn.setOnClickListener(this);
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
			case R.id.connectBtn:
				Intent intent = new Intent(this,MinaService.class);
				startService(intent);
				Log.e("tag", "connect to server");	
				break;
			case R.id.sendBtn:
				SessionManager.getInstance().writeToServer("123");
				Log.e("tag", "send message to server");
				break;
		}
	}
}

运行结果截屏:

logcat结果:

服务端日志:

总结:

  • 代码编写完成,测试的时候,如果是笔记本电脑,可以让手机和电脑在一个WiFi下,这样手机可以通过IP访问电脑上的服务器。
  • 在android7以上版本中,对权限做了很多限制,这里涉及socket连接,因此需要放开网络限制。配置在AndroidMenifest.xml中。否则会出现socket连接异常,permission denied。
  • 如果用的是台式机,那么手机有可能在无线网络下很难连接上服务器,可以考虑将服务端代码部署到腾讯云,这样开启服务之后,可以通过设置腾讯云主机公网IP的方式访问mina服务端,本实验就是将mina server部署在腾讯云主机上测试的。

最后附上安卓客户端工程完整代码:https://github.com/buejee/minaclient

服务端代码很简单,就一个类,上面的MinaServer.java代码就可以使用,这里不再贴出。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

mina框架实现长连接与android客户端通信 的相关文章

  • centos7安装rustup

    rust安装 xff0c 因为被墙的原因 xff0c 在国内几乎很难安装 xff0c 需要通过代理安装 但是rustup却很容易 xff0c 一般在linux下 xff0c 通过官方指定的下列语句 xff0c 基本可以安装rustup cu
  • TiDB在Centos7上通过源码编译安装

    这里难以编译安装的是tikv tidb的三大部分tidb pd tikv中tidb pd均是采用go语言编写 xff0c 安装go语言包即可编译 xff0c 唯独tikv是采用rust语言写的 xff0c 他的编译是最复杂的 而且编译环境非
  • Cloudera Manager 5.12.0图文详解安装过程

    这里介绍的是cdh5的离线安装方式 xff0c 需要的文件提前准备好 xff0c 安装过程会快一些 安装前提 xff1a 机器配置内存一定要高 xff0c 我这里安装的虚拟机均是redhat7 xff1a 内存分别是6G 4G 4G 准备的
  • Failed to get D-Bus connection: Operation not permitted

    docker容器centos7中 xff0c 通过systemctl start service出现下错误 Failed to get D Bus connection Operation not permitted docker中的容器启
  • C++中如何求数组长度

    C 43 43 中没有直接提供求数组长度的方法 xff0c 提供了sizeof begin end 等方法 xff0c 可以供求数组长度使用 可以通过两种方式来求数组长度 xff0c 这里使用模版类 一个是使用sizeof 做除法 xff0
  • IDEA+scala插件开发spark程序

    spark由scala语言编写 xff0c 开发spark程序 xff0c 自然也少不了scala环境 xff0c 这里介绍如何利用Intellij IDEA开发spark 1 环境准备 jdk scala idea这些对于本文来说都已经默
  • hadoop-3.0.1源码编译需要注意的事项

    这次尝试了一下源码编译最新的hadoop3 0 1 xff0c 发现了几个和原来不太一样的地方 记录下来 xff1a 1 需要的jdk不再是原来的1 7 xff0c 直接jdk1 8就编译通过了 xff1b 2 以前安装需要安装编译依赖cm
  • hadoop-3.0.1单节点安装部署

    hadoop 3 0 1编译上和原来有不同的地方 xff0c 部署时 xff0c 也有一些需要注意的地方 安装部署过程一笔带过 xff1a a 设置免秘钥登录 b 设置jdk环境变量 c 配置编译好的hadoop环境变量 xff0c HAD
  • Dll动态链接库创建与隐式链接方式使用

    C 43 43 动态链接库的背景 xff1a windows操作系统诞生以来 xff0c dll就作为win操作系统的基础 xff0c 通常情况下dll不能直接运行 xff0c 也无法接收消息 xff0c 只能供其他可执行程序或者dll来调
  • def模块定义方式创建动态链接库与动态加载使用dll

    前面介绍了通过宏定义 declspec dllexport 的方式创建动态链接库 xff0c 需要定义头文件和宏 xff0c 定义函数时需要使用宏定义 xff0c 这种方式过于繁琐 xff0c 这里介绍使用模块定义文件的方式创建动态链接库
  • Echarts-图表根据值的不同展示成不同的颜色

    series name 39 直接访问 39 type 39 bar 39 barWidth 39 60 39 data 10 52 200 334 390 330 220 itemStyle normal color function p
  • error: no default toolchain configured

    当我们在windows上通过msi方式安装过rust之后 xff0c 运行rustc或者cargo命令行命令时 xff0c 出现如题所示的错误 stackoverflow上有一个issue 说过这个问题 最后的解决办法是通过rustup命令
  • hive启动报错汇总以及解决办法

    1 Relative path in absolute URI system java io tmpdir 7D 7Bsystem user name 7D p span style font size 12px 需要将hive site
  • Hive使用JsonSerDe格式化json数据

    hive默认使用分隔符如空格 xff0c 分号 xff0c 34 34 xff0c 制表符 t来格式化数据记录 xff0c 对于复杂数据类型如json nginx日志等 xff0c 就没有办法拆分了 xff0c 这时候需要更加强大的SerD
  • ClassNotFoundException: org.apache.spark.sql.hive.thriftserver.SparkSQLCLIDriver解决办法

    我们通过源码编译的spark 2 3 0来启动spark sql进行sql操作 xff0c 结果出现如下错误 xff1a Spark assembly has been built with Hive including Datanucle
  • centos7源码编译安装lua:lua5.1升级lua5.3

    我们通过yum安装的lua默认是5 1版本 xff0c 如果不符合我们有的程序需要 xff0c 可以通过源码编译安装最新版lua 过程记录如下 下载 gt 解压 gt 编译三步曲 wget http www lua org ftp lua
  • linux下通过md5sum和sha1sum来校验下载文件的正确性

    在linux下 xff0c 我们经常通过wget curl等命令在某官方网站下载一个zip或者tar gz格式文件 xff0c 有时候 xff0c 我们不会去关注这个下载文件后面的md5值和sha1值 xff0c 这两个值其实有很大的用处
  • centos7源码安装git-2.3.0踩坑记录

    下载源码 gt 解压 gt 编译三步曲 我这里下载的是xz格式的文件 xff0c 第一步需要xz解压为tar格式文件 xff0c 然后通过tar xf命令解压 wget https mirrors edge kernel org pub s
  • adb与aapt获取安卓手机app的versionCode

    adb命令通常可以查看app包名以及app包的位置 xff0c 但是无法获取详细信息 xff0c 如版本号 这里结合aapt可执行程序来获取app的versionCode 这里以新浪微博app为例 xff0c 另外 xff0c 这里介绍的过
  • 我的第一个Android WebView实例

    以前做前端开发的时候 xff0c 经常遇到需要把H5页面直接在app里面打开的情况 xff0c 比如论坛 抽奖活动 秒杀活动等 xff0c 将H5嵌入WebView的做法 xff0c 可以减少客户端开发 xff0c 升级等 xff0c 小功

随机推荐