实验要求:
- 综合Swing界面、多线程和Java的网络通信功能,实现仿QQ聊天:
(1)界面设计如下:
(2)要求在服务器端利用多线程响应客户端请求;
// 服务线程(内部类),用于处理客户端的服务线程
class ServerThread extends Thread {}
将每一个请求服务的客户线程对象加入到一个列表中,以便维护整个聊天室内的成员互相通信。
private ArrayList<ServerThread> clientList = null;
// 将该客户加入列表中
clientList.add(svthread);
代码:
服务端Server.java :
package com.junlin.exer10_;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ScrollPaneConstants;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
/**
* @Description:
* @author: junlin623
* @create: 2022-12-04 9:43
*/
public class Server extends JFrame {
String name; //名称
int port; //服务器开启的端口号
JTextArea textArea; //文本域
JTextField jTextField; //发送消息文本框
ServerSocket server; //服务端套接字
PrintWriter pw; //打印输出流,负责写出服务端的相应数据
//服务器响应客户端的集合,主要用来将服务器的响应数据发送给所有客户端
private List<ResponseThread> clientList = new ArrayList<>();
public Server(String name, int port) {
super(name);
this.name = name;
this.port = port;
addComponent(); //添加面板中的组件
try {
server = new ServerSocket(this.port);
} catch (IOException e) {
e.printStackTrace();
}
new Thread(new ServerThread()).start();
textArea.append("服务器已经启动!\n");
this.setSize(380, 452);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
/**
* 添加组件到面板中
*/
private void addComponent() {
Container contentpane = this.getContentPane();
contentpane.setLayout(new FlowLayout());
textArea = new JTextArea(20,30);
jTextField = new JTextField();
jTextField.setPreferredSize(new Dimension(350, 30));
//创建滚动面板
JScrollPane jScrollPane = new JScrollPane(textArea);
jScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
jScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
JButton sendBtn = new JButton("发送");
//服务端发送按钮事件绑定
sendBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String msg = jTextField.getText();
textArea.append(name + ": " + msg + "\n");
sendAll(name + ": " + msg);
jTextField.setText("");
}
});
//清空记录事件绑定
JButton clearBtn = new JButton("清空聊天记录");
clearBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
textArea.setText("");
}
});
contentpane.add(jScrollPane);
contentpane.add(jTextField);
contentpane.add(sendBtn);
contentpane.add(clearBtn);
}
//发送消息给所有的客户端
void sendAll(String msg) {
if(!"".equals(msg)) {
for(ResponseThread rt : clientList) {
rt.send(msg);
}
}
}
//服务器线程负责与请求的客户端建立连接, 并将与客户端连接的线程加到集合中
class ServerThread implements Runnable {
@Override
public void run() {
while (true) {
Socket client = null;
try {
client = server.accept();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("一个客户端建立了连接");
ResponseThread c = new ResponseThread(client);
clientList.add(c); // 管理所有的成员
new Thread(c).start();
}
}
}
// 服务线程(内部类),用于处理客户端的服务线程
class ResponseThread implements Runnable {
Socket client; //这个线程就表示与client建立的连接
BufferedReader br; //负责读出客户端发送来的数据
public ResponseThread(Socket client) {
this.client = client;
try {
br = new BufferedReader(new InputStreamReader(this.client.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}
//给本线程中的客户端发送信息
void send(String msg) {
try {
//负责写出服务器发送的数据
pw = new PrintWriter(this.client.getOutputStream());
} catch (IOException e) {
throw new RuntimeException(e);
}
pw.println(msg);
pw.flush();
}
@Override
public void run() {
while(true) {
//接收来自客户端的消息
String msg = null;
try {
msg = br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
if(msg != null && !"".equals(msg.trim())) {
textArea.append(msg + "\n");
//在服务器端将消息发送给所有的客户端
sendAll(msg);
}
}
}
}
}
客户端Client.java:
package com.junlin.exer10_;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ScrollPaneConstants;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
/**
* @Description:
* @author: junlin623
* @create: 2022-12-04 9:43
*/
public class Client extends JFrame {
Socket socket;
String name;
JTextArea textArea;
JTextField jTextField;
PrintWriter pw;
int port;
public Client(String name, int port) {
super(name);
this.name = name;
this.port = port; //表示客户端与服务器建立连接的端口号
addComponent();
connectWithServer();
new Thread(new ReceiveThread(socket)).start();
this.setSize(380, 452);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
/**
* 与服务器建立连接
*/
private void connectWithServer() {
try {
socket = new Socket("127.0.0.1", port);
textArea.append(this.name + "已经与服务器建立连接!\n");
pw = new PrintWriter(this.socket.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 添加组件到面板中
*/
private void addComponent() {
Container contentpane = this.getContentPane();
contentpane.setLayout(new FlowLayout());
textArea = new JTextArea(20,30);
jTextField = new JTextField();
jTextField.setPreferredSize(new Dimension(350, 30));
//创建滚动面板,并将文本域放入其中
JScrollPane jScrollPane = new JScrollPane(textArea);
jScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
jScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
JButton sendBtn = new JButton("发送");
sendBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String msg = jTextField.getText();
jTextField.setText("");
pw.println(name + ": " + msg);
pw.flush();
}
});
JButton clearBtn = new JButton("清空聊天记录");
clearBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
textArea.setText("");
}
});
contentpane.add(jScrollPane);
contentpane.add(jTextField);
contentpane.add(sendBtn);
contentpane.add(clearBtn);
}
//客户端接收线程
class ReceiveThread implements Runnable {
private Socket client;
BufferedReader br; //负责读出数据
public ReceiveThread(Socket client) {
this.client = client;
try {
InputStreamReader isr = new InputStreamReader(this.client.getInputStream());
br = new BufferedReader(isr);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
while(true) {
//接收数据
try {
String msg = br.readLine();
if(!"".equals(msg.trim())) {
textArea.append(msg + "\n");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
主类JavaMain.java:
package com.junlin.exer10_;
/**
* @Description:
* @author: junlin623
* @create: 2022-12-03 23:51
*/
public class JavaMain {
public static void main(String[] args) {
new Server("服务器", 6666);
new Client("客户端一", 6666);
new Client("客户端二", 6666);
}
}