Java聊天室之實現(xiàn)聊天室客戶端功能
一、題目描述
題目實現(xiàn):實現(xiàn)聊天室客戶端。運行程序,用戶登錄服務(wù)器后,可以從用戶列表中選擇單個用戶進(jìn)行聊天,也可以選擇多個用戶進(jìn)行聊天。
二、解題思路
創(chuàng)建一個服務(wù)類:ChatClientFrame,繼承JFrame類。用于進(jìn)行用戶登錄、發(fā)送聊天信息和顯示聊天信息,在該類中完成窗體界面的設(shè)計。
定義createClientSocket)方法,用于創(chuàng)建套接字對象、輸出流對象以及啟動線程對象對服務(wù)器轉(zhuǎn)發(fā)的信息進(jìn)行處理。
定義內(nèi)部線程類ClientThread,用于對服務(wù)器端轉(zhuǎn)發(fā)的信息進(jìn)行處理,并顯示在相應(yīng)的控件中。
定義發(fā)送聊天信息的send()方法。
技術(shù)重點:
通過線程對接收到的信息進(jìn)行處理,其中分為3種情況,第一種接收到的是登錄用戶,第二種接收到的是退出提示,第三種接收到的是消息。
使用maven-assembly-plugin插件,把引用的第三方j(luò)ar包打到項目的jar包中。
啟動上一題的服務(wù)端,運行服務(wù)端

啟動多個客戶端:
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é)點執(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時表示已經(jīng)登錄,為false時表示未登錄
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)記是否為列表框添加列表項,為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實例
String dateString = df.format(new Date()); // 格式化為日期
df = DateFormat.getTimeInstance(DateFormat.MEDIUM);// 獲得DateFormat實例
String timeString = df.format(new Date()); // 格式化為時間
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ā)送信息文本框獲得焦點
}
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
};
worker.execute();
//new ClientThread(socket).start();// 創(chuà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)建向量對象,用于存儲發(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實例
String dateString = df.format(new Date()); // 格式化為日期
df = DateFormat.getTimeInstance(DateFormat.MEDIUM);// 獲得DateFormat實例
String timeString = df.format(new Date()); // 格式化為時間
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ā)送信息文本框獲得焦點
}
/**
* 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();// 定義向量,用于存儲登錄用戶
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); // 實例化圖像對象
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)建菜單項
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); // 為彈出菜單添加菜單項
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ù)端啟動:

第一個客戶端:小小 啟動

第二個客戶端:虛虛 啟動

第三個客戶端:竹竹 啟動

小小給竹竹發(fā)消息

竹竹回復(fù)小小:

小小收到回復(fù):

以上就是Java聊天室之實現(xiàn)聊天室客戶端功能的詳細(xì)內(nèi)容,更多關(guān)于Java聊天室的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot項目導(dǎo)入外部jar包的詳細(xì)指南
在開發(fā)SpringBoot項目時,我們經(jīng)常需要引入一些外部的jar包來增強項目的功能,這些jar包可能不是Maven中央倉庫中的,或者我們想要使用特定版本的jar包,本文將詳細(xì)介紹如何在SpringBoot項目中導(dǎo)入外部jar包,需要的朋友可以參考下2024-10-10
Object.wait()與Object.notify()的用法詳細(xì)解析
以下是對java中Object.wait()與Object.notify()的用法進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過來參考下2013-09-09
Java用20行代碼實現(xiàn)抖音小視頻批量轉(zhuǎn)換為gif動態(tài)圖
這篇文章主要介紹了Java用20行代碼實現(xiàn)抖音小視頻批量轉(zhuǎn)換為gif動態(tài)圖,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
初識Java基礎(chǔ)之?dāng)?shù)據(jù)類型與運算符
Java是一種強類型語言,每個變量都必須聲明其數(shù)據(jù)類型,下面這篇文章主要給大家介紹了關(guān)于Java基礎(chǔ)之?dāng)?shù)據(jù)類型與運算符的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-10-10
如何利用反射生成?MyBatisPlus中QueryWrapper動態(tài)條件
這篇文章主要介紹了如何利用反射生成?MyBatisPlus中QueryWrapper動態(tài)條件,分享在MyBatisPlus中經(jīng)常會用到代碼來構(gòu)造查詢條件等內(nèi)容,需要的小伙伴可以參考一下2022-02-02

