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();
}
}
}