MINA 实现聊天功能

Wesley13
• 阅读 417

原文同步至http://www.waylau.com/mina-chat/

在《MINA 快速入门》一文中,我们介绍了如何利用 MINA 快速构建一个 Time Server(时间服务器)。在《Netty 实现聊天功能》一文,我们也介绍了如何用 Netty 实现聊天功能。由于 MINA 和 Netty 是同一个作者,架构类似,如果你掌握其中一个,学习另外一个也不是难事。现在我们就用 MINA 来实现聊天功能。

##准备

  • JDK 7+
  • Maven 3.2.x
  • MINA 2.x
  • Eclipse 4.x

##服务端

让我们从 handler (处理器)的实现开始,handler 是由 MINA 生成用来处理 I/O 事件的, 处理器继承自 IoHandlerAdapter。

###SimpleChatServerHandler.java

public class SimpleChatServerHandler extends IoHandlerAdapter { // (1)

    private final Set<IoSession> sessions = Collections
        .synchronizedSet(new HashSet<IoSession>()); //  (2)

    @Override
    public void sessionCreated(IoSession session) throws Exception {// (3)
        sessions.add(session);
        broadcast(" has join the chat", session);
    }

    @Override
    public void sessionClosed(IoSession session) throws Exception {// (4)
        sessions.remove(session);
        broadcast(" has left the chat", session);
    }

    @Override
    public void messageReceived(IoSession session, Object message)
            throws Exception {// (5)
        String str = message.toString();
        broadcast(str, session);
    }

    @Override
    public void sessionIdle(IoSession session, IdleStatus status)
            throws Exception {// (6)
        System.out.println("[Server] IDLE " + session.getRemoteAddress()
                + session.getIdleCount(status));
    }

    @Override
    public void exceptionCaught(IoSession session, Throwable cause) {
        cause.printStackTrace();// (7)
        System.out.println("[Server] Client:" + session.getRemoteAddress()
                + "异常");
        // 遇到未捕获的异常,则关闭连接
        session.close(true);
    }

    /**
     * 广播消息
     * 
     * @param message
     */
    private void broadcast(String message, IoSession exceptSession) {// (8)
        synchronized (sessions) {
            for (IoSession session : sessions) {
                if (session.isConnected()) {
                    if (session.equals(exceptSession)) {
                        session.write("[You]" + message);
                    } else {
                        session.write("[Client" + session.getRemoteAddress()
                                + "] " + message);
                    }

                }
            }
        }
    }
}

1.SimpleChatServerHandler 继承自 IoHandlerAdapter,这个类实现了 IoHandler 接口,IoHandlerAdapter 提供了许多事件处理的接口方法,然后你可以覆盖这些方法。现在仅仅只需要继承 IoHandlerAdapter 类而不是你自己去实现接口方法。

2.Set<IoSession> sessions 用来存储所有的 连接上来的 session.

3.覆盖了 sessionCreated() 事件处理方法。每当从服务端收到新的客户端连接时,客户端的 IoSession 就存入存入 sessions 列表中,并通知列表中的其他客户端 IoSession

4.覆盖了 sessionClosed() 事件处理方法。每当从服务端收到客户端断开时,客户端的 IoSession 从 sessions 列表中,并通知列表中的其他客户端 IoSession

5.覆盖了 messageReceived() 事件处理方法。每当从服务端读到客户端写入信息时,将信息广播给其他客户端的 IoSession。

6.覆盖了 sessionIdle() 事件处理方法。服务端监听到客户端闲置情况

7.exceptionCaught() 事件处理方法是当出现 Throwable 对象才会被调用,即当 MINA 由于 IO 错误或者处理器在处理事件时抛出的异常时。在大部分情况下,捕获的异常应该被记录下来并且把关联的 IoSession 给关闭掉。然而这个方法的处理方式会在遇到不同异常的情况下有不同的实现,比如你可能想在关闭连接之前发送一个错误码的响应消息。

8.broadcast() 服务器用于广播的方法

###SimpleChatServer.java

编写一个 main() 方法来启动服务端。

public class SimpleChatServer {

    public static void main(String[] args) {
        int port;
        if (args.length > 0) {
            port = Integer.parseInt(args[0]);
        } else {
            port = 8080;
        }
        
        SocketAcceptor acceptor = new NioSocketAcceptor(); // (1)
        
        acceptor.getFilterChain().addLast( "codec", 
                new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));      // (2)
        acceptor.setHandler(new SimpleChatServerHandler()); // (3)
        
        acceptor.getSessionConfig().setReadBufferSize(2048); // (4)
        acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 100);// (5)
        
        try {
            acceptor.bind(new InetSocketAddress(port)); // (6)
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("[Server]Listening on port " + port);
    }
}

