创作本文目的:记录自己的学习历程
一、实现TCP电子词典
1.要求
项目要求:
- 登录注册功能,不能重复登录,重复注册
- 单词查询功能
- 历史记录功能,存储单词,意思,以及查询时间
- 基于TCP,支持多客户端连接
- 采用数据库保存用户信息与历史记录
- 将dict.txt的数据导入到数据库中保存。
- 按下ctrl+c退出客户端后,注销该客户端的登录信息
格式要求:
- main函数只跑逻辑,不允许跑功能代码
- 功能代码封装成函数
2.流程图
3.代码
服务器
#include "function.h"
typedef void (*sighandler_t)(int);
void handler(int sig)
{
//回收僵尸进程
while(waitpid(-1, NULL, WNOHANG) > 0);
}
int main(int argc, const char *argv[])
{
int newfd = 0;
pid_t pid = 0;
sqlite3 *db=init_db();
//数据库起始化
if(NULL==db)
{
fprintf(stderr,"起始化失败\n");
return -1;
}
//捕获17号信号 SIGCHLD
sighandler_t s = signal(17, handler);
if(SIG_ERR == s)
{
ERR_MSG("signal");
return -1;
}
//创建流式套接字
int sfd = socket(AF_INET,SOCK_STREAM,0);
if(sfd<0)
{
ERR_MSG("socket");
return -1;
}
//允许端口快速重用
int reuse = 1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
{
ERR_MSG("setsocket");
return -1;
}
//填写服务器地址信息结构体
struct sockaddr_in ser;
ser.sin_family = AF_INET;
ser.sin_port = htons(PORT);
ser.sin_addr.s_addr = inet_addr(IP);
//绑定
if(bind(sfd,(struct sockaddr *)&ser,sizeof(ser))!=0)
{
ERR_MSG("bind");
return -1;
}
//监听
if(listen(sfd,10)!=0)
{
ERR_MSG("listen");
return -1;
}
//保存客户端的地址信息结构体
struct sockaddr_in cli;
socklen_t addrlen = sizeof(cli);
while(1)
{
//父进程
//生成通信的文件描述符
int newfd = accept(sfd,(struct sockaddr *)&cli,&addrlen);
if(newfd<0)
{
ERR_MSG("accept");
return -1;
}
pid = fork();
if(pid>0)
{
//节省父进程文件描述符
close(newfd);
}
else if(0==pid)
{
//节省子进程文件描述符
close(sfd);
//与客户端交互函数
interact(newfd,db,cli);
//交互结束就关闭
close(newfd);
exit(0);
}
else
{
ERR_MSG("fork");
return -1;
}
}
//关闭数据库
if(sqlite3_close(db)!=SQLITE_OK)
{
fprintf(stderr,"__%d__ sqlite3_close:%s\n",__LINE__,sqlite3_errmsg(db));
}
//关闭文件描述符
close(sfd);
return 0;
}
客户端
#include "function.h"
int main(int argc, const char *argv[])
{
char choose = 0;
//创建流式套接字
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
//填充要连接的服务器的地址信息结构体
struct sockaddr_in ser;
ser.sin_family = AF_INET;
ser.sin_port = htons(PORT);
ser.sin_addr.s_addr = inet_addr(IP);
//连接服务器 connect
if(connect(sfd, (struct sockaddr*)&ser, sizeof(ser)) < 0)
{
ERR_MSG("connect");
return -1;
}
//选择界面
while(1)
{
system("clear");
printf("-----------------------\n");
printf("--------1. 注册--------\n");
printf("--------2. 登录--------\n");
printf("--------3. 退出--------\n");
printf("-----------------------\n");
printf("请输入选项>>>");
choose = getchar();
while(getchar()!=10);
switch(choose)
{
case '1':
//注册函数
my_register(sfd);
break;
case '2':
//登录函数
login(sfd);
break;
case '3':
//退出
goto END;
break;
default:
printf("输入错误,请重新输入\n");
}
printf("输入任意字符清屏>>>");
while(getchar()!=10);
}
END:
//关闭套接字
close(sfd);
return 0;
}
功能代码
#include "function.h"
//数据库起始化
sqlite3 *init_db()
{
sqlite3 *db=NULL;
//打开数据库
if(sqlite3_open("./dict.db", &db) != SQLITE_OK)
{
fprintf(stderr, "__%d__ sqlite3_open:%s\n", __LINE__,
sqlite3_errmsg(db));
return NULL;
}
//创建账户信息表格
char sql[400] = "create table if not exists user (username char primary key,password char,state int)";
char* errmsg = NULL;
if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "__%d__ sqlite3_exec:%s\n", __LINE__,errmsg);
return NULL;
}
//创建历史记录表格
sprintf(sql,"create table if not exists record (username char,word char,mean char,time char)");
if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "__%d__ sqlite3_exec:%s\n", __LINE__,errmsg);
return NULL;
}
//导入dict.txt文本到数据库
if(import_dict(db)!=0)
{
fprintf(stderr,"__%d__ 词典导入失败\n",__LINE__);
return NULL;
}
//用户表全部更新为下线状态
sprintf(sql, "update user set state=%d where state=%d;",NOTLOGIN,LOGIN);
if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "__%d__ error_code:%d sqlite3_exec:%s\n", __LINE__,sqlite3_errcode(db),errmsg);
return NULL;
}
printf("数据库起始化完成\n");
return db;
}
//导入dict.txt文本到数据库
int import_dict(sqlite3* db)
{
char sql[200] = "create table if not exists dict (word char, mean char)";
char* errmsg = NULL;
//创建表格
if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "__%d__ sqlite3_exec:%s\n", __LINE__,errmsg);
return -1;
}
char **pres = NULL; //存储查询结果的首地址
int row=0; //查询结果的行数
int column=0; //查询结果的列数
sprintf(sql,"select * from dict;");
if(sqlite3_get_table(db,sql,&pres,&row,&column,&errmsg)!=SQLITE_OK)
{
fprintf(stderr,"__%d__ sqlite3_get_table:%s\n",__LINE__,errmsg);
return -1;
}
if(row>7000)
{
printf("词典已导入\n");
return 0;
}
//创建表格
if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "__%d__ sqlite3_exec:%s\n", __LINE__,errmsg);
return -1;
}
//打开文件
FILE* fp = fopen("./dict.txt", "r");
if(NULL == fp)
{
ERR_MSG("fopen");
return -1;
}
//循环读取一行,直到读取完毕为止
char buf[300] = "";
char word[50]="", mean[300]="";
int i = 0;
while(fgets(buf, sizeof(buf)