基于 C 语言的网络单词查询系统设计与实现(客户端 + 服务器端)

一、项目概述

本文将介绍一个基于 C 语言开发的网络单词查询系统,该系统包含客户端和服务器端两部分,支持用户注册、登录、单词查询及历史记录查询等功能。系统采用 TCP socket 实现网络通信,使用 SQLite 数据库存储用户信息、单词数据及查询记录,支持多客户端同时连接,具备完整的用户交互流程。

二、技术栈与开发环境

  • 开发语言:C 语言
  • 网络通信:TCP/IP socket 编程
  • 数据库:SQLite3
  • 多线程:pthread 库
  • 开发环境:Linux(Ubuntu 20.04)
  • 编译工具:gcc

三、系统架构设计

  1. 整体架构

    采用客户端 - 服务器(C/S)架构,客户端负责用户交互和请求发送,服务器端负责处理并发请求、数据库操作和业务逻辑。

  2. 模块划分

    • 客户端:连接管理、用户交互、消息发送 / 接收、命令处理
    • 服务器端:socket 监听、客户端管理、线程池、数据库操作、业务逻辑处理
  3. 数据流程

    客户端通过 TCP 连接发送命令(注册 / 登录 / 查询等),服务器端解析命令后执行对应操作(数据库读写),并将结果返回给客户端,客户端展示处理结果。

四、核心功能实现

1. 网络通信模块

  • 客户端:使用socket()创建套接字,connect()连接服务器,send()/recv()发送 / 接收数据,通过 pthread 实现消息接收线程
  • 服务器端:socket()创建监听套接字,bind()绑定端口,listen()监听连接,accept()接收客户端连接,为每个客户端创建独立线程处理请求。

2. 数据库模块

  • 采用 SQLite3 实现数据持久化,设计 3 张核心表:

    • users:存储用户名和密码(用户认证)
    • words:存储单词及释义(字典数据)
    • history:存储用户查询记录(带时间戳)
  • 核心数据库操作:

    • 用户注册 / 登录验证
    • 单词查询与结果返回
    • 查询历史记录的保存与读取

3. 多线程并发处理

  • 服务器端为每个客户端连接创建独立线程,通过互斥锁(pthread_mutex_t)保护共享资源(客户端列表)
  • 实现多用户同时在线操作,互不干扰

4. 客户端交互界面

  • 基于控制台的交互菜单,支持用户直观操作
  • 实时显示连接状态和登录信息,提供清晰的操作反馈

5.服务器端源码

server.c

cpp 复制代码
#include<myhead.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <signal.h>
#include <sqlite3.h>
#include <time.h>

#define MAX_CLIENTS 100//宏定义最多有100个客户端连接
#define BUFFER_SIZE 4096
#define MAX_USERNAME 50
#define MAX_PASSWORD 50
#define MAX_WORD 100
#define MAX_MEANING 500
#define PORT 8888

typedef struct {
    int socket; // 客户端套接字描述符
    char username[MAX_USERNAME];// 客户端登录的用户名
    int is_logged_in; // 登录状态(1表示已登录,0表示未登录)
    pthread_t thread; // 处理该客户端的线程ID
} client_t;

client_t clients[MAX_CLIENTS];//clients是client_t类型的结构体数组
int client_count = 0;
pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER;
sqlite3 *db;

// 函数声明
void *handle_client(void *arg);
void remove_client(int client_socket);
void broadcast_message(const char *message, int exclude_socket);
int init_database();
int import_dict_data();
int register_user(const char *username, const char *password);
int login_user(const char *username, const char *password);
int logout_user(const char *username);
int search_word(const char *word, char *meaning);
int save_history(const char *username, const char *word, const char *meaning);
int get_history(const char *username, char *history, size_t max_len);
void signal_handler(int sig);

void signal_handler(int sig) {
    printf("\n服务器收到中断信号,正在清理资源...\n");
    pthread_mutex_lock(&clients_mutex);
    for (int i = 0; i < MAX_CLIENTS; i++) {
        if (clients[i].socket != -1) {
            close(clients[i].socket);
        }
    }
    pthread_mutex_unlock(&clients_mutex);
    sqlite3_close(db);
    printf("服务器已退出\n");
    exit(0);
}

