基于 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

#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 8888typedef 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();  // 即使导入失败也继续运行服务器// 创建socketserver_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

#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 _WIN32system("cls");
#elsesystem("clear");
#endif
}

fun.c

#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();}// 创建socketclient_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);// 关闭socketclose(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

#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. 核心功能测试

    • 多客户端并发连接测试
    • 用户注册与登录验证
    • 单词查询响应速度
    • 历史记录存储与展示

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/95289.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/95289.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《JAVA EE企业级应用开发》第一课笔记

《JAVA EE企业级应用开发》第一课笔记 文章目录《JAVA EE企业级应用开发》第一课笔记课程主题&#xff1a;三层架构与SSM框架概述一、核心架构&#xff1a;三层架构 (MVC)1. 表现层 (Presentation Layer)2. 业务逻辑层 (Business Logic Layer)3. 数据持久层 (Data Persistence …

RT-DETR网络结构

1.前言 本章主要来介绍下RT-DETR的网络结构,参考的依旧是ultralytics实现的RT-DETR-L,代码如下: ultralytics/ultralytics: Ultralytics YOLO 🚀 首先谈谈我对RT-DETR的浅显认识,他不像是YOLOv8这种纯CNN实现的网络,也不像是Vit这种以Transformer实现的网络,他是前一…

Python 文件复制实战指南:从基础操作到高效自动化的最佳实践

Python 文件复制实战指南:从基础操作到高效自动化的最佳实践 1. 引言:文件复制为何是自动化的核心能力? 在日常开发与运维工作中,文件复制是一项基础却至关重要的操作。无论是备份日志、同步配置、部署代码,还是批量迁移数据,都离不开对文件的精准复制与路径管理。而 Py…

WebSocket的基本使用方法

一. 与HTTP对比WebSocket 是一种在单个 TCP 连接上实现全双工&#xff08;双向&#xff09;通信的网络协议&#xff0c;它解决了传统 HTTP 协议 “请求 - 响应” 模式的局限性&#xff0c;让客户端&#xff08;如浏览器&#xff09;和服务器能建立持久连接&#xff0c;实现实时…

架构选型:为何用对象存储替代HDFS构建现代数据湖

在过去十余年的大数据浪潮中&#xff0c;Hadoop及其核心组件HDFS&#xff08;Hadoop分布式文件系统&#xff09;无疑是整个技术生态的基石。它开创性地解决了海量数据的分布式存储难题&#xff0c;支撑了无数企业从数据中挖掘价值。然而&#xff0c;随着数据规模的指数级增长以…

智能养花谁更优?WebIDE PLOY技术与装置的结合及实践价值 —— 精准养护的赋能路径

一、WebIDEPLOY 技术支撑下的智能养花系统核心构成在 WebIDEPLOY 技术的框架下&#xff0c;智能养花装置形成了一套精准协同的闭环系统&#xff0c;其核心在于通过技术整合实现 “监测 - 决策 - 执行 - 远程交互” 的无缝衔接&#xff0c;让植物养护更贴合城市居民的生活节奏。…

基于llama.cpp在CPU环境部署Qwen3

大家好,我是奇文王语,NLP爱好者,长期分享大模型实战技巧,欢迎关注交流。 最近两天在研究如何使用小规模参数的模型在CPU环境上进行落地应用,比如模型Qwen3-0.6B。开始使用Transformers库能够正常把模型服务进行部署起来,但是通过测试速度比较慢,用户的体验会比较差。 …

‌NAT穿透技术原理:P2P通信中的打洞机制解析‌

要说网络世界里的 “幕后功臣”&#xff0c;NAT 绝对得算一个&#xff0c;大家伙儿有没有琢磨过&#xff0c;为啥家里的电脑、手机&#xff0c;还有公司那一堆设备&#xff0c;都能同时连上网&#xff0c;还不打架呢&#xff1f; NAT 这东西&#xff0c;全名叫网络地址转换&am…

工业 5G + AI:智能制造的未来引擎

工业 5G AI&#xff1a;智能制造的未来引擎 文章目录工业 5G AI&#xff1a;智能制造的未来引擎摘要一、为什么工业需要 5G&#xff1f;二、工业 5G 的典型应用场景1. 智能制造工厂2. 远程控制与运维3. 智慧物流与仓储4. 能源、电力、矿山5. 智慧港口与交通三、成功案例解析1…

