如何用MIna实现群聊?(基于Mina的群聊聊天室)

前几天学了Mina框架,发现作为一个网络通信的框架,确实比之前用socket好用太多,它很容易的实现了异步通信,学完之后被朋友问道是怎么实现异步通信的,我才发现,我学东西太浅了,没有钻研精神,带着这个疑问我在百度上搜了大神们的文章,后来才知道,Mina之所以能实现异步通信,是因为Mina是多线程的网络通信框架,Mina把这些线程封装起来,这样调用的时候就不会发生阻塞了!
// IoAcceptor acceptor = new NioSocketAcceptor();

言归正传,今天用Mina来写一个群聊聊天室。(明天再写私聊)
实现功能
1.一个客户端发消息所有的客户端都可以接收
2.接收的同时,还要显示是哪个客户端发送的
3.有客户端退出聊天室的时,其他客户端也要接收到某个客户端退出的信息

一、接下来,代码展示
导包!导包!导包!
服务器端:

package mina_group_chat_0829;

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.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.HashSet;
import java.util.Set;

public class MinaServer {
public static void main(String[] args) throws IOException {
    //1.创建Ioservice实例
    //创建一个非阻塞的server端的Socket
    IoAcceptor acceptor = new NioSocketAcceptor();

    //2.过滤链
    acceptor.getFilterChain().addLast("logger",new LoggingFilter());
    acceptor.getFilterChain().addLast("codec",new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"))));

    //处理器
    //MyIoHandler()继承IoHandlerAdapter
    //IoHandlerAdapter实现IoHandler接口
    //接口不能new,所以需要他的实现子类,因为要实现不同的业务,所以要继承实现子类,重写方法
    acceptor.setHandler(new MyIoHandler());

    //连接
    acceptor.bind(new InetSocketAddress(9999));
    System.out.println("服务器开启");

}


}

class MyIoHandler extends IoHandlerAdapter {
private int count = 1;
Set<IoSession> sessions = new HashSet<IoSession>();

public MyIoHandler() {
    super();
}

@Override
public void sessionCreated(IoSession session) throws Exception {
    super.sessionCreated(session);
    sessions.add(session);
    //session.getRemoteAddress():获得当前通话的ip地址
    System.out.println("有客户端接入:"+session.getRemoteAddress());
}

@Override
public void sessionOpened(IoSession session) throws Exception {
    super.sessionOpened(session);
    System.out.println("第"+count+"个客户端登录,address:"+session.getRemoteAddress());
    count++;
}

@Override
public void sessionClosed(IoSession session) throws Exception {
    super.sessionClosed(session);
    broadcast(session.getRemoteAddress()+"会话通道关闭",session);
}

@Override
public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
    super.sessionIdle(session, status);
    broadcast(session.getRemoteAddress()+"会话休眠",session);
//     System.out.println("服务器:会话休眠!"+session.getIdleCount(status));
}

@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
    super.exceptionCaught(session, cause);
    broadcast(session.getRemoteAddress()+"有异常出现",session);
}

@Override
public void messageReceived(IoSession session, Object message) throws Exception {
    super.messageReceived(session, message);
    //业务:如果客户端只输入quit,代表当前会话通道断开,不能进行通话了
    String str = message.toString();
    //trim():去掉str的前后空白格
    if(str.trim().equalsIgnoreCase("quit"))
    {
        session.closeNow();
        return;
    }
    System.out.println("服务器:接收信息..."+str);
    //把接收到的信息广播出去
    broadcast(str,session);

}

@Override
public void messageSent(IoSession session, Object message) throws Exception {
    super.messageSent(session, message);
    System.out.println("服务器发送消息:"+message);
}

@Override
public void inputClosed(IoSession session) throws Exception {
    super.inputClosed(session);
    System.out.println("服务器:输入关闭...");
}

//广播
public  void broadcast(String message,IoSession exceptSession){
    synchronized (sessions){
        for(IoSession session:sessions){
            if(session.isConnected()){//保持通信
                if(session.equals(exceptSession)){
                    session.write("[you]"+message);
                }else{
                    session.write("[客户端"+exceptSession.getRemoteAddress()+"]"+message);
                }
            }
        }
    }
}
}

客户端:

package mina_group_chat_0829;

import org.apache.mina.core.future.ConnectFuture;
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.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketConnector;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;