int init_database() {
    int rc = sqlite3_open("word_database.db", &db);
    if (rc != SQLITE_OK) {
        printf("数据库打开失败: %s\n", sqlite3_errmsg(db));
        return 0;
    }

    const char *create_users = 
        "CREATE TABLE IF NOT EXISTS users ("
        "id INTEGER PRIMARY KEY AUTOINCREMENT,"
        "username TEXT UNIQUE NOT NULL,"
        "password TEXT NOT NULL"
        ");";

    const char *create_words = 
        "CREATE TABLE IF NOT EXISTS words ("
        "id INTEGER PRIMARY KEY AUTOINCREMENT,"
        "word TEXT UNIQUE NOT NULL,"
        "meaning TEXT NOT NULL"
        ");";

    const char *create_history = 
        "CREATE TABLE IF NOT EXISTS history ("
        "id INTEGER PRIMARY KEY AUTOINCREMENT,"
        "username TEXT NOT NULL,"
        "word TEXT NOT NULL,"
        "meaning TEXT NOT NULL,"
        "query_time TIMESTAMP DEFAULTFAULT CURRENT_TIMESTAMP"
        ");";

    char *err_msg = NULL;
    if (sqlite3_exec(db, create_users, NULL, NULL, &err_msg) != SQLITE_OK) {
        printf("创建用户表失败: %s\n", err_msg);
        sqlite3_free(err_msg);
        return 0;
    }
    if (sqlite3_exec(db, create_words, NULL, NULL, &err_msg) != SQLITE_OK) {
        printf("创建单词表失败: %s\n", err_msg);
        sqlite3_free(err_msg);
        return 0;
    }
    if (sqlite3_exec(db, create_history, NULL, NULL, &err_msg) != SQLITE_OK) {
        printf("创建历史记录表失败: %s\n", err_msg);
        sqlite3_free(err_msg);
        return 0;
    }

    printf("数据库初始化成功\n");
    return 1;
}

int import_dict_data() {
    FILE *file = fopen("dict.txt", "r");
    if (!file) {
        printf("dict.txt文件不存在!请在服务器目录创建该文件\n");
        return 0;
    }

    char line[BUFFER_SIZE];
    char word[MAX_WORD];
    char meaning[MAX_MEANING];
    int count = 0;
	// 定义SQL插入语句,使用INSERT OR IGNORE:当插入的word已存在时忽略此次插入(避免重复)
	// ?是参数占位符,后续通过绑定函数设置具体值,防止SQL注入并提高效率
    const char *insert_sql = "INSERT OR IGNORE INTO words (word, meaning) VALUES (?, ?);";
    sqlite3_stmt *stmt;//结构体类型
    //sqlite3_prepare_v2 是 SQLite 库提供的一个函数,用于将 SQL 语句编译(预处理)为二进制格式的准备语句(prepared statement),以便后续高效执行。
    if (sqlite3_prepare_v2(db, insert_sql, -1, &stmt, NULL) != SQLITE_OK) {
        printf("SQL预处理失败: %s\n", sqlite3_errmsg(db));
        fclose(file);
        return 0;
    }

    while (fgets(line, sizeof(line), file)) {
        line[strcspn(line, "\n")] = '\0';
        char *tab_pos = strchr(line, '\t');
        if (!tab_pos) continue;

        *tab_pos = '\0';
        strncpy(word, line, MAX_WORD - 1);
        strncpy(meaning, tab_pos + 1, MAX_MEANING - 1);

        sqlite3_bind_text(stmt, 1, word, -1, SQLITE_STATIC);
        sqlite3_bind_text(stmt, 2, meaning, -1, SQLITE_STATIC);
        if (sqlite3_step(stmt) == SQLITE_DONE) {
            count++;
        }
        sqlite3_reset(stmt);
    }

    sqlite3_finalize(stmt);
    fclose(file);
    printf("成功导入 %d 个单词\n", count);
    return 1;
}

int register_user(const char *username, const char *password) {
    // 修复:确保用户名和密码不包含特殊字符
    if (strchr(username, '|') || strchr(username, ' ') || 
        strchr(password, '|') || strchr(password, ' ')) {
        return 0;
    }

    const char *insert_sql = "INSERT INTO users (username, password) VALUES (?, ?);";
    sqlite3_stmt *stmt;

    if (sqlite3_prepare_v2(db, insert_sql, -1, &stmt, NULL) != SQLITE_OK) {
        printf("注册SQL预处理失败: %s\n", sqlite3_errmsg(db));
        return 0;
    }

    sqlite3_bind_text(stmt, 1, username, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, password, -1, SQLITE_STATIC);

    int result = (sqlite3_step(stmt) == SQLITE_DONE) ? 1 : 0;
    sqlite3_finalize(stmt);
    return result;
}

// 修复:登录验证逻辑,确保正确比对密码
int login_user(const char *username, const char *password) {
    const char *query_sql = "SELECT password FROM users WHERE username = ?;";
    sqlite3_stmt *stmt;

    if (sqlite3_prepare_v2(db, query_sql, -1, &stmt, NULL) != SQLITE_OK) {
        printf("登录SQL预处理失败: %s\n", sqlite3_errmsg(db));
        return 0;
    }

    sqlite3_bind_text(stmt, 1, username, -1, SQLITE_STATIC);
    int rc = sqlite3_step(stmt);

    if (rc != SQLITE_ROW) {
        sqlite3_finalize(stmt);
        return 0; // 用户名不存在
    }

    // 修复:正确获取数据库中的密码并比对
    const char *stored_pwd = (const char *)sqlite3_column_text(stmt, 0);
    int result = (strcmp(stored_pwd, password) == 0) ? 1 : 0;
    sqlite3_finalize(stmt);
    return result;
}

