基于 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
英文地址,請注明出處:http://en.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中直接使用&#…