Java实现简易即时通讯系统

发布于:2025-06-25 ⋅ 阅读:(21) ⋅ 点赞:(0)

我们想要实现一个类似QQ的即时通讯程序。由于这是一个复杂的项目,我们将分步骤进行,并只实现核心功能。

核心功能包括:

1. 用户注册与登录

2. 添加好友

3. 发送消息(点对点)

4. 接收消息

我们将使用Socket编程来实现。为了简化,我们不使用数据库,而是使用文件存储用户信息和好友关系。

我们将创建两个主要部分:服务器和客户端。

服务器端:

- 处理用户注册、登录请求

- 管理用户连接(在线状态)

- 转发消息

客户端:

- 提供用户界面(命令行界面)

- 连接服务器

- 发送注册、登录、添加好友、发送消息等请求

- 接收来自服务器的消息并显示

由于时间限制,我们将简化一些设计:

- 用户信息存储:用户名、密码(明文存储,实际中应加密)

- 好友关系存储:每个用户的好友列表

- 消息转发:服务器接收到消息后,查找目标用户是否在线,如果在线则转发,否则存储为离线消息(离线消息功能我们暂不实现,但会留出扩展)

步骤:

1. 设计服务器和客户端之间的通信协议(自定义简单协议)

2. 实现服务器端

3. 实现客户端

通信协议设计:

我们使用文本协议,以行为单位,每行一个指令或数据。每条消息以换行符结束。

消息类型:

- 注册:REGISTER:<username>:<password>

服务器回复:REGISTER_SUCCESS 或 REGISTER_FAILURE:<reason>

- 登录:LOGIN:<username>:<password>

服务器回复:LOGIN_SUCCESS 或 LOGIN_FAILURE:<reason>

- 添加好友:ADD_FRIEND:<friend_username>

服务器回复:ADD_FRIEND_SUCCESS:<friend_username> 或 ADD_FRIEND_FAILURE:<reason>

同时,如果对方在线,服务器会向对方发送一个通知:FRIEND_REQUEST:<username>

- 发送消息:MESSAGE:<receiver>:<message_content>

服务器转发给接收者:MESSAGE:<sender>:<message_content>

- 退出:LOGOUT

服务器还需要维护在线用户的Socket连接。

注意:在实际中,客户端需要能够同时接收消息(即使用单独的线程接收消息并显示)

由于是简化版,我们不考虑安全性和性能。

开始实现:

服务器端:

- 使用ServerSocket监听端口。

- 每个客户端连接使用一个线程处理(ClientHandler)。

- 维护一个在线用户列表(ConcurrentHashMap):用户名->输出流(用于发送消息)

客户端:

- 连接到服务器,开启两个线程:一个用于发送命令,一个用于接收服务器消息并显示。

我们先实现注册和登录功能。

由于不使用数据库,我们使用文件存储用户信息。每个用户一个文件,存储在users目录下,文件名为用户名.txt,内容为密码(第一行)和好友列表(后续每行一个好友用户名)。

同样,好友关系我们也会在用户文件中存储,但这样可能不是最优,但为了简单。

具体步骤:

1. 创建服务器项目结构。

2. 编写服务器主程序,监听端口,接受客户端连接,为每个客户端启动一个线程。

3. 编写ClientHandler,处理客户端发来的各种请求。

4. 实现注册、登录功能。

5. 客户端程序:连接服务器,提供命令行界面输入指令。

由于代码较长,我们将分部分实现。

我们先写服务器端:

Java即时通讯系统(类似QQ)

下面是一个使用Java实现的简易即时通讯系统,模拟QQ的核心功能。这个实现包括图形界面、用户管理、好友列表和实时聊天功能。

设计思路

  1. 客户端-服务器架构

    • 服务器处理用户注册、登录、消息路由

    • 客户端提供用户界面和通讯功能

  2. 核心功能

    • 用户注册与登录

    • 好友管理(添加、删除)

    • 实时文本聊天

    • 消息通知

    • 用户状态显示(在线/离线)

  3. 技术实现

    • Java Swing用于GUI

    • Socket编程实现网络通信

    • 多线程处理并发请求

完整代码实现

1. 服务器端代码

import java.io.*;
import java.net.*;
import java.util.*;

public class ChatServer {
    private static final int PORT = 12345;
    private static final Map<String, PrintWriter> clients = new HashMap<>();
    private static final Map<String, Set<String>> friends = new HashMap<>();