int search_word(const char *word, char *meaning) {
    const char *query_sql = "SELECT meaning FROM words WHERE word = ?;";
    sqlite3_stmt *stmt;

    if (sqlite3_prepare_v2(db, query_sql, -1, &stmt, NULL) != SQLITE_OK) {
        printf("查询SQL预处理失败: %s\n", sqlite3_errmsg(db));
        return 0;
    }

    sqlite3_bind_text(stmt, 1, word, -1, SQLITE_STATIC);
    int rc = sqlite3_step(stmt);

    if (rc != SQLITE_ROW) {
        sqlite3_finalize(stmt);
        return 0;
    }

    const char *result_meaning = (const char *)sqlite3_column_text(stmt, 0);
    strncpy(meaning, result_meaning, MAX_MEANING - 1);
    sqlite3_finalize(stmt);
    return 1;
}

int save_history(const char *username, const char *word, const char *meaning) {
    const char *insert_sql = "INSERT INTO history (username, word, meaning) VALUES (?, ?, ?);";
    sqlite3_stmt *stmt;

    if (sqlite3_prepare_v2(db, insert_sql, -1, &stmt, NULL) != SQLITE_OK) {
        printf("历史记录SQL预处理失败: %s\n", sqlite3_errmsg(db));
        return 0;
    }

    sqlite3_bind_text(stmt, 1, username, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, word, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 3, meaning, -1, SQLITE_STATIC);

    int result = (sqlite3_step(stmt) == SQLITE_DONE) ? 1 : 0;
    sqlite3_finalize(stmt);
    return result;
}

int get_history(const char *username, char *history, size_t max_len) {
    const char *query_sql = 
        "SELECT word, meaning, query_time FROM history "
        "WHERE username = ? ORDER BY query_time DESC LIMIT 10;";
    sqlite3_stmt *stmt;

    if (sqlite3_prepare_v2(db, query_sql, -1, &stmt, NULL) != SQLITE_OK) {
        printf("历史查询SQL预处理失败: %s\n", sqlite3_errmsg(db));
        return 0;
    }

    sqlite3_bind_text(stmt, 1, username, -1, SQLITE_STATIC);
    memset(history, 0, max_len);
    strncat(history, "=== 查询历史 ===\n", max_len - strlen(history) - 1);
    int count = 0;

    while (sqlite3_step(stmt) == SQLITE_ROW && strlen(history) < max_len - 100) {
        const char *word = (const char *)sqlite3_column_text(stmt, 0);
        const char *meaning = (const char *)sqlite3_column_text(stmt, 1);
        const char *time = (const char *)sqlite3_column_text(stmt, 2);

        char line[200];
        snprintf(line, sizeof(line), "%d. %s - %s [%s]\n", 
                 ++count, word, meaning, time);
        strncat(history, line, max_len - strlen(history) - 1);
    }

    if (count == 0) {
        strncat(history, "暂无查询记录\n", max_len - strlen(history) - 1);
    }

    sqlite3_finalize(stmt);
    return count;
}

void broadcast_message(const char *message, int exclude_socket) {
    pthread_mutex_lock(&clients_mutex);
    for (int i = 0; i < MAX_CLIENTS; i++) {
        if (clients[i].socket != -1 && clients[i].is_logged_in && clients[i].socket != exclude_socket) {
            send(clients[i].socket, message, strlen(message), 0);
        }
    }
    pthread_mutex_unlock(&clients_mutex);
}

void remove_client(int client_socket) {
    pthread_mutex_lock(&clients_mutex);
    for (int i = 0; i < MAX_CLIENTS; i++) {
        if (clients[i].socket == client_socket) {
            if (clients[i].is_logged_in) {
                char msg[100];
                snprintf(msg, sizeof(msg), "SYSTEM: 用户 %s 已下线", clients[i].username);
                broadcast_message(msg, client_socket);
            }
            close(clients[i].socket);
            clients[i].socket = -1;
            clients[i].is_logged_in = 0;
            memset(clients[i].username, 0, MAX_USERNAME);
            client_count--;
            printf("客户端 %d 已移除,当前在线数: %d\n", client_socket, client_count);
            break;
        }
    }
    pthread_mutex_unlock(&clients_mutex);
}

