首页 > 其他分享 >简单课设:基于TCP协议的客户/服务器聊天室

简单课设:基于TCP协议的客户/服务器聊天室

时间:2024-07-03 13:55:43浏览次数:3  
标签:username 聊天室 String 课设 TCP static private new 客户端

        随着计算机的普及,网络编程也显得愈发重要,同时掌握实现客户/服务器程序的编写方法也不可忽视。通过学习,我们将了解TCP协议在网络通信中的重要性,掌握如何使用在Linux或Windows平台上编写简单的TCP客户/服务器程序。课程设计分为两个部分:服务器端和客户端。在服务器端,我们将学习如何创建TCP/IP套接字、绑定套接字到端口、监听传入的连接以及处理连接请求。在客户端,我们将学习如何创建TCP/IP套接字、连接到服务器端口、发送数据、接收响应数据以及关闭连接。通过完成这些任务,我们将能够理解网络编程的基本原理,包括套接字的创建和使用、数据的发送和接收、以及连接的建立和关闭。这门课程设计旨在帮助我们建立起对网络编程的基本认识,为日后深入学习网络编程打下坚实的基础。

1架构设计

客户端-服务器架构:采用客户端-服务器模式,客户端负责发送和接收消息,服务器负责转发消息和管理用户。

单服务器多客户端模式:服务器支持多个客户端连接,实现多用户同时聊天的功能。

用户文件:存储用户信息,如用户名、密码等。

2通信协议

使用TCP协议实现客户端与服务器之间的通信,保证消息的可靠传输。

3客户端设计

用户界面:提供用户友好的界面,包括消息显示区域、消息输入框、在线用户列表等。

消息发送:用户输入消息后,通过TCP协议将消息发送到服务器。

消息接收:接收服务器转发的其他用户消息,并在界面上显示。

4服务器设计

用户管理:维护用户信息和在线状态,处理用户的注册、登录和退出操作。

消息转发:接收客户端消息并转发给指定用户,实现实时聊天功能。

在线用户列表:维护在线用户列表,及时更新并向客户端推送。

5项目结构

6代码

package server;

import client.ClientHandler;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


/**
 * 多线程服务器类,管理客户端连接和用户信息
 */
public class MultiThreadedServer {
    // 服务器端口
    private static final int PORT = 10001;

    // 客户端处理器列表
    public static final List<ClientHandler> clients = new ArrayList<>();

    // 用户信息
    private static Properties userInfo;

    // 创建固定大小的线程池
    private static final ExecutorService executor = Executors.newFixedThreadPool(10);


