Java聊天室之實(shí)現(xiàn)聊天室服務(wù)端功能
一、題目描述
題目實(shí)現(xiàn):實(shí)現(xiàn)聊天室服務(wù)器端功能。運(yùn)行程序,服務(wù)端等待客戶端連接,并顯示客戶端的連接信息。
二、解題思路
創(chuàng)建一個(gè)服務(wù)類:ChatServerFrame,繼承JFrame類
定義一個(gè)Hashtable對(duì)象,用于存儲(chǔ)登錄用戶的用戶名和套接字對(duì)象。
定義createSocket()方法,用于創(chuàng)建服務(wù)器套接字對(duì)象、獲得連接到服務(wù)器的客戶端套接字對(duì)象以及啟動(dòng)線程對(duì)象對(duì)客戶端發(fā)送的信息進(jìn)行處理。
定義內(nèi)部線程類ServerThread用于對(duì)客戶端的連接信息以及發(fā)送的信息進(jìn)行處理和轉(zhuǎn)發(fā)。
技術(shù)重點(diǎn):
本實(shí)例使用Hashtable類來(lái)存儲(chǔ)連接到服務(wù)器的用戶名和套接字對(duì)象,并使用String類的 startWith()方法判斷客戶端發(fā)送信息的類型,從而實(shí)現(xiàn)了向服務(wù)器端添加登錄用戶、發(fā)送退出信息、通過(guò)服務(wù)器轉(zhuǎn)發(fā)客戶端發(fā)送的信息等功能,最終完成了聊天室服務(wù)器端程序的開(kāi)發(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>
* 修改記錄:
* 修改后版本 修改人 修改日期 修改內(nèi)容
* 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對(duì)象
private Socket socket; // 聲明Socket對(duì)象socket
private Hashtable<String, Socket> map = new Hashtable<String, Socket>();// 用于存儲(chǔ)連接到服務(wù)器的用戶和客戶端套接字對(duì)象
public void createSocket() {
try {
server = new ServerSocket(9527);// 創(chuàng)建服務(wù)器套接字對(duì)象
while (true) {
ta_info.append("等待新客戶連接......\n");
socket = server.accept();// 獲得套接字對(duì)象
ta_info.append("客戶端連接成功。" + socket + "\n");
new ServerThread(socket).start();// 創(chuàng)建并啟動(dòng)線程對(duì)象
}
} 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);// 添加鍵值對(duì)
Set<String> set = map.keySet();// 獲得集合中所有鍵的Set視圖
Iterator<String> keyIt = set.iterator();// 獲得所有鍵的迭代器
while (keyIt.hasNext()) {
String receiveKey = keyIt.next();// 獲得表示接收信息的鍵
Socket s = map.get(receiveKey);// 獲得與該鍵對(duì)應(yīng)的套接字對(duì)象
PrintWriter out = new PrintWriter(s
.getOutputStream(), true);// 創(chuàng)建輸出流對(duì)象
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);// 添加鍵值對(duì)
Set<String> set = map.keySet();// 獲得集合中所有鍵的Set視圖
Iterator<String> keyIt = set.iterator();// 獲得所有鍵的迭代器
while (keyIt.hasNext()) {
String receiveKey = keyIt.next();// 獲得表示接收信息的鍵
Socket s = map.get(receiveKey);// 獲得與該鍵對(duì)應(yīng)的套接字對(duì)象
PrintWriter out = new PrintWriter(s
.getOutputStream(), true);// 創(chuàng)建輸出流對(duì)象
out.println("退出:" + key);// 發(fā)送信息
out.flush();// 刷新輸出緩沖區(qū)
}
} else {// 轉(zhuǎn)發(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);// 獲得與該鍵對(duì)應(yīng)的套接字對(duì)象
PrintWriter out = new PrintWriter(s.getOutputStream(), true);// 創(chuàng)建輸出流對(duì)象
out.println("MSG:" + info);// 發(fā)送信息
out.flush();// 刷新輸出緩沖區(qū)
}
}
}
}
}
}
} catch (IOException e) {
ta_info.append(socket + "已經(jīng)退出。\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("聊天室服務(wù)器端");
setBounds(100, 100, 385, 266);
final JScrollPane scrollPane = new JScrollPane();
getContentPane().add(scrollPane, BorderLayout.CENTER);
ta_info = new JTextArea();
scrollPane.setViewportView(ta_info);
//托盤(pán)
if (SystemTray.isSupported()){ // 判斷是否支持系統(tǒng)托盤(pán)
URL url= ResourceUtil.getResource("server.png",null); // 獲取圖片所在的URL
ImageIcon icon = new ImageIcon(url); // 實(shí)例化圖像對(duì)象
Image image=icon.getImage(); // 獲得Image對(duì)象
TrayIcon trayIcon=new TrayIcon(image); // 創(chuàng)建托盤(pán)圖標(biāo)
trayIcon.addMouseListener(new MouseAdapter(){ // 為托盤(pán)添加鼠標(biāo)適配器
public void mouseClicked(MouseEvent e){ // 鼠標(biāo)事件
if (e.getClickCount()==2){ // 判斷是否雙擊了鼠標(biāo)
showFrame(); // 調(diào)用方法顯示窗體
}
}
});
trayIcon.setToolTip("系統(tǒng)托盤(pán)"); // 添加工具提示文本
PopupMenu popupMenu=new PopupMenu(); // 創(chuàng)建彈出菜單
MenuItem exit=new MenuItem("退出"); // 創(chuàng)建菜單項(xiàng)
exit.addActionListener(new ActionListener() { // 添加事件監(jiān)聽(tīng)器
public void actionPerformed(final ActionEvent arg0) {
System.exit(0); // 退出系統(tǒng)
}
});
popupMenu.add(exit); // 為彈出菜單添加菜單項(xiàng)
trayIcon.setPopupMenu(popupMenu); // 為托盤(pán)圖標(biāo)加彈出菜彈
SystemTray systemTray=SystemTray.getSystemTray(); // 獲得系統(tǒng)托盤(pán)對(duì)象
try{
systemTray.add(trayIcon); // 為系統(tǒng)托盤(pán)加托盤(pán)圖標(biāo)
}catch(Exception e){
e.printStackTrace();
}
}
}
public void showFrame(){
this.setVisible(true); // 顯示窗體
this.setState(Frame.NORMAL);
}
}
服務(wù)器啟動(dòng)