void *handle_client(void *arg) {
    client_t *client = (client_t *)arg;
    char buffer[BUFFER_SIZE];
    char response[BUFFER_SIZE];

    printf("新客户端连接: %d\n", client->socket);

    while (1) {
        memset(buffer, 0, BUFFER_SIZE);
        int recv_len = recv(client->socket, buffer, BUFFER_SIZE - 1, 0);
        if (recv_len <= 0) {
            printf("客户端 %d 断开连接\n", client->socket);
            break;
        }

        buffer[recv_len] = '\0';
        printf("客户端 %d 命令: %s\n", client->socket, buffer);

        char *command = strtok(buffer, " ");
        char *params = strtok(NULL, "");

        if (!command) continue;

        // 处理注册命令
        if (strcmp(command, "REGISTER") == 0) {
            if (!params) {
                strcpy(response, "ERROR: 注册格式错误,正确格式: REGISTER 用户名|密码");
                send(client->socket, response, strlen(response), 0);
                continue;
            }
            
            char *user = strtok(params, "|");
            char *pwd = strtok(NULL, "|");
            
            if (!user || !pwd) {
                strcpy(response, "ERROR: 注册格式错误,正确格式: REGISTER 用户名|密码");
                send(client->socket, response, strlen(response), 0);
                continue;
            }
            
            // 修复:检查用户名和密码长度
            if (strlen(user) >= MAX_USERNAME || strlen(pwd) >= MAX_PASSWORD) {
                strcpy(response, "ERROR: 用户名或密码过长");
                send(client->socket, response, strlen(response), 0);
                continue;
            }
            
            if (register_user(user, pwd)) {
                snprintf(response, sizeof(response), "SUCCESS: 注册成功,用户名: %s", user);
            } else {
                strcpy(response, "ERROR: 注册失败,用户名已存在或包含特殊字符");
            }
            send(client->socket, response, strlen(response), 0);

        // 处理登录命令
        } else if (strcmp(command, "LOGIN") == 0) {
            if (client->is_logged_in) {
                strcpy(response, "ERROR: 已登录,无需重复登录");
                send(client->socket, response, strlen(response), 0);
                continue;
            }
            
            if (!params) {
                strcpy(response, "ERROR: 登录格式错误,正确格式: LOGIN 用户名|密码");
                send(client->socket, response, strlen(response), 0);
                continue;
            }
            
            char *user = strtok(params, "|");
            char *pwd = strtok(NULL, "|");
            
            if (!user || !pwd) {
                strcpy(response, "ERROR: 登录格式错误,正确格式: LOGIN 用户名|密码");
                send(client->socket, response, strlen(response), 0);
                continue;
            }
            
            // 修复:验证登录逻辑
            if (login_user(user, pwd)) {
                // 检查是否已在其他地方登录
                pthread_mutex_lock(&clients_mutex);
                int already_login = 0;
                for (int i = 0; i < MAX_CLIENTS; i++) {
                    if (clients[i].socket != -1 && clients[i].is_logged_in && 
                        strcmp(clients[i].username, user) == 0) {
                        already_login = 1;
                        break;
                    }
                }
                
                if (already_login) {
                    pthread_mutex_unlock(&clients_mutex);
                    strcpy(response, "ERROR: 该用户已在其他设备登录");
                    send(client->socket, response, strlen(response), 0);
                    continue;
                }
                
                // 登录成功
                strncpy(client->username, user, MAX_USERNAME - 1);
                client->is_logged_in = 1;
                pthread_mutex_unlock(&clients_mutex);
                
                snprintf(response, sizeof(response), "SUCCESS: 登录成功,欢迎 %s", user);
                send(client->socket, response, strlen(response), 0);
                
                char broadcast_msg[100];
                snprintf(broadcast_msg, sizeof(broadcast_msg), "SYSTEM: 用户 %s 已上线", user);
                broadcast_message(broadcast_msg, client->socket);
            } else {
                strcpy(response, "ERROR: 登录失败,用户名或密码错误");
                send(client->socket, response, strlen(response), 0);
            }

        // 处理登出命令
        } else if (strcmp(command, "LOGOUT") == 0) {
            if (!client->is_logged_in) {
                strcpy(response, "ERROR: 未登录,无需登出");
                send(client->socket, response, strlen(response), 0);
                continue;
            }
            
            char username[MAX_USERNAME];
            strncpy(username, client->username, MAX_USERNAME);
            
            pthread_mutex_lock(&clients_mutex);
            client->is_logged_in = 0;
            memset(client->username, 0, MAX_USERNAME);
            pthread_mutex_unlock(&clients_mutex);
            
            strcpy(response, "SUCCESS: 登出成功");
            send(client->socket, response, strlen(response), 0);
            
            char broadcast_msg[100];
            snprintf(broadcast_msg, sizeof(broadcast_msg), "SYSTEM: 用户 %s 已下线", username);
            broadcast_message(broadcast_msg, client->socket);

        // 处理单词查询
        } else if (strcmp(command, "SEARCH") == 0) {
            if (!client->is_logged_in) {
                strcpy(response, "ERROR: 请先登录");
                send(client->socket, response, strlen(response), 0);
                continue;
            }
            
            if (!params) {
                strcpy(response, "ERROR: 请输入要查询的单词");
                send(client->socket, response, strlen(response), 0);
                continue;
            }
            
            char meaning[MAX_MEANING];
            if (search_word(params, meaning)) {
                snprintf(response, sizeof(response), "SUCCESS: %s - %s", params, meaning);
                save_history(client->username, params, meaning);
            } else {
                snprintf(response, sizeof(response), "ERROR: 未找到单词 %s", params);
            }
            send(client->socket, response, strlen(response), 0);

        // 处理历史记录查询
        } else if (strcmp(command, "HISTORY") == 0) {
            if (!client->is_logged_in) {
                strcpy(response, "ERROR: 请先登录");
                send(client->socket, response, strlen(response), 0);
                continue;
            }
            
            // 修复:预留足够空间给前缀
            char history[BUFFER_SIZE - 20];  // 预留20字节给"SUCCESS: "前缀
            int count = get_history(client->username, history, sizeof(history));
            
            if (count > 0) {
                snprintf(response, BUFFER_SIZE, "SUCCESS: %s", history);
            } else {
                strcpy(response, "SUCCESS: 暂无查询历史");
            }
            send(client->socket, response, strlen(response), 0);

        // 处理退出命令
        } else if (strcmp(command, "QUIT") == 0) {
            strcpy(response, "SUCCESS: 正在断开连接");
            send(client->socket, response, strlen(response), 0);
            break;

        } else {
            strcpy(response, "ERROR: 未知命令");
            send(client->socket, response, strlen(response), 0);
        }
    }
    
    remove_client(client->socket);
    return NULL;
}