    public static void main(String[] args) {
        System.out.println("QQ服务器启动中...");
        try (ServerSocket serverSocket = new ServerSocket(PORT)) {
            System.out.println("服务器已启动,监听端口: " + PORT);
            
            while (true) {
                new ClientHandler(serverSocket.accept()).start();
            }
        } catch (IOException e) {
            System.err.println("服务器异常: " + e.getMessage());
        }
    }

    private static class ClientHandler extends Thread {
        private Socket socket;
        private PrintWriter out;
        private BufferedReader in;
        private String username;

        public ClientHandler(Socket socket) {
            this.socket = socket;
        }

        public void run() {
            try {
                in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                out = new PrintWriter(socket.getOutputStream(), true);

                // 用户认证
                while (true) {
                    String command = in.readLine();
                    if (command == null) {
                        return;
                    }
                    
                    String[] parts = command.split(":", 3);
                    if (parts[0].equals("REGISTER")) {
                        String username = parts[1];
                        String password = parts[2];
                        
                        if (clients.containsKey(username)) {
                            out.println("REGISTER_FAIL:用户名已存在");
                        } else {
                            clients.put(username, out);
                            friends.put(username, new HashSet<>());
                            out.println("REGISTER_SUCCESS");
                            System.out.println("用户注册: " + username);
                            break;
                        }
                    } else if (parts[0].equals("LOGIN")) {
                        String username = parts[1];
                        String password = parts[2];
                        
                        if (clients.containsKey(username)) {
                            this.username = username;
                            clients.put(username, out);
                            out.println("LOGIN_SUCCESS");
                            sendFriendList(username);
                            System.out.println("用户登录: " + username);
                            break;
                        } else {
                            out.println("LOGIN_FAIL:用户名不存在");
                        }
                    }
                }

                // 处理客户端请求
                String inputLine;
                while ((inputLine = in.readLine()) != null) {
                    System.out.println(username + " 发送: " + inputLine);
                    
                    if (inputLine.startsWith("ADD_FRIEND:")) {
                        String friend = inputLine.substring(11);
                        if (clients.containsKey(friend)) {
                            friends.get(username).add(friend);
                            friends.get(friend).add(username);
                            out.println("FRIEND_ADDED:" + friend);
                            notifyUser(friend, "FRIEND_REQUEST:" + username);
                        } else {
                            out.println("FRIEND_NOT_FOUND:" + friend);
                        }
                    } else if (inputLine.startsWith("MESSAGE:")) {
                        String[] msgParts = inputLine.split(":", 3);
                        if (msgParts.length >= 3) {
                            String to = msgParts[1];
                            String message = msgParts[2];
                            sendMessage(username, to, message);
                        }
                    } else if (inputLine.startsWith("GET_FRIENDS")) {
                        sendFriendList(username);
                    }
                }
            } catch (IOException e) {
                System.err.println("客户端连接异常: " + e.getMessage());
            } finally {
                if (username != null) {
                    clients.remove(username);
                    System.out.println(username + " 已断开连接");
                }
                try {
                    socket.close();
                } catch (IOException e) {
                    // 忽略
                }
            }
        }

        private void sendFriendList(String username) {
            Set<String> friendSet = friends.get(username);
            if (friendSet != null) {
                StringBuilder friendList = new StringBuilder("FRIEND_LIST:");
                for (String friend : friendSet) {
                    friendList.append(friend).append(",");
                    if (clients.containsKey(friend)) {
                        friendList.append("online,");
                    } else {
                        friendList.append("offline,");
                    }
                }
                out.println(friendList.toString());
            }
        }

        private void sendMessage(String from, String to, String message) {
            PrintWriter writer = clients.get(to);
            if (writer != null) {
                writer.println("MESSAGE:" + from + ":" + message);
            } else {
                // 用户离线,存储消息待发送
                out.println("USER_OFFLINE:" + to);
            }
        }

        private void notifyUser(String username, String message) {
            PrintWriter writer = clients.get(username);
            if (writer != null) {
                writer.println(message);
            }
        }
    }
}

2. 客户端代码

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.util.*;

public class QQClient extends JFrame {
    private JTextField usernameField, passwordField, friendField;
    private JPasswordField passwordFieldHidden;
    private JButton loginButton, registerButton, addFriendButton;
    private JTextArea chatArea, messageArea;
    private JList<String> friendList;
    private DefaultListModel<String> friendListModel;
    private PrintWriter out;
    private BufferedReader in;
    private String currentUser;
    private Map<String, JFrame> chatWindows = new HashMap<>();

    public QQClient() {
        setTitle("QQ即时通讯");
        setSize(600, 400);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new BorderLayout());