边缘计算设备 RK3576芯片

RK3576是瑞芯微&#xff08;Rockchip&#xff09;公司专为人工智能物联网&#xff08;AIoT&#xff09;市场精心设计的一款高算力、高性能及低功耗的国产化应用处理器。该处理器采用了先进的ARM架构&#xff0c;集成了四个ARM Cortex-A72高性能核心与四个ARM Cortex-A53高效能核…

ROS1系列学习笔记之T265的Python数据订阅显示、串口输出到凌霄飞控,以及开机自启动设置等一些问题处理方法(持续更新)

前言 关于T265的环境配置与安装&#xff0c;在前两期的ROS笔记中已经提及&#xff0c;包括英特尔本家的SDK安装&#xff0c;以及对应支持版本的ROS支持开发工具包。 ROS1系列学习笔记之Linux&#xff08;Ubuntu&#xff09;的环境安装、依赖准备、踩坑提示&#xff08;硬件以…

UART控制器——ZYNQ学习笔记14

UART 控制器是一个全双工异步收发控制器&#xff0c; MPSoC 内部包含两个 UART 控制器&#xff0c; UART0 和 UART1。每一个 UART 控制器支持可编程的波特率发生器、 64 字节的接收 FIFO 和发送 FIFO、产生中断、 RXD 和TXD 信号的环回模式设置以及可配置的数据位长度、停止位和…

C++ 登录状态机项目知识笔记

C 登录状态机项目知识笔记 1. 项目源码 1.1 login_state_machine.h #pragma once#include <string>// 登录状态枚举 enum class LoginState { IDLE, AUTHENTICATING, SUCCESS, FAILURE, LOCKED };// 登录事件枚举 enum class LoginEvent { REQUEST, SUCCESS, FAILURE, RE…

docker-nacos-v3

nacos官网&#xff1a; Redirecting to: https://nacos.io/ 服务发现和服务健康监测 Nacos 支持基于 DNS 和基于 RPC 的服务发现。服务提供者使用 原生SDK、OpenAPI、或一个独立的Agent TODO注册 Service 后&#xff0c;服务消费者可以使用DNS TODO 或HTTP&API查找和发现服…

DevOps 详解:文化、实践与工具链

目录一、DevOps 定义与核心目标二、DevOps 关键原则与实践1. 持续集成&#xff08;CI&#xff0c;Continuous Integration&#xff09;2. 持续交付&#xff08;CD&#xff0c;Continuous Delivery&#xff09;3. 持续部署&#xff08;Continuous Deployment&#xff09;4. 监控…

人工智能之数学基础:常用的连续型随机变量的分布

本文重点 本文将介绍概率中非常重要的连续型随机变量的分布,主要有均匀分布、指数分布、正态分布 均匀分布 若随机变量X的概率密度为: 如果概率密度函数如上所示,则称X服从区间[ a, b]上的均匀分布,记作X~U[a,b] 均匀分布的概率密度函数的计算如下: 指数分布 指数分布…

【开题答辩全过程】以 校园帮帮团跑腿系统的设计与实现为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人&#xff0c;语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…

Milvus 向量数据库开发实战指南

Milvus向量数据库是什么&#xff1f;-CSDN博客 一、核心概念解析 1.1 基础概念 1.1.1 Bitset&#xff08;位集&#xff09; 高效的数据表示方式&#xff0c;使用位数组替代传统数据类型 默认情况下&#xff0c;位值根据特定条件设置为 0 或 1 1.1.2 通道机制 PChannel&am…

vcruntime140.dll丢失解决办法

解决办法 安装Microsoft Visual C Redistributable https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?viewmsvc-170

LabVIEW实现跨 VI 簇按钮控制功能

​在 LabVIEW 开发场景中&#xff0c;常需实现不同 VI 间的交互操作。本功能借助 VI Server 技术&#xff0c;突破 VI 边界&#xff0c;实现对目标 VI 中簇内按钮控件的属性读取与控制&#xff0c;为多 VI 协同、对VI里已经实现的功能&#xff0c;可以在其他VI中直接使用&#…