int main() {
    int server_socket, client_socket;
    struct sockaddr_in server_addr, client_addr;//创建服务器和客户端绑定信息的结构体
    socklen_t client_len = sizeof(client_addr);
    
    // 初始化信号处理
    signal(SIGINT, signal_handler);
    
    // 初始化数据库
    if (!init_database()) {
        printf("数据库初始化失败,无法启动服务器\n");
        return 1;
    }
    
    // 导入字典数据
    import_dict_data();  // 即使导入失败也继续运行服务器
    
    // 创建socket
    server_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (server_socket == -1) {
        perror("Socket创建失败");
        exit(EXIT_FAILURE);
    }
    
    // 设置socket选项
    int opt = 1;
    setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));//实现端口的快速复用
    
    // 配置服务器地址并绑定
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(PORT);   
    if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("绑定失败");
        exit(EXIT_FAILURE);
    }
    
    // 监听连接
    if (listen(server_socket, 5) < 0) {
        perror("监听失败");
        exit(EXIT_FAILURE);
    }
    
    printf("服务器启动成功,监听端口 %d...\n", PORT);
    
    // 初始化客户端数组
    for (int i = 0; i < MAX_CLIENTS; i++) {
        clients[i].socket = -1;
        clients[i].is_logged_in = 0;
        memset(clients[i].username, 0, MAX_USERNAME);
    }
    
    // 接受客户端连接
    while ((client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &client_len))) {
        pthread_mutex_lock(&clients_mutex);
        
        // 查找空闲位置
        int i;
        for (i = 0; i < MAX_CLIENTS; i++) {
            if (clients[i].socket == -1) {
                clients[i].socket = client_socket;
                clients[i].is_logged_in = 0;
                client_count++;
                break;
            }
        }
        
        pthread_mutex_unlock(&clients_mutex);
        
        if (i == MAX_CLIENTS) {
            printf("达到最大客户端数量限制\n");
            close(client_socket);
            continue;
        }
        
        // 创建线程处理客户端
        if (pthread_create(&clients[i].thread, NULL, handle_client, &clients[i]) != 0) {
            perror("创建线程失败");
            remove_client(client_socket);
        } else {
            printf("客户端 %d 连接成功,当前在线数: %d\n", client_socket, client_count);
        }
    }
    
    close(server_socket);
    sqlite3_close(db);
    return 0;
}
    

6.客户端代码

main.c

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "fun.h"

#define MAX_INPUT 1024
#define MAX_USERNAME 50
#define MAX_PASSWORD 50

// 函数声明
void show_menu();
void handle_connection();
void handle_register();
void handle_login();
void handle_logout();
void handle_search();
void handle_history();
void clear_screen();
void show_status();

int main() {
    int choice;
    char input[MAX_INPUT];
    
    printf("=== 单词查询系统 ===\n");
    printf("支持用户注册、登录、单词查询及历史记录功能\n\n");
    
    while (1) {
        show_status();
        show_menu();
        
        printf("请选择操作 (1-7): ");
        if (fgets(input, MAX_INPUT, stdin) == NULL) {
            break;
        }
        
        choice = atoi(input);
        
        switch (choice) {
            case 1:
                handle_connection();
                break;
            case 2:
                handle_register();
                break;
            case 3:
                handle_login();
                break;
            case 4:
                handle_search();
                break;
            case 5:
                handle_history();
                break;
            case 6:
                handle_logout();
                break;
            case 7:
                printf("正在退出程序...\n");
                disconnect_from_server();
                return 0;
            default:
                printf("无效的选择,请重新输入\n");
                break;
        }
        
        printf("\n按回车键继续...");
        fgets(input, MAX_INPUT, stdin);
        clear_screen();
    }
    
    return 0;
}

