學習編程最重要的就是興趣引導,所以在學習之前能夠看到一個項目成型是非常有必要的。
QQ微信作為大家經常使用的社交軟件,一定特別貼切實際。

今天就帶大家去了解一下,一個簡單的QQ聊天工具其實並不複雜。
首先把這個聊天的項目簡單的分為四步,也就是整體的框架(目錄)。
Java項目——簡單的QQ聊天工具(目錄)
- 一、聊天演示
- 二、服務器端
- 三、客戶端
- 四、Util工具類

聊天演示
這個是我自己模擬的截圖,由於只有一台電腦,所以只能運行多個窗口進行演示(你也可以多台電腦演示)。
演示截圖
這裡的服務器端是Server,客戶端有三個分別是111、222和333,當我們正常聊天時是群聊狀態,想要進行私聊可以進行@XXX:即可,該系統可以完成多台電腦的聯機。
服務器端
下面給大家詳細的講解一下關於QQ群聊和私聊的具體思想:
首先這個程序用了TCP協議,也叫做三次握手協議。為什麼這樣講呢?
因為在這個TCP協議中分客戶端和服務器端,客戶端要想向服務器端發送消息,要先給服務器打個招呼,判斷服務器是否能正常工作。

如果可以,服務器會給一個回復,當客戶端接到這個肯定的回復後才能向服務器發送消息,所以需要先啟動服務器端,其中,服務器端和客戶端之間的信息傳輸都是以流的方式進行的,如何啟動服務器端呢?
這個是我寫的代碼,裡面都有注釋:由於從JDK中複製過來的代碼格式會被平台調整,所以這裡照齊上可能不太好看。
package com.TCP;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
/**
* 該類實現的是服務器端,也只有啟動了服務器,客戶端才能進行信息的交流
* @author 11852
*
*/
public class Server {
//這個list集合是用來存儲各個客戶端的,每當有一個客戶端建立了連接,這裡就會存儲起來
private static List<Channel> list = new ArrayList<Channel>();
public static void main(String[] args) throws IOException {
System.out.println(“—Server—“);
// 指定端口,使用ServerSocket創建服務器,此時創建了一個名字為server端口號為12345
的服務器
ServerSocket server = new ServerSocket(12345);
boolean flag = true;
while (flag) {
// 阻塞式等待連接accept,服務器調用accept方法,即是獲得一個客戶端的連接
//如果沒有客戶端連接,則該程序處於堵塞狀態
Socket client = server.accept();
System.out.println(“一個客戶端建立了連接”);
//客戶端與服務器建立了連接之後,獲取該客戶端的輸入流和輸出流對象
Channel channel = new Channel(client);
list.add(channel);// 用list容器管理所有的人員
//這裡是實現多線程,即每個客戶端都可以進行與服務器端的交流
new Thread(channel).start();
}
server.close();
}
// 一個客戶端代表一個Channel
static class Channel implements Runnable {
private DataInputStream dis;
private DataOutputStream dos;
private Socket client;
private boolean isRunning;
private String name;
//這是構造方法
public Channel(Socket client) {
this.client = client;
try {
dis = new DataInputStream(client.getInputStream());
isRunning = true;
name = receive();
} catch (IOException e) {
relese();
}
try {
dos = new DataOutputStream(client.getOutputStream());
} catch (IOException e) {
relese();
}
}
// 接收消息
private String receive() {
String msg = “”;
try {
msg = dis.readUTF();
} catch (IOException e) {
relese();
}
return msg;
}
// 發送消息
private void send(String msg) {
try {
dos.writeUTF(msg);
} catch (IOException e) {
relese();
}
}
// 群聊,發給別人
private void sendOthers(String msg) {
// 私聊格式@XXX:這裡是找到以@開頭的信息
if (msg.startsWith(“@”)) {
int idx = msg.indexOf(“:”);
String targetName = msg.substring(1, idx);
msg = msg.substring(idx + 1);
for (Channel other : list) {
if (other.name.equals(targetName)) {
other.send(this.name + “:” + msg);
}
}
} else {
for (Channel other : list) {
if (other == this) {
continue;
} else {
other.send(this.name + “:” + msg);
}
}
}
}
// 釋放資源
private void relese() {
this.isRunning = false;
//這裡的Util是自定義的一個類
Util.close(dis, dos, client);
}
@Override
public void run() {
while (isRunning) {
String msg = receive();
if (!msg.equals(“”)) {
sendOthers(msg);
}
}
}
}
}
(由於從JDK中複製過來的代碼格式會被平台調整,所以這裡照齊上可能不太好看。)

