import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.Random;
public class LoginPageSwing extends JFrame {
private JTextField usernameField; //用户名
private JPasswordField passwordField; //密码
private static String Rusername; //当前线程的用户名传出去
private JTextField captchaField; //显示验证码文字框
private JLabel captchaLabel; //显示验证码图片
private String generatedCaptcha; //生成验证码
public LoginPageSwing() {
// 设置窗口标题
super("Login Page");
// 设置字体
Font labelFont = new Font("Arial", Font.PLAIN, 14);
Font fieldFont = new Font("Arial", Font.PLAIN, 12);
// 创建文本框和密码框
usernameField = new JTextField(20);
passwordField = new JPasswordField(20);
//创建验证码控件
captchaField = new JTextField(10);
captchaLabel = new JLabel();
// 创建标签
JLabel usernameLabel = new JLabel("Username:");
usernameLabel.setFont(labelFont);
JLabel passwordLabel = new JLabel("Password:");
passwordLabel.setFont(labelFont);
JLabel captchaInputLabel = new JLabel("验证码");
// 创建按钮
JButton loginButton = new JButton("Login");
loginButton.setFont(labelFont);
JButton registerButton = new JButton("Register");
registerButton.setFont(labelFont);
// 设置按钮事件处理
loginButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
login();
}
});
registerButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
openRegistrationPage();
}
});
// 创建面板,并设置布局管理器为网格布局
JPanel panel = new JPanel(new GridLayout(5, 2, 10, 10));
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); // 设置面板边距
panel.add(usernameLabel);
panel.add(usernameField);
panel.add(passwordLabel);
panel.add(passwordField);
panel.add(loginButton);
panel.add(registerButton);
panel.add(captchaInputLabel);
panel.add(captchaField);
// panel.add(captchaLabel);
// panel.add(new JPanel()); // 空白占位,使布局整齐
// 设置布局管理器为边界布局
setLayout(new BorderLayout());
// 添加面板到窗口中央
add(panel, BorderLayout.CENTER);
generatedCaptcha = generateCaptcha();
updateCaptchaImage();
// 包装captchaLabel,并使用FlowLayout来实现底部居中
JPanel captchaPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
captchaPanel.add(captchaLabel);
add(captchaPanel, BorderLayout.SOUTH);
// 设置窗口大小和关闭操作
setSize(400, 350);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setLocationRelativeTo(null); // 将窗口置于屏幕中央
setVisible(true);
}
private String generateCaptcha() {
Random random = new Random();
int captcha = 1000 + random.nextInt(9000);
return String.valueOf(captcha);
}
private void updateCaptchaImage() {
int width = 120;
int height = 30;
BufferedImage captchaImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = captchaImage.createGraphics();
// 设置验证码图片的背景和边框
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, width, height);
g2d.setColor(Color.BLACK);
g2d.drawRect(0, 0, width - 1, height - 1);
// 设置验证码文本
g2d.setFont(new Font("Arial", Font.BOLD, 18));
g2d.setColor(Color.BLACK);
g2d.drawString(generatedCaptcha, 30, 20);
g2d.dispose();
// 将验证码图片显示在标签上
ImageIcon icon = new ImageIcon(captchaImage);
captchaLabel.setIcon(icon);
}
private void login() {
String username = usernameField.getText();
char[] passwordChars = passwordField.getPassword();
String password = new String(passwordChars);
// 与数据库匹配
boolean userExists = false;
User user = CRUD.select(username, password);
if (user != null) {
String enteredCaptcha = captchaField.getText();
if (!enteredCaptcha.equals(generatedCaptcha)) {
JOptionPane.showMessageDialog(this, "Invalid captcha. Please try again.");
generatedCaptcha = generateCaptcha();
updateCaptchaImage();
return;
}
else if (user.getUsername().equals(username) && user.getPasswd().equals(password) && user.getIsfalse()==0) {
userExists = true;
System.out.println("登陆成功");
CRUD.update(1,user.getUsername());
Rusername = user.getUsername();
}else if (user.getIsfalse() == 1){
JOptionPane.showMessageDialog(this, "账户已经登陆,无法重复登陆");
}
}
if (userExists) {
JOptionPane.showMessageDialog(this, "Login successful!");
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
//群聊的话可能在这里更改代码
new QQChatPageSwing();
dispose();
}
});
} else {
JOptionPane.showMessageDialog(this, "Login failed. Please check your username and password.");
}
}
private void openRegistrationPage() {
// 在这里打开注册页面
RegistrationPageSwing registrationPage = new RegistrationPageSwing();
registrationPage.setLocationRelativeTo(this); // 让注册页面相对于登录页面居中显示
}
public static String username(){
return Rusername;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new LoginPageSwing();
}
});
}
}
登陆页面和注册页面几乎差不多,不过多阐述。其中输入密码也是加密的方式,不会显示,按钮的点击效果会通过数据库索引来查询结果。
private void connectToServer() {
try {
socket = new Socket("localhost", 9000);
//写入消息实体类,方便服务器处理
os = new ObjectOutputStream(socket.getOutputStream());
is = new ObjectInputStream(socket.getInputStream());
os.writeObject(new Message(username, "online", "登陆成功", 0, 0));
} catch (IOException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Unable to connect to the server.", "Error", JOptionPane.ERROR_MESSAGE);
System.exit(1);
}
}
//线程监听服务器发的消息
private void startListening() {
new Thread(() -> {
try {
while (true) {
Message message = (Message) is.readObject();
if ((message.getContent()) == null) {
break; // 结束循环
}
handleServerResponse(message);
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
if (socket == null) {
try {
is.close();
os.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
这里展示的部分客户端代码是实现与服务器交互逻辑,聊天室的实现一定会使用到多线程,所以不免会出现线程同步等一系列问题。主要逻辑就是客户端的消息都发给服务器,然后服务器解析之后再转发。其中消息也是封装在一个实体类中,方便操作。
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(PORT);
System.out.println("Server listening on port " + PORT);
while (true) {
Socket clientSocket = serverSocket.accept();
System.out.println("New client connected: " + clientSocket);
ObjectOutputStream oos = new ObjectOutputStream(clientSocket.getOutputStream());
ObjectInputStream ois = new ObjectInputStream(clientSocket.getInputStream());
clientSockets.put(clientSocket, oos);
ClientHandler clientHandler = new ClientHandler(clientSocket, oos, ois);
Thread thread = new Thread(clientHandler);
thread.start();
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//线程处理消息
private static class ClientHandler implements Runnable {
private Socket clientSocket;
private ObjectInputStream ois;
private ObjectOutputStream oos;
public ClientHandler(Socket clientSocket, ObjectOutputStream oos, ObjectInputStream ois) throws IOException, ClassNotFoundException {
this.clientSocket = clientSocket;
this.oos = oos;
this.ois = ois;
}
@Override
public void run() {
while (true) {
Message message = null;
//接受每一条Message
try {
message = (Message) ois.readObject();
//处理Message
handleClientMessage(message);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
if (clientSocket == null) {
// 关闭资源
try {
oos.close();
ois.close();
clientSocket.close();
break;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
每一个客户端都是一个子线程,与之对应的是服务器这边也要开启线程进行管理,其中我是每个子页面都是一个线程。
共享聊天室运行结果,唯一的遗憾是在线用户列表不能实现同步功能
私聊聊天室展示
这里我从277的客户端点击在线列表中的dai,就能进入私聊页面,并且消息不会在共享页面中显示。
## 以上就是程序展示效果,如有需要请在评论区留言,或者私信。