// 显示主菜单
void show_menu() {
    printf("\n=== 功能菜单 ===\n");
    printf("1. 连接到服务器\n");
    printf("2. 用户注册\n");
    printf("3. 用户登录\n");
    printf("4. 查询单词\n");
    printf("5. 查看查询历史\n");
    printf("6. 用户登出\n");
    printf("7. 退出程序\n");
    printf("================\n");
}

// 显示当前状态
void show_status() {
    printf("\n=== 当前状态 ===\n");
    if (is_connected()) {
        printf("🔗 连接状态: 已连接到服务器\n");
    } else {
        printf("🔗 连接状态: 未连接到服务器\n");
    }
    
    if (is_logged_in()) {
        printf("👤 登录状态: 已登录 (%s)\n", get_username());
    } else {
        printf("👤 登录状态: 未登录\n");
    }
    printf("================\n");
}

// 处理服务器连接
void handle_connection() {
    char server_ip[100];
    char port_str[10];
    int port;
    
    if (is_connected()) {
        printf("已经连接到服务器,无需重复连接\n");
        return;
    }
    
    printf("请输入服务器IP地址 (默认: 127.0.0.1): ");
    fgets(server_ip, sizeof(server_ip), stdin);
    server_ip[strcspn(server_ip, "\n")] = 0;
    
    if (strlen(server_ip) == 0) {
        strcpy(server_ip, "127.0.0.1");
    }
    
    printf("请输入端口号 (默认: 8888): ");
    fgets(port_str, sizeof(port_str), stdin);
    port_str[strcspn(port_str, "\n")] = 0;
    
    if (strlen(port_str) == 0) {
        port = 8888;
    } else {
        port = atoi(port_str);
    }
    
    printf("正在连接到服务器 %s:%d...\n", server_ip, port);
    
    if (connect_to_server(server_ip, port)) {
        printf("连接成功!\n");
    } else {
        printf("连接失败!\n");
    }
}

// 处理用户注册
void handle_register() {
    char username[MAX_USERNAME];//用户名
    char password[MAX_PASSWORD];//首次输入的密码
    char confirm_password[MAX_PASSWORD];//再次输入的密码
    
    if (!is_connected()) {
        printf("请先连接到服务器\n");
        return;
    }
    
    if (is_logged_in()) {
        printf("您已登录,请先登出再注册新用户\n");
        return;
    }
    
    printf("请输入用户名: ");
    fgets(username, sizeof(username), stdin);
    username[strcspn(username, "\n")] = 0;
    
    if (strlen(username) == 0) {
        printf("用户名不能为空\n");
        return;
    }
    
    printf("请输入密码: ");
    fgets(password, sizeof(password), stdin);
    password[strcspn(password, "\n")] = 0;
    
    if (strlen(password) == 0) {
        printf("密码不能为空\n");
        return;
    }
    
    printf("请确认密码: ");
    fgets(confirm_password, sizeof(confirm_password), stdin);
    confirm_password[strcspn(confirm_password, "\n")] = 0;
    
    if (strcmp(password, confirm_password) != 0) {//判断两次输入的密码是否一致
        printf("两次输入的密码不一致\n");
        return;//返回操作主页并清屏
    }
    
    printf("正在注册用户 %s...\n", username);
    
    if (register_user(username, password)) {
        printf("注册请求已发送,请等待服务器响应...\n");
    } else {
        printf("注册请求发送失败\n");
    }
}

// 处理用户登录
void handle_login() {
    char username[MAX_USERNAME];
    char password[MAX_PASSWORD];
    
    if (!is_connected()) {
        printf("请先连接到服务器\n");
        return;
    }
    
    if (is_logged_in()) {
        printf("您已登录,无需重复登录\n");
        return;
    }
    
    printf("请输入用户名: ");
    fgets(username, sizeof(username), stdin);
    username[strcspn(username, "\n")] = 0;
    
    if (strlen(username) == 0) {
        printf("用户名不能为空\n");
        return;
    }
    
    printf("请输入密码: ");
    fgets(password, sizeof(password), stdin);
    password[strcspn(password, "\n")] = 0;
    
    if (strlen(password) == 0) {
        printf("密码不能为空\n");
        return;
    }
    
    printf("正在登录用户 %s...\n", username);
    
    if (login_user(username, password)) {
        printf("登录请求已发送,请等待服务器响应...\n");
    } else {
        printf("登录请求发送失败\n");
    }
}

// 处理用户登出
void handle_logout() {
    if (!is_connected()) {
        printf("未连接到服务器\n");
        return;
    }
    
    if (!is_logged_in()) {
        printf("您尚未登录,无需登出\n");
        return;
    }
    
    printf("正在登出用户 %s...\n", get_username());
    
    if (logout_user()) {
        printf("登出请求已发送,请等待服务器响应...\n");
    } else {
        printf("登出请求发送失败\n");
    }
}

