运行环境
操作系统:Windows 11 家庭版
运行软件:IntelliJ IDEA 2023.3.6
Navicat Premium 16
相关配置:JDK 17.0.10
mysql-connector-j-8.4.0.jar
Mysql 8.0.38
源代码文件
数据库建表语句
CREATE TABLE `user_info` (
`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`login_status` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`username`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
ServerGUI
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.net.*;
import java.sql.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
// 服务器GUI类,负责显示日志信息和当前登录用户状态
public class ServerGUI extends JFrame {
private static JTextArea logArea;
private JButton viewUsersButton;
public ServerGUI() {
setTitle("基于socket的多用户登录系统服务器端");
setSize(600, 500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
// 设置布局管理器
setLayout(new BorderLayout());
// 创建日志显示区
logArea = new JTextArea();
logArea.setEditable(false);
logArea.setFont(new Font("宋体", Font.PLAIN, 14)); // 设置字体为宋体
JScrollPane scrollPane = new JScrollPane(logArea);
add(scrollPane, BorderLayout.CENTER);
// 创建查看当前用户登录状态按钮
viewUsersButton = new JButton("查看当前用户登录状态");
viewUsersButton.setFont(new Font("宋体", Font.BOLD, 14)); // 设置按钮字体为宋体加粗
viewUsersButton.setBackground(new Color(255, 255, 102)); // 设置按钮背景颜色为亮黄色
viewUsersButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
List<String> loggedInUsers = getLoggedInUsernames();
StringBuilder userList = new StringBuilder("当前登录用户:\n");
for (String user : loggedInUsers) {
userList.append(user).append("\n");
}
userList.append("总用户数: ").append(getLoggedInUserCount());
JOptionPane.showMessageDialog(ServerGUI.this, userList.toString(), "用户登录状态", JOptionPane.INFORMATION_MESSAGE);
}
});
// 将按钮添加到南部(底部)并横向铺满
JPanel buttonPanel = new JPanel(new BorderLayout());
buttonPanel.add(viewUsersButton, BorderLayout.CENTER);
add(buttonPanel, BorderLayout.SOUTH);
// 显示界面
setVisible(true);
}
// 静态方法:更新日志信息
public static void appendLog(String message) {
logArea.append(message + "\n");
}
// 获取当前登录用户数
public static int getLoggedInUserCount() {
String sql = "SELECT COUNT(*) AS count FROM user_info WHERE login_status = true";
try (Connection conn = DBUtil.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
return rs.getInt("count");
}
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
// 获取当前登录用户名列表
public static List<String> getLoggedInUsernames() {
List<String> usernames = new ArrayList<>();
String sql = "SELECT username FROM user_info WHERE login_status = true";
try (Connection conn = DBUtil.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
usernames.add(rs.getString("username"));
}
} catch (SQLException e) {
e.printStackTrace();
}
return usernames;
}
// 主方法,用于启动服务器
public static void main(String[] args) {
int port = 6666; // 定义服务器端口
ServerGUI gui = new ServerGUI(); // 创建并显示服务器GUI
try (ServerSocket serverSocket = new ServerSocket(port)) { // 创建服务器套接字
appendLog("服务器启动,运行在端口:" + port);
// 不断接受新的客户端连接
while (true) {
Socket clientSocket = serverSocket.accept(); // 接受客户端连接
new Thread(new UserThread(clientSocket)).start(); // 为每个客户端连接启动一个新的线程
}
} catch (IOException e) { // 捕捉IO异常
e.printStackTrace();
}
}
}
// 用户线程类,实现Runnable接口,用于处理每个客户端的请求
class UserThread implements Runnable {
private final Socket socket; // 客户端套接字
private DataInputStream dis; // 数据输入流
private DataOutputStream dos; // 数据输出流
private static List<String> loggedInUsers = new ArrayList<>(); // 存储当前登录的用户
private static AtomicInteger clientCounter = new AtomicInteger(0); // 客户端计数器
private String clientName; // 客户端名称
// 构造函数,初始化客户端套接字
public UserThread(Socket socket) {
this.socket = socket;
this.clientName = "客户端-" + clientCounter.incrementAndGet();
}
// 线程运行方法
@Override
public void run() {
try {
dis = new DataInputStream(socket.getInputStream()); // 获取输入流
dos = new DataOutputStream(socket.getOutputStream()); // 获取输出流
ServerGUI.appendLog(clientName + " 连接");
// 不断读取客户端的操作请求
while (true) {
String action = dis.readUTF(); // 读取客户端发送的操作指令
ServerGUI.appendLog(clientName + " 操作: " + action);
switch (action) {
case "register": // 注册操作
String regUsername = dis.readUTF();
String regPassword = dis.readUTF();
registerUser(regUsername, regPassword); // 调用注册方法
break;
case "login": // 登录操作
String loginUsername = dis.readUTF();
String loginPassword = dis.readUTF();
loginUser(loginUsername, loginPassword); // 调用登录方法
break;
case "changePassword": // 修改密码操作
String changeUsername = dis.readUTF();
String oldPassword = dis.readUTF();
String newPassword = dis.readUTF();
changePassword(changeUsername, oldPassword, newPassword); // 调用修改密码方法
break;
case "logout": // 登出操作
String logoutUsername = dis.readUTF();
logout(logoutUsername); // 调用登出方法
break;
default: // 未知操作
dos.writeUTF("未知操作");
ServerGUI.appendLog(clientName + " 操作结果: 未知操作");
}
}
} catch (IOException e) { // 捕捉IO异常
ServerGUI.appendLog(clientName + " 断开连接");
} finally {
closeResources(); // 关闭资源
}
}
// 注册用户方法
private void registerUser(String username, String password) {
if (username == null || username.trim().isEmpty() || password == null || password.trim().isEmpty()) {
sendMsg("用户名或密码不能为空"); // 检查用户名和密码是否为空
ServerGUI.appendLog(clientName + " 注册结果: 用户名或密码不能为空");
return;
}
String sql = "INSERT INTO user_info (username, password, login_status) VALUES (?, ?, FALSE)";
try (Connection conn = DBUtil.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, username);
pstmt.setString(2, password);
int result = pstmt.executeUpdate();
if (result > 0) {
dos.writeUTF("注册成功");
ServerGUI.appendLog(clientName + " 注册结果: 成功");
} else {
dos.writeUTF("注册失败");
ServerGUI.appendLog(clientName + " 注册结果: 失败");
}
} catch (SQLException e) {
if (e.getSQLState().equals("23000")) {
sendMsg("用户名已存在");
ServerGUI.appendLog(clientName + " 注册结果: 用户名已存在");
} else {
sendMsg("数据库错误:" + e.getMessage());
ServerGUI.appendLog(clientName + " 注册结果: 数据库错误:" + e.getMessage());
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 登录用户方法
private void loginUser(String username, String password) {
if (username == null || username.trim().isEmpty() || password == null || password.trim().isEmpty()) {
sendMsg("用户名或密码不能为空");
ServerGUI.appendLog(clientName + " 登录结果: 用户名或密码不能为空");
return;
}
String checkUserSql = "SELECT * FROM user_info WHERE username = ?";
String loginSql = "SELECT * FROM user_info WHERE username = ? AND password = ?";
try (Connection conn = DBUtil.getConnection();
PreparedStatement checkStmt = conn.prepareStatement(checkUserSql)) {
checkStmt.setString(1, username);
ResultSet checkRs = checkStmt.executeQuery();
if (checkRs.next()) {
boolean isLoggedIn = checkRs.getBoolean("login_status");
if (isLoggedIn) {
dos.writeUTF("用户已登录");
ServerGUI.appendLog(clientName + " 登录结果: 用户已登录");
return;
}
try (PreparedStatement loginStmt = conn.prepareStatement(loginSql)) {
loginStmt.setString(1, username);
loginStmt.setString(2, password);
ResultSet loginRs = loginStmt.executeQuery();
if (loginRs.next()) {
updateLoginStatus(username, true);
dos.writeUTF("登录成功");
ServerGUI.appendLog(clientName + " 登录结果: 成功");
} else {
dos.writeUTF("用户名或密码错误");
ServerGUI.appendLog(clientName + " 登录结果: 用户名或密码错误");
}
}
} else {
dos.writeUTF("用户不存在");
ServerGUI.appendLog(clientName + " 登录结果: 用户不存在");
}
} catch (SQLException | IOException e) {
e.printStackTrace();
}
}
// 修改密码方法
private void changePassword(String username, String oldPassword, String newPassword) {
if (username == null || username.trim().isEmpty() || oldPassword == null || oldPassword.trim().isEmpty() || newPassword == null || newPassword.trim().isEmpty()) {
sendMsg("用户名、当前密码或新密码不能为空");
ServerGUI.appendLog(clientName + " 修改密码结果: 用户名、当前密码或新密码不能为空");
return;
}
if (oldPassword.equals(newPassword)) {
sendMsg("新密码不能与当前密码相同");
ServerGUI.appendLog(clientName + " 修改密码结果: 新密码不能与当前密码相同");
return;
}
String checkUser = "SELECT * FROM user_info WHERE username = ? AND password = ?";
String updatePw = "UPDATE user_info SET password = ? WHERE username = ?";
try (Connection conn = DBUtil.getConnection();
PreparedStatement checkStmt = conn.prepareStatement(checkUser)) {
checkStmt.setString(1, username);
checkStmt.setString(2, oldPassword);
ResultSet rs = checkStmt.executeQuery();
if (rs.next()) {
boolean isLoggedIn = rs.getBoolean("login_status");
if (!isLoggedIn) {
sendMsg("用户未登录,无法修改密码");
ServerGUI.appendLog(clientName + " 修改密码结果: 用户未登录,无法修改密码");
return;
}
try (PreparedStatement updateStmt = conn.prepareStatement(updatePw)) {
updateStmt.setString(1, newPassword);
updateStmt.setString(2, username);
int rowsAffected = updateStmt.executeUpdate();
if (rowsAffected > 0) {
dos.writeUTF("密码修改成功");
ServerGUI.appendLog(clientName + " 修改密码结果: 成功");
} else {
dos.writeUTF("密码修改失败,未知错误");
ServerGUI.appendLog(clientName + " 修改密码结果: 失败,未知错误");
}
}
} else {
dos.writeUTF("当前密码错误");
ServerGUI.appendLog(clientName + " 修改密码结果: 当前密码错误");
}
} catch (SQLException | IOException e) {
e.printStackTrace();
}
}
// 用户登出方法
private void logout(String username) {
updateLoginStatus(username, false);
sendMsg("退出登录成功");
ServerGUI.appendLog(clientName + " 登出结果: 成功");
synchronized (loggedInUsers) {
loggedInUsers.remove(username);
}
}
// 更新用户登录状态方法
private void updateLoginStatus(String username, boolean status) {
String sql = "UPDATE user_info SET login_status = ? WHERE username = ?";
try (Connection conn = DBUtil.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setBoolean(1, status);
pstmt.setString(2, username);
pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
// 发送消息方法
private void sendMsg(String msg) {
try {
dos.writeUTF(msg);
} catch (IOException e) {
e.printStackTrace();
}
}
// 关闭资源方法
private void closeResources() {
try {
if (dis != null) dis.close();
if (dos != null) dos.close();
if (socket != null) socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 数据库工具类,负责获取数据库连接
class DBUtil {
private static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver"; // 驱动类
private static final String DB_URL = "jdbc:mysql://localhost/user_info?useSSL=false&serverTimezone=UTC"; // 数据库URL
private static final String USER = "root"; // 数据库用户名
private static final String PASS = "123456"; // 数据库密码
// 获取数据库连接
public static Connection getConnection() throws SQLException {
try {
Class.forName(JDBC_DRIVER); // 加载JDBC驱动
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return DriverManager.getConnection(DB_URL, USER, PASS); // 返回数据库连接
}
}
ClientGUI
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.net.Socket;
public class ClientGUI {
// 定义一些基本的颜色和字体,以便整个界面使用
private final Color backgroundColor = new Color(235, 245, 251);
private final Color textColor = Color.BLACK;
private final Color inputFieldBackgroundColor = new Color(255, 255, 255);
private final Font labelFont = new Font("宋体", Font.BOLD, 14);
private final Font buttonFont = new Font("宋体", Font.PLAIN, 12);
private final Font textAreaFont = new Font("宋体", Font.PLAIN, 12);
private JFrame frame;
private JPanel panel;
private JButton btnRegister, btnLogin, btnChangePassword, btnLogout;
private JTextField usernameField, passwordField, newPasswordField;
private JLabel usernameLabel, passwordLabel, newPasswordLabel, resultLabel;
private JTextArea resultTextArea;
private Socket socket;
private DataInputStream dis;
private DataOutputStream dos;
public ClientGUI(String address, int port) {
try {
socket = new Socket(address, port);
dis = new DataInputStream(socket.getInputStream());
dos = new DataOutputStream(socket.getOutputStream());
System.out.println("已连接到服务器");
initializeGUI();
} catch (IOException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null, "未连接到服务器", "Error", JOptionPane.ERROR_MESSAGE);
}
}
private void initializeGUI() {
frame = new JFrame("基于socket的多用户登录系统客户端");
frame.setSize(600, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new JPanel(new GridLayout(4, 3));
panel.setBackground(backgroundColor); // 设置面板背景色
frame.add(panel);
// 初始化组件
usernameLabel = new JLabel("用户名称");
passwordLabel = new JLabel("当前密码");
newPasswordLabel = new JLabel("新设密码");
resultLabel = new JLabel("操作结果");
usernameField = new JTextField();
passwordField = new JTextField();
newPasswordField = new JTextField();
resultTextArea = new JTextArea(5, 20);
// 应用字体和颜色
setComponentStyles();
usernameLabel.setFont(labelFont);
passwordLabel.setFont(labelFont);
newPasswordLabel.setFont(labelFont);
resultLabel.setFont(labelFont);
usernameField.setFont(textAreaFont);
passwordField.setFont(textAreaFont);
newPasswordField.setFont(textAreaFont);
resultTextArea.setFont(textAreaFont);
JScrollPane scrollPane = new JScrollPane(resultTextArea, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
// 将组件添加到面板
addComponentsToPanel(scrollPane);
// 给按钮添加事件监听器
attachEventListeners();
frame.setVisible(true);
}
private void setComponentStyles() {
btnRegister = new JButton("用户注册");
btnLogin = new JButton("用户登录");
btnChangePassword = new JButton("修改密码");
btnLogout = new JButton("退出登录");
// 为每个按钮设置独特的背景色
btnRegister.setBackground(new Color(52, 152, 219)); // 浅蓝色
btnLogin.setBackground(new Color(46, 204, 113)); // 浅绿色
btnChangePassword.setBackground(new Color(155, 89, 182)); // 浅紫色
btnLogout.setBackground(new Color(231, 76, 60)); // 浅红色
// 设置按钮文字颜色
Color textColor = Color.WHITE;
JButton[] buttons = {btnRegister, btnLogin, btnChangePassword, btnLogout};
for (JButton button : buttons) {
button.setForeground(textColor);
button.setFont(buttonFont);
}
JTextField[] textFields = {usernameField, passwordField, newPasswordField};
for (JTextField textField : textFields) {
textField.setBackground(inputFieldBackgroundColor);
textField.setForeground(this.textColor);
}
resultTextArea.setEditable(false);
resultTextArea.setBackground(Color.WHITE);
resultTextArea.setForeground(this.textColor);
}
private void addComponentsToPanel(JScrollPane scrollPane) {
panel.add(usernameLabel);
panel.add(usernameField);
panel.add(btnRegister);
panel.add(passwordLabel);
panel.add(passwordField);
panel.add(btnLogin);
panel.add(newPasswordLabel);
panel.add(newPasswordField);
panel.add(btnChangePassword);
panel.add(resultLabel);
panel.add(scrollPane);
panel.add(btnLogout);
}
private void attachEventListeners() {
btnRegister.addActionListener(e -> register());
btnLogin.addActionListener(e -> login());
btnChangePassword.addActionListener(e -> changePassword());
btnLogout.addActionListener(e -> logout());
}
private void register() {
String username = usernameField.getText();
String password = passwordField.getText();
try {
dos.writeUTF("register");
dos.writeUTF(username);
dos.writeUTF(password);
String response = dis.readUTF();
resultTextArea.setText(response);
} catch(IOException e) {
resultTextArea.setText("Error during registration: " + e.getMessage());
e.printStackTrace();
}
}
private void login() {
String username = usernameField.getText();
String password = passwordField.getText();
try {
dos.writeUTF("login");
dos.writeUTF(username);
dos.writeUTF(password);
String response = dis.readUTF();
resultTextArea.setText(response);
} catch(IOException e) {
resultTextArea.setText("Error during login: " + e.getMessage());
e.printStackTrace();
}
}
private void changePassword() {
String username = usernameField.getText();
String oldPassword = passwordField.getText();
String newPassword = newPasswordField.getText();
try {
dos.writeUTF("changePassword");
dos.writeUTF(username);
dos.writeUTF(oldPassword);
dos.writeUTF(newPassword);
String response = dis.readUTF();
resultTextArea.setText(response);
} catch(IOException e) {
resultTextArea.setText("Error changing password: " + e.getMessage());
e.printStackTrace();
}
}
private void logout() {
String username = usernameField.getText();
try {
dos.writeUTF("logout");
dos.writeUTF(username);
String response = dis.readUTF();
resultTextArea.setText(response);
} catch(IOException e) {
resultTextArea.setText("Error during logout: " + e.getMessage());
e.printStackTrace();
}
}
public static void main(String[] args) {
new ClientGUI("localhost", 6666);
}
}
源代码示例
运行结果截图
注意事项
1、请自行修改数据库相关参数配置,本人配置如代码中所示
2、本代码中并无加密等功能,主要实现的是登录功能