Java聊天室之實(shí)現(xiàn)客戶(hù)端群聊功能
一、題目描述
題目實(shí)現(xiàn):不同的客戶(hù)端之間需要進(jìn)行通信,一個(gè)客戶(hù)端與其他的多個(gè)客戶(hù)端進(jìn)行通信,實(shí)現(xiàn)群聊功能。
實(shí)現(xiàn)一個(gè)客戶(hù)端與其他多個(gè)客戶(hù)端進(jìn)行通信,運(yùn)行程序,服務(wù)器啟動(dòng)后,啟動(dòng)3個(gè)客戶(hù)端程序,然后通過(guò)第一個(gè)客戶(hù)端向另外兩個(gè)客戶(hù)端發(fā)送信息,則另外的兩個(gè)客戶(hù)端都會(huì)收到服務(wù)器發(fā)送的信息。
二、解題思路
創(chuàng)建一個(gè)服務(wù)類(lèi):ClientOneToManyServerFrame,繼承JFrame類(lèi)
定義一個(gè)createSocket()方法,用于創(chuàng)建服務(wù)Socket和監(jiān)聽(tīng)客戶(hù)端程序。以及創(chuàng)建并啟動(dòng)線(xiàn)程對(duì)象并將接收到的客戶(hù)端發(fā)送的信息轉(zhuǎn)發(fā)給其他客戶(hù)端。
創(chuàng)建一個(gè)客戶(hù)端類(lèi):ClientOneToManyClientFrame,繼承JFrame類(lèi)
定義一個(gè)createClientSocket()方法,用于創(chuàng)建與服務(wù)器連接的Socket對(duì)象,輸出流對(duì)象,以及啟動(dòng)線(xiàn)程對(duì)象接收服務(wù)器端轉(zhuǎn)發(fā)的信息。
技術(shù)重點(diǎn):
在服務(wù)器端通過(guò)線(xiàn)程對(duì)客戶(hù)端發(fā)送的信息進(jìn)行監(jiān)聽(tīng),當(dāng)有客戶(hù)端發(fā)送信息時(shí),就會(huì)將該信息發(fā)送給其他已經(jīng)登錄到服務(wù)器的客戶(hù)端,但是不會(huì)向發(fā)送方發(fā)送該信息,在客戶(hù)端也通過(guò)線(xiàn)程來(lái)監(jiān)聽(tīng)服務(wù)器轉(zhuǎn)發(fā)的信息。 (1)在服務(wù)器端創(chuàng)建線(xiàn)程類(lèi)ServerThread,用于接收客戶(hù)端發(fā)送的信息,并轉(zhuǎn)發(fā)給其他已經(jīng)連接到服務(wù)器的客戶(hù)端。
(2)在客戶(hù)端創(chuàng)建線(xiàn)程類(lèi)ClientThread,用于接收服務(wù)器端轉(zhuǎn)發(fā)的客戶(hù)端信息,并在客戶(hù)端的文本域中顯示接收到的信息。
啟動(dòng)多個(gè)客戶(hù)端:
1、把項(xiàng)目打成jar包:利用maven 的clean install
會(huì)在target目錄下生成jar包
2、進(jìn)入target目錄,使用java -cp的命令運(yùn)行指定的類(lèi)
java -cp 命令中 cp 指的就是classpath。使用該命令可以運(yùn)行jar中的某個(gè)指定的類(lèi)(要包含全路徑的包名)
進(jìn)入cmd命令模式
運(yùn)行服務(wù)端
java -cp basics98-1.0-SNAPSHOT.jar com.xiaoxuzhu.ClientOneToManyServerFrame
運(yùn)行多個(gè)客戶(hù)端
java -cp basics98-1.0-SNAPSHOT.jar com.xiaoxuzhu.ClientOneToManyClientFrame
三、代碼詳解
ClientOneToManyServerFrame
package com.xiaoxuzhu; import java.awt.BorderLayout; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.Vector; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTextArea; /** * Description: * * @author xiaoxuzhu * @version 1.0 * * <pre> * 修改記錄: * 修改后版本 修改人 修改日期 修改內(nèi)容 * 2022/6/5.1 xiaoxuzhu 2022/6/5 Create * </pre> * @date 2022/6/5 */ public class ClientOneToManyServerFrame extends JFrame { private JTextArea ta_info; private ServerSocket server; // 聲明ServerSocket對(duì)象 private Socket socket; // 聲明Socket對(duì)象socket private Vector<Socket> vector = new Vector<Socket>();// 用于存儲(chǔ)連接到服務(wù)器的客戶(hù)端套接字對(duì)象 public void createSocket() { try { server = new ServerSocket(9527); while (true) { ta_info.append("等待新客戶(hù)連接......\n"); socket = server.accept();// 創(chuàng)建套接字對(duì)象 vector.add(socket);// 將套接字對(duì)象添加到向量對(duì)象中 ta_info.append("客戶(hù)端連接成功。" + socket + "\n"); new ServerThread(socket).start();// 創(chuàng)建并啟動(dòng)線(xiàn)程對(duì)象 } } catch (IOException e) { e.printStackTrace(); } } class ServerThread extends Thread { Socket socket; public ServerThread(Socket socket) { this.socket = socket; } public void run() { try { BufferedReader in = new BufferedReader(new InputStreamReader( socket.getInputStream()));// 創(chuàng)建輸入流對(duì)象 while (true) { String info = in.readLine();// 讀取信息 for (Socket s : vector) {// 遍歷所有客戶(hù)端套接字對(duì)象 if (s != socket) {// 如果不是發(fā)送信息的套接字對(duì)象 PrintWriter out = new PrintWriter(s .getOutputStream(), true);// 創(chuàng)建輸出流對(duì)象 out.println(info);// 發(fā)送信息 out.flush();// 刷新輸出緩沖區(qū) } } } } catch (IOException e) { ta_info.append(socket + "已經(jīng)退出。\n"); vector.remove(socket);// 移除退出的客戶(hù)端套接字 } } } /** * Launch the application * * @param args */ public static void main(String args[]) { ClientOneToManyServerFrame frame = new ClientOneToManyServerFrame(); frame.setVisible(true); frame.createSocket(); } /** * Create the frame */ public ClientOneToManyServerFrame() { super(); setTitle("客戶(hù)端一對(duì)多通信——服務(wù)器端程序"); setBounds(100, 100, 385, 266); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); final JScrollPane scrollPane = new JScrollPane(); getContentPane().add(scrollPane, BorderLayout.CENTER); ta_info = new JTextArea(); scrollPane.setViewportView(ta_info); } }
ClientOneToManyClientFrame
package com.xiaoxuzhu; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.net.UnknownHostException; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; /** * Description: * * @author xiaoxuzhu * @version 1.0 * * <pre> * 修改記錄: * 修改后版本 修改人 修改日期 修改內(nèi)容 * 2022/6/5.1 xiaoxuzhu 2022/6/5 Create * </pre> * @date 2022/6/5 */ public class ClientOneToManyClientFrame extends JFrame{ private JTextArea ta_info; private JTextField tf_send; PrintWriter out;// 聲明輸出流對(duì)象 /** * Launch the application * * @param args */ public static void main(String args[]) { EventQueue.invokeLater(new Runnable() { public void run() { try { ClientOneToManyClientFrame frame = new ClientOneToManyClientFrame(); frame.setVisible(true); frame.createClientSocket(); } catch (Exception e) { e.printStackTrace(); } } }); } public void createClientSocket() { try { Socket socket = new Socket("127.0.0.1", 9527);// 創(chuàng)建套接字對(duì)象 out = new PrintWriter(socket.getOutputStream(), true);// 創(chuàng)建輸出流對(duì)象 new ClientThread(socket).start();// 創(chuàng)建并啟動(dòng)線(xiàn)程對(duì)象 } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } class ClientThread extends Thread { Socket socket; public ClientThread(Socket socket) { this.socket = socket; } public void run() { try { BufferedReader in = new BufferedReader(new InputStreamReader( socket.getInputStream()));// 創(chuàng)建輸入流對(duì)象 while (true) { String info = in.readLine();// 讀取信息 ta_info.append(info + "\n");// 在文本域中顯示信息 if (info.equals("88")) { break;// 結(jié)束線(xiàn)程 } } } catch (IOException e) { e.printStackTrace(); } } } private void send() { String info = tf_send.getText();// 獲得輸入的信息 if (info.equals("")) { return;// 如果沒(méi)輸入信息則返回,即不發(fā)送 } if (info.equals("88")) { System.exit(0);// 如果沒(méi)輸入信息是88,則退出 } out.println(info);// 發(fā)送信息 out.flush();// 刷新輸出緩沖區(qū) tf_send.setText(null);// 清空文本框 } /** * Create the frame */ public ClientOneToManyClientFrame() { super(); setTitle("客戶(hù)端一對(duì)多通信——客戶(hù)端程序"); setBounds(100, 100, 385, 266); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); final JPanel panel = new JPanel(); getContentPane().add(panel, BorderLayout.SOUTH); final JLabel label = new JLabel(); label.setText("輸入聊天內(nèi)容:"); panel.add(label); tf_send = new JTextField(); tf_send.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { send();// 調(diào)用方法發(fā)送信息 } }); tf_send.setPreferredSize(new Dimension(180, 25)); panel.add(tf_send); final JButton button = new JButton(); button.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { send();// 調(diào)用方法發(fā)送信息 } }); button.setText("發(fā) 送"); panel.add(button); final JScrollPane scrollPane = new JScrollPane(); getContentPane().add(scrollPane, BorderLayout.CENTER); ta_info = new JTextArea(); scrollPane.setViewportView(ta_info); // } }
服務(wù)器啟動(dòng)
客戶(hù)端1向其他客戶(hù)端群發(fā)信息
客戶(hù)端2接收到的信息
客戶(hù)端3接收到的信息
以上就是Java聊天室之實(shí)現(xiàn)客戶(hù)端群聊功能的詳細(xì)內(nèi)容,更多關(guān)于Java聊天室的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解如何使用MongoDB+Springboot實(shí)現(xiàn)分布式ID的方法
這篇文章主要介紹了詳解如何使用MongoDB+Springboot實(shí)現(xiàn)分布式ID的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09IDEA 當(dāng)前在線(xiàn)人數(shù)和歷史訪(fǎng)問(wèn)量的示例代碼
這篇文章主要介紹了IDEA 當(dāng)前在線(xiàn)人數(shù)和歷史訪(fǎng)問(wèn)量的實(shí)例代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08java匿名內(nèi)部類(lèi)實(shí)例代碼詳解
這篇文章主要介紹了java匿名內(nèi)部類(lèi)實(shí)例代碼詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12SpringBoot+Redis防止惡意刷新與暴力請(qǐng)求接口的實(shí)現(xiàn)
這篇文章主要為大家介紹了如何利用springboot和Redis來(lái)實(shí)現(xiàn)防止惡意刷新與暴力請(qǐng)求接口,文中的示例代碼講解詳細(xì),需要的可以參考一下2022-06-06淺談MyBatisPlus中LocalDateTime引發(fā)的一些問(wèn)題和解決辦法
MyBatisPlus進(jìn)行數(shù)據(jù)庫(kù)操作時(shí),我們經(jīng)常會(huì)遇到處理日期時(shí)間類(lèi)型的需求,本文主要介紹了淺談MyBatisPlus中LocalDateTime引發(fā)的一些問(wèn)題和解決辦法,具有一定的參考價(jià)值,感興趣的可以了解一下2024-07-07