// 处理单词查询
void handle_search() {
    char word[100];
    
    if (!is_connected()) {
        printf("请先连接到服务器\n");
        return;
    }
    
    if (!is_logged_in()) {
        printf("请先登录\n");
        return;
    }
    
    printf("请输入要查询的单词: ");
    fgets(word, sizeof(word), stdin);
    word[strcspn(word, "\n")] = 0;
    
    if (strlen(word) == 0) {
        printf("单词不能为空\n");
        return;
    }
    
    printf("正在查询单词 %s...\n", word);
    
    if (search_word(word)) {
        printf("查询请求已发送,请等待服务器响应...\n");
    } else {
        printf("查询请求发送失败\n");
    }
}

// 处理历史记录查询
void handle_history() {
    if (!is_connected()) {
        printf("请先连接到服务器\n");
        return;
    }
    
    if (!is_logged_in()) {
        printf("请先登录\n");
        return;
    }
    
    printf("正在获取查询历史记录...\n");
    
    if (get_history()) {
        printf("历史记录请求已发送,请等待服务器响应...\n");
    } else {
        printf("历史记录请求发送失败\n");
    }
}

// 清屏函数(跨平台支持)
void clear_screen() {
#ifdef _WIN32
    system("cls");
#else
    system("clear");
#endif
}

fun.c

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <signal.h>
#include "fun.h"

#define BUFFER_SIZE 1024
#define MAX_USERNAME 50

// 客户端信息结构体
typedef struct {
    int socket;
    char username[MAX_USERNAME];
    int is_connected;
    int is_logged_in;
    pthread_t receive_thread;
} client_info_t;

client_info_t client_info = {0};

// 函数声明
void *receive_messages(void *arg);
void handle_server_response(const char *response);
void signal_handler(int sig);

