- 什么是Mina?
Apache MINA(Multipurpose Infrastructure for Network Applications) 是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络应用程序提供了非常便利的框架。当前发行的 MINA 版本支持基于 Java NIO 技术的 TCP/UDP 应用程序开发、串口通讯程序(只在最新的预览版中提供),MINA 所支持的功能也在进一步的扩展中。
总之:我们简单理解它是一个封装底层IO操作,提供高级操作API的通讯框架!
- 什么是NIO技术?
在JDK1.4以前,I/O输入输出处理,我们把它称为旧I/O处理,在JDK1.4开始,java提供了一系列改进的输入/输出新特性,这些功能被称为新I/O(NEW I/O),新添了许多用于处理输入/输出的类,这些类都被放在java.nio包及子包下,并且对原java.io包中的很多类以NIO为基础进行了改写,新添了满足新I/O的功能。
Java NIO和IO的主要区别
面向缓冲(Buffer)
-
什么是长连接、短连接、心跳包?请参考这里
- 常见的Java NIO框架有哪些?
Mina、Netty、Grizzly、xSocket等,区别如下:
Mina
Mina(Multipurpose Infrastructure for Network Applications) 是 Apache组织一个较新的项目,它为开发高性能和高可用性的网络应用程序提供了非常便利的框架。当前发行的 Mina 版本2.04支持基于 JavaNIO 技术的 TCP/UDP 应用程序开发、串口通讯程序,Mina 所支持的功能也在进一步的扩展中。目前,正在使用Mina的应用包括:Apache Directory Project、AsyncWeb、AMQP(Advanced MessageQueuing Protocol)、RED5 Server(Macromedia? FlashMedia RTMP)、ObjectRADIUS、 Openfire等等。
Netty
Netty是一款异步的事件驱动的网络应用框架和工具,用于快速开发可维护的高性能、高扩展性协议服务器和客户端。也就是说,Netty是一个NIO客户端/服务器框架,支持快速、简单地开发网络应用,如协议服务器和客户端。它极大简化了网络编程,如TCP和UDP套接字服务器。
Grizzly
Grizzly是一种应用程序框架,专门解决编写成千上万用户访问服务器时候产生的各种问题。使用JAVANIO作为基础,并隐藏其编程的复杂性。容易使用的高性能的API。带来非阻塞socketd到协议处理层。利用高性能的缓冲和缓冲管理使用高性能的线程池。
xSocket
xSocket:是一个轻量级的基于nio的服务器框架用于开发高性能、可扩展、多线程的服务器。该框架封装了线程处理、异步读/写等方面。(只是对Java的NIO做了最简单的封装,以便于开发使用。)
OK,我们现在可以看看三者的简单对比了。
首先,从设计的理念上来看,Mina的设计理念是最为优雅的。当然,由于Netty的主导作者与Mina的主导作者是同一人,出自同一人之手的Netty在设计理念上与Mina基本上是一致的。而Grizzly在设计理念上就较差了点,几乎是JavaNIO的简单封装。
其次,从项目的出身来看,Mina出身于开源界的大牛Apache组织,Netty出身于商业开源大亨Jboss,而Grizzly则出身于土鳖Sun公司。从其出身可以看到其应用的广泛程序,到目前为止,我见到业界还是使用Mina多一些,而Netty也在慢慢的应用起来,而Grizzly则似乎只有Sun自已的项目使用了,如果还有其他的公司或开源项目在使用,那就算我孤陋寡闻。 最后,从入门的文档来说,由于Mina见世时间相对较长,官方以及民间的文档与入门示例都相当的多。Netty的官方文档也做得很好,而民间文档就要相对于Mina少一些了。至于Grizzly,不管是官方还是民间,都很少见到其文档。
- 常见的Mina使用场景有哪些?
如:客户端和服务端进行socket通讯时,服务端需要知道客户端的是否在线,这就要求客户端要一直定时发送心跳请求告知服务端;通讯服务中断后,客户端自动发起重新连接等等。。。有同学说:这功能几个普通函数就能实现,没啥特别的啊?开始我也是这样想的,但慢慢了解Mina框架后,发现很多底层操作都封装好了,简单易用,真的比自己砌砖爽多了……自己实现请参考 - Mina如何实现通讯?
下面以Mina实现Android客户端与服务器端通信为例,原文参考:二、服务器端代码
MinaService.java package com.czhappy.mina; 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; public class MinaService { 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 DemoServerHandler()); acceptor.getSessionConfig().setReadBufferSize(2048); acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); try { acceptor.bind(new InetSocketAddress(9226)); } catch (Exception e) { e.printStackTrace(); } System.out.println("启动服务"); } /** * @ClassName: DemoServerHandler * @Description: 负责session对象的创建和监听以及消息的创建和接收监听 * @author chenzheng * @date 2016-12-9 下午3:57:11 */ private static class DemoServerHandler extends IoHandlerAdapter{ //服务器与客户端创建连接 @Override public void sessionCreated(IoSession session) throws Exception { System.out.println("服务器与客户端创建连接..."); super.sessionCreated(session); } @Override public void sessionOpened(IoSession session) throws Exception { System.out.println("服务器与客户端连接打开..."); super.sessionOpened(session); } //消息的接收处理 @Override public void messageReceived(IoSession session, Object message) throws Exception { // TODO Auto-generated method stub super.messageReceived(session, message); String str = message.toString(); Date date = new Date(); session.write(date.toString()); System.out.println("接收到的数据:"+str); } @Override public void messageSent(IoSession session, Object message) throws Exception { // TODO Auto-generated method stub super.messageSent(session, message); } @Override public void sessionClosed(IoSession session) throws Exception { // TODO Auto-generated method stub super.sessionClosed(session); } } }
三、Android客户端代码
ConnectionConfig.java package com.czhappy.minaclient; import android.content.Context; /** * Description:构建者模式 */ 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 = "192.168.168.20"; private int port = 9226; private int readBufferSize = 10240; private long connectionTimeout = 10000; 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; } private void applyConfig(ConnectionConfig config){ config.context = this.context; config.ip = this.ip; config.port = this.port; config.readBufferSize = this.readBufferSize; config.connectionTimeout = this.connectionTimeout; } public ConnectionConfig builder(){ ConnectionConfig config = new ConnectionConfig(); applyConfig(config); return config; } } } ConnectionManager.java package com.czhappy.minaclient; import android.content.Context; import android.content.Intent; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; 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 java.lang.ref.WeakReference; import java.net.InetSocketAddress; public class ConnectionManager { private static final String BROADCAST_ACTION = "com.commonlibrary.mina.broadcast"; private static final String MESSAGE = "message"; private ConnectionConfig mConfig; private WeakReference<Context> mContext; private NioSocketConnector mConnection; private IoSession mSession; private InetSocketAddress mAddress; public ConnectionManager(ConnectionConfig config){ this.mConfig = config; this.mContext = new WeakReference<Context>(config.getContext()); init(); } private void init() { mAddress = new InetSocketAddress(mConfig.getIp(), mConfig.getPort()); mConnection = new NioSocketConnector(); mConnection.getSessionConfig().setReadBufferSize(mConfig.getReadBufferSize()); mConnection.getFilterChain().addLast("logging", new LoggingFilter()); mConnection.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory())); mConnection.setHandler(new DefaultHandler(mContext.get())); mConnection.setDefaultRemoteAddress(mAddress); } /** * 与服务器连接 * @return */ public boolean connnect(){ Log.e("tag", "准备连接"); try{ ConnectFuture future = mConnection.connect(); future.awaitUninterruptibly(); mSession = future.getSession(); SessionManager.getInstance().setSeesion(mSession); }catch (Exception e){ e.printStackTrace(); Log.e("tag", "连接失败"); return false; } return mSession == null ? false : true; } /** * 断开连接 */ public void disContect(){ mConnection.dispose(); mConnection=null; mSession=null; mAddress=null; mContext = null; Log.e("tag", "断开连接"); } private static class DefaultHandler extends IoHandlerAdapter{ private Context mContext; private DefaultHandler(Context context){ this.mContext = context; } @Override public void sessionOpened(IoSession session) throws Exception { super.sessionOpened(session); } @Override public void messageReceived(IoSession session, Object message) throws Exception { Log.e("tag", "接收到服务器端消息:"+message.toString()); if(mContext!=null){ Intent intent = new Intent(BROADCAST_ACTION); intent.putExtra(MESSAGE, message.toString()); LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent); } } } } MinaService.java package com.czhappy.minaclient; 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", "启动线程尝试连接"); } @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{ private Context context; boolean isConnection; ConnectionManager mManager; public ConnectionThread(String name, Context context){ super(name); this.context = context; ConnectionConfig config = new ConnectionConfig.Builder(context) .setIp("192.168.168.20") .setPort(9226) .setReadBufferSize(10240) .setConnectionTimeout(10000).builder(); mManager = new ConnectionManager(config); } @Override protected void onLooperPrepared() { while(true){ isConnection = mManager.connnect(); if(isConnection){ Log.e("tag", "连接成功"); break; } try { Log.e("tag", "尝试重新连接"); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } } public void disConnect(){ mManager.disContect(); } } } SessionManager.java package com.czhappy.minaclient; import android.util.Log; import org.apache.mina.core.session.IoSession; 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 setSeesion(IoSession session){ this.mSession = session; } public void writeToServer(Object msg){ if(mSession!=null){ Log.e("tag", "客户端准备发送消息"); mSession.write(msg); } } public void closeSession(){ if(mSession!=null){ mSession.closeOnFlush(); } } public void removeSession(){ this.mSession=null; } } MinaTestActivity.java package com.czhappy.minaclient; 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.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.TextView; public class MinaTestActivity extends AppCompatActivity implements View.OnClickListener{ private TextView start_service_tv, send_tv, receive_tv; private MessageBroadcastReceiver receiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_mina_test); initView(); registerBroadcast(); } private void registerBroadcast() { receiver = new MessageBroadcastReceiver(); IntentFilter filter = new IntentFilter("com.commonlibrary.mina.broadcast"); LocalBroadcastManager.getInstance(this).registerReceiver(receiver, filter); } private void initView() { receive_tv = (TextView) this.findViewById(R.id.receive_tv); start_service_tv = (TextView) this.findViewById(R.id.start_service_tv); start_service_tv.setOnClickListener(this); send_tv = (TextView) this.findViewById(R.id.send_tv); send_tv.setOnClickListener(this); } @Override public void onClick(View v) { switch(v.getId()){ case R.id.start_service_tv: Log.e("tag", "点击启动服务"); Intent intent = new Intent(this, MinaService.class); startService(intent); break; case R.id.send_tv: Log.e("tag", "点击发送消息"); SessionManager.getInstance().writeToServer("hello123"); break; } } private void unregisterBroadcast(){ LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver); } @Override protected void onDestroy() { super.onDestroy(); stopService(new Intent(this, MinaService.class)); unregisterBroadcast(); } private class MessageBroadcastReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { receive_tv.setText(intent.getStringExtra("message")); } } }
四、运行结果
1.点击启动服务,观察客户端是否与服务器端连接成功;
2.点击发送消息,观察消息是否发送成功,如果失败判断失败在哪一步。客户端运行结果:
服务器端运行结果: - 我使用Mina遇到的坑
1)客户端无法得到服务器的返回值
解决方案:一定要正确设置编码、解码器,千万不要直接搬别人的代码。
StringDecoder和StringEncoder报文内容格式,请与服务端约定好。
2)客户端频繁发送心跳包,解决方法:参考这里
- 利用Mina实现聊天室,可以参考这里。
- 其他参考