本文主要是介绍Android长连接之mina,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
一、概述
今天在工作一朋友Q我,问我长连接一般用什么做,我说我用的Mina,朋友表示没听过,于是打算写一篇相关的博文供大家讨论。
首先什么是mina?
它的官方定义:一个能够帮助用户开发高性能和高伸缩性网络应用程序的框架。它通过Java nio技术基于TCP/IP和UDP/IP协议提供了抽象的、事件驱动的、异步的API。
简单来说尼,就是一个优化过的长连接框架。
好了,先上图:
服务端的控制台显示
客户端发送“你好”,服务端控制台显示“服务端接收的消息:你好”,然后再返回字段“服务端给返回的消息:你好”客户端接收并显示。
好了,演示完毕,下面开始开启我们的代码之旅吧。
二、服务端搭建
/*** Mina长连接服务端搭建* * @author 刘洋巴金* @date 2017-4-6* */
public class MinaService {public static void main(String[] args){// 继承IoService,服务器端接收器IoAcceptor acceptor = new NioSocketAcceptor();// 添加过滤器acceptor.getFilterChain().addLast("logf", new LoggingFilter()); // 日志过滤器acceptor.getFilterChain().addLast("objType", new ProtocolCodecFilter(new ObjectSerializationCodecFactory())); // 只能传输序列化后的对象// 回调acceptor.setHandler(new MyHandler());// 进行一些初始化配置acceptor.getSessionConfig().setReadBufferSize(2048); // 设置读缓存区大小acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); // 如果10秒钟没有任何读写,就设置成空闲状态。 BOTH_IDLE(读和写)// 设置监听端口号,开始监听了try {acceptor.bind(new InetSocketAddress(8888));System.out.println("启动成功");} catch (Exception e) {e.printStackTrace();}}
IoAcceptor:它主要负责在一个客户端和该服务之间创建连接。NioSocketAcceptor创建一个基于TCP/IP的非阻塞的server的socket。
acceptor.getFilterChain().addLast(...):添加一些过滤链。
handler:是个回调
然后进行一些初始化配置,比如超过几秒没有通讯就设置成空闲。
然后acceptor.bind(new InetSocketAddress(8888)); 设置监听的端口号,开始监听了。
/*** 负责session的创建及消息发送和接收的监听* */
private static class MyHandler extends IoHandlerAdapter{// session创建时回调public void sessionCreated(IoSession session) throws Exception {super.sessionCreated(session);System.out.println("sessionCreated " );}// session打开时回调public void sessionOpened(IoSession session) throws Exception {super.sessionOpened(session);System.out.println("sessionOpened " );}// 消息接收时回调public void messageReceived(IoSession session, Object message)throws Exception {super.messageReceived(session, message);String str = message.toString();session.write("服务端给返回的消息:"+str+"\n"); // 给客户端返回System.out.println("服务端接收消息: " + str);}// 消息发送时回调public void messageSent(IoSession session, Object message) throws Exception {super.messageSent(session, message);System.out.println("messageSent " );}// session关闭时回调public void sessionClosed(IoSession session) throws Exception {super.sessionClosed(session);}
}
handler用于回调各种状态。messageReceived 当客户端发送的消息到达时回调session.write(""); 给客户端返回
三、客户端搭建
/*** 参数配置* * @author 刘洋巴金* @date 2017-4-6* */
public class ConnectionConfig {private Context context;private String ip;private int port;private int readBufferSize;private long connectionTimeout;public Context getContext() {return context;}public String getIp() {return ip;}public int getPort() {return port;}public int getReadBufferSize() {return readBufferSize;}public long getConnectionTimeout() {return connectionTimeout;}public static class Builder{private Context context;private String ip;private int port;private int readBufferSize;private long connectionTimeout;public Builder(Context context){this.context = 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 connectionTimeout) {this.connectionTimeout = connectionTimeout;return this;}public ConnectionConfig builder(){ConnectionConfig config = new ConnectionConfig();config.context = this.context;config.ip = this.ip;config.port = this.port;config.readBufferSize = this.readBufferSize;config.connectionTimeout = this.connectionTimeout;return config;}
}
}
首先先创建个ConnectionConfig,该类用了构建者模式,该类主要作用是设置一些常用的属性。
/*** 连接管理器 * * @author 刘洋巴金* @date 2017-4-6* */
public class ConnectionManager {private ConnectionConfig config;private WeakReference<Context> mContext;private IoConnector mConnector;private SocketAddress mAddress;public ConnectionManager(ConnectionConfig config) {this.config = config;mContext = new WeakReference<Context>(config.getContext());// 初始化init();}/*** 初始化* */private void init() {// 继承IoService,客户端连接器mConnector = new NioSocketConnector();// 添加过滤器mConnector.getFilterChain().addLast("logf", new LoggingFilter()); // 日志过滤器mConnector.getFilterChain().addLast("objType", new ProtocolCodecFilter(new ObjectSerializationCodecFactory())); // 只能传输序列化后的对象// 回调mConnector.setHandler(new MyHandler(mContext.get()));// 进行一些初始化配置mConnector.getSessionConfig().setReadBufferSize(2048); // 设置读缓存区大小// 设置IP地址mAddress = new InetSocketAddress(config.getIp(), config.getPort());mConnector.setDefaultRemoteAddress(mAddress);}
创建连接管理者,基本配置与服务端搭建类似
NioSocketConnector创建客户端,然后设置过滤链,然后设置ip和端口号,config就是上面我们自己建的配置文件。然后设置handler进行回调。
/*** 负责session的创建及消息发送和接收的监听* */
private static class MyHandler extends IoHandlerAdapter{private Context mContext;private MyHandler(Context context){this.mContext = context;}// session打开时回调public void sessionOpened(IoSession session) throws Exception {super.sessionOpened(session);}// 消息接收时回调public void messageReceived(IoSession session, Object message)throws Exception {super.messageReceived(session, message);if(mContext != null){Intent intent = new Intent("com.bs.myMsg");intent.putExtra("message", message.toString());LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);}}// 消息发送时回调public void messageSent(IoSession session, Object message) throws Exception {super.messageSent(session, message);}
}
messageReceived接收服务端返回的消息,然后启动本地广播,本地广播的使用和正常广播使用一样,只是本地广播LocalBroadcastManager.getInstance(mContext)只能被本应用接收广播。其他应用接收不到该广播。
/*** 服务* * @author 刘洋巴金* @date 2017-4-6* */
public class MyService extends Service{@Overridepublic IBinder onBind(Intent intent) {// TODO Auto-generated method stubreturn null;}private ConnectionManager mManager; // 连接管理private ConnectThread mConnectThread; // 线程@Overridepublic void onCreate() {super.onCreate();mConnectThread = new ConnectThread("mina", getApplicationContext());}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {mConnectThread.start();return super.onStartCommand(intent, flags, startId);}@Overridepublic void onDestroy() {// TODO Auto-generated method stubsuper.onDestroy();mConnectThread.disConnect();mConnectThread = null;}class ConnectThread extends HandlerThread{public ConnectThread(String name, Context context) {super(name);// 配置ConnectionConfig config = new ConnectionConfig.Builder(context).setIp("192.168.0.103").setPort(8888).setReadBufferSize(2048).setConnectionTimeout(10).builder();// 连接管理器mManager = new ConnectionManager(config);}@Overrideprotected void onLooperPrepared() {// TODO Auto-generated method stubsuper.onLooperPrepared();// 异步进行连接for (;;) {// 如果连接成功,则跳出循环if(mManager.connect()){Toast.makeText(getApplicationContext(), "连接成功", Toast.LENGTH_SHORT).show();break;}}// 3秒连接一次try {Thread.sleep(3000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}// 用于断开连接public void disConnect(){mManager.disConnect();}}
}
然后启动我们的服务,因为连接服务端是耗时操作,所以要放到子线程,我用的是HandlerThread,当然也可以自己写线程。
onLooperPrepared相当于run方法,我们做的操作是,在这里面一直进行连接,如果连接失败就继续连接,直至连接成功为止。
/*** 进行连接,并获取Session* */
public boolean connect() {try {ConnectFuture connectFuture = mConnector.connect(); // 可以查询连接操作的状态connectFuture.awaitUninterruptibly(); // 它会将程序阻塞住,直到连接创建好,所以,当这行代码结束后,// 就可以直接获取session。IoSession mSession = connectFuture.getSession();SessionManager.getInstance().setSession(mSession); // 存储sessionreturn true;} catch (Exception e) {e.printStackTrace();return false;}
}
ConnectionManager 中的connect方法,进行连接服务端,如果连接成功了,就返回true
这样就获取到了session,然我们存储起来,以便使用。
/*** 会话管理器 * * @author 刘洋巴金* @date 2017-4-6* */
public class SessionManager {private static SessionManager instance = new SessionManager();private SessionManager(){}public static SessionManager getInstance() {return instance;}private IoSession session;public IoSession getSession() {return session;}public void setSession(IoSession session) {this.session = session;}/*** 写消息* */public void writeMag(String msg){if(session != null){session.write(msg);}}/*** 关闭* */public void closeSession(){if(session != null){session.closeOnFlush();session = null;}}
}
然后是MainActivity的2个按钮的点击
@Override
public void onClick(View v) {switch (v.getId()) {case R.id.btn_conn: // 开启服务Intent in = new Intent(MainActivity.this, MyService.class);startService(in);break;case R.id.btn_sendMsg: // 发送消息// 文本消息String msg = et_msg.getText().toString();SessionManager.getInstance().writeMag(msg); // 发送消息break;default:break;}
}
先开启服务,连接服务端,然后是取出我们存储的session,给服务端发送消息。
然后再注册广播,接收ConnectionManager发送过来的广播。
/*** 注册消息广播* */
private void registerMsgReceiver() {mMsgReceiver = new MsgReceiver();IntentFilter filter = new IntentFilter("com.bs.myMsg");LocalBroadcastManager.getInstance(MainActivity.this).registerReceiver(mMsgReceiver, filter);
}/*** 广播接收* */
class MsgReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {String msgStr = intent.getStringExtra("message");rmsg += msgStr+"\n";tv_msg_show.setText(rmsg);}
}
OK了。
四、mina流程
1.远程服务器通过IoService与客户端建立连接,然后得到session。
2.远程服务器发数据到我们的session
3.然后我们的session会把我们的数据发送给IoFiterChain(过滤链)中进行过滤
4.过滤链将符合条件的数据发送到Application layer(数据处理器)中,客户端做的就是根据IoHandler书写自己的业务逻辑。
五、demo
Android长连接之mina
※1.首先记得改端口号和IP
2.记得电脑和手机要在一个IP段上,也就是同一个wife
这篇关于Android长连接之mina的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!