用Java程式碼實現一個簡單的聊天室功能

東海老男人發表於2020-09-24

一.客戶端的建立

1.我們可以用Socket來建立客戶端

/**
  *@類名 Client
  *@描述 TODO 客戶端 1
  *@版本 1.0
  *@建立人 XuKang
  *@建立時間 2020/9/24 16:18
  **/
public class Client {
	public static void main(String[] args) throws UnknownHostException, IOException {
		System.out.println("-----Client1-----");
		BufferedReader br =new BufferedReader(new InputStreamReader(System.in));
		System.out.println("請輸入使用者名稱:");
		String name =br.readLine();
		//1、建立連線: 使用Socket建立客戶端 +服務的地址和埠
		Socket client =new Socket("localhost",8888);
		//2、客戶端傳送訊息
		new Thread(new Send(client,name)).start();  
		new Thread(new Receive(client)).start();
	}
}

2.這時我們需要接受其他客戶端傳送的資料,我們需要建立一個客戶端的接收方法和傳送方法,我可以用阻塞式的方式進行接收和傳送,考慮到多執行緒的安全性,可以實現Runnable
1.Send傳送端:

/**
  *@類名 Send
  *@描述 TODO 使用多執行緒封裝:傳送端
  *@版本 1.0
  *@建立人 XuKang
  *@建立時間 2020/9/24 16:23
  **/
public class Send implements Runnable {
	private BufferedReader console ;
	private DataOutputStream dos;
	private Socket client;
	private boolean isRunning;
	private String name;
	public Send(Socket client,String name) {
		this.client =client;
		console =new BufferedReader(new InputStreamReader(System.in));
		this.isRunning = true;
		this.name = name;
		try {
			dos =new DataOutputStream(client.getOutputStream());
			//傳送名稱
			send(name);
		} catch (IOException e) {
			System.out.println("==1==");
			this.release();
		}	
	}
	@Override
	public void run() {
		while(isRunning) {
			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(e);
			System.out.println("===3==");
			release();
		}
	}
	/**
	 * 從控制檯獲取訊息
	 * @return
	 */
	private String getStrFromConsole() {
		try {
			return  console.readLine();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return "";
	}
	//釋放資源
	private void release() {
		this.isRunning = false;
		CloseUtils.close(dos,client);
	}

}

2.接收端

/**
  *@類名 Receive
  *@描述 TODO 使用多執行緒封裝:接收端
  *@版本 1.0
  *@建立人 XuKang
  *@建立時間 2020/9/24 16:22
  **/
public class Receive implements Runnable {
	private DataInputStream dis ;
	private Socket client;
	private boolean isRunning;
	public Receive(Socket client) {
		this.client = client;
		this.isRunning = true;
		try {
			dis =new DataInputStream(client.getInputStream());
		} catch (IOException e) {
			System.out.println("====離開一位=====");
			release();
		}
	}
	//接收訊息
	private String receive() {
		String msg ="";
		try {
			msg =dis.readUTF();
		} catch (IOException e) {
			System.out.println("====接收訊息異常====");
			release();
		}
		return msg;
	}
	
	@Override
	public void run() {		
		while(isRunning) {
			String msg =receive();
			if(!msg.equals("")) {
				System.out.println(msg);
			}
		}
	}
	//釋放資源
	private void release() {
		this.isRunning = false;
		CloseUtils.close(dis,client);
	}

}

3.統一釋放資源的方法可以提出,服務的也用得上

/**
  *@類名 CloseUtils
  *@描述 TODO 工具類,統一釋放資源
  *@版本 1.0
  *@建立人 XuKang
  *@建立時間 2020/9/24 16:20
  **/
public class CloseUtils {
	/**
	 * 釋放資源
	 */
	public static void close(Closeable... targets ) {
		for(Closeable target:targets) {
			try {
				if(null!=target) {
					target.close();
				}
			}catch(Exception e) {

			}
		}
	}
}

二客戶端的建立

服務端用ServerSocket建立,如果我們吧服務的和客戶端看成事一個通訊通道(Channel),那麼每個客戶端的接入都會建立一個通訊通道,那麼通訊通道的建立也需要實現多執行緒,可以實現Runnable介面,我們存放通道可以用執行緒容器CopyOnWriteArrayList來存放通道。

/**
  *@類名 Chat
  *@描述 TODO Socket服務端(測試類)
  *@版本 1.0
  *@建立人 XuKang
  *@建立時間 2020/9/24 16:17
  **/
public class Chat {

	private static CopyOnWriteArrayList<Channel> all =new CopyOnWriteArrayList<Channel>();

	public static void main(String[] args) throws IOException {
		System.out.println("-----Server-----");
		// 1、指定埠 使用ServerSocket建立伺服器
		ServerSocket server =new ServerSocket(8888);
		// 2、阻塞式等待連線 accept
		while(true) {
				Socket  client =server.accept(); 
				System.out.println("一個客戶端建立了連線");
				Channel c =new Channel(client);
				all.add(c); //管理所有的成員
				new Thread(c).start();			
			}		
		}
		//一個客戶代表一個Channel
		static class Channel implements Runnable{
			private DataInputStream dis;
			private DataOutputStream dos;
			private Socket  client;			
			private boolean isRunning;
			private String name;
			public Channel(Socket  client) {
				this.client = client;
				try {
					dis = new DataInputStream(client.getInputStream());
					dos =new DataOutputStream(client.getOutputStream());
					isRunning =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();
				}
			}
			/**
			 * @方法名 sendOthers
			 * @描述 TODO 群聊:獲取自己的訊息,發給其他人,需要設定isSys為false
			 * 		 TODO 私聊: 約定資料格式: @xxx:msg
			 * @引數 msg 傳送內容
			 * @返回值
			 * @建立人 XuKang
			 * @建立時間 2020/9/24 16:28
			 */
			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);
					for(Channel other: all) {
						if(other.name.equals(targetName)) {//目標
							other.send(this.name +"悄悄地對您說:"+msg);
							break;
						}
					}
				}else {				
					for(Channel other: all) {
						if(other==this) { //自己
							continue;
						}
						if(!isSys) {
							other.send(this.name +"對所有人說:"+msg);//群聊訊息
						}else {
							other.send(msg); //系統訊息
						}
					}
				}
			}
			//釋放資源
			private void release() {
				this.isRunning = false;
				CloseUtils.close(dis,dos,client);
				//退出
				all.remove(this);
				sendOthers(this.name+"離開大家庭...",true);
			}
			@Override
			public void run() {
				while(isRunning) {
					String msg = receive() ;
					if(!msg.equals("")) {
						//send(msg);
						sendOthers(msg,false);
					}
				}
			}
		}
}

三、效果如下

1.啟動服務端

在這裡插入圖片描述

2.啟動客戶端

在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述

3.傳送訊息

在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述

總結

此案例只能用來打發時間,入門網路程式設計可以參考一下,真正的開發不會這麼弄

相關文章