客戶端
客戶端中用到了接收信息、發送信息以及釋放資源
package com.TCP;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException {
System.out.println(“—Client—“);
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println(“請輸入用戶名”);
String name = br.readLine();
// 建立連接,使用Socket創建連接,括號內置服務器的地址和端口
Socket client = new Socket(“localhost”, 12345);
// 客戶端發送消息
new Thread(new Send(client, name)).start();
new Thread(new Receive(client)).start();
}
}
(由於從JDK中複製過來的代碼格式會被平台調整,所以這裡照齊上可能不太好看。)

在實現客戶端的時候,我這裡是在控制台輸入的信息,也可以進行跨級聊天,但前提是要在同一個局域網內,對於客戶端來說,需要客服端進行發送信息和接收信息,首先看看接收信息吧
package com.TCP;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
public class Receive implements Runnable {
private DataInputStream dis;
private Socket client;
private boolean isRunning;
//這裡是傳過來了一個客戶端對象,並獲得了輸入流對象
public Receive(Socket client) {
this.client = client;
try {
dis = new DataInputStream(client.getInputStream());
isRunning = true;
} catch (IOException e) {
release();
}
}
//這裡是實現了獲取信息
private String receive() {
String msg = “”;
try {
msg = dis.readUTF();
} catch (IOException e) {
release();
}
return msg;
}
//這是重寫了run方法,實現多線程,也就是多個客戶端都能與服務器打交道
@Override
public void run() {
while (isRunning) {
String msg = receive();
if (!msg.equals(“”)) {
System.out.println(msg);
}
}
}
// 釋放資源
private void release() {
this.isRunning = false;
Util.close(dis, client);
}
}
(由於從JDK中複製過來的代碼格式會被平台調整,所以這裡照齊上可能不太好看。)

然後就是發送類了,發送跟接收差不多,基本思路是一樣的
package com.TCP;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
public class Send implements Runnable {
private BufferedReader console;
private DataOutputStream dos;
private Socket client;
private boolean isRunning;
// 這裡用到了名字,就是知道是誰誰發的信息
public Send(Socket client, String name) {
console = new BufferedReader(new InputStreamReader(System.in));
this.client = client;
try {
dos = new DataOutputStream(client.getOutputStream());
this.isRunning = true;
send(name);
} catch (IOException e) {
release();
}
}
// 這裡是重寫了run方法
@Override
public void run() {
while (isRunning) {
String msg = getStrFromConsole();
if (!msg.equals(“”)) {
send(msg);
}
}
}
// 這裡是發送消息
private void send(String msg) {
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
release();
}
}
// 這裡是獲取控制台輸入的信息
private String getStrFromConsole() {
String msg = “”;
try {
msg = console.readLine();
} catch (IOException e) {
release();
}
return msg;
}
// 釋放資源
private void release() {
this.isRunning = false;
Util.close(dos, client);
}
}
由於從JDK中複製過來的代碼格式會被平台調整,所以這裡照齊上可能不太好看。

Util工具類
這個就是Util類了,這個類就是實現了釋放資源。
package com.TCP;
import java.io.Closeable;
/**
* 工具類
*
* @author 11852
*
*/
public class Util {
// 釋放資源
public static void close(Closeable… targets) {
for (Closeable target : targets) {
try {
if (target != null) {
target.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
原創文章,作者:投稿專員,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/255216.html
微信掃一掃
支付寶掃一掃