系統(tǒng)托盤(pán)

多學(xué)一個(gè)知識(shí)點(diǎn)
想把這個(gè)項(xiàng)目代碼打成Jar包,獨(dú)立運(yùn)行,脫離IDEA,可以嗎?
按照上一題學(xué)到的方式,來(lái)試試
1、把項(xiàng)目打成jar包:利用maven 的clean install

會(huì)在target目錄下生成jar包

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

運(yùn)行服務(wù)端
java -cp basics99-1.0-SNAPSHOT.jar com.xiaoxuzhu.ChatServerFrame
看報(bào)錯(cuò)了

這是因?yàn)轫?xiàng)目引用了第三方j(luò)ar包,maven打jar時(shí),只是打當(dāng)前的項(xiàng)目的內(nèi)容,沒(méi)有把第三方Jar包打進(jìn)去。
解決方案:
使用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é)點(diǎn)執(zhí)行jar包合并操作 -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>還是使用maven 的clean install,會(huì)在target目錄下生成jar包
進(jìn)入target目錄,進(jìn)入CMD命令模式
java -jar basics99-1.0-SNAPSHOT.jar

啟動(dòng)效果:

到此這篇關(guān)于Java聊天室之實(shí)現(xiàn)聊天室服務(wù)端功能的文章就介紹到這了,更多相關(guān)Java聊天室內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springmvc數(shù)據(jù)回顯實(shí)現(xiàn)原理實(shí)例解析
這篇文章主要介紹了Springmvc數(shù)據(jù)回顯實(shí)現(xiàn)原理實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09
Spring系統(tǒng)屬性及spring.properties配置文件示例詳解
spring中有一個(gè)SpringProperties類,來(lái)保存spring的系統(tǒng)屬性,本文結(jié)合實(shí)例代碼對(duì)Spring系統(tǒng)屬性及spring.properties配置文件相關(guān)知識(shí)給大家介紹的非常詳細(xì),需要的朋友參考下吧2023-07-07
關(guān)于Java中byte[]?和?String互相轉(zhuǎn)換問(wèn)題
這篇文章主要介紹了Java中byte[]?和?String互相轉(zhuǎn)換問(wèn)題,通過(guò)用例給大家介紹了通過(guò)String類將String轉(zhuǎn)換成byte[]或者byte[]轉(zhuǎn)換成String,具體實(shí)例代碼跟隨小編一起看看吧2022-01-01
Java實(shí)現(xiàn)XML格式與JSON格式互相轉(zhuǎn)換的方法
這篇文章主要介紹了Java實(shí)現(xiàn)XML格式與JSON格式互相轉(zhuǎn)換的方法,方法通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),選擇使用哪種格式通常取決于項(xiàng)目的需求和上下文,所以格式轉(zhuǎn)換就成了我們必備的技能,具體實(shí)現(xiàn)代碼跟隨小編一起看看吧2023-10-10
Java動(dòng)態(tài)替換properties文件中鍵值方式
這篇文章主要介紹了Java動(dòng)態(tài)替換properties文件中鍵值方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08
Springboot集成任務(wù)調(diào)度實(shí)現(xiàn)過(guò)程
這篇文章主要介紹了Springboot集成任務(wù)調(diào)度實(shí)現(xiàn)過(guò)程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
Springboot應(yīng)用中過(guò)濾器如何修改response的header和body內(nèi)容
這篇文章主要介紹了Springboot應(yīng)用中過(guò)濾器如何修改response的header和body內(nèi)容問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07

