Java實(shí)現(xiàn)在線聊天室(層層遞進(jìn))
本文實(shí)例為大家分享了Java實(shí)現(xiàn)在線聊天室的具體代碼,供大家參考,具體內(nèi)容如下
- 本文講述了從實(shí)現(xiàn)單個(gè)多個(gè)客戶的收發(fā)信息(基礎(chǔ)簡易版),到各種實(shí)現(xiàn)代碼的封裝(oop版),實(shí)現(xiàn)群聊(群聊過渡版),到最后實(shí)現(xiàn)私聊(終極版)的過程
- 本文內(nèi)容是在初步學(xué)習(xí)網(wǎng)絡(luò)編程時(shí),練習(xí)強(qiáng)化記憶時(shí)的學(xué)習(xí)總結(jié)
- 主要利用了TCP網(wǎng)絡(luò)編程和多線程
- 如有問題,歡迎指出
綜合案例:在線聊天室
需求:使用TCP的Socket實(shí)現(xiàn)一個(gè)聊天室
- 服務(wù)器端:一個(gè)線程專門發(fā)送消息,一個(gè)線程專門接收消息
- 客戶端:一個(gè)線程專門發(fā)送消息,一個(gè)線程專門接收消息
1. 基礎(chǔ)簡易版
1.1 一個(gè)客戶收發(fā)多條消息
目標(biāo):實(shí)現(xiàn)一個(gè)客戶可以正常收發(fā)多條信息
服務(wù)器
/** ?* 在線聊天室: 服務(wù)器 ?* 目標(biāo):實(shí)現(xiàn)一個(gè)客戶可以正常收發(fā)多條消息 ?* ?服務(wù)器不生產(chǎn)內(nèi)容,相當(dāng)于一個(gè)轉(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("一個(gè)客戶端建立了連接"); ? ? ? ? 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):實(shí)現(xiàn)一個(gè)客戶可以正常收發(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 多個(gè)客戶收發(fā)多條消息(不使用多線程)
- 目標(biāo):實(shí)現(xiàn)多個(gè)客戶可以正常收發(fā)多條信息
- 出現(xiàn)排隊(duì)問題:其他客戶必須等待之前的客戶退出,才能收發(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("一個(gè)客戶端建立了連接"); ? ? ? ? ? ? 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 多個(gè)客戶收發(fā)多條消息(多線程)
- 目標(biāo):實(shí)現(xiàn)多個(gè)客戶可以正常收發(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("一個(gè)客戶端建立了連接"); ? ? ? ? ? ? // 加入多線程 ? ? ? ? ? ? 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):封裝使用多線程實(shí)現(xiàn)多個(gè)客戶可以正常收發(fā)多條消息
- 1、線程代理 Channel,一個(gè)客戶代表一個(gè) Channel
- 2、實(shí)現(xiàn)方法:接收消息 - receive; 發(fā)送消息 - send; 釋放資源 - release;
- 3、其中釋放資源 release方法中利用工具類 Utils:實(shí)現(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("一個(gè)客戶端建立了連接"); ? ? ? ? ? ? new Thread(new Channel(client)).start(); ? ? ? ? } ? ? } ? ? // 一個(gè)客戶代表一個(gè)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)造時(shí)出現(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:實(shí)現(xiàn)Closeable接口,利用可變參數(shù),達(dá)到釋放資源的作用
示例代碼
public class Utils { ? ? public static void close(Closeable... targets) { ? ? ? ? ? ? // Closeable是IO流中接口,"..."可變參數(shù) ? ? // IO流和Socket都實(shí)現(xiàn)了Closeable接口,可以直接用 ? ? ? ? for (Closeable target: targets) { ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? // 只要是釋放資源就要加入空判斷 ? ? ? ? ? ? ? ? if (null != target) { ? ? ? ? ? ? ? ? ? ? target.close(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? }catch (Exception e) { ?? ??? ??? ??? ?e.printStackTrace(); ? ? ? ? ? ? } ? ? ? ? } ? ? } }
客戶端:啟用兩個(gè)線程Send和Receive實(shí)現(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類
實(shí)現(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)造時(shí)異常---"); ? ? ? ? ? ? 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類
實(shí)現(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)造時(shí)異常---"); ? ? ? ? ? ? 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):加入容器,實(shí)現(xiàn)群聊
服務(wù)器端
1、建立 CopyOnWriteArrayList<Channel> 容器,容器中的元素是Channel客戶端代理。要對元素進(jìn)行修改同時(shí)遍歷時(shí),推薦使用此容器,避免出問題。
2、實(shí)現(xiàn)方法void sendOthers(String msg)將信息發(fā)送給除自己外的其他人。
服務(wù)器端實(shí)現(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("一個(gè)客戶端建立了連接"); ? ? ? ? ? ? Channel c = new Channel(client); ? ? ? ? ? ? all.add(c); ? ? // 管理所有的成員 ? ? ? ? ? ? new Thread(c).start(); ? ? ? ? } ? ? } ? ? // 一個(gè)客戶代表一個(gè)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)造時(shí)出現(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); ? ? ? ? } ? ? } }
客戶端實(shí)現(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類實(shí)現(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)造時(shí)異常"); ? ? ? ? ? ? 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類實(shí)現(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)造時(shí)異常---"); ? ? ? ? ? ? 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); ? ? ? ? ? ? } ? ? ? ? } ? ? } }
運(yùn)行結(jié)果
4. 終極版:實(shí)現(xiàn)私聊
私聊形式:@XXX:
實(shí)現(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):
返回一個(gè)字符串,該字符串是此字符串的子字符串substring(int beginIndex, int endIndex):
返回一個(gè)字符串,該字符串是此字符串的子字符串
服務(wù)器端實(shí)現(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("一個(gè)客戶端建立了連接"); ? ? ? ? ? ? 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)造時(shí)出現(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); ? ? ? ? } ? ? } }
客戶端實(shí)現(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類實(shí)現(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)造時(shí)異常"); ? ? ? ? ? ? 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類實(shí)現(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)造時(shí)異常---"); ? ? ? ? ? ? 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類實(shí)現(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í)有所幫助,也希望大家多多支持腳本之家。
- java實(shí)現(xiàn)在線聊天系統(tǒng)
- Java實(shí)現(xiàn)在線聊天功能
- Java網(wǎng)絡(luò)編程UDP實(shí)現(xiàn)多線程在線聊天
- Java網(wǎng)絡(luò)編程實(shí)例——簡單模擬在線聊天
- Java使用TCP實(shí)現(xiàn)在線聊天的示例代碼
- Java GUI編程實(shí)現(xiàn)在線聊天室
- Java中使用websocket實(shí)現(xiàn)在線聊天功能
- java Socket實(shí)現(xiàn)網(wǎng)頁版在線聊天
- 基于Java Socket實(shí)現(xiàn)一個(gè)簡易在線聊天功能(一)
- java基于UDP實(shí)現(xiàn)在線聊天功能
相關(guān)文章
JAVA實(shí)現(xiàn)redis分布式雙重加鎖的示例代碼
在高并發(fā)環(huán)境下,通過Redis分布式鎖實(shí)現(xiàn)數(shù)據(jù)唯一性校驗(yàn)非常關(guān)鍵,為避免用戶數(shù)據(jù)重復(fù),可使用Redis鎖或集合數(shù)據(jù)結(jié)構(gòu)進(jìn)行前置檢查,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-10-10IDEA2020 1.1中Plugins加載不出來的問題及解決方法
這篇文章主要介紹了IDEA2020 1.1中Plugins加載不出來的問題,本文還給大家提到了IDEA 2020.1.1 找不到程序包和符號的問題,感興趣的朋友跟隨小編一起看看吧2020-06-06Java實(shí)現(xiàn)JDK動態(tài)代理的原理詳解
這篇文章主要介紹了Java實(shí)現(xiàn)JDK動態(tài)代理的原理詳解,Java常用的動態(tài)代理模式有JDK動態(tài)代理,也有cglib動態(tài)代理,本文重點(diǎn)講解JDK的動態(tài)代理,需要的小伙伴可以參考一下的相關(guān)資料2022-07-07Java設(shè)計(jì)模式常用的七大原則總結(jié)
今天給大家總結(jié)了Java設(shè)計(jì)模式的七大原則,主要有單一職責(zé)原則,接口隔離原則,依賴倒轉(zhuǎn)原則,里氏替換原則等,文中有非常詳細(xì)的介紹,需要的朋友可以參考下2021-06-06Java使用lambda自定義Arrays.sort排序規(guī)則說明
這篇文章主要介紹了Java使用lambda自定義Arrays.sort排序規(guī)則說明,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05