Java聊天室之實現聊天室服務端功能
一、題目描述
題目實現:實現聊天室服務器端功能。運行程序,服務端等待客戶端連接,并顯示客戶端的連接信息。
二、解題思路
創(chuàng)建一個服務類:ChatServerFrame,繼承JFrame類
定義一個Hashtable對象,用于存儲登錄用戶的用戶名和套接字對象。
定義createSocket()方法,用于創(chuàng)建服務器套接字對象、獲得連接到服務器的客戶端套接字對象以及啟動線程對象對客戶端發(fā)送的信息進行處理。
定義內部線程類ServerThread用于對客戶端的連接信息以及發(fā)送的信息進行處理和轉發(fā)。
技術重點:
本實例使用Hashtable類來存儲連接到服務器的用戶名和套接字對象,并使用String類的 startWith()方法判斷客戶端發(fā)送信息的類型,從而實現了向服務器端添加登錄用戶、發(fā)送退出信息、通過服務器轉發(fā)客戶端發(fā)送的信息等功能,最終完成了聊天室服務器端程序的開發(fā)。
三、代碼詳解
引入hutool的pom
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-core</artifactId> <version>5.6.5</version> </dependency>
ChatServerFrame
package com.xiaoxuzhu;
import cn.hutool.core.io.resource.ResourceUtil;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
/**
* Description:
*
* @author xiaoxuzhu
* @version 1.0
*
* <pre>
* 修改記錄:
* 修改后版本 修改人 修改日期 修改內容
* 2022/6/5.1 xiaoxuzhu 2022/6/5 Create
* </pre>
* @date 2022/6/5
*/
public class ChatServerFrame extends JFrame {
private JTextArea ta_info;
private ServerSocket server; // 聲明ServerSocket對象
private Socket socket; // 聲明Socket對象socket
private Hashtable<String, Socket> map = new Hashtable<String, Socket>();// 用于存儲連接到服務器的用戶和客戶端套接字對象
public void createSocket() {
try {
server = new ServerSocket(9527);// 創(chuàng)建服務器套接字對象
while (true) {
ta_info.append("等待新客戶連接......\n");
socket = server.accept();// 獲得套接字對象
ta_info.append("客戶端連接成功。" + socket + "\n");
new ServerThread(socket).start();// 創(chuàng)建并啟動線程對象
}
} catch (IOException e) {
e.printStackTrace();
}
}
class ServerThread extends Thread {
Socket socket;
public ServerThread(Socket socket) {
this.socket = socket;
}
public void run() {
try {
ObjectInputStream ins = new ObjectInputStream(socket
.getInputStream());
while (true) {
Vector v = null;
try {
v = (Vector) ins.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
if (v != null && v.size() > 0) {
for (int i = 0; i < v.size(); i++) {
String info = (String) v.get(i);// 讀取信息
String key = "";
if (info.startsWith("用戶:")) {// 添加登錄用戶到客戶端列表
key = info.substring(3, info.length());// 獲得用戶名并作為鍵使用
map.put(key, socket);// 添加鍵值對
Set<String> set = map.keySet();// 獲得集合中所有鍵的Set視圖
Iterator<String> keyIt = set.iterator();// 獲得所有鍵的迭代器
while (keyIt.hasNext()) {
String receiveKey = keyIt.next();// 獲得表示接收信息的鍵
Socket s = map.get(receiveKey);// 獲得與該鍵對應的套接字對象
PrintWriter out = new PrintWriter(s
.getOutputStream(), true);// 創(chuàng)建輸出流對象
Iterator<String> keyIt1 = set.iterator();// 獲得所有鍵的迭代器
while (keyIt1.hasNext()) {
String receiveKey1 = keyIt1.next();// 獲得鍵,用于向客戶端添加用戶列表
out.println(receiveKey1);// 發(fā)送信息
out.flush();// 刷新輸出緩沖區(qū)
}
}
} else if (info.startsWith("退出:")) {
key = info.substring(3);// 獲得退出用戶的鍵
map.remove(key);// 添加鍵值對
Set<String> set = map.keySet();// 獲得集合中所有鍵的Set視圖
Iterator<String> keyIt = set.iterator();// 獲得所有鍵的迭代器
while (keyIt.hasNext()) {
String receiveKey = keyIt.next();// 獲得表示接收信息的鍵
Socket s = map.get(receiveKey);// 獲得與該鍵對應的套接字對象
PrintWriter out = new PrintWriter(s
.getOutputStream(), true);// 創(chuàng)建輸出流對象
out.println("退出:" + key);// 發(fā)送信息
out.flush();// 刷新輸出緩沖區(qū)
}
} else {// 轉發(fā)接收的消息
key = info.substring(info.indexOf(":發(fā)送給:") + 5,
info.indexOf(":的信息是:"));// 獲得接收方的key值,即接收方的用戶名
String sendUser = info.substring(0, info
.indexOf(":發(fā)送給:"));// 獲得發(fā)送方的key值,即發(fā)送方的用戶名
Set<String> set = map.keySet();// 獲得集合中所有鍵的Set視圖
Iterator<String> keyIt = set.iterator();// 獲得所有鍵的迭代器
while (keyIt.hasNext()) {
String receiveKey = keyIt.next();// 獲得表示接收信息的鍵
if (key.equals(receiveKey) && !sendUser.equals(receiveKey)) {// 與接受用戶相同,但不是發(fā)送用戶
Socket s = map.get(receiveKey);// 獲得與該鍵對應的套接字對象
PrintWriter out = new PrintWriter(s.getOutputStream(), true);// 創(chuàng)建輸出流對象
out.println("MSG:" + info);// 發(fā)送信息
out.flush();// 刷新輸出緩沖區(qū)
}
}
}
}
}
}
} catch (IOException e) {
ta_info.append(socket + "已經退出。\n");
}
}
}
public static void main(String args[]) {
ChatServerFrame frame = new ChatServerFrame();
frame.setVisible(true);
frame.createSocket();
}
/**
* Create the frame
*/
public ChatServerFrame() {
super();
addWindowListener(new WindowAdapter() {
public void windowIconified(final WindowEvent e) {
setVisible(false);
}
});
setTitle("聊天室服務器端");
setBounds(100, 100, 385, 266);
final JScrollPane scrollPane = new JScrollPane();
getContentPane().add(scrollPane, BorderLayout.CENTER);
ta_info = new JTextArea();
scrollPane.setViewportView(ta_info);
//托盤
if (SystemTray.isSupported()){ // 判斷是否支持系統(tǒng)托盤
URL url= ResourceUtil.getResource("server.png",null); // 獲取圖片所在的URL
ImageIcon icon = new ImageIcon(url); // 實例化圖像對象
Image image=icon.getImage(); // 獲得Image對象
TrayIcon trayIcon=new TrayIcon(image); // 創(chuàng)建托盤圖標
trayIcon.addMouseListener(new MouseAdapter(){ // 為托盤添加鼠標適配器
public void mouseClicked(MouseEvent e){ // 鼠標事件
if (e.getClickCount()==2){ // 判斷是否雙擊了鼠標
showFrame(); // 調用方法顯示窗體
}
}
});
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) {
System.exit(0); // 退出系統(tǒng)
}
});
popupMenu.add(exit); // 為彈出菜單添加菜單項
trayIcon.setPopupMenu(popupMenu); // 為托盤圖標加彈出菜彈
SystemTray systemTray=SystemTray.getSystemTray(); // 獲得系統(tǒng)托盤對象
try{
systemTray.add(trayIcon); // 為系統(tǒng)托盤加托盤圖標
}catch(Exception e){
e.printStackTrace();
}
}
}
public void showFrame(){
this.setVisible(true); // 顯示窗體
this.setState(Frame.NORMAL);
}
}
服務器啟動