public class MinaClient {
public static void main(String[] args) throws IOException {
    //1.连接器,
    NioSocketConnector connector = new NioSocketConnector();
    //2.设置过滤链
    connector.getFilterChain().addLast("logger",new LoggingFilter());
    connector.getFilterChain().addLast("codec",
            new ProtocolCodecFilter(
                    new TextLineCodecFactory(
                            Charset.forName("UTF-8"))));

    //3.设置处理器
    connector.setHandler(new MyHandler());

    //4.连接地址
    ConnectFuture connect =
            connector.connect(new InetSocketAddress("localhost",9999));
    System.out.println("客户端已连接!");

    int flag = 0;
    while(flag==0){
        //读取控制台的信息
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        String str = bf.readLine();
        //通过连接获得会话,通过会话把内容传输到服务器
        connect.getSession().write(str);
        if(str.equals("quit")){
          flag=1;
        }

    }

}

}
class MyHandler extends IoHandlerAdapter {
public MyHandler() {
    super();
}

@Override
public void sessionCreated(IoSession session) throws Exception {
    super.sessionCreated(session);
   // System.out.println("客户端:会话被创建!");

}

@Override
public void sessionOpened(IoSession session) throws Exception {
    super.sessionOpened(session);
   // System.out.println("客户端:会话通道打开!");

}

@Override
public void sessionClosed(IoSession session) throws Exception {
    super.sessionClosed(session);
   // System.out.println("客户端:会话关闭!");
}

@Override
public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
    super.sessionIdle(session, status);
    //System.out.println("客户端:会话休眠!");
}

@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
    super.exceptionCaught(session, cause);
    //System.out.println("客户端:有异常出现!");
}

@Override
public void messageReceived(IoSession session, Object message) throws Exception {
    super.messageReceived(session, message);
    System.out.println(message);
}

@Override
public void messageSent(IoSession session, Object message) throws Exception {
    super.messageSent(session, message);
    System.out.println("客户端:"+message);//只在服务器端显示

}

@Override
public void inputClosed(IoSession session) throws Exception {
    super.inputClosed(session);
    System.out.println("客户端:输入关闭!");
}
}

二、运行:

简易群聊室

为什么B站视频要审核这么久!!!上传视频真心不易!!

三、总结
代码看完了,接下来我们一起看一下,这三个功能是怎么实现的?
1.一个客户端发消息所有的客户端都可以接收

这里自定义了一个broadcast(String message,Iosession exceptSession)方法,
每个通话通道被创建就被写入集合,List里面,然后通过broadcast(),遍历集合,
比对当前会话 exceptSession是不是集合里的当前会话,是的话就不广播给自己,
不是的话就广播出来!

2.接收的同时,还要显示是哪个客户端发送的

这里用到了session.getRemoteAddress()方法

3.有客户端退出聊天室的时,其他客户端也要接收到某个客户端退出的信息

同样用到broadcast()方法

代码可以直接复制,运行。复制不是目的,学习才是精髓。。。。算了,算了,求求大家点赞,关注我吧!

  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Mina介绍:https://blog.csdn.net/haoranhaoshi/article/details/89102597 工程内容介绍(由浅入深,稳扎稳打): test1:建立Server和Client。有Client加入,Server广播给所有Client。 test2:在控制台中Server可以给所有Client发送数据,Client可以给Sever和其他Client发送数据。 test3:(1)特定角色的Client处理消息。 (2)特定角色的Client收到消息。 Client与Server建立Session后,Server存储Client的Role和Session映射(同一个角色可映射多个Session)。角色Role在MinaClientHandler中。修改Role,启动多个Client测试。Client在控制台中Role:Message的格式给对应Role的Client发送Message。 test4:(1)Client启动时如果没有Server,就先建立Server。在test3(2)基础上修改MinaClient的session = future.getSession();即可。 (2)如果建立Server的Client离开了,让另一个Client建立Server。 (3)Client给Server发送数据,如果失败,重新发送。 test5:一个Client有一个Server。Client和其他Client的Server建立Session。 test6:如果Client连接Server失败,就只建立Server,如果Server离开,第一个检测到的Client建立Server。 一个进程中包括Server和Client,进程A和B通信,Server负责接收另一个进程的消息,Client负责发送给另一个进程消息,无需Server独立启动,或者绑定在一个进程中,保证最后离线,或者绑定在一个进程中,进程离线后绑定在其他进程中。把Client中的OWN_SERVER_PORT和ANOTHER_SERVER_PORT调换后启动另一个进程,即可测试。 test7:解决相同角色,分工不同: (1)连接建立时传角色,同一个角色,多个Client,连接时间区分Client (2)连接建立时传角色和功能ID,功能ID配置到启动参数,发消息时消息头传角色和消息ID (3)细化角色。 (4)Client发送功能ID,根据功能ID、Client_ID映射文件得到Client_ID,Client_ID配置到启动参数。 (5)连接失去时,更新角色和Session的Map。 test3.type1对应test3(1) test3.type2对应test3(2) 待做: Server不能通过控制台广播到每一个Client,ioSession.write(scanner.next());之后的代码行,会等下一次控制台输入,Enter按下后执行。 test4(2)(3)、test6、test7 已经转去Vert.x了
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值