java实现建议聊天室

功能 介绍:私聊,群聊
模型介绍:

java im 聊天框架_java im 聊天框架

项目结构

java im 聊天框架_java_02

1.服务端
package edu.cc.chat05;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.management.loading.PrivateClassLoader;

import com.sun.org.apache.bcel.internal.generic.AllocationInstruction;


/**
 * 在线聊天室:服务端
 *目标:私聊和群聊
 *封装利于维护
 * @author chunchun
 *@date 2020年6月15日
 * @projectname chatsocket_demo
 */
public class Chat {
	//private List<Channel> all = new  ArrayList<Channel>();可以使用,但在socket编程使用CopyOnWriteArrayList更好
	private static CopyOnWriteArrayList<Channel> all = new  CopyOnWriteArrayList<Channel>();
	public static void main(String[] args) throws IOException {
		System.out.println("-----server----");
		//指定端口 使用ServerSocket 创建服务器
		ServerSocket server = new ServerSocket(8888);
		//阻塞式等待连接accept
		//死循环使其可以连接多个客户端
		while(true) {
		//与相应客户端连接
		Socket client = server.accept();
		System.out.println("一个客户端建立连接");
		//建立通信管道,用于消息发送与接收。连接一个客户端建立一个管道
		Channel c = new Channel(client);
		//把所有客户端添加到CopyOnWriteArrayList集合精选管理,为群聊和私聊做准备
		all.add(c);///管理所有的成员
		//new Thread(new Channel(client)).start();
		//连接一个客户端建立一个线程,使每个客户端独立
		new Thread(c).start();
		}
	}
	//一个客户一个Channel
	static 	class  Channel implements Runnable{
		private DataInputStream dis= null;
		private DataOutputStream dos= null;
		private Socket client;
	    private boolean isRuning;
	    private String name;
	    //构造函数,与客户端连接的同时初始化输入输出流并获取客户端的名字,同时通知已经进入的成员该成员进入
		public Channel(Socket client) {
			this.client = client;
			try {
				dis = new DataInputStream(client.getInputStream());
			    dos = new DataOutputStream(client.getOutputStream());
			    isRuning =true;
			    //获取名称
			    this.name = receive();
			    //欢迎你的到来
			    this.send("欢迎你的到来");
			    //通知已经进入的成员该成员进入聊天室
			    sendOthers(this.name+"来到了春春聊天室",true);
			} catch (IOException e) {
				System.out.println("----1-----");
				release();
			}
		}
		//接收消息
		private String receive(){
			String msg ="";
			try {
				msg = dis.readUTF();
			} catch (IOException e) {
				System.out.println("----2-----");
				release();//出异常就释放资源
			}
			return msg;
		}
		//发送消息
		private void send(String msg) {
			try {
				dos.writeUTF(msg);
				dos.flush();
			} catch (IOException e) {
				System.out.println("----3-----");
				release();
			}
		}
		//群聊
		
		/**
		 * @param msg
		 * 获取自己的msg利用容器遍历其他人的Channel调用其他人的send方法
		 * 私聊:约定数据格式:  @xxxx:msg
		 */
		private void sendOthers(String msg,boolean isSys) {
			
			boolean isPrivate = msg.startsWith("@");
			if(isPrivate) {//私聊
				int idx = msg.indexOf(":");
				//获取目标和数据
				String targetName = msg.substring(1,idx);
				msg = msg.substring(idx+1);
				//遍历CopyOnWriteArrayList容器找到要私发的客户端并单独给次客户端发送消息
				for(Channel other:all) {
					if(other.name.equals(targetName)) {
						other.send(this.name+"悄悄的对你说"+msg);//私聊消息内容
						
					}
				}
				
				
			}else {//群聊
			for(Channel other:all) {
				if(other==this) {
					continue;
				}
				if(!isSys) {
				other.send(this.name+"对所有人说:"+msg);//群聊消息
				}else {
					other.send(msg);//系统消息
			}
			}
			}
		}
		//释放资源
		private void release() {
			this.isRuning = false;
			Cutils.close(dis,dos,client);
			//退出
			all.remove(this);
			sendOthers(this.name+"离开春春聊天室", true);//退出通知
		}
		@Override
		public void run() {
			//死循环保证可以不断精选消息的发送
			while(isRuning) {
				String msg =receive();
				if(!msg.equals("")) {
					//send(msg);
					sendOthers(msg,false);
				}
			}
		}
	}

}
2.客户端
package edu.cc.chat05;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