系統(tǒng)托盤

多學一個知識點
想把這個項目代碼打成Jar包,獨立運行,脫離IDEA,可以嗎?
按照上一題學到的方式,來試試
1、把項目打成jar包:利用maven 的clean install

會在target目錄下生成jar包

2、進入target目錄,使用java -cp的命令運行指定的類
java -cp 命令中 cp 指的就是classpath。使用該命令可以運行jar中的某個指定的類(要包含全路徑的包名)
進入cmd命令模式

運行服務端
java -cp basics99-1.0-SNAPSHOT.jar com.xiaoxuzhu.ChatServerFrame
看報錯了

這是因為項目引用了第三方jar包,maven打jar時,只是打當前的項目的內容,沒有把第三方Jar包打進去。
解決方案:
使用maven的插件 maven-assembly-plugin
pom的配置如下,可參考
<?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>basics99</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.ChatServerFrame</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>還是使用maven 的clean install,會在target目錄下生成jar包
進入target目錄,進入CMD命令模式
java -jar basics99-1.0-SNAPSHOT.jar

啟動效果:

到此這篇關于Java聊天室之實現聊天室服務端功能的文章就介紹到這了,更多相關Java聊天室內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Spring系統(tǒng)屬性及spring.properties配置文件示例詳解
spring中有一個SpringProperties類,來保存spring的系統(tǒng)屬性,本文結合實例代碼對Spring系統(tǒng)屬性及spring.properties配置文件相關知識給大家介紹的非常詳細,需要的朋友參考下吧2023-07-07
Java動態(tài)替換properties文件中鍵值方式
這篇文章主要介紹了Java動態(tài)替換properties文件中鍵值方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08
Springboot應用中過濾器如何修改response的header和body內容
這篇文章主要介紹了Springboot應用中過濾器如何修改response的header和body內容問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07

