Java-項目-001-多人聊天帶界面(界面利用可視化swt)-2020-6-25
零、操作效果
一、ClientApp.java
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Text;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList; //用於存儲客戶信息
import java.util.StringTokenizer; //引入 StringTokenizer 類,用於信息分離
import java.net.*;
import java.io.*;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.List;
public class ClientApp {
List clientsList;// 用戶列表
Socket socket = null;
BufferedReader cin = null;
PrintStream cout = null;
String clientName = "";
Label L1, L2, L3;
Button connectServer, send, disconnect;
// 連接服務器按鈕、發送信息按鈕、斷開連接按鈕
Text ipAddress, textPort, textClientName, talkMessage, textAreaMessage;
// IP地址輸入框、端口輸入框、用戶名輸入框、聊天信息輸入框、聊天信息顯示區
protected Shell shell;
/**
* Launch the application.
*
* @param args
*/
public static void main(String[] args) {
try {
ClientApp window = new ClientApp();
window.open();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Open the window.
*/
public void open() {
Display display = Display.getDefault();
createContents();
shell.open();
shell.layout();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
/**
* Create contents of the window.
*/
protected void createContents() {
shell = new Shell();
shell.setSize(930, 550);
shell.setText("簡單聊天系統-客戶端");
// shell.setFont(SWTResourceManager.getFont("宋體", 14, SWT.NORMAL));
L1 = new Label(shell, SWT.NONE);
// L1.setFont(SWTResourceManager.getFont("宋體", 14, SWT.NORMAL));
L1.setBounds(10, 15, 120, 40);
L1.setText("服務器IP");
L2 = new Label(shell, SWT.NONE);
// L2.setFont(SWTResourceManager.getFont("宋體", 14, SWT.NORMAL));
L2.setBounds(282, 15, 60, 40);
L2.setText("端口");
L3 = new Label(shell, SWT.NONE);
// L3.setFont(SWTResourceManager.getFont("宋體", 14, SWT.NORMAL));
L3.setBounds(471, 15, 120, 40);
L3.setText("客戶名稱");
ipAddress = new Text(shell, SWT.BORDER);
// ipAddress.setFont(SWTResourceManager.getFont("宋體", 13, SWT.NORMAL));
ipAddress.setText("127.0.0.1");
ipAddress.setBounds(126, 14, 140, 40);
textPort = new Text(shell, SWT.BORDER);
// textPort.setFont(SWTResourceManager.getFont("宋體", 13, SWT.NORMAL));
textPort.setText("4321");
textPort.setBounds(345, 14, 120, 40);
talkMessage = new Text(shell, SWT.BORDER);
// talkMessage.setBackground(SWTResourceManager.getColor(SWT.COLOR_WHITE));
// talkMessage.setFont(SWTResourceManager.getFont("宋體", 11, SWT.NORMAL));
talkMessage.setText("");
talkMessage.setBounds(10, 90, 484, 90);
textAreaMessage = new Text(shell, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
textAreaMessage.setEditable(false);
// textAreaMessage.setFont(SWTResourceManager.getFont("宋體", 11, SWT.NORMAL));
// textAreaMessage.setBackground(SWTResourceManager.getColor(SWT.COLOR_WHITE));
textAreaMessage.setBounds(282, 204, 616, 280);
textClientName = new Text(shell, SWT.BORDER);
// textClientName.setFont(SWTResourceManager.getFont("宋體", 13, SWT.NORMAL));
textClientName.setBounds(597, 10, 110, 45);
clientsList = new List(shell, SWT.BORDER);
// clientsList.setFont(SWTResourceManager.getFont("宋體", 11, SWT.NORMAL));
clientsList.setBounds(22, 204, 244, 280);
connectServer = new Button(shell, SWT.NONE);
// connectServer.setForeground(SWTResourceManager.getColor(SWT.COLOR_DARK_CYAN));
// connectServer.setFont(SWTResourceManager.getFont("宋體", 14, SWT.NORMAL));
connectServer.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
try {
InetAddress ip = InetAddress.getByName(ipAddress.getText());
int port = Integer.parseInt(textPort.getText());
socket = new Socket(ip, port);
textAreaMessage.append("系統提示:與聊天服務器系統開始連接......\n");
} catch (IOException e1) {
textAreaMessage.append("服務器端口打開出錯\n");
}
if (socket != null) {
textAreaMessage.append("系統提示:與服務器連接成功......\n");
clientName = textClientName.getText().trim();
try {
cin = new BufferedReader(new InputStreamReader(socket.getInputStream()));
cout = new PrintStream(socket.getOutputStream());
String str = "PEOPLE:" + clientName;
cout.println(str);
ReadMessageThread readThread = new ReadMessageThread();
readThread.start();
} catch (IOException e3) {
textAreaMessage.append("輸入輸出異常\n");
}
}
}
});
connectServer.setBounds(726, 15, 172, 40);
connectServer.setText("連接服務器");
send = new Button(shell, SWT.NONE);
// send.setForeground(SWTResourceManager.getColor(SWT.COLOR_DARK_CYAN));
// send.setFont(SWTResourceManager.getFont("宋體", 14, SWT.NORMAL));
send.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
String str = talkMessage.getText();
str = "MSG:" + clientName + ":" + str;
cout.println(str);
}
});
send.setBounds(500, 100, 200, 40);
send.setText("發送信息");
disconnect = new Button(shell, SWT.NONE);
// disconnect.setForeground(SWTResourceManager.getColor(SWT.COLOR_DARK_CYAN));
// disconnect.setFont(SWTResourceManager.getFont("宋體", 14, SWT.NORMAL));
disconnect.setBounds(726, 100, 172, 40);
disconnect.setText("\u65AD\u5F00\u8FDE\u63A5");
disconnect.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
String str = "QUIT";
cout.println(str);
textAreaMessage.append("客戶請求斷開連接\n");
}
});
}
class ReadMessageThread extends Thread {
boolean isRun = true;
public void list(final ArrayList<String> imessage) {
Display.getDefault().syncExec(new Runnable() {
public void run() {
clientsList.removeAll();
for (String temp : imessage)
clientsList.add(temp);
}
});
}
public void appendTextArea(String str) {
final String str1 = str;
Display.getDefault().syncExec(new Runnable() {
public void run() {
textAreaMessage.append(str1);
}
});
}
public void run() {
String line = "";
while (isRun) {
try {
line = cin.readLine();// 從端口讀入一條信息
} catch (IOException e) {
this.appendTextArea("輸入輸出異常\n");
}
StringTokenizer st = new StringTokenizer(line, ":");
String keyword = st.nextToken();// 存儲關鍵字,判斷消息類型
if (keyword.equalsIgnoreCase("QUIT")) {
try {
socket.close();
this.appendTextArea("接收到服務器同意斷開信息,套接字關閉\n");
} catch (IOException e) {
this.appendTextArea("套接字關閉異常\n");
}
isRun = false;
} else if (keyword.equalsIgnoreCase("PEOPLE")) {
ArrayList<String> imessage = new ArrayList<String>();
while (st.hasMoreTokens())
imessage.add(st.nextToken());
this.list(imessage);
} else {
// 接收的是來自服務器的廣播信息
// 將信息的餘下內容全部提取,並去掉首字符(冒號),並顯示
String message = st.nextToken("\0");
message = message.substring(1);
this.appendTextArea(message + "\n");
}
}
}
}
}
二、ServerApp.java
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Label;
import java.net.*;
import java.io.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList; //引入 ArrayList 類,用於存儲客戶信息
import java.util.StringTokenizer; //引入 StringTokenizer 類,用於信息分離
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
public class ServerApp {
ServerSocket server = null;// server成員變量的作用是用來打開服務器的端口
ConnectSocket connect = null;
static ArrayList<Client> clients = new ArrayList<Client>();
// Socket socket = null;// socket成員變量的作用是用來與客戶端建立套接字連接
// BufferedReader cin = null;// cin成員變量的作用是用來建立輸入流
// PrintStream cout = null;// cout成員變量的作用是用來建立輸出流
Label label;
Text textPort;//端口輸入框
Button actionListener;//監聽按鈕
Text connectionArea, notificationArea;//已連接用戶列表、信息顯示框
protected Shell shell;
/**
* Launch the application.
*
* @param args
*/
public static void main(String[] args) {
try {
ServerApp window = new ServerApp();
window.open();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Open the window.
*/
public void open() {
Display display = Display.getDefault();
createContents();
shell.open();
shell.layout();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
/**
* Create contents of the window.
*/
protected void createContents() {
shell = new Shell();
shell.setSize(680, 515);
shell.setText("簡單聊天系統-服務端");
//shell.setFont(SWTResourceManager.getFont("宋體", 14, SWT.NORMAL));
Label label = new Label(shell, SWT.NONE);
//label.setFont(SWTResourceManager.getFont("宋體", 15, SWT.NORMAL));
label.setBounds(52, 9, 118, 50);
label.setText("\u76D1\u542C\u7AEF\u53E3");
textPort = new Text(shell, SWT.BORDER);
//textPort.setFont(SWTResourceManager.getFont("宋體", 13, SWT.NORMAL));
textPort.setBounds(194, 10, 200, 60);
textPort.setText("4321");
actionListener = new Button(shell, SWT.NONE);
//actionListener.setForeground(SWTResourceManager.getColor(SWT.COLOR_LIST_SELECTION));
//actionListener.setFont(SWTResourceManager.getFont("宋體", 14, SWT.NORMAL));
actionListener.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
try {
// 打開服務器端口
server = new ServerSocket(Integer.parseInt(textPort.getText()));
notificationArea.append("系統提示:聊天服務器系統開始啓動...... \n");
} catch (IOException e1) { // 捕捉可能產生的異常
notificationArea.append("服務器端口打開出錯\n");
}
if (server != null) {
/* 開始用戶連接 */
ConnectSocket connect = new ConnectSocket();
connect.start();
}
}
});
actionListener.setBounds(440, 10, 200, 60);
actionListener.setText("開始監聽");
connectionArea = new Text(shell, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
//connectionArea.setFont(SWTResourceManager.getFont("宋體", 11, SWT.NORMAL));
//connectionArea.setBackground(SWTResourceManager.getColor(SWT.COLOR_WHITE));
connectionArea.setBounds(20, 90, 620, 164);
connectionArea.setEditable(false);
notificationArea = new Text(shell, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
//notificationArea.setFont(SWTResourceManager.getFont("宋體", 11, SWT.NORMAL));
//notificationArea.setBackground(SWTResourceManager.getColor(SWT.COLOR_WHITE));
notificationArea.setBounds(20, 270, 620, 179);
notificationArea.setEditable(false);
}
public boolean checkName(Client newClient) {
for (int i = 0; i < clients.size(); i++) {
// 取出每一個連接對象元素
Client c = (Client) clients.get(i);
// 如果不是對象自身但名字相同,則表明出現了重複名稱
if ((c != newClient) && (c.name).equals(newClient.name))
return false;
}
return true;
}
public void notifyRoom() {
// 服務器發送到客戶的信息字符串,“PEOPLE”表示是客戶列表信息
String people = "PEOPLE";
for (int i = 0; i < clients.size(); i++) {
// 獲取每一個服務器—客戶交互信息線程
Client c = (Client) clients.get(i);
// 將用戶名稱添加到發送信息字符串
people += ":" + c.name;
}
// 調用信息廣播成員方法,將用戶列表信息發送到所有客戶端
sendClients(people);
}
public void sendClients(String msg) {
for (int i = 0; i < clients.size(); i++) {
// 獲取每一個服務器—客戶交互信息線程
Client c = (Client) clients.get(i);
// 通過線程的發送方法將信息發送出去
c.send(msg);
}
}
public void disconnect(Client c) {
try {
Display.getDefault().syncExec(new Runnable() {
public void run() {
// 需要操作的ui線程的代碼
connectionArea.append(c.name + "斷開連接\n");
}
});
// 向客戶發送斷開信息
c.send("QUIT");
clients.remove(c);
c.socket.close();// 斷開連接
} catch (IOException e) {
notificationArea.append("客戶斷開錯誤\n");
}
}
class Client extends Thread {
String name; // 用來存儲客戶的連接姓名
BufferedReader dis; // 用於實現接受從客戶端發送來的數據流
PrintStream ps; // 用來實現向客戶端發送信息的打印流
Socket socket; // 用於建立套接字
boolean isRun = true;// 控制 Client 線程運行狀態
// 採用異步方式更新主界面中的圖形組件
public void appendConnectionArea(String str) {
final String str1 = str;
Display.getDefault().syncExec(new Runnable() {
public void run() {
connectionArea.append(str1);
}
});
}
public Client(Socket s) { // Client 線程的構造器
socket = s; // 將服務器-客戶端建立連接所形成的套接字傳遞到該線程
try {
// 存儲特定客戶 socket 的輸入流,接受客戶發送到服務器端的信息
dis = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 存儲特定客戶的輸出流,發送服務器信息給客戶*/
ps = new PrintStream(socket.getOutputStream());
// 讀取接收到的信息,該信息爲客戶登陸信息
String info = dis.readLine();
// 將信息用":"分離
StringTokenizer stinfo = new StringTokenizer(info, ":");
// 用 head 存儲關鍵區分字
String head = stinfo.nextToken();
// 第二個數據段是客戶的名稱
name = stinfo.nextToken();
connectionArea.append("系統消息:" + name + "已經連接\n");
} catch (IOException e) {
notificationArea.append("系統消息:用戶連接出錯\n");
}
}
// 實現向客戶端發送信息的方法
public void send(String msg) {
ps.println(msg);
ps.flush();
}
// 讀取客戶端發送過來的信息
public void run() {
while (isRun) {
String line = null;
try {
/* 讀取客戶端發送的信息 */
line = dis.readLine();
} catch (IOException e) {
// 如果出錯,則要關閉連接,並更新客戶列表信息
appendConnectionArea("系統消息:讀客戶信息出錯");
disconnect(this);
notifyRoom();
return;
}
// 對讀入的信息進行分離,以確定信息類型
StringTokenizer st = new StringTokenizer(line, ":");
String keyword = st.nextToken();// 關鍵字,判斷消息類型
if (keyword.equalsIgnoreCase("MSG")) {
/*
* 將接收到的客戶聊天信息,通過調用信息廣播成員方法, 發送到所有客戶端
*/
sendClients(line);
// 如果關鍵字是 QUIT,則是客戶端發送的退出信息
} else if (keyword.equalsIgnoreCase("QUIT")) {
// 發送同意斷開信息到客戶端
send("QUIT");
// 關閉連接,並更新客戶列表信息
disconnect(this);
notifyRoom();
// 結束當前線程
isRun = false;
}
}
}
}
class ConnectSocket extends Thread {
public void appendInformation() {
Display.getDefault().syncExec(new Runnable() {
public void run() {
Client c = new Client(socket);
clients.add(c);
if (checkName(c)) {
c.start();
notifyRoom();
} else {
disconnect(c);
}
}
});
}
Socket socket;
public void run() {
while (true) {
while (true) {
try {
socket = server.accept();
} catch (IOException e2) {
connectionArea.append("客戶連接失敗\n");
}
this.appendInformation();
}
}
}
}
}
三、test.java
public class test {
public static void main(String[] args) {
try {
ClientApp window = new ClientApp();
window.open();
} catch (Exception e) {
e.printStackTrace();
}
}
}