在 Linux 环境下,利用 C 语言实现一个支持多用户并发访问的词典系统是一个很有意义的实践项目。本文将详细介绍如何通过 TCP socket 通信、多线程编程和 SQLite 数据库,构建一个包含用户注册、登录、单词查询及背词本功能的完整词典应用。
项目概述
本项目由三个核心部分组成:
- 服务器端(finds.c):采用多线程模型,支持多客户端同时连接,负责处理用户认证、单词查询、历史记录存储等核心逻辑
- 客户端(findc.c):提供交互式菜单界面,实现与服务器的通信交互
- 单词库(dict.txt):存储单词及释义的文本文件
技术栈:
- 网络通信:TCP socket
- 并发处理:POSIX 线程(pthread)
- 数据存储:SQLite3 数据库
- 开发环境:Linux(Ubuntu)
核心功能设计
1. 系统架构
- 客户端 – 服务器模型:基于 TCP 协议实现可靠通信
- 多线程并发:服务器为每个客户端连接创建独立线程,实现并发处理
- 数据持久化:使用 SQLite3 存储用户信息和查询历史
2. 数据库设计
创建两个核心表:
sql
— 用户信息表
create table if not exists usrinfo(
name varchar(20),
password varchar(20)
);
— 历史查询记录表(背词本)
create table if not exists historyinfo(
word varchar(20),
name varchar(20),
time varchar(20)
);
3. 通信协议设计
定义消息结构体MSG实现数据交互:
c
运行
typedef struct{
int type; // 消息类型(R-注册/L-登录/Q-查询/H-历史/E-退出)
char name[20]; // 用户名
char data[256]; // 数据(密码/单词/释义)
}MSG;
服务器端实现(finds.c)
1. 多线程并发处理
服务器通过pthread_create为每个客户端连接创建线程,核心代码:
c
运行
while(1){
int newconfd = accept(sfd, NULL, NULL); // 接收客户端连接
pthread_t id;
pthread_create(&id, NULL, dealclient, &newconfd); // 创建线程
pthread_detach(id); // 分离线程,自动回收资源
}
线程处理函数dealclient循环读取客户端消息,根据消息类型分发处理:
c
运行
void* dealclient(void* p){
int newconfd = *(int*)p;
MSG m;
while(1){
read(newconfd, &m, sizeof(m)); // 读取消息
switch(m.type){
case E: dealexit(&m, newconfd); break;
case R: dealregister(&m, newconfd); break;
case L: deallogin(newconfd, &m); break;
case Q: dealquery(newconfd, &m); break;
case H: dealhistory(newconfd, &m); break;
}
}
}
2. 核心功能模块
(1)用户注册与登录
- 注册时检查用户名唯一性,通过sqlite3_get_table查询用户是否存在
- 登录时验证用户名和密码,成功后返回登录状态
c
运行
// 注册处理示例
void dealregister(MSG *pm, int newconfd){
char sql[100];
char **result = NULL;
int row, col;
sprintf(sql, "select *from usrinfo where name='%s';", pm->name);
sqlite3_get_table(db, sql, &result, &row, &col, NULL);
if(!row){ // 用户名不存在,允许注册
sprintf(sql, "insert into usrinfo values('%s','%s')", pm->name, pm->data);
sqlite3_exec(db, sql, NULL, NULL, NULL);
strcpy(pm->data, "注册成功");
}else{
strcpy(pm->data, "用户已存在");
}
sqlite3_free_table(result); // 释放结果集,避免内存泄漏
write(newconfd, pm, sizeof(MSG));
}
(2)单词查询与历史记录
- 从dict.txt中查询单词,使用strncmp进行高效匹配
- 查询成功后自动记录到历史表,包含单词、用户名和查询时间
c
运行
// 单词查询处理
void dealquery(int newconfd, MSG *pm){
char sql[100];
int ret = searchword(pm->data); // 从词典文件查询
if(ret){ // 查询成功
time_t t = time(NULL);
char *ptime = ctime(&t);
// 插入历史记录
sprintf(sql, "insert into historyinfo values('%s','%s','%s');",
pm->data, pm->name, ptime);
sqlite3_exec(db, sql, NULL, NULL, NULL);
}else{
strcpy(pm->data, "单词不存在");
}
write(newconfd, pm, sizeof(MSG));
}
(3)背词本(历史记录)查询
- 按用户名查询历史记录,通过循环发送多条记录,并以sendover标记结束
- 使用usleep(3)避免 TCP 粘包问题
c
运行
void dealhistory(int newconfd, MSG *pm){
char sql[100];
sprintf(sql, "select *from historyinfo where name='%s';", pm->name);
char **result = NULL;
int row, col;
sqlite3_get_table(db, sql, &result, &row, &col, NULL);
if(row){ // 有查询记录
for(int i=0; i<(row+1)*col; i++){ // 循环发送记录
strcpy(pm->data, result[i]);
write(newconfd, pm, sizeof(MSG));
usleep(3); // 避免粘包
}
strcpy(pm->data, "sendover"); // 发送结束标记
write(newconfd, pm, sizeof(MSG));
}
sqlite3_free_table(result);
}
客户端实现(findc.c)
1. 交互界面设计
- 一级菜单:注册、登录、退出
- 二级菜单(登录后):查单词、背词本、退出
c
运行
// 二级菜单示例
void show(int cfd, MSG* pm){
int choose;
while(1){
puts("1查单词 2背词本 3退出");
scanf("%d", &choose);
switch(choose){
case 1: dealquery(cfd, pm); break;
case 2: dealhistory(cfd, pm); break;
case 3: dealexit(cfd); exit(0);
}
}
}
2. 单词查询流程
- 循环输入单词(按#结束),发送查询请求
- 接收并显示服务器返回的单词释义
c
运行
void dealquery(int cfd, MSG* pm){
pm->type = Q;
while(1){
puts("请输入要查询的单词按#结束");
scanf("%s", pm->data);
if(!strcmp(pm->data, "#")) break;
write(cfd, pm, sizeof(MSG));
read(cfd, pm, sizeof(MSG));
puts(pm->data); // 显示查询结果
}
}
编译与运行
服务器编译
需要链接 SQLite3 和 pthread 库:
bash
gcc finds.c -o server -lsqlite3 -lpthread
客户端编译
bash
gcc findc.c -o client
运行步骤
bash
./server 8888
bash
./client 127.0.0.1 8888
问题与解决方案
SQLite3 内存泄漏
每次使用sqlite3_get_table后必须调用sqlite3_free_table释放结果集
TCP 粘包问题
通过usleep(3)在连续发送多条记录时添加微小延迟
多线程数据库安全
使用全局数据库连接时,确保 SQLite 编译时开启线程安全选项(默认开启)
未定义引用错误
编译时忘记链接库会导致undefined reference,需添加-lsqlite3 -lpthread
总结与扩展
本项目实现了一个功能完整的词典系统,包含用户认证、单词查询和历史记录管理。可进一步扩展的功能:
- 添加密码加密存储(如 MD5 哈希)
- 实现单词本分页查询
- 增加单词发音功能
- 优化词典查询算法(如二分查找)
通过这个项目,不仅能掌握 Linux 网络编程和多线程技术,还能深入理解客户端 – 服务器架构和数据库应用,为更复杂的网络应用开发打下基础。
评论前必须登录!
注册