// 连接到服务器
int connect_to_server(const char *server_ip, int port) {
    // 如果已经连接,先断开
    if (client_info.is_connected) {
        disconnect_from_server();
    }
    
    // 创建socket
    client_info.socket = socket(AF_INET, SOCK_STREAM, 0);
    if (client_info.socket == -1) {
        printf("创建socket失败\n");
        return 0;
    }
    
    // 设置服务器地址
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    
    // 转换IP地址
    if (inet_pton(AF_INET, server_ip, &server_addr.sin_addr) <= 0) {
        printf("无效的IP地址\n");
        close(client_info.socket);
        return 0;
    }
    
    // 连接服务器
    if (connect(client_info.socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        printf("连接服务器失败\n");
        close(client_info.socket);
        return 0;
    }
    
    // 初始化客户端信息
    client_info.is_connected = 1;
    client_info.is_logged_in = 0;
    memset(client_info.username, 0, MAX_USERNAME);
    
    // 设置信号处理
    signal(SIGINT, signal_handler);
    
    // 创建接收消息线程
    if (pthread_create(&client_info.receive_thread, NULL, receive_messages, NULL) != 0) {
        printf("创建接收线程失败\n");
        close(client_info.socket);
        client_info.is_connected = 0;
        return 0;
    }
    
    printf("成功连接到服务器 %s:%d\n", server_ip, port);
    return 1;
}

// 发送消息到服务器
int send_message(const char *message) {
    if (!client_info.is_connected) {
        printf("未连接到服务器\n");
        return 0;
    }
    
    int bytes_sent = send(client_info.socket, message, strlen(message), 0);
    if (bytes_sent == -1) {
        printf("发送消息失败\n");
        return 0;
    }
    
    return 1;
}

// 注册用户
int register_user(const char *username, const char *password) {
    if (!client_info.is_connected) {
        printf("未连接到服务器\n");
        return 0;
    }
    
    char message[BUFFER_SIZE];
    snprintf(message, BUFFER_SIZE, "REGISTER %s|%s", username, password);
    return send_message(message);
}

// 用户登录
int login_user(const char *username, const char *password) {
    if (!client_info.is_connected) {
        printf("未连接到服务器\n");
        return 0;
    }
    
    char message[BUFFER_SIZE];
    snprintf(message, BUFFER_SIZE, "LOGIN %s|%s", username, password);
    
    if (send_message(message)) {
        // 先暂存用户名,等待服务器验证成功后再确认登录状态
        strncpy(client_info.username, username, MAX_USERNAME - 1);
        return 1;
    }
    
    return 0;
}

// 用户登出
int logout_user() {
    if (!client_info.is_connected) {
        printf("未连接到服务器\n");
        return 0;
    }
    
    if (!client_info.is_logged_in) {
        printf("您尚未登录\n");
        return 0;
    }
    
    if (send_message("LOGOUT")) {
        client_info.is_logged_in = 0;
        return 1;
    }
    
    return 0;
}

// 查询单词
int search_word(const char *word) {
    if (!client_info.is_connected) {
        printf("未连接到服务器\n");
        return 0;
    }
    
    if (!client_info.is_logged_in) {
        printf("请先登录\n");
        return 0;
    }
    
    char message[BUFFER_SIZE];
    snprintf(message, BUFFER_SIZE, "SEARCH %s", word);
    return send_message(message);
}

// 获取历史记录
int get_history() {
    if (!client_info.is_connected) {
        printf("未连接到服务器\n");
        return 0;
    }
    
    if (!client_info.is_logged_in) {
        printf("请先登录\n");
        return 0;
    }
    
    return send_message("HISTORY");
}

// 断开与服务器的连接
void disconnect_from_server() {
    if (client_info.is_connected) {
        // 如果已登录,先发送登出命令
        if (client_info.is_logged_in) {
            send_message("LOGOUT");
            sleep(1); // 等待登出命令处理
        }
        
        // 发送退出命令
        send_message("QUIT");
        
        // 等待接收线程结束
        pthread_join(client_info.receive_thread, NULL);
        
        // 关闭socket
        close(client_info.socket);
        
        // 重置客户端信息
        client_info.is_connected = 0;
        client_info.is_logged_in = 0;
        memset(client_info.username, 0, MAX_USERNAME);
        
        printf("已断开与服务器的连接\n");
    }
}

// 信号处理函数
void signal_handler(int sig) {
    printf("\n收到中断信号,正在退出...\n");
    disconnect_from_server();
    exit(0);
}

// 接收服务器消息的线程
void *receive_messages(void *arg) {
    char buffer[BUFFER_SIZE];
    
    while (client_info.is_connected) {
        memset(buffer, 0, BUFFER_SIZE);
        int bytes_received = recv(client_info.socket, buffer, BUFFER_SIZE - 1, 0);
        
        if (bytes_received <= 0) {
            printf("与服务器的连接已断开\n");
            client_info.is_connected = 0;
            client_info.is_logged_in = 0;
            break;
        }
        
        buffer[bytes_received] = '\0';
        handle_server_response(buffer);
    }
    
    return NULL;
}

// 处理服务器响应
void handle_server_response(const char *response) {
    if (strncmp(response, "SUCCESS:", 8) == 0) {
        printf("✅ %s\n", response + 8);
        
        // 处理登录成功的情况
        if (strstr(response, "登录成功") != NULL) {
            client_info.is_logged_in = 1;
        }
    } else if (strncmp(response, "ERROR:", 6) == 0) {
        printf("❌ %s\n", response + 6);
        
        // 处理登录失败的情况
        if (strstr(response, "登录失败") != NULL) {
            memset(client_info.username, 0, MAX_USERNAME);
        }
    } else if (strncmp(response, "SYSTEM:", 7) == 0) {
        printf("📢 %s\n", response + 7);
    } else {
        printf("收到消息: %s\n", response);
    }
}

// 检查是否已连接到服务器
int is_connected() {
    return client_info.is_connected;
}

// 检查是否已登录
int is_logged_in() {
    return client_info.is_logged_in;
}

// 获取当前用户名
const char* get_username() {
    return client_info.username;
}
    

fun.h

cpp 复制代码
#ifndef FUN_H
#define FUN_H

#include <stdio.h>

// 函数声明
int connect_to_server(const char *server_ip, int port);
int send_message(const char *message);
int register_user(const char *username, const char *password);
int login_user(const char *username, const char *password);
int logout_user();
int search_word(const char *word);
int get_history();
void disconnect_from_server();
int is_connected();
int is_logged_in();
const char* get_username();

#endif // FUN_H
    

五、系统测试与运行效果

  1. 环境搭建

    • 服务器端:编译时需链接-lsqlite3 -lpthread库,准备dict.txt字典文件(格式:单词 \t 释义)
    • 客户端:直接编译即可,运行时输入服务器 IP 和端口连接
  2. 操作流程演示

    • 连接服务器 → 注册 / 登录 → 查询单词 → 查看历史 → 登出 / 退出
  3. 核心功能测试

    • 多客户端并发连接测试
    • 用户注册与登录验证
    • 单词查询响应速度
    • 历史记录存储与展示
相关推荐
程序员的世界你不懂7 小时前
【Flask】测试平台开发,实现全局邮件发送工具 第十二篇
网络·python·flask
九河云7 小时前
AI+云,双擎驱动——华为云让智能触手可及
网络·人工智能·科技·安全·华为云
Molder6217 小时前
【计算机408计算机网络】第四章:自底向上五层模型之网络层
网络·笔记·后端·计算机网络·考研
phltxy7 小时前
网络原理——TCP/UDP/IP
java·网络·tcp/ip·udp
梅见十柒7 小时前
UNIX网络编程笔记:同步
网络·经验分享·笔记·tcp/ip·udp·unix
Mrliu__8 小时前
Python基础 实现 学生管理系统
网络·python·microsoft
多读书1938 小时前
JavaEE初阶网络原理-初识
网络·java-ee
奋斗的蛋黄8 小时前
CGroup 资源控制组 + Docker 网络模式
网络·docker·容器
Reicher9 小时前
计算机网络总览
网络·计算机网络·php