Apache MINA是一个网络应用程序框架,可帮助用户轻松开发高性能和高可扩展性的网络应用程序。 它通过Java NIO在各种传输(如TCP / IP和UDP / IP)上提供抽象的事件驱动异步API。本文通过一个小例程来实现客户端与服务器的通信。
开发环境:
- 客户端:Android Studio
- 服务器:eclipse
MINA JAR包:需要到 Apache MINA 官方网站下载。
eclipse服务端程序
首先在eclipse中新建一个java工程,解压刚从mina官网下载的压缩包,将里面的slf4j-api-1.7.25.jar,mina-core-2.0.19.jar这两个JAR包导入工程中,导入成功后是这样的:
下面开始编写服务器程序,show the code:
主程序
public class MinaServer {
public static final int PORT = 9023;
public static void main(String[] args) throws IOException {
IoAcceptor acceptor = new NioSocketAcceptor();
acceptor.getFilterChain().addLast("logger", new LoggingFilter());
acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(
new TextLineCodecFactory(Charset.forName("UTF-8"))));
acceptor.setHandler(new ServerHandler());
acceptor.getSessionConfig().setReadBufferSize(2048);
acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10);
acceptor.bind(new InetSocketAddress(PORT));
}
}
Handler类
public class ServerHandler extends IoHandlerAdapter{
@Override
public void exceptionCaught( IoSession session, Throwable cause ) throws Exception
{
cause.printStackTrace();
}
@Override
public void messageReceived( IoSession session, Object message ) throws Exception
{
String str = message.toString();
if( str.trim().equalsIgnoreCase("quit") ) {
session.close();
return;
}
System.out.println(str);
}
}
主要分5个步骤实现:
- 创建NioSocketAcceptor对象来监听客户端的连接请求
- 为过滤链添加过滤器:这里只添加了两个过滤器LoggingFilter和ProtocolCodecFilter。LoggingFilter将记录所有信息,例如新创建的会话,收到的消息,发送的消息,关闭的会话等。ProtocolCodecFilter将二进制或协议特定数据转换为消息对象,反之亦然。
- 设置IoHandler,自定义的ServerHandler继承了IoHandlerAdapter,并重写了exceptionCaught和messageReceived方法,前者是捕获异常的处理,后者是对服务器接收到的消息处理,除了这两个方法,还有其他的方法如messageSent等。
- 设置读缓冲区大小和空闲时间
- 绑定指定的端口号
服务器端的框架如下图所示,由IoAcceptor监听请求,经过一系列IoFilter后传到IoHandler中处理。
Android Studio客户端程序
在客户端中同样需要依赖slf4j-api-1.7.25.jar,mina-core-2.0.19.jar这两个JAR包,将其拷贝到libs目录下,然后在模块的build.gradle文件中添加如下依赖声明:
implementation files('libs/mina-core-2.0.19.jar')
implementation files('libs/slf4j-api-1.7.25.jar')
等项目同步完成就可以使用了。
布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context=".MainActivity"
android:orientation="horizontal">
<EditText
android:id="@+id/editText"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3"
android:hint="message to send"
android:background="#c3dad6"/>
<Button
android:id="@+id/button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Send" />
</LinearLayout>
布局文件中放置了一个编辑框和一个按钮,主要用来向服务器发送消息。
MainActivity.java
public class MainActivity extends AppCompatActivity {
public static final String IP = ""; //your IP
public static final int PORT = 9023;
private ConnectFuture future;
private IoSession session = null;
private EditText editText;
private Button button;
private NioSocketConnector connector;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText = findViewById(R.id.editText);
button = findViewById(R.id.button); //init view
connector = new NioSocketConnector(); //construct the connector in client
connector.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new TextLineCodecFactory())); //add a filter
connector.setHandler(new ClientHandler()); //set the handler
new Thread(new Runnable() {
@Override
public void run() {
for (;;) {
try {
future = connector.connect(new InetSocketAddress(IP, PORT)); //bind to server
future.awaitUninterruptibly();
session = future.getSession();
System.err.println("connect success");
break;
} catch (RuntimeIoException e) {
System.err.println("connect fail");
e.printStackTrace();
}
}
}
}).start();
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String sendMsgText = editText.getText().toString();
if (session != null){
session.write(sendMsgText);
editText.setText("");
}
}
});
}
}
ClientHandler.java
public class ClientHandler extends IoHandlerAdapter{
@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
super.exceptionCaught(session, cause);
}
}
客户端的程序也比较简单,首先创建一个connector对象,然后添加ProtocolCodecFilter过滤器和IoHandler,在IoHandler中没有添加其他的业务逻辑,只是简单的捕获了异常。需要注意的是由于连接到服务器是一种异步任务,所以单独开了一个子线程来执行。客户端与服务器成功连接后,对按钮进行监听,将文本消息写进session,发送给服务器。
还有就是客户端和服务器的端口要一样,一般在1024到65535之间,因为1024之下的端口是系统保留的。服务器IP就是你电脑的IP地址,可以在dos中输入ipconfig命令查看,要注意的是让电脑和手机在同一个局域网里。
最后别忘了在清单文件中声明网络权限
<uses-permission android:name="android.permission.INTERNET" />
运行
先在eclipse中运行服务器程序,然后安装客户端程序到手机,启动APP,与服务器连接成功之后,就可以发送消息给服务器了。