    /**
     * 主方法,启动服务器并监听客户端连接
     */
    public static void main(String[] args) {
        userInfo = loadUserInfo(); // 加载用户信息

        // 创建服务器套接字
        try (ServerSocket serverSocket = new ServerSocket(PORT)) {
            System.out.println("服务器已启动,等待客户端连接...");

            while (true) {
                // 接受客户端连接
                Socket clientSocket = serverSocket.accept();
                System.out.println("客户端连接成功: " + clientSocket);

                // 创建客户端处理器
                ClientHandler clientHandler = new ClientHandler(clientSocket);

                // 添加客户端处理器到列表
                clients.add(clientHandler);

                // 在线程池中执行客户端处理器
                executor.execute(clientHandler);

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 加载用户信息
     * @return 用户信息
     */
    private static Properties loadUserInfo() {
        // 创建属性对象
        Properties prop = new Properties();

        try (FileInputStream fis = new FileInputStream("userinfo.properties")) {
            // 加载属性文件
            prop.load(fis);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 返回属性对象
        return prop;
    }


    /**
     * 注册用户
     * @param username 用户名
     * @param password 密码
     * @return 注册结果
     */
    public static synchronized boolean registerUser(String username, String password) {
        // 如果用户信息中不包含用户名
        if (!userInfo.containsKey(username)) {
            // 设置用户名和密码
            userInfo.setProperty(username, password);

            //保存用户信息
            saveUserInfo();

            return true;
        }

        // 返回注册失败
        return false;
    }

    /**
     * 验证用户身份
     * @param username 用户名
     * @param password 密码
     * @return 验证结果
     */
    public static synchronized boolean authenticateUser(String username, String password) {
        // 获取存储的密码
        String storedPassword = userInfo.getProperty(username);

        // 返回密码是否匹配
        return storedPassword != null && storedPassword.equals(password);

    }


    /**
     * 保存用户信息
     */
    private static synchronized void saveUserInfo() {
        try (FileOutputStream fos = new FileOutputStream("userinfo.properties")) {
            // 存储用户信息到文件
            userInfo.store(fos, null);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 广播消息给所有客户端
     * @param message 消息内容
     */
    public static void broadcastMessage(String message) {
        // 遍历客户端处理器列表
        for (ClientHandler client : clients) {
                // 发送消息给客户端
                client.sendMessage(message);
            }

        }


    /**
     * 移除客户端
     * @param clientHandler 客户端处理器
     */
    public static void removeClient(ClientHandler clientHandler) {
        // 从客户端处理器列表中移除客户端处理器
        clients.remove(clientHandler);

        // 广播用户下线消息
        broadcastMessage("用户 "
                + clientHandler.getUsername() +
                " 下线了");

        // 广播在线用户列表
        broadcastOnlineUsers();
    }


    /**
     * 广播在线用户列表
     */
    public static void broadcastOnlineUsers() {
        // 创建在线用户列表字符串
        StringBuilder onlineUsers = new StringBuilder("在线用户列表:\n");

        // 遍历客户端处理器列表
        for (ClientHandler client : clients) {
            // 添加用户名到在线用户列表字符串
            onlineUsers.append(client.getUsername()).append("\n");
        }

        // 广播在线用户列表消息
        broadcastMessage(onlineUsers.toString());

    }
}
package client;


import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.net.Socket;


/**
 * 客户端 GUI 类,实现用户登录、注册和聊天功能
 */
public class ClientGUI {
    // 服务器主机
    private static final String SERVER_HOST = "127.0.0.1";
    // 服务器端口
    private static final int SERVER_PORT = 10001;
    // 客户端套接字
    private static Socket socket;
    // 读取服务器消息的输入流
    private static BufferedReader in;
    // 发送消息到服务器的输出流
    private static BufferedWriter out;
    // 聊天消息显示区域
    private static JTextArea chatArea;
    // 消息输入框
    private static JTextField messageField;
    // 用户名输入框
    private static JTextField usernameField;
    // 密码输入框
    private static JPasswordField passwordField;


    /**
     * 主方法,创建登录界面并连接服务器
     */
    public static void main(String[] args) {
        // 创建并显示登录界面
        createAndShowLoginGUI();

        try {
            // 连接服务器
            socket = new Socket(SERVER_HOST, SERVER_PORT);
            System.out.println("连接成功");

            //初始化输入输出流
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

            // 启动服务器消息监听线程
            new Thread(new ServerListener()).start();

        } catch (IOException e) {
            e.printStackTrace();

        }
    }


    /**
     * 创建并显示登录界面
     */
    private static void createAndShowLoginGUI() {
        JFrame frame = new JFrame("登录"); // 创建登录界面窗口

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置关闭操作
        frame.setSize(300, 200); // 设置窗口大小
        frame.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("密码:")); // 添加密码标签
        passwordField = new JPasswordField(); // 创建密码输入框
        loginPanel.add(passwordField); // 添加密码输入框
        JButton loginButton = new JButton("登录"); // 创建登录按钮

        loginButton.addActionListener(e -> { // 添加登录按钮点击事件
            String username = usernameField.getText().trim(); // 获取用户名
            String password = new String(passwordField.getPassword()).trim(); // 获取密码
            sendLoginMessage(username, password); // 发送登录消息
            frame.dispose(); // 关闭登录界面
        });

        loginPanel.add(loginButton); // 添加登录按钮


        JButton registerButton = new JButton("注册"); // 创建注册按钮

        registerButton.addActionListener(e -> { // 添加注册按钮点击事件
            String username = usernameField.getText().trim(); // 获取用户名
            String password = new String(passwordField.getPassword()).trim(); // 获取密码
            sendRegisterMessage(username, password); // 发送注册消息
        });

        loginPanel.add(registerButton); // 添加注册按钮


        frame.add(loginPanel, BorderLayout.CENTER); // 将登录面板添加到窗口中

        frame.setVisible(true); // 显示窗口

        frame.setLocationRelativeTo(null); // 居中显示

    }


    /**
     * 创建并显示聊天界面
     */
    private static void createAndShowChatGUI() {
        JFrame frame = new JFrame("聊天室"); // 创建聊天界面窗口
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置关闭操作
        frame.setSize(400, 300); // 设置窗口大小
        frame.setLayout(new BorderLayout()); // 设置布局

        chatArea = new JTextArea(); // 创建聊天消息显示区域
        chatArea.setEditable(false); // 设置为不可编辑
        JScrollPane scrollPane = new JScrollPane(chatArea); // 创建滚动面板
        frame.add(scrollPane, BorderLayout.CENTER); // 将滚动面板添加到窗口中

        JPanel inputPanel = new JPanel(new BorderLayout()); // 创建输入面板
        messageField = new JTextField(); // 创建消息输入框
        JButton sendButton = new JButton("发送"); // 创建发送按钮

        sendButton.addActionListener(e -> { // 添加发送按钮点击事件
            String message = messageField.getText().trim(); // 获取消息内容
            sendMessage(message); // 发送消息
            messageField.setText(""); // 清空消息输入框
        });

        inputPanel.add(messageField, BorderLayout.CENTER); // 将消息输入框添加到输入面板
        inputPanel.add(sendButton, BorderLayout.EAST); // 将发送按钮添加到输入面板
        frame.add(inputPanel, BorderLayout.SOUTH); // 将输入面板添加到窗口中


        frame.setVisible(true); // 显示窗口

        frame.setLocationRelativeTo(null); // 居中显示

    }


    /**
     * 发送登录消息
     * @param username 用户名
     * @param password 密码
     */
    private static void sendLoginMessage(String username, String password) {
        try {
            // 发送登录消息格式为 "login:用户名:密码"
            out.write("login:" + username + ":" + password + "\n");

            // 刷新输出流
            out.flush();

        } catch (IOException e) {
            e.printStackTrace();

        }

    }


    /**
     * 发送注册消息
     * @param username 用户名
     * @param password 密码
     */
    private static void sendRegisterMessage(String username, String password) {
        try {
            // 发送注册消息格式为 "register:用户名:密码"
            out.write("register:" + username + ":" + password + "\n");

            // 刷新输出流
            out.flush();

        } catch (IOException e) {
            e.printStackTrace();

        }
    }


    /**
     * 发送消息
     * @param message 消息内容
     */
    private static void sendMessage(String message) {
        try {
            // 发送消息内容
            out.write(message + "\n");

            // 刷新输出流
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();

        }
    }


    /**
     * 服务器消息监听线程
     */
    private static class ServerListener implements Runnable {
        @Override
        public void run() {
            try {
                String message;
                // 循环读取服务器消息
                while ((message = in.readLine()) != null) {
                    switch (message) {
                        case "login_success" -> {
                            System.out.println("登录成功");

                            // 创建并显示聊天界面
                            createAndShowChatGUI();
                        }
                        case "login_fail" -> {
                            System.out.println("登录失败");

                            // 弹出登录失败对话框
                            JOptionPane.showMessageDialog(null,
                                    "登录失败,请检查用户名和密码",
                                    "登录失败",
                                    JOptionPane.ERROR_MESSAGE);
                        }
                        case "register_success" -> {
                            System.out.println("注册成功");

                            // 弹出注册成功对话框
                            JOptionPane.showMessageDialog(null,
                                    "注册成功,请登录",
                                    "注册成功",
                                    JOptionPane.INFORMATION_MESSAGE);
                        }
                        case "register_fail" -> {
                            System.out.println("注册失败");

                            // 弹出注册失败对话框
                            JOptionPane.showMessageDialog(null,
                                    "注册失败,用户名已存在",
                                    "注册失败",
                                    JOptionPane.ERROR_MESSAGE);
                        }

                        // 添加其他消息到聊天消息显示区域
                        default -> chatArea.append(message + "\n");

                    }
                }
            } catch (IOException e) {
                e.printStackTrace();

            }
        }
    }
}
package client;

import server.MultiThreadedServer;

import java.io.*;
import java.net.Socket;


/**
 * 客户端处理器类,实现了Runnable接口,用于处理客户端的消息和连接
 */
public class ClientHandler implements Runnable {
    // 客户端套接字
    private final Socket socket;
    // 读取客户端消息的输入流
    private BufferedReader in;
    // 向客户端发送消息的输出流
    private BufferedWriter out;
    // 客户端用户名
    private String username;


    /**
     * 构造函数,初始化客户端处理器
     *
     * @param socket 客户端套接字
     */
    public ClientHandler(Socket socket) {
        this.socket = socket;
        try {
            // 初始化输入流和输出流
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

        } catch (IOException e) {
            e.printStackTrace();

        }
    }


    /**
     * 线程运行函数,处理客户端消息
     */
    @Override
    public void run() {
        try {
            String message;
            // 循环读取客户端消息
            while ((message = in.readLine()) != null) {
                if (message.startsWith("login:")) {
                    // 处理登录请求
                    String[] parts = message.split(":");
                    String username = parts[1];
                    String password = parts[2];
                    handleLogin(username, password);

                } else if (message.startsWith("register:")) {
                    // 处理注册请求
                    String[] parts = message.split(":");
                    String username = parts[1];
                    String password = parts[2];
                    handleRegister(username, password);

                } else if (message.startsWith("private:")) {
                    // 处理私聊消息
                    handlePrivateMessage(message);

                } else {
                    // 发送消息到所有客户端
                    MultiThreadedServer.broadcastMessage(username + ": " + message);

                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                // 关闭套接字和从客户端的输入输出流
                socket.close();
                MultiThreadedServer.removeClient(this);

            } catch (IOException e) {
                e.printStackTrace();

            }
        }
    }


    /**
     * 处理登录请求
     *
     * @param username 用户名
     * @param password 密码
     */
    private void handleLogin(String username, String password) {
        if (MultiThreadedServer.authenticateUser(username, password)) {
            // 登录成功,发送消息给客户端
            sendMessage("login_success");
            this.username = username;

            // 广播用户上线消息给所有客户端
            MultiThreadedServer.broadcastMessage("用户 " + username + " 上线了");

            // 广播在线用户列表给所有客户端
            MultiThreadedServer.broadcastOnlineUsers();

        } else {
            // 登录失败,发送消息给客户端
            sendMessage("login_fail");
        }
    }


    /**
     * 处理注册请求
     *
     * @param username 用户名
     * @param password 密码
     */
    private void handleRegister(String username, String password) {
        if (MultiThreadedServer.registerUser(username, password)) {
            // 注册成功,发送消息给客户端
            sendMessage("register_success");
        } else {
            // 注册失败,发送消息给客户端
            sendMessage("register_fail");
        }
    }


    /**
     * 处理私聊消息
     *
     * @param message 私聊消息内容
     */
    private void handlePrivateMessage(String message) {
        // 分割私聊消息
        String[] parts = message.split(":", 3);
        String targetUsername = parts[1];
        String privateMessage = parts[2];

        // 查找目标客户端
        ClientHandler targetClient = findClientByUsername(targetUsername);

        if (targetClient != null) {
            // 如果目标客户端在线,发送私聊消息给目标客户端,并发送确认消息给发送方
            targetClient.sendMessage("(私聊) " + username + ": " + privateMessage);
            sendMessage("私聊消息已发送给 " + targetUsername + ": " + privateMessage);
        } else {
            // 如果目标客户端不在线,发送消息给发送方
            sendMessage("用户 " + targetUsername + " 不存在或不在线");
        }
    }


    /**
     * 根据用户名查找客户端,并返回相应的ClientHandler对象。
     *
     * @param username 要查找的用户名
     * @return 如果找到匹配的客户端,则返回对应的ClientHandler对象;否则返回null。
     */
    private ClientHandler findClientByUsername(String username) {
        // 遍历客户端列表,查找匹配的用户名
        for (ClientHandler client : MultiThreadedServer.clients) {
            // 如果客户端用户名不为null且与目标用户名相匹配,则返回该客户端
            if (client.getUsername() != null && client.getUsername().equals(username)) {
                return client;
            }
        }

        // 如果未找到匹配的客户端,则返回null
        return null;
    }


    /**
     * 向客户端发送消息。
     *
     * @param message 要发送的消息内容
     */
    public void sendMessage(String message) {
        try {
            // 将消息写入输出流并刷新
            out.write(message + "\n");
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 获取客户端的用户名。
     * @return 客户端的用户名
     */
    public String getUsername() {
        return username;
    }
}

7运行结果

首先要先启动服务端,再启动客户端

用户信息如下:

测试注册功能:

回去查看结果

测试登录功能:

测试多用户登录系统:

测试群聊信息:

测试私聊信息:

测试用户下线功能:

8总结

        本聊天室系统是基于客户端-服务器架构设计的,通过TCP协议实现客户端与服务器之间的通信。系统具备了用户注册、登录、实时聊天、用户列表和聊天记录等基本功能,同时保证了系统的安全、可靠和性能良好。数据库设计合理,用户信息和聊天记录得以有效存储和管理。客户端提供了直观友好的界面,使用户操作简便。服务器实现了用户管理、消息转发和在线用户列表维护等核心功能。整体而言,该聊天室系统具备了一定的实用性和可靠性,能够满足用户的基本聊天需求。

标签:username,聊天室,String,课设,TCP,static,private,new,客户端
From: https://blog.csdn.net/qq_61062215/article/details/140149967

相关文章

  • golang 实现tcp socket 通信
    都说golang适合网络编程,知道今天写了tcp我才知道他到底有多简洁。(虽然已经对着golang的webrtc研究很久了)首先是client端代码:packagemainimport( "bufio" "fmt" "net")funcmain(){ conn,err:=net.Dial("tcp","192.168.1.106:8080") iferr!=ni......
  • 三次握手,四次挥手,以及Socket、TCP、HTTP之间的区别
    网络开发1.网站:最全的socket,tcp,http三者之间的区别和原理_cocket与tcpip-CSDN博客1.网络七层1.定义:网络七层(简称:OSI),即开放式系统互连。该体系架构标准定义了网络互连的七层框架(物理层、数据链路层、网络层、传输层、会话层、表示层和应用层)2.socket,tcp,http三者之间的区别和......
  • 电脑配件管理系统(数据库课设)
     1.课设要求描述 ●实现电脑配件的增删查改功能管理●实现管理员和用户的不同登录身份管理●实现管理员登录之后对电脑配件的管理●实现用户登录后进行查询和领用配件的功能2.制作思路及基础讲解此项目主要是用于完成大二下半学期的数据库课设,随手记录。技术面大二下之......
  • 深入理解TCP协议格式(WireShark分析)
    传输控制协议(TCP)是互联网中最为关键的通信协议之一。了解TCP协议的细节不仅对于网络工程师至关重要,对于任何涉及网络通信的软件开发人员而言都是必备的知识。本文旨在深入探讨TCP协议,从协议的基本概述到其工作机制,以及如何通过实际代码实现和工具分析来理解其运作。TCP协......
  • 图文并茂! TCP的“三次握手”是如何进行的?
    对于TCP连接,相信大家都不陌生,这种连接确保了通信双方之间的可靠性和一致性。无论是在学习计算机网络,还是在面试过程中,TCP都是一个比较常考的知识点,然而这个知识点却很容易被遗忘。为了帮助大家更容易理解和记住该知识点,本文通过图文并茂的形式向大家解释了TCP三次握手的过程。......
  • 在Linux中,如何实时抓取并显示当前系统中tcp 80 端口的网络数据信息?
    在Linux中,实时抓取并显示当前系统中TCP80端口的网络数据信息,可以使用tcpdump这个强大的命令行工具。以下是详细的步骤和命令:打开终端:首先,打开一个具有足够权限的终端窗口。通常,需要使用sudo或以root身份执行tcpdump,因为它需要访问底层网络接口。确定网络接口:使用ifconfig(在......
  • 在Linux中,如何使用 tcpdump 监听主机为 192.168.1.1,tcp 端⼝为 80 的数据,并将将输出结
    在Linux中,要使用tcpdump监听目标为主机IP为192.168.1.1,且TCP端口为80的数据包,并将输出结果保存到名为tcpdump.log的文件中,可以按照以下步骤操作:打开终端:首先,你需要在具有足够权限的用户账户下打开一个终端窗口。通常,root权限是必要的,因为普通用户可能无法监听网络接口的所有流......
  • 58-DOS与DDOS分析(正常TCP会话与SYN Flood攻击、ICMP Flood 攻击、SNMP放大攻击等)
    目录正常TCP会话与SYNFlood攻击1、正常的三次握手过程:2、SYNFlood攻击一、攻击windows系统:二、攻击web网站:拒绝服务攻击工具-Hping3-SynFlood攻击拒绝服务攻击工具--Hping3--ICMPFlood攻击 sockstress攻击Sockstress防范 DNS放大攻击产生大流量的攻......
  • TCP与UDP_三次握手_四次挥手
    TCPvsUDPTCP数据具体可以通过CiscoPacketTracer工具查看:UDP数据三次握手、四次挥手为什么是3/4次?这牵扯到单工、双工通信的问题TCP建立连接:表白TCP释放连接:分手TCP—建立连接—三次握手解释:首先,启动服务器,让服务器进入监听状态(监听客户端的连接请......
  • docker拉取镜像失败error pulling image configuration: download failed after attem
    最近很多朋友遇到docker拉取镜像失败的问题因为一些网络问题,无法访问docker官方镜像仓库,我们可以通过设置阿里云镜像加速器的方式解决该问题。解决方法:1.访问阿里云官网,并登录https://www.aliyun.com/2.搜索容器镜像服务3.点击立即开通4.根据提示免费开通个人版,开通......