目录
多人聊天室需求分析
服务器
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("聊天室创建成功");
Socket accept;
ServerThread thread;
while (true){
//接收套接字
accept = serverSocket.accept();
//服务器线程
thread = new ServerThread(accept);
thread.start();
//线程启动,将线程加入集合
ArrayListDB.list.add(thread);
}
}
}
客户端
public class Client {
public static void main(String[] args) throws IOException {
Socket socket = new Socket(InetAddress.getLocalHost(),8888);
new ReaderThread(socket).start();
new WriteThread(socket).start();
}
}
数据存储Arraylist数组类
public class ArrayListDB {
public static ArrayList<ServerThread> list = new ArrayList<>();
}
用户类
public class User {
private String name;
public User() {
}
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
线程类
服务器线程
public class ServerThread extends Thread {
private Socket socket;
private User user;
//读取客户端发送的消息再发送给其他用户
private BufferedReader br;
private BufferedWriter bw;
public ServerThread(Socket socket) throws IOException {
this.socket = socket;
bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//避免空指针异常
user = new User("1");
}
//拿到User对象
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public void run() {
try {
//用户注册----第一次客户端发送过来的数据
register();
//消息转发
forword();
} catch (IOException e) {
//客户端强制关闭,没有通过esc关闭
offline();
} finally {
IOUtils.closeAll(br, bw, socket);
}
}
//转发消息
private void forword() throws IOException {
//获取用户输入的消息
String str = "";
while (true) {
//1.获取用户输入的数据
str = br.readLine();
//2.判断是否为退出指令
if ("esc".equalsIgnoreCase(str)) {
//下线
offline();
//将esc发送给所有客户端,告诉客户端服务器下线了
send("esc");
break;
}
//3.判断是否为私聊
if(str.startsWith("@")){
oneByone(str);
continue;
}
//4.群聊
sendToOther(user.getName()+":"+str);
}
}
//私聊-----str 私聊的消息
private void oneByone(String str) throws IOException {
//使用正则表达式判断是否符合私聊规则--规则:@+2-8个字符的名字+英文:或中文:
boolean flag = str.matches("[@].{2,8}[::].+");
if(!flag){
send("系统:如需私聊,请符合格式,@某某某:....");
//结束方法,重新输入
return;
}
//获取对方名字
String receiveName = str.split("[::]")[0].substring(1);
//检查是否有此名字,调用方法检查名字是否存在
flag = chackname(receiveName);
if(flag){
send("系统:私聊用户不存在或用户已下线,请重新输入");
return;
}
//获取私聊内容
str = str.substring(receiveName.length()+2);
//发消息给指定的人
sendToOne(receiveName,str);
}
/**
* 发送私聊消息
* @param receiveName 接收方的名字
* @param str 私聊内容
*/
private void sendToOne(String receiveName, String str) throws IOException {
//遍历集合
for (ServerThread s:ArrayListDB.list) {
//找到私聊对象
if(s.getUser().getName().equals(receiveName)){
s.send(this.getUser().getName()+"私聊:"+str);
}
}
}
//下线方法
private void offline() {
//告诉当前客户端你已经退出
try {
send("系统:你已退出聊天室");
//通知其他用户
sendToOther("系统:" + user.getName() + "已退出聊天室");
} catch (IOException e) {
try {
//通知其他用户当前用户退出聊天室
sendToOther("系统:" + user.getName() + "已退出聊天室");
} catch (IOException ioException) {
ioException.printStackTrace();
}
}finally {
//把集合中服务器的线程清理掉
ArrayListDB.list.remove(this);
}
}
//用户注册
private void register() throws IOException {
String name = "";
boolean flag = false;
do {
//读取客户端发送的名字
name = br.readLine();
//判断名字是否重复
flag = chackname(name);
//提示用户重新输入
if (!flag) {
send("系统:用户名已存在,请重新输入");
continue;
}
//判断用户名长度是否合理
flag = chackLength(name);
if (!flag) {
send("系统:用户名长度不合理,请输入2-8个字符");
continue;
}
//判断用户名是否为敏感词
if ("系统".equals(name)) {
send("系统:不能以“系统”当做用户名,请重新输入");
continue;
}
//用户名合理,保存用户名
this.user.setName(name);
//通知当前用户注册用户注册成功
send("系统:恭喜" + name + "这个名字注册成功,欢迎进入Java交流群");
//通知当前连接上系统的其他用户
sendToOther("系统:" + name + "进入Java交流群");
break;
} while (true);
}
//发送给其他人
private void sendToOther(String str) throws IOException {
for (ServerThread s : ArrayListDB.list) {
//如果是自己就不发送
if (s != this&& !"1".equals(s.getUser().getName())) {
s.send(str);
}
}
}
//判断用户名长度方法
private boolean chackLength(String name) {
//避免空指针异常
if (name != null) {
if (name.length() >= 2 && name.length() <= 8) {
return true;
}
}
return false;
}
//发送给客户端的方法
private void send(String s) throws IOException {
bw.write(s);
bw.newLine();
bw.flush();
}
//判断名字是否重复的方法
/**
* 判断用户名是否重复
* @param name 用户名
* @return 重复返回false
*/
private boolean chackname(String name) {
for (ServerThread t : ArrayListDB.list) {
if (t.getUser().getName().equals(name)) {
return false;
}
}
return true;
}
}
客户端读取线程
public class ReaderThread extends Thread{
private BufferedReader br;
private Socket socket;
//读取线程构造方法,传入套接字
public ReaderThread(Socket socket) throws IOException {
this.socket = socket;
//获取输入流
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
@Override
public void run() {
try{
String str = "";
while (true){
str = br.readLine();
if("esc".equalsIgnoreCase(str)){
//如果输入esc,停机
System.exit(0);
}else {
System.out.println(str);
}
}
} catch (IOException e) {
System.out.println("系统:服务器已关闭");
System.exit(0);
}finally {
IOUtils.closeAll(br,socket);
}
}
}
客户端写入线程
//客户端写线程
public class WriteThread extends Thread{
private Socket socket;
private BufferedReader br;
private BufferedWriter bw;
//创建写线程构造方法,需传入套接字
public WriteThread(Socket socket) throws IOException {
this.socket = socket;
//创建输入流获取控制台录入的信息,存入缓冲区
br = new BufferedReader(new InputStreamReader(System.in));
//创建输出流
bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
}
@Override
public void run() {
try{
System.out.println("系统:请输入名字进行注册");
String str = "";
while (true){
str = br.readLine();
if(str != null){
//先写出去
bw.write(str);
bw.newLine();
bw.flush();
//如果是esc则停机
if("esc".equalsIgnoreCase(str)){
System.exit(0);
}
}
}
} catch (IOException e) {
System.out.println("客户端读取异常");
e.printStackTrace();
}finally {
IOUtils.closeAll(bw,br,socket);
}
}
}
关流工具类
public class IOUtils {
public static void closeAll(Closeable... closeables){
for (Closeable c:closeables) {
if(c != null){
try {
c.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}