MINA2官方教程翻译(8)传输特性之UDP
该教程可以帮助你使用MINA框架编写基于UDP的Socket应用程序。在这篇教程中,我们将编写一个server端程序,server 可以通过连接该程序来展现client端程序的内存使用情况。现实中的很多程序都已经具备与该程序类似的功能,可以监控程序来内存使用情况。
构建代码
MINA 2.0的最终版本还没有release,但是你可以下载最新的版本。如果你希望从trunk构建代码,可以参考开发者指南。
应用程序
如上图所示,该server在端口18567监听,client端连接server端并向server端发送内存使用数据。在上图所展示应用中,共有两个client连接到了server。
程序执行流程
- server在18567端口监听客户端请求;
- client连接server端,server会在Session Created事件处理时增加一个Tab页;
- clients向server发送内存使用数据;
- server端把接收到的数据展现在Tab上。
Server端代码分析
我们可以在MINA的示例代的org.apache.mina.example.udp包下找到这些代码。对于每个实例,我们只需要关系MINA相关的代码。
要想构建该server,我们需要做如下操作:
- 创建一个数据报socket,监听client端请求。(请参考MemoryMonitor.java);
- 创建一个IoHandler来处理MINA框架生成的各种事件。(请参考MemoryMonitorHandler.java)。
第一步可以通过该程序片来实现:
- NioDatagramAcceptor acceptor = new NioDatagramAcceptor();
- acceptor.setHandler(new MemoryMonitorHandler(this));
这里,我们创建一个NioDatagramAcceptor来监听client端请求,并且设置它的IoHandler。变量"PORT"是一个 int值。下一步我们家filter链中加入了一个logging filter。LoggingFilter是一个非常不错的记录日志的方式,它可以在很多节点处生成日志信息,这些热值能够展现出MINA框架的工作方 式。
- DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
- chain.addLast("logger", new LoggingFilter());
下一步我们深入一下UDP传输所特有的代码。我们设置acceptor可以重用address。
- DatagramSessionConfig dcfg = acceptor.getSessionConfig();
- dcfg.setReuseAddress(true);acceptor.bind(new InetSocketAddress(PORT));
当然,最后一件事情是调用bind()方法来绑定端口。
IoHandler实现
Server端主要关心如下三个事件:
- Session创建
- Message接收
- Session关闭
详细代码如下:
Session Created Event
- @Override
- public void sessionCreated(IoSession session) throws Exception {
- SocketAddress remoteAddress = session.getRemoteAddress();
- server.addClient(remoteAddress);
- }
在session创建事件中,我们调用addClient()方法来增加一个Tab页。
Message Received Event
- @Override
- public void messageReceived(IoSession session, Object message) throws Exception {
- if (message instanceof IoBuffer) {
- IoBuffer buffer = (IoBuffer) message;
- SocketAddress remoteAddress = session.getRemoteAddress();
- server.recvUpdate(remoteAddress, buffer.getLong());
- }
- }
在message接收事件中,我们从接收到的消息中得到所关心的内存使用的数据;应用程序还需要发送响应信息。在这个方法中,处理消息和发送响应都是通过session完成的。
Session Closed Event
- @Override
- public void sessionClosed(IoSession session) throws Exception {
- System.out.println("Session closed...");
- SocketAddress remoteAddress = session.getRemoteAddress();
- server.removeClient(remoteAddress);
- }
在session关闭事件中,我们需要删除对应的Tab页。
Client端代码分析
在这一节,我们将解释一下客户端代码。实现客户端我们需要进行如下操作:
- 创建Socket并连接到server端
- 设置IoHandler
- 收集内存使用信息
- 发送数据到server端
我们从MemMonClient.java开始,可以在org.apache.mina.example.udp.client包下找到它。开始的几行代码非常简单:
- connector = new NioDatagramConnector();
- connector.setHandler( this );
- ConnectFuture connFuture = connector.connect( new InetSocketAddress("localhost", MemoryMonitor.PORT ));
这里我们创建了一个NioDatagramConnector,设置了它的handler并且连接到server端。我们必须在 InetSocketAddress对象中设定host,否则程序不能正常运行。该程序是在Windows XP环境下开发运行的,所以与其他环境可能存在差别。下一步我们等待确认client已经连接到server端,一旦连接建立,我们可以开始向 server端发送数据。代码如下:
- connFuture.addListener( new IoFutureListener(){
- public void operationComplete(IoFuture future) {
- ConnectFuture connFuture = (ConnectFuture)future;
- if( connFuture.isConnected() ){
- session = future.getSession();
- try {
- sendData();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- } else {
- log.error("Not connected...exiting");
- }
- }
- });
这里我们在ConnectFuture对象中加入一个listener,当client连接到server端时,operationComplete方法将被回调,这时我们开始发送数据。我们通过调用sendData方法向server端发送数据,该方法如下:
- private void sendData() throws InterruptedException {
- for (int i = 0; i < 30; i++) {
- long free = Runtime.getRuntime().freeMemory();
- IoBuffer buffer = IoBuffer.allocate(8);
- buffer.putLong(free);
- buffer.flip();
- session.write(buffer);
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- throw new InterruptedException(e.getMessage());
- }
- }
- }
该方法会在30秒内每秒向server端发送当前的剩余内存Cincinnati。你可以看到我们分配了一个足够大的IoBuffer来装载一个long型的变量。最后这个buffer被flipped并发送至server端。