/**
 * 
 *目标:私聊和群聊
 * @author chunchun
 *@date 2020年6月15日
 * @projectname chatsocket_demo
 */
public class Client {
	public static void main(String[] args) throws  IOException {
		System.out.println("----client----");
	 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	System.out.println("亲输入用户名");	
	//获取客户端输入的用户名
	String name =br.readLine();
	 //建立连接,使用SOcket创建客户端+服务器的地址和端口
		Socket client =new Socket("localhost",8888);
		//客户端发送消息
		new Thread(new Send(client,name)).start();
		//客户端接收消息
		new Thread(new Receive(client)).start();
	}

}
3.一些封装类
3.1Cutils 用于释放资源
package edu.cc.chat05;

import java.io.Closeable;

/**
 * 工具类
 * @author chunchun
 *@date 2020年6月15日
 * @projectname chatsocket_demo
 */
public class Cutils {
//释放资源
	//使用Closeable来关闭接口
	//参数Closeable ... targets传入可变参数数组
	public static void close(Closeable ... targets) {
		for(Closeable target:targets) {
			try {
				if(null!=target) {
					target.close();
				}
			} catch (Exception e) {
			}
		}
	}
}
3.2Receive用于接收服务器发来的消息
package edu.cc.chat05;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;

/**客户端的接收器
 * @author chunchun
 *@date 2020年6月15日
 * @projectname chatsocket_demo
 */
public class Receive implements Runnable{
	private  DataInputStream dis ;
	private Socket client;
	private boolean isRuning;
	public Receive(Socket client) {
		isRuning = true;
		this.client = client;
		try {
			dis = new DataInputStream(client.getInputStream());
		} catch (IOException e) {
			System.out.println("===2===");
			release();
		}
	}
	//接收消息
			private String receive(){
				String msg ="";
				try {
					msg = dis.readUTF();
				} catch (IOException e) {
					System.out.println("====4===");
					release();//出异常就释放资源
				}
				return msg;
			}
	@Override
	//次现场用于循环接收消息
	public void run() {
		while(isRuning) {
			String msg = receive();
			if(!msg.equals("")) {
				System.out.println(msg);
			}
		}
		
	}
	private void release() {
		this.isRuning = false;
		Cutils.close(dis,client);
	}

}
3.3用于发送消息到服务器
package edu.cc.chat05;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

/**
 * 客户端的发送器
 * @author chunchun
 *@date 2020年6月15日
 * @projectname chatsocket_demo
 */
public class Send implements Runnable{
	private BufferedReader console;
	private DataOutputStream dos;
	private Socket client;
	private boolean isRuning;
	private String name;
	public Send(Socket client,String name) {
		this.client = client;
		isRuning =true;
		this.name=name;
		console = new BufferedReader(new InputStreamReader(System.in));
	    try {
			dos = new DataOutputStream(client.getOutputStream());
			//发送名称
			send(name);
		} catch (IOException e) {
			System.out.println("===1==");
			this.release();
		}
	}
	@Override
	//此线程用于循环发送消息
	public void run() {
		System.out.println(isRuning);
		while(isRuning) {
			String msg = getStrFromConsole();
			if(!msg.equals("")) {
				send(msg);
			}
		}
	}
	//发送消息
			private void send(String msg) {
				try {
					dos.writeUTF(msg);
					dos.flush();
				} catch (IOException e) {
					System.out.println("===3==");
					release();
				}
			}
	//从控制台获取消息
	private String getStrFromConsole() {
		try {
			return console.readLine();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return "";
	}
	private void release() {
		this.isRuning = false;
		Cutils.close(dos,client);
	}
}
运行效果图
群聊效果图

java im 聊天框架_System_03


私聊效果图

java im 聊天框架_客户端_04


退出提示

java im 聊天框架_System_05


代码可以直接运行,需要者可以前往我的GitHub下载代码,地址如下

https://github.com/zzc-2180886679/chat_socket.git