项目名称
- 多线程聊天室
项目简介
-
多个用户可以在同一局域网下进行聊天.
-
类图:
-
项目执行流程
客户端通过对应端口号,和IP地址t与服务器端建立连接后,进行注册,然后通过输入输出流,进行通信。
服务器端收到信息后,进行解析,判断客户端的需求(私聊,群聊,下线等),从而进行相应的处理。
因为采取多线程的方式,服务器端,线程比较多,采取线程池的方式进行处理。每个客户端仅需要一个发送和一个接受线程,而且也不牵扯什么资源共享的问题,直接继承Thread类来实现多线程。
用一个ConcurrentHashMap存储当前在线用户信息。key–Socket,Value–String(用户名)
项目基本功能
- 注册
- 私聊
- 群聊
- 下线
- 显示当前在线的用户
项目扩展功能
- 参数灵活化。将IP地址,端口号,线程池的线程数通过外界传入。
- 添加登录功能。用户注册过之后,下线之后,下次直接登陆即可,无需重新注册。
- 收到历史私聊信息。用户下线之后,有人私聊对其发消息,当用户再次上线后,可以查收到。
- 客户端异常关闭,服务器端进行相应的处理。
扩展方案
实现参数灵活化:
- 通过键盘将参数运行时读入。
- 通过main方法的args参数传入。
- 通过文件读入。
- 通过数据库传入。
最终选择main方法的参数进行传入,因为需要的参数(端口号,IP地址,线程数目)比较少,且很方便,便于施行。
实现登录功能
- 在HandelClient类中,添加一个静态内部类User,存放用户的用户名和密码及与对象相关联的一个消息文件(文件后期实现上线后收到私聊消息有用)。
- 在HandelClient类中,添加一个静态常量USER_SET,本身是一个CopyOnWriteArraySet(线程安全的Set集合)。
- 用户进行注册时,就用用户的信息生成User对象,存进Set中。之所以选择Set是因为Set里面的元素不可重复,且查询时间复杂度O(1).
- 用户在选择登陆功能时,只需判断Set中是否包含该对象即可,存在就将该用户放进ConcurrentHashMap中就OK了。
实现收到历史私聊信息。
业务需求说明,用户下线之后,有人对其私聊,将该信息存储起来,待用户再次上线后,将消息发过去。
- 在进行私聊时,判断用户想要聊天的目标用户,如果该用户在ConcurrentHashMap中,证明该用户还在线,否则判断是不是在CopyOnWriteArraySet中,在就说明该目标用户是一个合法的注册过的.
- 因此将相应的消息存储在目标用户的文件中,当该目标用户上线之后,将所有消息发送过去,并且将文件内容清空(很重要)。
客户端异常关闭,服务器端进行相应的处理。
待续…
遇到的问题及解决方案
- 问题一:客户端收不到消息(1.服务器没发过来? 2.客户端读不来?)
1.没有使用println。
2.使用了仍然没有发出去,没有flush。 - 问题二:一次注册,终生使用
程序结束时,将用户名和密码按行写进文件
程序运行时,从文件里面按行都出来,拆分生成新对象,放进Set集合中。 - 问题三:流采用自动关闭的方式
流关闭的太快了,导致Socket中断。最终只能采取手动关闭。 - 问题四:IP地址的验证
相对来说比较简单,字符串拆分加判断就OK了。 - 文件内容的问题
用户上线之后,将私聊消息发过去之后,需要将文件内容清空,否则下次依然发原始的信息。
项目源码
https://github.com/excellent01/Java-Code
项目难点
- 实现登陆功能,
- 下线后再次上线可以收到私聊消息。
知识体系
- Java集合框架
- JUC包下的线程安全的集合
- 多线程
- Java网络编程
- JavaIO
- 其它知识
效果展示
服务器端启动
客户端启动并注册
群聊功能
登录功能及上线后收到私聊信息
不足之处
因为暂时没有学习数据库,不能将用户最终的信息,存入数据库,只能使用文件代替.
小段代码:IP地址的校验
private static boolean hostIsLegal(String host) {
if(host == null)
return true;
String[] arr = host.split("\\.");
if(arr.length != 4){
return false;
}
for(String str : arr){
try{
int num = Integer.parseInt(str);
if(num < 0 || num > 255){
return false;
}
}catch (NumberFormatException e){
return false;
}
}
if(host.endsWith("\\.")){
return false;
}
return true;
}