聊天室简介
聊天室使用mina完成,用户打开客户端连接服务器,随即在控制台输入并得到信息。将私聊与群聊使用集合存入数据进行判断然后给予信息反馈,群聊时可@全体成员或任意人员,私聊#与@不能同时进行,聊天室将进行友好的提示,帮助用户更好的了解操作的方法。
-----------由于控制台输入,有些问题无法避免,请见谅-----------
jar包环境配置
使用mina进行聊天室的建立,需要的jar包有:
commons-logging-1.0.3.jar
mina-core-2.1.3.jar
slf4j-api-1.7.26.jar
聊天室展示
测试1:
测试2:
客户端类
用户打开客户端后输入名字后验证是否存在,此时主线程进行睡眠状态,当验证成功时子线程将打断主线程睡眠,群体广播提示登录聊天室,进行群聊时可以私聊他人,秘密你我共知。群聊时@他人,系统将给予此人相应的提示,@和#用户时请按指示使用,输入错误给予提示。。。退出登录时,系统广播给予提示。
package chat;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.Scanner;
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;
public class MinaClient {
public static String name = "";
public static void main(String[] args) {
// TODO Auto-generated method stub
MinaClient mc = new MinaClient();
//创建IoService实例
NioSocketConnector connector = new NioSocketConnector();
//2,设置过滤链 日志、编解码
//通过连接器获得当前的过滤链对象
connector.getFilterChain().addLast("logger", new LoggingFilter());
connector.getFilterChain().addLast("codec",
new ProtocolCodecFilter(
new TextLineCodecFactory(
Charset.forName("UTF-8"))));
Thread t = Thread.currentThread();
//3,绑定IO处理器,传入当前线程用于唤醒
connector.setHandler(new MinaClientHandler(t));
//连接地址
ConnectFuture connect =
connector.connect(new InetSocketAddress("localhost",8888));
System.out.println("客户端连接成功!!");
Scanner scan = new Scanner(System.in);
//用户开启客户端,输入属于自己的名称
do {
if(MinaClient.name.contains(":")) {
System.out.println("昵称中不能包含:,请重新输入");
}else {
System.out.println("请输入昵称?");
}
MinaClient. name = scan.next();
}while(MinaClient.name.contains(":"));
//从连接对象中获得当前的会话对象
IoSession session = connect.getSession();
//服务器未开启时为空
if(session==null) {
System.out.println("服务器未开启,请等待服务器开启再进入···");
System.exit(0);
}
//传给服务器名字
session.write(MinaClient.name);
//睡眠,等待输入正确名字后唤醒
try {
t.sleep(1000000000000L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
}
int count = 0;
//eclipse中文输入中文在其他行时会把汉字敲打的字母打印
System.out.println("\t·····为了用户体验,请eclipse用户输入中文前请将光标移动到最后一行(无文字行)·····");
while(true) {
//开始聊天
System.out.println("--私聊 # 和艾特 @ 请在名字后加一个空格- -");
String content = scan.nextLine();
if(count==0) {
count++;
session.write(MinaClient.name+":已进入聊天室~~~~~~~~~~~~~~");
}
//输入姓名后自动执行了一次输入空白
if("".equals(content)&&count!=1) {
System.out.println("········不能输入空白信息,请重新输入");
}else if(content.contains("@")&&content.contains("#")) {
System.out.println("········不能#并@,请重新输入");
}else {
session.write(MinaClient.name+":"+content);
}
try {
t.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
class MinaClientHandler extends IoHandlerAdapter {
Thread t ;
public MinaClientHandler(Thread t) {
super();
this.t = t;
}
@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
// TODO Auto-generated method stub
System.out.println("client异常提示");
super.exceptionCaught(session, cause);
}
@Override
public void inputClosed(IoSession session) throws Exception {
// TODO Auto-generated method stub
System.out.println("client关闭:"+session);
super.inputClosed(session);
}
@Override
public void messageReceived(IoSession session, Object message) throws Exception {
// TODO Auto-generated method stub
//打印数据
//判断登录成功时唤醒主线程
if(message.equals(MinaClient.name+"登录成功")) {
t.interrupt();
}
System.out.println("\n"+message);
//当用户名重复时提示并使用户重新输入
if(message.equals(MinaClient.name+"已存在,请重新登录")) {
System.out.println("请重新输入昵称");
Scanner scan = new Scanner(System.in);
MinaClient. name = scan.next();
session.write(MinaClient.name);
}
super.messageReceived(session, message);
}
@Override
public void messageSent(IoSession session, Object message) throws Exception {
// TODO Auto-generated method stub
//System.out.println("client发送消息:"+message);
super.messageSent(session, message);
}
@Override
public void sessionClosed(IoSession session) throws Exception {
// TODO Auto-generated method stub
System.out.println("client当前会话关闭");
System.exit(0);
super.sessionClosed(session);
}
@Override
public void sessionCreated(IoSession session) throws Exception {
// TODO Auto-generated method stub
//System.out.println("client会话被新创建...");
super.sessionCreated(session);
}
@Override
public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
// TODO Auto-generated method stub
//System.out.println("client会话休眠中");
super.sessionIdle(session, status);
}
}
实体类
保存登录用户的信息,在服务类中提供业务判断和信息修改
class MySession {
private IoSession session;//同ID
private String name;//姓名
private int state;// 状态,1 登录或 0 未登录
public MySession(IoSession session, int state) {
super();
this.session = session;
this.state = state;
}
public IoSession getSession() {
return session;
}
public void setSession(IoSession session) {
this.session = session;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
@Override
public boolean equals(Object arg0) {
// TODO Auto-generated method stub
MySession temp = (MySession) arg0;
if (name.equals(temp.getName())) {
return true;
}
return super.equals(arg0);
}
@Override
public int hashCode() {
// TODO Auto-generated method stub
return name.hashCode();
}
}
服务端类
客户端连接时赋给实体类登录状态为0并存入未登录状态集合中,当判断名字不存在时改为1存入登录状态集合,名字存在即进入0对应的业务代码块。用户进入此类判断是否为第一次输入名字,修改实体类信息。
@与#拥有aimChat方法与singleChat,群聊有boradCast,退出有exitChat进行业务处理。
package chat;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Vector;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.service.IoService;
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;
public class MinaServer {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
// 1,创建IoService的实例
IoAcceptor acceptor = new NioSocketAcceptor();
// 2,设置过滤链 日志、编解码
// 通过接收器获得当前的过滤链对象
acceptor.getFilterChain().addLast("logger", new LoggingFilter());
acceptor.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"))));
// 3,绑定IO处理器
acceptor.setHandler(new MyServerHandler());
// 4,监听端口
acceptor.bind(new InetSocketAddress(8888));
System.out.println("服务端启动,并监听端口:8888");
}
}
class MyServerHandler extends IoHandlerAdapter {
// 连接却未登录状态的客户端
private Vector<MySession> vms = new Vector<MySession>();
// 连接且登录状态的客户端
private LinkedHashSet<MySession> sessions = new LinkedHashSet<MySession>();
@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
// TODO Auto-generated method stub
// 断开连接后为false
// 客户端断开,集合移除该用户
if (!session.isConnected()) {
exitChat(session);
}
super.exceptionCaught(session, cause);
}
@Override
public void inputClosed(IoSession session) throws Exception {
// TODO Auto-generated method stub
System.out.println("服务端关闭:" + session);
super.inputClosed(session);
}
@Override
public void messageReceived(IoSession session, Object message) throws Exception {
// TODO Auto-generated method stub
// 第一步,获得数据
System.out.println(message);
MySession ms = null;
// 将第一次进入客户端数据赋值给ms,第二次进入方法时登录状态已经改变。
for (MySession mySession : vms) {
if (mySession.getSession() == session) {
ms = mySession;
}
}
// 第二步,解析自定义的协议,解析出来发送者、内容、接收者
// 清除名字包含的空格
String data = ((String) message).trim();
int first = data.indexOf(":");
String name = "";
// 用户的名字,客户端发送信息一定会带:,为-1一定是输入名字时
if (first == -1) {
name = data;
ms.setName(name);
}
// 用户发送的信息,一次接收一行,空白不会接收,也不会从客户端发送过来
String msg = null;
if (data.contains(":")) {
msg = data.substring(first + 1);
}
// 第三步,判定该session是否已连接,并且是第一次发送数据
if (!sessions.contains(ms)) {
sessions.add(ms);
ms.getSession().write(ms.getName() + "登录成功");
System.out.println(ms.getName() + "已成功登录");
// 改变用户状态
ms.setState(1);
}
// 未登录状态验证名字是否重复
if (ms.getState() == 0) {
Iterator<MySession> iterator = sessions.iterator();
while (iterator.hasNext()) {
MySession ms2 = iterator.next();
if (ms.getName().equals(ms2.getName())) {
ms.getSession().write(ms.getName() + "已存在,请重新登录");
}
}
}
if (msg != null && !msg.equals("")) {
// 第四步,选择私聊对象
if (msg.contains("@")) {
aimChat(ms, msg);
} else if (msg.contains("#")) {
singleChat(ms, msg);
} else {
boradCast(ms, msg);
}
}
super.messageReceived(session, message);
}
private void boradCast(MySession ms, String msg) {
System.out.println("开始广播");
for (MySession ms2 : sessions) {
ms2.getSession().write(ms.getName() + ":" + msg);
}
}
private void aimChat(MySession ms, String msg) {
boolean flag = false;
if (msg.contains("@全体成员 ") || msg.endsWith("@全体成员")) {
for (MySession msTemp : sessions) {
flag = true;
// @除自己外所有人
if (ms.getSession() != msTemp.getSession()) {
msTemp.getSession().write("@有人艾特你:\n" + ms.getName() + ":" + msg);
}
}
ms.getSession().write(ms.getName() + ":" + msg);
} else {
for (MySession msTemp : sessions) {
// 没有被@的人收到信息不提示
if (!msg.contains("@" + msTemp.getName() + " ") && !msg.endsWith("@" + msTemp.getName())) {
msTemp.getSession().write(ms.getName() + ":" + msg);
} else if (ms.getSession() != msTemp.getSession()) {
// 有目标对象时改为true,不进行无提示广播
flag = true;
// 提示被@用户.并将内容发送
msTemp.getSession().write("@有人艾特你:\n" + ms.getName() + ":" + msg);
} else {
flag = true;
ms.getSession().write("\t\t\t\t!!!!!请不要艾特自己!!!!!!");
}
}
}
// 找不到@的对象,广播信息
if (!flag) {
System.out.println("@转广播");
ms.getSession().write("\t\t\t\t!!!!!未找到@对象!!!!!!");
}
}
private void singleChat(MySession ms, String msg) {
String targetName = "";
String content = "";
boolean flag = false;
System.out.println("开始私聊");
if (msg.contains("#" + ms.getName() + " ")) {
ms.getSession().write("\t\t\t\t!!!!!请不要私聊自己!!!!!!");
} else {
for (MySession msTemp : sessions) {
// 如果私聊该对象,进行局部变量状态改变
if (msg.contains("#" + msTemp.getName() + " ") || msg.endsWith("#" + msTemp.getName())) {
flag = true;
targetName = msTemp.getName();
content = msg.replace("#" + targetName, "");
// 找到接收者session.并将内容发送
msTemp.getSession().write(ms.getName() + "悄悄对你说:" + content);
}
}
if (!flag) {
// 没有找到私聊的对象
ms.getSession().write("\t\t\t\t!!!!!没有你要找的人!!!!!!");
} else {
ms.getSession().write("你偷偷对" + targetName + "说:" + content);
}
}
}
private void exitChat(IoSession session) {
Iterator<MySession> iterator = sessions.iterator();
while (iterator.hasNext()) {
MySession ms = iterator.next();
if (ms.getSession() == session) {
sessions.remove(ms);
vms.remove(ms);
// TODO Auto-generated method stub
boradCast(ms, ms.getName() + "已退出聊天室~~~~~~~~~~~~~~");
}
}
}
@Override
public void messageSent(IoSession session, Object message) throws Exception {
// TODO Auto-generated method stub
System.out.println("服务端 发送消息:" + message);
super.messageSent(session, message);
}
@Override
public void sessionClosed(IoSession session) throws Exception {
exitChat(session);
super.sessionClosed(session);
}
@Override
public void sessionCreated(IoSession session) throws Exception {
// TODO Auto-generated method stub
System.out.println("服务端 会话被新创建...");
// 用户连接,存入集合
MySession ms = new MySession(session, 0);
vms.add(ms);
super.sessionCreated(session);
}
@Override
public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
// TODO Auto-generated method stub
System.out.println("服务端 会话休眠中");
super.sessionIdle(session, status);
}
}
class MySession {
private IoSession session;
private String name;
private int state;// 状态,1 登录或 0 未登录
public MySession(IoSession session, int state) {
super();
this.session = session;
this.state = state;
}
public IoSession getSession() {
return session;
}
public void setSession(IoSession session) {
this.session = session;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
@Override
public boolean equals(Object arg0) {
// TODO Auto-generated method stub
MySession temp = (MySession) arg0;
if (name.equals(temp.getName())) {
return true;
}
return super.equals(arg0);
}
@Override
public int hashCode() {
// TODO Auto-generated method stub
return name.hashCode();
}
}