        // 登录面板
        JPanel loginPanel = new JPanel(new GridLayout(3, 2));
        loginPanel.add(new JLabel("用户名:"));
        usernameField = new JTextField();
        loginPanel.add(usernameField);
        loginPanel.add(new JLabel("密码:"));
        passwordFieldHidden = new JPasswordField();
        loginPanel.add(passwordFieldHidden);
        loginButton = new JButton("登录");
        registerButton = new JButton("注册");
        JPanel buttonPanel = new JPanel(new GridLayout(1, 2));
        buttonPanel.add(loginButton);
        buttonPanel.add(registerButton);
        loginPanel.add(buttonPanel);
        add(loginPanel, BorderLayout.NORTH);

        // 主面板
        JPanel mainPanel = new JPanel(new GridLayout(1, 2));
        friendListModel = new DefaultListModel<>();
        friendList = new JList<>(friendListModel);
        friendList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        mainPanel.add(new JScrollPane(friendList));
        chatArea = new JTextArea();
        chatArea.setEditable(false);
        mainPanel.add(new JScrollPane(chatArea));
        add(mainPanel, BorderLayout.CENTER);

        // 底部面板
        JPanel bottomPanel = new JPanel(new BorderLayout());
        bottomPanel.add(new JLabel("添加好友:"), BorderLayout.WEST);
        friendField = new JTextField();
        bottomPanel.add(friendField, BorderLayout.CENTER);
        addFriendButton = new JButton("添加");
        bottomPanel.add(addFriendButton, BorderLayout.EAST);
        add(bottomPanel, BorderLayout.SOUTH);

        // 事件监听
        loginButton.addActionListener(e -> login());
        registerButton.addActionListener(e -> register());
        addFriendButton.addActionListener(e -> addFriend());
        friendList.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent evt) {
                if (evt.getClickCount() == 2) {
                    openChatWindow();
                }
            }
        });

        setVisible(true);
    }

    private void connectToServer() {
        try {
            Socket socket = new Socket("localhost", 12345);
            out = new PrintWriter(socket.getOutputStream(), true);
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            
            // 启动消息接收线程
            new Thread(this::receiveMessages).start();
        } catch (IOException e) {
            JOptionPane.showMessageDialog(this, "无法连接到服务器: " + e.getMessage(), "连接错误", JOptionPane.ERROR_MESSAGE);
        }
    }

    private void login() {
        String username = usernameField.getText();
        String password = new String(passwordFieldHidden.getPassword());
        
        if (username.isEmpty() || password.isEmpty()) {
            JOptionPane.showMessageDialog(this, "用户名和密码不能为空", "输入错误", JOptionPane.WARNING_MESSAGE);
            return;
        }
        
        connectToServer();
        out.println("LOGIN:" + username + ":" + password);
    }

    private void register() {
        String username = usernameField.getText();
        String password = new String(passwordFieldHidden.getPassword());
        
        if (username.isEmpty() || password.isEmpty()) {
            JOptionPane.showMessageDialog(this, "用户名和密码不能为空", "输入错误", JOptionPane.WARNING_MESSAGE);
            return;
        }
        
        connectToServer();
        out.println("REGISTER:" + username + ":" + password);
    }

    private void addFriend() {
        String friend = friendField.getText();
        if (friend.isEmpty()) {
            JOptionPane.showMessageDialog(this, "请输入好友用户名", "输入错误", JOptionPane.WARNING_MESSAGE);
            return;
        }
        
        if (friend.equals(currentUser)) {
            JOptionPane.showMessageDialog(this, "不能添加自己为好友", "错误", JOptionPane.ERROR_MESSAGE);
            return;
        }
        
        out.println("ADD_FRIEND:" + friend);
        friendField.setText("");
    }

    private void openChatWindow() {
        String selectedFriend = friendList.getSelectedValue();
        if (selectedFriend == null) return;
        
        // 提取用户名(去掉状态信息)
        String friendName = selectedFriend.split(" \\(")[0];
        
        if (chatWindows.containsKey(friendName)) {
            chatWindows.get(friendName).toFront();
            return;
        }
        
        JFrame chatFrame = new JFrame("与 " + friendName + " 聊天");
        chatFrame.setSize(400, 300);
        chatFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        
        JTextArea chatHistory = new JTextArea();
        chatHistory.setEditable(false);
        chatFrame.add(new JScrollPane(chatHistory), BorderLayout.CENTER);
        
        JPanel inputPanel = new JPanel(new BorderLayout());
        messageArea = new JTextArea(3, 20);
        inputPanel.add(new JScrollPane(messageArea), BorderLayout.CENTER);
        
        JButton sendButton = new JButton("发送");
        sendButton.addActionListener(e -> {
            String message = messageArea.getText();
            if (!message.isEmpty()) {
                out.println("MESSAGE:" + friendName + ":" + message);
                chatHistory.append("我: " + message + "\n");
                messageArea.setText("");
            }
        });
        inputPanel.add(sendButton, BorderLayout.EAST);
        
        chatFrame.add(inputPanel, BorderLayout.SOUTH);
        chatFrame.setVisible(true);
        
        chatWindows.put(friendName, chatFrame);
        chatFrame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosed(WindowEvent e) {
                chatWindows.remove(friendName);
            }
        });
    }

    private void receiveMessages() {
        try {
            String message;
            while ((message = in.readLine()) != null) {
                System.out.println("收到消息: " + message);
                
                if (message.startsWith("LOGIN_SUCCESS")) {
                    currentUser = usernameField.getText();
                    chatArea.append("登录成功!欢迎 " + currentUser + "\n");
                } else if (message.startsWith("REGISTER_SUCCESS")) {
                    chatArea.append("注册成功!请登录\n");
                } else if (message.startsWith("LOGIN_FAIL") || message.startsWith("REGISTER_FAIL")) {
                    chatArea.append(message.substring(message.indexOf(':') + 1) + "\n");
                } else if (message.startsWith("FRIEND_LIST:")) {
                    friendListModel.clear();
                    String[] friends = message.substring(12).split(",");
                    for (int i = 0; i < friends.length - 1; i += 2) {
                        String friend = friends[i];
                        String status = friends[i + 1];
                        friendListModel.addElement(friend + " (" + status + ")");
                    }
                } else if (message.startsWith("MESSAGE:")) {
                    String[] parts = message.split(":", 3);
                    if (parts.length >= 3) {
                        String from = parts[1];
                        String content = parts[2];
                        
                        if (chatWindows.containsKey(from)) {
                            JFrame frame = chatWindows.get(from);
                            Component comp = frame.getContentPane().getComponent(0);
                            if (comp instanceof JScrollPane) {
                                JScrollPane scrollPane = (JScrollPane) comp;
                                JViewport viewport = scrollPane.getViewport();
                                Component comp2 = viewport.getView();
                                if (comp2 instanceof JTextArea) {
                                    ((JTextArea) comp2).append(from + ": " + content + "\n");
                                }
                            }
                        } else {
                            chatArea.append(from + " 发来消息: " + content + "\n");
                        }
                    }
                } else if (message.startsWith("FRIEND_REQUEST:")) {
                    String from = message.substring(15);
                    int option = JOptionPane.showConfirmDialog(this, 
                            from + " 想添加你为好友,是否接受?", 
                            "好友请求", 
                            JOptionPane.YES_NO_OPTION);
                    
                    if (option == JOptionPane.YES_OPTION) {
                        friends.get(currentUser).add(from);
                        friends.get(from).add(currentUser);
                        out.println("ADD_FRIEND:" + from);
                    }
                }
            }
        } catch (IOException e) {
            chatArea.append("与服务器断开连接\n");
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(QQClient::new);
    }
}