1.NioSocketAcceptor 基于 TCP/IP 的 socket 连接。

2.ProtocolCodecFilter 是编码和解码的过滤器链,将一个连入的 ByteBuffer 转化为消息 POJO,反之亦然。而TextLineCodecFactory是 MINA 提供的一个编解码是,可以方便处理基于文本的协议。

3.设置在处理器为之前创建的 SimpleChatServerHandler。

4.会话配置,设置字节缓存大小。

5.会话配置,设置闲置时间。

6.剩下的就是绑定端口然后启动服务。这里我们在机器上默认绑定了机器所有网卡上的 8080 端口。

恭喜!你已经完成了基于 MINA 聊天服务端程序。

##客户端

###SimpleChatClientHandler.java

客户端的处理类比较简单,只需要将读到的信息打印出来即可

public class SimpleChatClientHandler extends IoHandlerAdapter {
    @Override
    public void messageReceived(IoSession session, Object message) {
        String str = message.toString();
        System.out.println(str);
    }
}

###SimpleChatClient.java

编写一个 main() 方法来启动客户端。

public class SimpleChatClient {
    private static final long CONNECT_TIMEOUT = 30 * 1000L; // 30 秒;
    private static final String HOSTNAME = "127.0.0.1";
    private static final int PORT = 8080;
    /**
     * @param args
     */
    public static void main(String[] args) {
        
        NioSocketConnector connector = new NioSocketConnector(); // (1)
        connector.setConnectTimeoutMillis(CONNECT_TIMEOUT);
        connector.getFilterChain().addLast( "codec", 
                new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));
        connector.setHandler(new SimpleChatClientHandler());
        IoSession session;
        ConnectFuture future = connector.connect(new InetSocketAddress(
                HOSTNAME, PORT));  // (2)
        future.awaitUninterruptibly();
        session = future.getSession();

        while(true){
            try {
                BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
                session.write(in.readLine());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

1.NioSocketConnector 用于 TCP/IP 连接

2.连接到指定的服务器

##运行效果

先运行 SimpleChatServer,再可以运行多个 SimpleChatClient,控制台输入文本继续测试

MINA 实现聊天功能

MINA 实现聊天功能

MINA 实现聊天功能

##源码

https://github.com/waylau/apache-mina-2-user-guide-demossimplechat

##参考

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
2年前
Oracle VM VirtualBox 安装和使用
原文同步至http://www.waylau.com/aboutoraclevmvirtualbox/(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fwww.waylau.com%2Faboutoraclevmvirtualbox%2F)VirtualBox是一款开源
Stella981 Stella981
2年前
Gradle 2.3 发布
原文同步至:http://www.waylau.com/gradle23released(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fwww.waylau.com%2Fgradle23released)2月16日,Gradle团队发布了2.3。可以在http:/
Wesley13 Wesley13
2年前
P2P技术揭秘.P2P网络技术原理与典型系统开发
Modular.Java(2009.06)\.Craig.Walls.文字版.pdf:http://www.t00y.com/file/59501950(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fwww.t00y.com%2Ffile%2F59501950)\More.E
Wesley13 Wesley13
2年前
Java switch 语句使用 String 参数
原文同步至http://www.waylau.com/javaswitchusestring/(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fwww.waylau.com%2Fjavaswitchusestring%2F)当我尝试在switch语句使用String
Stella981 Stella981
2年前
Spring singleton bean 与 prototype bean 的依赖
本文同步至:http://www.waylau.com/springsingletonbeanswithprototypebeandependencies/(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fwww.waylau.com%2Fspringsingletonbe
Stella981 Stella981
2年前
Netty 实现聊天功能
原文同步至http://www.waylau.com/nettychat/(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fwww.waylau.com%2Fnettychat%2F)Netty(https://www.oschina.net/action/GoToLink?
Easter79 Easter79
2年前
Tomcat Maven Plugin使用
原文同步至:http://www.waylau.com/tomcatmavenplugin/(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fwww.waylau.com%2Ftomcatmavenplugin%2F)ApacheTomcat的Maven插件提供了go
Wesley13 Wesley13
2年前
URI、URL、URN 的联系和区别
原文同步至http://www.waylau.com/differenceofuriurlurn/(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fwww.waylau.com%2Fdifferenceofuriurlurn%2F)在Web应用中,URI、URL
Wesley13 Wesley13
2年前
Java 注释规范详解
原文同步至:http://www.waylau.com/javacommentsconventions/(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fwww.waylau.com%2Fjavacommentsconventions%2F)在Java的编写过程中我们需要对