Java聊天室之實(shí)現(xiàn)聊天室客戶端功能
一、題目描述
題目實(shí)現(xiàn):實(shí)現(xiàn)聊天室客戶端。運(yùn)行程序,用戶登錄服務(wù)器后,可以從用戶列表中選擇單個(gè)用戶進(jìn)行聊天,也可以選擇多個(gè)用戶進(jìn)行聊天。
二、解題思路
創(chuàng)建一個(gè)服務(wù)類:ChatClientFrame,繼承JFrame類。用于進(jìn)行用戶登錄、發(fā)送聊天信息和顯示聊天信息,在該類中完成窗體界面的設(shè)計(jì)。
定義createClientSocket)方法,用于創(chuàng)建套接字對象、輸出流對象以及啟動(dòng)線程對象對服務(wù)器轉(zhuǎn)發(fā)的信息進(jìn)行處理。
定義內(nèi)部線程類ClientThread,用于對服務(wù)器端轉(zhuǎn)發(fā)的信息進(jìn)行處理,并顯示在相應(yīng)的控件中。
定義發(fā)送聊天信息的send()方法。
技術(shù)重點(diǎn):
通過線程對接收到的信息進(jìn)行處理,其中分為3種情況,第一種接收到的是登錄用戶,第二種接收到的是退出提示,第三種接收到的是消息。
使用maven-assembly-plugin插件,把引用的第三方j(luò)ar包打到項(xiàng)目的jar包中。
啟動(dòng)上一題的服務(wù)端,運(yùn)行服務(wù)端
啟動(dòng)多個(gè)客戶端:
java -jar basics100-1.0-SNAPSHOT.jar
三、代碼詳解
引入hutool的pom,和使用maven-assembly-plugin插件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.xiaoxuzhu</groupId> <artifactId>basics100</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-core</artifactId> <version>5.6.5</version> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <archive> <manifest> <!--這里要替換成jar包main方法所在類 --> <mainClass>com.xiaoxuzhu.ChatClientFrame</mainClass> </manifest> <manifestEntries> <Class-Path>.</Class-Path> </manifestEntries> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <appendAssemblyId>false</appendAssemblyId> </configuration> <executions> <execution> <id>make-assembly</id> <!-- this is used for inheritance merges --> <phase>package</phase> <!-- 指定在打包節(jié)點(diǎn)執(zhí)行jar包合并操作 --> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
ChatClientFrame
package com.xiaoxuzhu; import cn.hutool.core.io.resource.ResourceUtil; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.ObjectOutputStream; import java.net.Socket; import java.net.URL; import java.net.UnknownHostException; import java.text.DateFormat; import java.util.Date; import java.util.Vector; import javax.swing.*; /** * Description: * * @author xiaoxuzhu * @version 1.0 * * <pre> * 修改記錄: * 修改后版本 修改人 修改日期 修改內(nèi)容 * 2022/6/6.1 xiaoxuzhu 2022/6/6 Create * </pre> * @date 2022/6/6 */ public class ChatClientFrame extends JFrame { private JTextField tf_newUser; private JList user_list; private JTextArea ta_info; private JTextField tf_send; private ObjectOutputStream out;// 聲明輸出流對象 private Socket socket; private boolean loginFlag = false;// 為true時(shí)表示已經(jīng)登錄,為false時(shí)表示未登錄 public static void main(String args[]) { System.out.println(SwingUtilities.isEventDispatchThread()); EventQueue.invokeLater(new Runnable() { public void run() { System.out.println(SwingUtilities.isEventDispatchThread()); try { ChatClientFrame frame = new ChatClientFrame(); frame.setVisible(true); frame.createClientSocket();// 調(diào)用方法創(chuàng)建套接字對象 } catch (Exception e) { e.printStackTrace(); } } }); } public void createClientSocket() { try { socket = new Socket("127.0.0.1", 9527);// 創(chuàng)建套接字對象 out = new ObjectOutputStream(socket.getOutputStream());// 創(chuàng)建輸出流對象 SwingWorker<Void,Void> worker=new SwingWorker<Void, Void>() { @Override protected Void doInBackground() throws Exception { try { System.out.println(SwingUtilities.isEventDispatchThread()); BufferedReader in = new BufferedReader(new InputStreamReader( socket.getInputStream()));// 創(chuàng)建輸入流對象 DefaultComboBoxModel model = (DefaultComboBoxModel) user_list .getModel();// 獲得列表框的模型 while (true) { String info = in.readLine().trim();// 讀取信息 if (!info.startsWith("MSG:")) {// 接收到的不是消息 if (info.startsWith("退出:")) {// 接收到的是退出消息 model.removeElement(info.substring(3));// 從用戶列表中移除用戶 } else {// 接收到的是登錄用戶 boolean itemFlag = false;// 標(biāo)記是否為列表框添加列表項(xiàng),為true不添加,為false添加 for (int i = 0; i < model.getSize(); i++) {// 對用戶列表進(jìn)行遍歷 if (info.equals((String) model.getElementAt(i))) {// 如果用戶列表中存在該用戶名 itemFlag = true;// 設(shè)置為true,表示不添加到用戶列表 break;// 結(jié)束for循環(huán) } } if (!itemFlag) { model.addElement(info);// 將登錄用戶添加到用戶列表 } } } else {// 如果獲得的是消息,則在文本域中顯示接收到的消息 DateFormat df = DateFormat.getDateInstance();// 獲得DateFormat實(shí)例 String dateString = df.format(new Date()); // 格式化為日期 df = DateFormat.getTimeInstance(DateFormat.MEDIUM);// 獲得DateFormat實(shí)例 String timeString = df.format(new Date()); // 格式化為時(shí)間 String sendUser = info.substring(4,info.indexOf(":發(fā)送給:"));// 獲得發(fā)送信息的用戶 String receiveInfo = info.substring(info.indexOf(":的信息是:")+6);// 獲得接收到的信息 ta_info.append(" "+sendUser + " " +dateString+" "+timeString+"\n "+receiveInfo+"\n");// 在文本域中顯示信息 ta_info.setSelectionStart(ta_info.getText().length()-1);// 設(shè)置選擇起始位置 ta_info.setSelectionEnd(ta_info.getText().length());// 設(shè)置選擇的結(jié)束位置 tf_send.requestFocus();// 使發(fā)送信息文本框獲得焦點(diǎn) } } } catch (IOException e) { e.printStackTrace(); } return null; } }; worker.execute(); //new ClientThread(socket).start();// 創(chuàng)建并啟動(dòng)線程對象 } 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() { } } private void send() { if (!loginFlag) { JOptionPane.showMessageDialog(null, "請先登錄。"); return;// 如果用戶沒登錄則返回 } String sendUserName = tf_newUser.getText().trim();// 獲得登錄用戶名 String info = tf_send.getText();// 獲得輸入的發(fā)送信息 if (info.equals("")) { return;// 如果沒輸入信息則返回,即不發(fā)送 } Vector<String> v = new Vector<String>();// 創(chuàng)建向量對象,用于存儲(chǔ)發(fā)送的消息 Object[] receiveUserNames = user_list.getSelectedValues();// 獲得選擇的用戶數(shù)組 if (receiveUserNames.length <= 0) { return;// 如果沒選擇用戶則返回 } for (int i = 0; i < receiveUserNames.length; i++) { String msg = sendUserName + ":發(fā)送給:" + (String) receiveUserNames[i] + ":的信息是: " + info;// 定義發(fā)送的信息 v.add(msg);// 將信息添加到向量 } try { out.writeObject(v);// 將向量寫入輸出流,完成信息的發(fā)送 out.flush();// 刷新輸出緩沖區(qū) } catch (IOException e) { e.printStackTrace(); } DateFormat df = DateFormat.getDateInstance();// 獲得DateFormat實(shí)例 String dateString = df.format(new Date()); // 格式化為日期 df = DateFormat.getTimeInstance(DateFormat.MEDIUM);// 獲得DateFormat實(shí)例 String timeString = df.format(new Date()); // 格式化為時(shí)間 String sendUser = tf_newUser.getText().trim();// 獲得發(fā)送信息的用戶 String receiveInfo = tf_send.getText().trim();// 顯示發(fā)送的信息 ta_info.append(" "+sendUser + " " +dateString+" "+timeString+"\n "+receiveInfo+"\n");// 在文本域中顯示信息 tf_send.setText(null);// 清空文本框 ta_info.setSelectionStart(ta_info.getText().length()-1);// 設(shè)置選擇的起始位置 ta_info.setSelectionEnd(ta_info.getText().length());// 設(shè)置選擇的結(jié)束位置 tf_send.requestFocus();// 使發(fā)送信息文本框獲得焦點(diǎn) } /** * Create the frame */ public ChatClientFrame() { super(); setTitle("聊天室客戶端"); setBounds(100, 100, 385, 288); 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 JSplitPane splitPane = new JSplitPane(); splitPane.setDividerLocation(100); getContentPane().add(splitPane, BorderLayout.CENTER); final JScrollPane scrollPane = new JScrollPane(); splitPane.setRightComponent(scrollPane); ta_info = new JTextArea(); ta_info.setFont(new Font("", Font.BOLD, 14)); scrollPane.setViewportView(ta_info); final JScrollPane scrollPane_1 = new JScrollPane(); splitPane.setLeftComponent(scrollPane_1); user_list = new JList(); user_list.setModel(new DefaultComboBoxModel(new String[] { "" })); scrollPane_1.setViewportView(user_list); final JPanel panel_1 = new JPanel(); getContentPane().add(panel_1, BorderLayout.NORTH); final JLabel label_1 = new JLabel(); label_1.setText("用戶名稱:"); panel_1.add(label_1); tf_newUser = new JTextField(); tf_newUser.setPreferredSize(new Dimension(140, 25)); panel_1.add(tf_newUser); final JButton button_1 = new JButton(); button_1.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { if (loginFlag) {// 已登錄標(biāo)記為true JOptionPane.showMessageDialog(null, "在同一窗口只能登錄一次。"); return; } String userName = tf_newUser.getText().trim();// 獲得登錄用戶名 Vector v = new Vector();// 定義向量,用于存儲(chǔ)登錄用戶 v.add("用戶:" + userName);// 添加登錄用戶 try { out.writeObject(v);// 將用戶向量發(fā)送到服務(wù)器 out.flush();// 刷新輸出緩沖區(qū) } catch (IOException ex) { ex.printStackTrace(); } tf_newUser.setEnabled(false);// 禁用用戶文本框 loginFlag = true;// 將已登錄標(biāo)記設(shè)置為true } }); button_1.setText("登 錄"); panel_1.add(button_1); final JButton button_2 = new JButton(); button_2.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { String exitUser = tf_newUser.getText().trim(); Vector v = new Vector(); v.add("退出:" + exitUser); try { out.writeObject(v); out.flush();// 刷新輸出緩沖區(qū) } catch (IOException ex) { ex.printStackTrace(); } System.exit(0); // 退出系統(tǒng) } }); button_2.setText("退 出"); panel_1.add(button_2); //托盤 if (SystemTray.isSupported()){ // 判斷是否支持系統(tǒng)托盤 URL url= ResourceUtil.getResource("client.png",null); // 獲取圖片所在的URL ImageIcon icon = new ImageIcon(url); // 實(shí)例化圖像對象 Image image=icon.getImage(); // 獲得Image對象 TrayIcon trayIcon=new TrayIcon(image); // 創(chuàng)建托盤圖標(biāo) trayIcon.addMouseListener(new MouseAdapter(){ // 為托盤添加鼠標(biāo)適配器 public void mouseClicked(MouseEvent e){ // 鼠標(biāo)事件 if (e.getClickCount()==2){ // 判斷是否雙擊了鼠標(biāo) showFrame(); // 調(diào)用方法顯示窗體 } } }); trayIcon.setToolTip("系統(tǒng)托盤"); // 添加工具提示文本 PopupMenu popupMenu=new PopupMenu(); // 創(chuàng)建彈出菜單 MenuItem exit=new MenuItem("退出"); // 創(chuàng)建菜單項(xiàng) exit.addActionListener(new ActionListener() { // 添加事件監(jiān)聽器 public void actionPerformed(final ActionEvent arg0) { String exitUser = tf_newUser.getText().trim(); Vector v = new Vector(); v.add("退出:" + exitUser); try { out.writeObject(v); out.flush();// 刷新輸出緩沖區(qū) } catch (IOException ex) { ex.printStackTrace(); } System.exit(0); // 退出系統(tǒng) } }); popupMenu.add(exit); // 為彈出菜單添加菜單項(xiàng) trayIcon.setPopupMenu(popupMenu); // 為托盤圖標(biāo)加彈出菜彈 SystemTray systemTray=SystemTray.getSystemTray(); // 獲得系統(tǒng)托盤對象 try{ systemTray.add(trayIcon); // 為系統(tǒng)托盤加托盤圖標(biāo) }catch(Exception e){ e.printStackTrace(); } } } public void showFrame(){ this.setVisible(true); // 顯示窗體 this.setState(Frame.NORMAL); } }
上一題服務(wù)端啟動(dòng):
第一個(gè)客戶端:小小 啟動(dòng)
第二個(gè)客戶端:虛虛 啟動(dòng)
第三個(gè)客戶端:竹竹 啟動(dòng)
小小給竹竹發(fā)消息
竹竹回復(fù)小小:
小小收到回復(fù):
以上就是Java聊天室之實(shí)現(xiàn)聊天室客戶端功能的詳細(xì)內(nèi)容,更多關(guān)于Java聊天室的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot項(xiàng)目導(dǎo)入外部jar包的詳細(xì)指南
在開發(fā)SpringBoot項(xiàng)目時(shí),我們經(jīng)常需要引入一些外部的jar包來增強(qiáng)項(xiàng)目的功能,這些jar包可能不是Maven中央倉庫中的,或者我們想要使用特定版本的jar包,本文將詳細(xì)介紹如何在SpringBoot項(xiàng)目中導(dǎo)入外部jar包,需要的朋友可以參考下2024-10-10Java多線程編程小實(shí)例模擬停車場系統(tǒng)
這是一個(gè)關(guān)于Java多線程編程的例子,用多線程的思想模擬停車場管理系統(tǒng),這里分享給大家,供需要的朋友參考。2017-10-10Object.wait()與Object.notify()的用法詳細(xì)解析
以下是對java中Object.wait()與Object.notify()的用法進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過來參考下2013-09-09Java用20行代碼實(shí)現(xiàn)抖音小視頻批量轉(zhuǎn)換為gif動(dòng)態(tài)圖
這篇文章主要介紹了Java用20行代碼實(shí)現(xiàn)抖音小視頻批量轉(zhuǎn)換為gif動(dòng)態(tài)圖,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04初識(shí)Java基礎(chǔ)之?dāng)?shù)據(jù)類型與運(yùn)算符
Java是一種強(qiáng)類型語言,每個(gè)變量都必須聲明其數(shù)據(jù)類型,下面這篇文章主要給大家介紹了關(guān)于Java基礎(chǔ)之?dāng)?shù)據(jù)類型與運(yùn)算符的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-10-10Java實(shí)現(xiàn)高并發(fā)秒殺的七種方式
本文主要介紹了Java實(shí)現(xiàn)高并發(fā)秒殺的六種方式,包括使用緩存、數(shù)據(jù)庫樂觀鎖、數(shù)據(jù)庫悲觀鎖、分布式鎖、隊(duì)列限流、令牌桶算法和限流器,具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03如何利用反射生成?MyBatisPlus中QueryWrapper動(dòng)態(tài)條件
這篇文章主要介紹了如何利用反射生成?MyBatisPlus中QueryWrapper動(dòng)態(tài)條件,分享在MyBatisPlus中經(jīng)常會(huì)用到代碼來構(gòu)造查詢條件等內(nèi)容,需要的小伙伴可以參考一下2022-02-02