系统功能说明

  1. 用户管理

    • 新用户注册

    • 已有用户登录

    • 密码保护(实际应用中应加密存储)

  2. 好友管理

    • 添加好友(双向确认)

    • 显示好友在线状态

    • 好友列表管理

  3. 即时通讯

    • 双击好友打开聊天窗口

    • 实时文本消息发送与接收

    • 消息历史记录

  4. 界面设计

    • 简洁直观的登录界面

    • 好友列表显示在线状态

    • 独立的聊天窗口

    • 消息通知系统

使用说明

  1. 启动服务器

    java ChatServer
  2. 启动客户端

    java QQClient
  3. 操作流程

    • 注册新账号或登录已有账号

    • 在底部输入框中输入好友用户名,点击"添加"

    • 双击好友列表中的用户名打开聊天窗口

    • 在聊天窗口中输入消息并点击"发送"

扩展建议

  1. 增强安全性

    • 密码加密存储

    • 添加SSL/TLS加密通信

  2. 功能扩展

    • 添加群聊功能

    • 支持文件传输

    • 添加离线消息存储

    • 实现消息历史记录

  3. 界面优化

    • 添加好友分组功能

    • 实现消息提醒声音

    • 添加表情符号支持

  4. 性能优化

    • 使用线程池管理连接

    • 添加数据库支持

    • 实现负载均衡

这个即时通讯系统实现了类似QQ的核心功能,包括用户注册登录、好友管理、实时聊天等。代码结构清晰,易于扩展,可以作为学习Java网络编程和GUI开发的良好示例。


网站公告

今日签到

点亮在社区的每一天
去签到