Java實現(xiàn)在線聊天室(層層遞進(jìn))
本文實例為大家分享了Java實現(xiàn)在線聊天室的具體代碼,供大家參考,具體內(nèi)容如下
- 本文講述了從實現(xiàn)單個多個客戶的收發(fā)信息(基礎(chǔ)簡易版),到各種實現(xiàn)代碼的封裝(oop版),實現(xiàn)群聊(群聊過渡版),到最后實現(xiàn)私聊(終極版)的過程
- 本文內(nèi)容是在初步學(xué)習(xí)網(wǎng)絡(luò)編程時,練習(xí)強化記憶時的學(xué)習(xí)總結(jié)
- 主要利用了TCP網(wǎng)絡(luò)編程和多線程
- 如有問題,歡迎指出
綜合案例:在線聊天室
需求:使用TCP的Socket實現(xiàn)一個聊天室
- 服務(wù)器端:一個線程專門發(fā)送消息,一個線程專門接收消息
- 客戶端:一個線程專門發(fā)送消息,一個線程專門接收消息
1. 基礎(chǔ)簡易版
1.1 一個客戶收發(fā)多條消息
目標(biāo):實現(xiàn)一個客戶可以正常收發(fā)多條信息
服務(wù)器
/**
?* 在線聊天室: 服務(wù)器
?* 目標(biāo):實現(xiàn)一個客戶可以正常收發(fā)多條消息
?* ?服務(wù)器不生產(chǎn)內(nèi)容,相當(dāng)于一個轉(zhuǎn)發(fā)站,將客戶端的請求轉(zhuǎn)發(fā)
?*/
public class MutiChat {
? ? public static void main(String[] args) throws IOException {
? ? ? ? System.out.println("-----Server-----");
? ? ? ? // 1、指定端口 ?使用ServerSocket創(chuàng)建服務(wù)器
? ? ? ? ServerSocket server = new ServerSocket(8888);
? ? ? ? // 2、利用Socket的accept方法,監(jiān)聽客戶端的請求。阻塞,等待連接的建立
? ? ? ? Socket client = server.accept();
? ? ? ? System.out.println("一個客戶端建立了連接");
? ? ? ? DataInputStream dis = new DataInputStream(client.getInputStream());
? ? ? ? DataOutputStream dos = new DataOutputStream(client.getOutputStream());
? ? ? ? boolean isRunning = true;
? ? ? ? while (isRunning) {
? ? ? ? ? ? // 3、接收消息
? ? ? ? ? ? String msg = dis.readUTF();
? ? ? ? ? ? // 4、返回消息
? ? ? ? ? ? dos.writeUTF(msg);
? ? ? ? ? ? dos.flush();
? ? ? ? }
? ? ? ? // 5、釋放資源
? ? ? ? dos.close();
? ? ? ? dis.close();
? ? ? ? client.close();
? ? }
}客戶端
/**
?* 在線聊天室: 客戶端
?* 目標(biāo):實現(xiàn)一個客戶可以正常收發(fā)(多條)信息
?*/
public class MutiClient {
? ? public static void main(String[] args) throws IOException {
? ? ? ? System.out.println("-----Client-----");
? ? ? ? // 1、建立連接:使用Socket創(chuàng)建客戶端 + 服務(wù)的地址和端口
? ? ? ? Socket client = new Socket("localhost", 8888);
? ? ? ? // 2、客戶端發(fā)送消息
? ? ? ? BufferedReader console = new BufferedReader(new InputStreamReader(System.in)); ?// 對接控制臺
? ? ? ? DataOutputStream dos = new DataOutputStream(client.getOutputStream());
? ? ? ? DataInputStream dis = new DataInputStream(client.getInputStream());
? ? ? ? boolean isRunning = true;
? ? ? ? while (isRunning) {
? ? ? ? ? ? String msg = console.readLine();
? ? ? ? ? ? dos.writeUTF(msg);
? ? ? ? ? ? dos.flush();
? ? ? ? ? ? // 3、獲取消息
? ? ? ? ? ? msg = dis.readUTF();
? ? ? ? ? ? System.out.println(msg);
? ? ? ? }
? ? ? ? // 4、釋放資源
? ? ? ? dos.close();
? ? ? ? dis.close();
? ? ? ? client.close();
? ? }
}1.2 多個客戶收發(fā)多條消息(不使用多線程)
- 目標(biāo):實現(xiàn)多個客戶可以正常收發(fā)多條信息
- 出現(xiàn)排隊問題:其他客戶必須等待之前的客戶退出,才能收發(fā)消息
服務(wù)器
public class MutiChat {
? ? public static void main(String[] args) throws IOException {
? ? ? ? System.out.println("-----Server-----");
? ? ? ? // 1、指定端口 ?使用ServerSocket創(chuàng)建服務(wù)器
? ? ? ? ServerSocket server = new ServerSocket(8888);
? ? ? ? // 2、利用Socket的accept方法,監(jiān)聽客戶端的請求。阻塞,等待連接的建立
? ? ? ? while (true) {
? ? ? ? ? ? Socket client = server.accept();
? ? ? ? ? ? System.out.println("一個客戶端建立了連接");
? ? ? ? ? ? DataInputStream dis = new DataInputStream(client.getInputStream());
? ? ? ? ? ? DataOutputStream dos = new DataOutputStream(client.getOutputStream());
? ? ? ? ? ? boolean isRunning = true;
? ? ? ? ? ? while (isRunning) {
? ? ? ? ? ? ? ? // 3、接收消息
? ? ? ? ? ? ? ? String msg = dis.readUTF();
? ? ? ? ? ? ? ? // 4、返回消息
? ? ? ? ? ? ? ? dos.writeUTF(msg);
? ? ? ? ? ? ? ? dos.flush();
? ? ? ? ? ? }
? ? ? ? ? ? // 5、釋放資源
? ? ? ? ? ? dos.close();
? ? ? ? ? ? dis.close();
? ? ? ? ? ? client.close();
? ? ? ? }
? ? }
}1.3 多個客戶收發(fā)多條消息(多線程)
- 目標(biāo):實現(xiàn)多個客戶可以正常收發(fā)多條信息
- 出現(xiàn)的問題:利用Lambda太復(fù)雜,代碼過多不好維護(hù);客戶端讀寫沒有分開,必須先寫后讀
服務(wù)器代碼
public class ThreadMutiChat {
? ? public static void main(String[] args) throws IOException {
? ? ? ? System.out.println("-----Server-----");
? ? ? ? // 1、指定端口 ?使用ServerSocket創(chuàng)建服務(wù)器
? ? ? ? ServerSocket server = new ServerSocket(8888);
? ? ? ? // 2、利用Socket的accept方法,監(jiān)聽客戶端的請求。阻塞,等待連接的建立
? ? ? ? while (true) {
? ? ? ? ? ? Socket client = server.accept();
? ? ? ? ? ? System.out.println("一個客戶端建立了連接");
? ? ? ? ? ? // 加入多線程
? ? ? ? ? ? new Thread(()->{
? ? ? ? ? ? ? ? DataInputStream dis = null;
? ? ? ? ? ? ? ? DataOutputStream dos = null;
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? dis = new DataInputStream(client.getInputStream());
? ? ? ? ? ? ? ? ? ? dos = new DataOutputStream(client.getOutputStream());
? ? ? ? ? ? ? ? } catch (IOException e) {
? ? ? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? boolean isRunning = true;
? ? ? ? ? ? ? ? while (isRunning) {
? ? ? ? ? ? ? ? ? ? // 3、接收消息
? ? ? ? ? ? ? ? ? ? String msg = null;
? ? ? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? ? ? msg = dis.readUTF();
? ? ? ? ? ? ? ? ? ? ? ? // 4、返回消息
? ? ? ? ? ? ? ? ? ? ? ? dos.writeUTF(msg);
? ? ? ? ? ? ? ? ? ? ? ? dos.flush();
? ? ? ? ? ? ? ? ? ? } catch (IOException e) {
// ? ? ? ? ? ? ? ? ? ? ? ?e.printStackTrace();
? ? ? ? ? ? ? ? ? ? ? ? isRunning = false; ?// 停止線程
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? // 5、釋放資源
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? if (null == dos) {
? ? ? ? ? ? ? ? ? ? ? ? dos.close();
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? } catch (IOException e) {
? ? ? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? if (null == dis) {
? ? ? ? ? ? ? ? ? ? ? ? dis.close();
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? } catch (IOException e) {
? ? ? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? if (null == client) {
? ? ? ? ? ? ? ? ? ? ? ? client.close();
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? } catch (IOException e) {
? ? ? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }).start();
? ? ? ? }
? ? }
}2. oop封裝版
目標(biāo):封裝使用多線程實現(xiàn)多個客戶可以正常收發(fā)多條消息
- 1、線程代理 Channel,一個客戶代表一個 Channel
- 2、實現(xiàn)方法:接收消息 - receive; 發(fā)送消息 - send; 釋放資源 - release;
- 3、其中釋放資源 release方法中利用工具類 Utils:實現(xiàn)Closeable接口、可變參數(shù)
- 好處:利用封裝,是代碼簡潔,便于維護(hù)
服務(wù)器端
示例代碼:
public class ThreadMutiChat {
? ? public static void main(String[] args) throws IOException {
? ? ? ? System.out.println("---服務(wù)器開始工作---");
? ? ? ? // 1、指定端口 ?使用ServerSocket創(chuàng)建服務(wù)器
? ? ? ? ServerSocket server = new ServerSocket(8888);
? ? ? ? // 2、利用Socket的accept方法,監(jiān)聽客戶端的請求。阻塞,等待連接的建立
? ? ? ? while (true) {
? ? ? ? ? ? Socket client = server.accept();
? ? ? ? ? ? System.out.println("一個客戶端建立了連接");
? ? ? ? ? ? new Thread(new Channel(client)).start();
? ? ? ? }
? ? }
? ? // 一個客戶代表一個Channel
? ? static class Channel implements Runnable {
? ? ? ? private DataInputStream dis = null;
? ? ? ? private DataOutputStream dos = null;
? ? ? ? private Socket client;
? ? ? ? private boolean isRunning;
? ? ? ? public Channel(Socket client) {
? ? ? ? ? ? this.client = client;
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? dis = new DataInputStream(client.getInputStream());
? ? ? ? ? ? ? ? dos = new DataOutputStream(client.getOutputStream());
? ? ? ? ? ? ? ? isRunning = true;
? ? ? ? ? ? } catch (IOException e) {
? ? ? ? ? ? ? ? System.out.println("---構(gòu)造時出現(xiàn)異常---");
? ? ? ? ? ? ? ? release();
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? // 接收消息
? ? ? ? private String receive() {
? ? ? ? ? ? String msg = ""; ? ? ? ?// 避免空指針
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? msg = dis.readUTF();
? ? ? ? ? ? } catch (IOException e) {
? ? ? ? ? ? ? ? System.out.println("---接受消息出現(xiàn)異常---");
? ? ? ? ? ? ? ? release();
? ? ? ? ? ? }
? ? ? ? ? ? return msg;
? ? ? ? }
? ? ? ? // 發(fā)送消息
? ? ? ? private void send(String msg) {
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? dos.writeUTF(msg);
? ? ? ? ? ? ? ? dos.flush();
? ? ? ? ? ? } catch (IOException e) {
? ? ? ? ? ? ? ? System.out.println("---發(fā)送消息出現(xiàn)異常---");
? ? ? ? ? ? ? ? release();
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? // 釋放資源
? ? ? ? private void release() {
? ? ? ? ? ? this.isRunning = false;
? ? ? ? ? ? // 封裝
? ? ? ? ? ? Utils.close(dis, dos, client);
? ? ? ? }
? ? ? ? // 線程體
? ? ? ? @Override
? ? ? ? public void run() {
? ? ? ? ? ? while (isRunning) {
? ? ? ? ? ? ? ? String msg = receive();
? ? ? ? ? ? ? ? if (!msg.equals("")) {
? ? ? ? ? ? ? ? ? ? send(msg);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? }
}工具類Utils:實現(xiàn)Closeable接口,利用可變參數(shù),達(dá)到釋放資源的作用
示例代碼
public class Utils {
? ? public static void close(Closeable... targets) { ? ? ? ?
? ? // Closeable是IO流中接口,"..."可變參數(shù)
? ? // IO流和Socket都實現(xiàn)了Closeable接口,可以直接用
? ? ? ? for (Closeable target: targets) {
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? // 只要是釋放資源就要加入空判斷
? ? ? ? ? ? ? ? if (null != target) {
? ? ? ? ? ? ? ? ? ? target.close();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }catch (Exception e) {
?? ??? ??? ??? ?e.printStackTrace();
? ? ? ? ? ? }
? ? ? ? }
? ? }
}客戶端:啟用兩個線程Send和Receive實現(xiàn)收發(fā)信息的分離
示例代碼
public class ThreadMutiClient {
? ? public static void main(String[] args) throws IOException {
? ? ? ? System.out.println("-----客戶端開始工作-----");
? ? ? ? Socket client = new Socket("localhost", 8888);
? ? ? ? new Thread(new Send(client)).start();
? ? ? ? new Thread(new Receive(client)).start();
? ? }
}使用多線程封裝客戶的發(fā)送端 – Send類
實現(xiàn)方法:
1、發(fā)送消息 - send()
2、從控制臺獲取消息 - getStrFromConsole()
3、釋放資源 - release()
4、線程體 - run()
示例代碼
public class Send implements Runnable {
? ? private BufferedReader console;
? ? private DataOutputStream dos;
? ? private Socket client;
? ? private boolean isRunning;
? ? public Send(Socket client) {
? ? ? ? this.client = client;
? ? ? ? console = new BufferedReader(new InputStreamReader(System.in)); ?// 對接控制臺
? ? ? ? try {
? ? ? ? ? ? dos = new DataOutputStream(client.getOutputStream());
? ? ? ? ? ? isRunning = true;
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? System.out.println("---客戶發(fā)送端構(gòu)造時異常---");
? ? ? ? ? ? release();
? ? ? ? }
? ? }
? ? // 從控制臺獲取消息
? ? private String getStrFromConsole() {
? ? ? ? try {
? ? ? ? ? ? return console.readLine();
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? return "";
? ? ? ? }
? ? }
? ? // 發(fā)送消息
? ? private void send(String msg) {
? ? ? ? try {
? ? ? ? ? ? dos.writeUTF(msg);
? ? ? ? ? ? dos.flush();
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? System.out.println("---客戶發(fā)送端發(fā)送消息異常---");
? ? ? ? ? ? release();
? ? ? ? }
? ? }
? ? @Override
? ? public void run() {
? ? ? ? while (isRunning) {
? ? ? ? ? ? String msg = getStrFromConsole();
? ? ? ? ? ? if (!msg.equals("")) {
? ? ? ? ? ? ? ? send(msg);
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? // 釋放資源
? ? private void release() {
? ? ? ? this.isRunning = false;
? ? ? ? Utils.close(dos,client);
? ? }
}使用多線程封裝客戶的接收端 – Receive類
實現(xiàn)方法:
1、接收消息 - send
2、釋放資源 - release()
3、線程體 - run()
示例代碼
public class Receive implements Runnable {
? ? private DataInputStream dis;
? ? private Socket client;
? ? private boolean isRunning;
? ? public Receive(Socket client) {
? ? ? ? this.client = client;
? ? ? ? try {
? ? ? ? ? ? dis = new DataInputStream(client.getInputStream());
? ? ? ? ? ? isRunning = true;
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? System.out.println("---客戶接收端構(gòu)造時異常---");
? ? ? ? ? ? release();
? ? ? ? }
? ? }
? ? // 接收消息
? ? private String receive() {
? ? ? ? String msg = "";
? ? ? ? try {
? ? ? ? ? ? msg = dis.readUTF();
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? System.out.println("---客戶接收端接收消息異常---");
? ? ? ? ? ? release();
? ? ? ? }
? ? ? ? return msg;
? ? }
? ? // 釋放資源
? ? private void release() {
? ? ? ? isRunning = false;
? ? ? ? Utils.close(dis, client);
? ? }
? ? @Override
? ? public void run() {
? ? ? ? while (isRunning) {
? ? ? ? ? ? String msg = receive();
? ? ? ? ? ? if (!msg.equals("")) {
? ? ? ? ? ? ? ? System.out.println(msg);
? ? ? ? ? ? }
? ? ? ? }
? ? }
}3. 群聊過渡版
目標(biāo):加入容器,實現(xiàn)群聊
服務(wù)器端
1、建立 CopyOnWriteArrayList<Channel> 容器,容器中的元素是Channel客戶端代理。要對元素進(jìn)行修改同時遍歷時,推薦使用此容器,避免出問題。
2、實現(xiàn)方法void sendOthers(String msg)將信息發(fā)送給除自己外的其他人。
服務(wù)器端實現(xiàn)代碼
public class Chat {
? ? // 建立 CopyOnWriteArrayList<Channel> 容器
? ? private static CopyOnWriteArrayList<Channel> all = new CopyOnWriteArrayList<>();
? ? public static void main(String[] args) throws IOException {
? ? ? ? System.out.println("---服務(wù)器開始工作---");
? ? ? ? ServerSocket server = new ServerSocket(8888);
? ? ? ? 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("---構(gòu)造時出現(xiàn)問題---");
? ? ? ? ? ? ? ? release();
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? // 接收消息
? ? ? ? private String receive() {
? ? ? ? ? ? String msg = ""; ? ? ? ?// 避免空指針
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? msg = dis.readUTF();
? ? ? ? ? ? } catch (IOException e) {
? ? ? ? ? ? ? ? System.out.println("---接受消息出現(xiàn)問題---");
? ? ? ? ? ? ? ? release();
? ? ? ? ? ? }
? ? ? ? ? ? return msg;
? ? ? ? }
? ? ? ? // 發(fā)送消息
? ? ? ? private void send(String msg) {
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? dos.writeUTF(msg);
? ? ? ? ? ? ? ? dos.flush();
? ? ? ? ? ? } catch (IOException e) {
? ? ? ? ? ? ? ? System.out.println("---發(fā)送消息出現(xiàn)問題---");
? ? ? ? ? ? ? ? release();
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? // 群聊:把自己的消息發(fā)送給其他人
? ? ? ? private void sendOthers(String msg, boolean isSys) {
? ? ? ? ? ? for (Channel other : all) {
? ? ? ? ? ? ? ? if (other == this) {
? ? ? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (!isSys) {
? ? ? ? ? ? ? ? ? ? other.send(this.name + ": " + msg); ?// 群聊消息
? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? other.send("系統(tǒng)消息:" + msg); ? ?// 系統(tǒng)消息
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? // 線程體
? ? ? ? @Override
? ? ? ? public void run() {
? ? ? ? ? ? while (isRunning) {
? ? ? ? ? ? ? ? String msg = receive();
? ? ? ? ? ? ? ? if (!msg.equals("")) {
? ? ? ? ? ? ? ? ? ? sendOthers(msg, false);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? // 釋放資源
? ? ? ? private void release() {
? ? ? ? ? ? this.isRunning = false;
? ? ? ? ? ? // 封裝
? ? ? ? ? ? Utils.close(dis, dos, client);
? ? ? ? ? ? // 退出
? ? ? ? ? ? all.remove(this);
? ? ? ? ? ? sendOthers(this.name + "離開聊天室", true);
? ? ? ? }
? ? }
}客戶端實現(xiàn)代碼
public class Client {
? ? public static void main(String[] args) throws IOException {
? ? ? ? System.out.println("-----客戶端開始工作-----");
? ? ? ? BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
? ? ? ? Socket client = new Socket("localhost", 8888);
? ? ? ? System.out.println("請輸入用戶名:"); ? ? ?// 不考慮重名
? ? ? ? String name = br.readLine();
? ? ? ? new Thread(new Send(client, name)).start();
? ? ? ? new Thread(new Receive(client)).start();
? ? }
}客戶端Send類實現(xiàn)代碼
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());
? ? ? ? ? ? // 發(fā)送名稱
? ? ? ? ? ? send(name);
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? System.out.println("Send類構(gòu)造時異常");
? ? ? ? ? ? release();
? ? ? ? }
? ? }
? ? // 從控制臺獲取消息
? ? private String getStrFromConsole() {
? ? ? ? try {
? ? ? ? ? ? return console.readLine();
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? return "";
? ? ? ? }
? ? }
? ? // 發(fā)送消息
? ? private void send(String msg) {
? ? ? ? try {
? ? ? ? ? ? dos.writeUTF(msg);
? ? ? ? ? ? dos.flush();
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? System.out.println("---客戶發(fā)送端發(fā)送消息異常---");
? ? ? ? ? ? release();
? ? ? ? }
? ? }
? ? @Override
? ? public void run() {
? ? ? ? while (isRunning) {
? ? ? ? ? ? String msg = getStrFromConsole();
? ? ? ? ? ? if (!msg.equals("")) {
? ? ? ? ? ? ? ? send(msg);
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? // 釋放資源
? ? private void release() {
? ? ? ? this.isRunning = false;
? ? ? ? Utils.close(dos,client);
? ? }
}客戶端Receive類實現(xiàn)代碼
public class Receive implements Runnable {
? ? private DataInputStream dis;
? ? private Socket client;
? ? private boolean isRunning;
? ? public Receive(Socket client) {
? ? ? ? this.client = client;
? ? ? ? try {
? ? ? ? ? ? dis = new DataInputStream(client.getInputStream());
? ? ? ? ? ? isRunning = true;
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? System.out.println("---客戶接收端構(gòu)造時異常---");
? ? ? ? ? ? release();
? ? ? ? }
? ? }
? ? // 接收消息
? ? private String receive() {
? ? ? ? String msg = "";
? ? ? ? try {
? ? ? ? ? ? msg = dis.readUTF();
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? System.out.println("---客戶接收端接收消息異常---");
? ? ? ? ? ? release();
? ? ? ? }
? ? ? ? return msg;
? ? }
? ? // 釋放資源
? ? private void release() {
? ? ? ? isRunning = false;
? ? ? ? Utils.close(dis, client);
? ? }
? ? @Override
? ? public void run() {
? ? ? ? while (isRunning) {
? ? ? ? ? ? String msg = receive();
? ? ? ? ? ? if (!msg.equals("")) {
? ? ? ? ? ? ? ? System.out.println(msg);
? ? ? ? ? ? }
? ? ? ? }
? ? }
}運行結(jié)果

4. 終極版:實現(xiàn)私聊
私聊形式:@XXX:
實現(xiàn)方法
1、boolean isPrivate = msg.startsWith("@")用于判斷是否為私聊
- 利用String類中方法
boolean startsWith(String prefix):測試此字符串是否以指定的前綴開頭
2、String targetName = msg.substring(1, index)用于判斷用戶名;msg = msg.substring(index+1)用于判斷發(fā)送的信息
- 利用String類中方法
substring(int beginIndex):返回一個字符串,該字符串是此字符串的子字符串substring(int beginIndex, int endIndex):返回一個字符串,該字符串是此字符串的子字符串
服務(wù)器端實現(xiàn)代碼
public class Chat {
? ? private static CopyOnWriteArrayList<Channel> all = new CopyOnWriteArrayList<>();
? ? public static void main(String[] args) throws IOException {
? ? ? ? System.out.println("---服務(wù)器開始工作---");
? ? ? ? ServerSocket server = new ServerSocket(8888);
? ? ? ? while (true) {
? ? ? ? ? ? Socket client = server.accept();
? ? ? ? ? ? System.out.println("一個客戶端建立了連接");
? ? ? ? ? ? Channel c = new Channel(client);
? ? ? ? ? ? all.add(c); ? ? // 管理所有的成員
? ? ? ? ? ? new Thread(c).start();
? ? ? ? }
? ? }
? ? 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("---構(gòu)造時出現(xiàn)問題---");
? ? ? ? ? ? ? ? release();
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? private String receive() {
? ? ? ? ? ? String msg = ""; ? ? ? ?// 避免空指針
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? msg = dis.readUTF();
? ? ? ? ? ? } catch (IOException e) {
? ? ? ? ? ? ? ? System.out.println("---接受消息出現(xiàn)問題---");
? ? ? ? ? ? ? ? release();
? ? ? ? ? ? }
? ? ? ? ? ? return msg;
? ? ? ? }
? ? ? ? private void send(String msg) {
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? dos.writeUTF(msg);
? ? ? ? ? ? ? ? dos.flush();
? ? ? ? ? ? } catch (IOException e) {
? ? ? ? ? ? ? ? System.out.println("---發(fā)送消息出現(xiàn)問題---");
? ? ? ? ? ? ? ? release();
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? /**
? ? ? ? ?* 群聊:把自己的消息發(fā)送給其他人
? ? ? ? ?* 私聊:約定數(shù)據(jù)格式:@XXX:msg
? ? ? ? ?* @param msg
? ? ? ? ?* @param isSys
? ? ? ? ?*/
? ? ? ? private void sendOthers(String msg, boolean isSys) {
? ? ? ? ? ? boolean isPrivate = msg.startsWith("@");
? ? ? ? ? ? if (isPrivate) { // 私聊
? ? ? ? ? ? ? ? int index = msg.indexOf(":"); ? ? ? // 第一次冒號出現(xiàn)的位置
? ? ? ? ? ? ? ? // 獲取目標(biāo)和數(shù)據(jù)
? ? ? ? ? ? ? ? String targetName = msg.substring(1, index);
? ? ? ? ? ? ? ? msg = msg.substring(index+1);
? ? ? ? ? ? ? ? for (Channel other: all) {
? ? ? ? ? ? ? ? ? ? if (other.name.equals(targetName)) { // 目標(biāo)
? ? ? ? ? ? ? ? ? ? ? ? other.send(this.name + "對您說悄悄話: " + msg); ?// 群聊消息
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? for (Channel other : all) {
? ? ? ? ? ? ? ? ? ? if (other == this) {
? ? ? ? ? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? if (!isSys) {
? ? ? ? ? ? ? ? ? ? ? ? other.send(this.name + ": " + msg); ?// 群聊消息
? ? ? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? other.send("系統(tǒng)消息:" + msg); ? ?// 系統(tǒng)消息
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? @Override
? ? ? ? public void run() {
? ? ? ? ? ? while (isRunning) {
? ? ? ? ? ? ? ? String msg = receive();
? ? ? ? ? ? ? ? if (!msg.equals("")) {
? ? ? ? ? ? ? ? ? ? sendOthers(msg, false);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? private void release() {
? ? ? ? ? ? this.isRunning = false;
? ? ? ? ? ? Utils.close(dis, dos, client);
? ? ? ? ? ? all.remove(this);
? ? ? ? ? ? sendOthers(this.name + "離開聊天室", true);
? ? ? ? }
? ? }
}客戶端實現(xiàn)代碼
public class Client {
? ? public static void main(String[] args) throws IOException {
? ? ? ? System.out.println("-----客戶端開始工作-----");
? ? ? ? BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
? ? ? ? Socket client = new Socket("localhost", 8888);
? ? ? ? System.out.println("請輸入用戶名:"); ? ? ?// 不考慮重名
? ? ? ? String name = br.readLine();
? ? ? ? new Thread(new Send(client, name)).start();
? ? ? ? new Thread(new Receive(client)).start();
? ? }
}客戶端Send類實現(xiàn)代碼
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());
? ? ? ? ? ? // 發(fā)送名稱
? ? ? ? ? ? send(name);
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? System.out.println("Send類構(gòu)造時異常");
? ? ? ? ? ? release();
? ? ? ? }
? ? }
? ? private String getStrFromConsole() {
? ? ? ? try {
? ? ? ? ? ? return console.readLine();
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? return "";
? ? ? ? }
? ? }
? ? private void send(String msg) {
? ? ? ? try {
? ? ? ? ? ? dos.writeUTF(msg);
? ? ? ? ? ? dos.flush();
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? System.out.println("---客戶發(fā)送端發(fā)送消息異常---");
? ? ? ? ? ? release();
? ? ? ? }
? ? }
? ? @Override
? ? public void run() {
? ? ? ? while (isRunning) {
? ? ? ? ? ? String msg = getStrFromConsole();
? ? ? ? ? ? if (!msg.equals("")) {
? ? ? ? ? ? ? ? send(msg);
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? private void release() {
? ? ? ? this.isRunning = false;
? ? ? ? Utils.close(dos,client);
? ? }
}客戶端Receive類實現(xiàn)代碼
public class Receive implements Runnable {
? ? private DataInputStream dis;
? ? private Socket client;
? ? private boolean isRunning;
? ? public Receive(Socket client) {
? ? ? ? this.client = client;
? ? ? ? try {
? ? ? ? ? ? dis = new DataInputStream(client.getInputStream());
? ? ? ? ? ? isRunning = true;
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? System.out.println("---客戶接收端構(gòu)造時異常---");
? ? ? ? ? ? release();
? ? ? ? }
? ? }
? ??
? ? private String receive() {
? ? ? ? String msg = "";
? ? ? ? try {
? ? ? ? ? ? msg = dis.readUTF();
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? System.out.println("---客戶接收端接收消息異常---");
? ? ? ? ? ? release();
? ? ? ? }
? ? ? ? return msg;
? ? }
? ??
? ? private void release() {
? ? ? ? isRunning = false;
? ? ? ? Utils.close(dis, client);
? ? }
? ??
? ? @Override
? ? public void run() {
? ? ? ? while (isRunning) {
? ? ? ? ? ? String msg = receive();
? ? ? ? ? ? if (!msg.equals("")) {
? ? ? ? ? ? ? ? System.out.println(msg);
? ? ? ? ? ? }
? ? ? ? }
? ? }
}Utils類實現(xiàn)代碼
public class Utils {
? ? public static void close(Closeable... targets) {
? ? ? ? for (Closeable target: targets) {
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? if (null != target) {
? ? ? ? ? ? ? ? ? ? target.close();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }catch (Exception e) {
? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? }
? ? ? ? }
? ? }
}輸出結(jié)果

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
IDEA2020 1.1中Plugins加載不出來的問題及解決方法
這篇文章主要介紹了IDEA2020 1.1中Plugins加載不出來的問題,本文還給大家提到了IDEA 2020.1.1 找不到程序包和符號的問題,感興趣的朋友跟隨小編一起看看吧2020-06-06
Java使用lambda自定義Arrays.sort排序規(guī)則說明
這篇文章主要介紹了Java使用lambda自定義Arrays.sort排序規(guī)則說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-05-05

