项目需求
功能描述
仿照有道云词典功能,实现一个自己的云词典。自行定义项目名,最终可以体现到简历中。
效果参考
功能矩阵
功能模块 | 功能点 | 功能点描述 | 优先级 | 备注 |
客户端 | 注册 | 可实现新用户的注册功能 | A | |
登录 | 支持用户登录校验,错误给出提示,正确进入操作菜单 | A | ||
查词 | 可以进行单词释义查询操作 | A | ||
查词历史 | 可查看当前用户的查词历史 | A | ||
服务器 | 注册 | 实现用户的注册功能,并永久保存到数据库中 | A | |
登录 | 实现用户登录请求的校验 | B | ||
查词 | 实现用户的查词请求并返回释义结果 | A | ||
历史 | 根据当前用户返回查词历史 | A | ||
心跳包检测 | tcp的keepalive机制,确保客户端是否在线 | C | 选做 |
流程图:
程序框架
通过网盘分享的文件:框架 提取码: pqqnhttps://pan.baidu.com/s/1fv-svjLrQhSTZKQ2t-Gd5w?pwd=pqqn
参考
server.c
//sever.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sqlite3.h>
#include <signal.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define DATABASE "my.db"
#define N 16
enum CMD
{
R = 1,
L,
Q,
H
};
typedef struct sockaddr SA;
typedef struct
{
int type;
char name[N];
char data[1000]; // password or word
} MSG;
static sqlite3 *db;
/**
* @brief 服务器处理注册
* 得到用户输入用户名和密码并拼接sql语句
* 执行sql语句
* 封装执行结果并回发客户端
*/
void do_register(int connfd, MSG *pbuf)
{
char *errmsg;
char sql[256] = "";
sprintf(sql, "insert into usr values('%s','%s');", pbuf->name, pbuf->data);
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != 0)
{
printf("注册失败:%s\n", errmsg);
strcpy(pbuf->data, "用户名名重复");
send(connfd, pbuf, sizeof(MSG), 0);
return;
}
strcpy(pbuf->data, "注册成功!");
printf("%s\n", pbuf->data);
send(connfd, pbuf, sizeof(MSG), 0);
return;
}
/**
* @brief 服务器处理登录
* 得到用户输入用户名和密码并拼接sql语句
* 执行sql语句
* 封装执行结果并回发客户端
*/
void do_login(int connfd, MSG *pbuf)
{
char *errmsg;
char **Result = NULL;
char **Re = NULL;
char sql[2000];
int lie, hang;
sprintf(sql, "select name from usr;");
if (sqlite3_get_table(db, sql, &Re, &hang, &lie, &errmsg) != 0)
{
printf("get table err:%s\n", errmsg);
return;
}
memset(sql, 0, sizeof(sql));
for (int i = 0; i < hang + 1; i++)
{
if (!strcmp(Re[i], pbuf->name))
{
sprintf(sql, "select pass from usr where name = \"%s\";", pbuf->name);
if (sqlite3_get_table(db, sql, &Result, &hang, &lie, &errmsg) != 0)
{
printf("get table err:%s\n", errmsg);
return;
}
if (!strcmp(pbuf->data, Result[1]))
strcpy(pbuf->data, "登陆成功!");
else
{
strcpy(pbuf->data, "用户名或密码错误");
}
send(connfd, pbuf, sizeof(MSG), 0);
printf("%s\n", pbuf->data);
memset(pbuf->data, 0, sizeof(pbuf->data));
memset(pbuf->name, 0, sizeof(pbuf->name));
memset(sql, 0, sizeof(sql));
return;
}
}
strcpy(pbuf->data, "用户名不存在");
send(connfd, pbuf, sizeof(MSG), 0);
printf("%s\n", pbuf->data);
memset(pbuf->data, 0, sizeof(pbuf->data));
memset(pbuf->name, 0, sizeof(pbuf->name));
memset(sql, 0, sizeof(sql));
return;
}
/**
* @brief 返回当前的时间,存到date中
* @return
*/
void get_date(char *date)
{
time_t t;
struct tm *tp;
time(&t);
tp = localtime(&t);
sprintf(date, "%d-%02d-%02d %02d:%02d:%02d", tp->tm_year + 1900,
tp->tm_mon + 1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec);
strftime(date, 64, "%Y-%m-%d %H:%M:%S", tp);
return;
}
/**
* @brief 服务器处理查询
* 查询单词释义
* 把本次查询添加到查询历史中
*/
void do_query(int connfd, MSG *pbuf)
{
char buf[1024] = "";
char *errmsg;
char **Result = NULL;
char sql[1000];
int lie,hang;
sprintf(sql, "select * from dic where word = '%s';", pbuf->data);
strcpy(buf, pbuf->data);
if (sqlite3_get_table(db, sql, &Result, &hang, &lie, &errmsg) != 0)
{
printf("get table err:%s\n", errmsg);
strcpy(pbuf->data, "没有查到该单词\n");
send(connfd, pbuf, sizeof(MSG), 0);
return;
}
if (hang == 0 || Result == NULL)
{
strcpy(pbuf->data, "没有查到该单词\n");
send(connfd, pbuf, sizeof(MSG), 0);
return;
}
// printf("Result[3]:%s\n",Result[3]);
// printf("hang: %d lie:%d\n",hang,lie);
printf("%s", Result[3]);
strcpy(pbuf->data, Result[3]);
send(connfd, pbuf, sizeof(MSG), 0);
memset(pbuf->data, 0, sizeof(pbuf->data));
get_date(pbuf->data);
memset(sql, 0, sizeof(sql));
sprintf(sql, "insert into record values('%s','%s','%s');", pbuf->name, pbuf->data, buf);
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != 0)
{
printf("into err:%s\n", errmsg);
return;
}
return;
}
/**
* @brief 查询历史处理
* 根据用户名来查找查询历史,并将结果返回给客户端
* 执行sql语句
* 封装执行结果并回发客户端
*/
void do_history(int connfd, MSG *pbuf)
{
char *errmsg;
char **Result=NULL;
char sql[1000];
int lie,hang;
printf("successs - --");
printf("%s----",pbuf->data);
memset(pbuf->data,0,sizeof(pbuf->data));
sprintf(sql,"select * from record ;");
if(sqlite3_get_table(db,sql,&Result,&hang,&lie,&errmsg) !=0)
{
printf("get table err:%s\n",errmsg);
strcpy(pbuf->data,"查询错误");
send(connfd,pbuf,sizeof(MSG),0);
return;
}
else if (hang == 0 || Result == NULL)
{
strcpy(pbuf->data,"查询失败\n");
printf("%s",pbuf->data);
send(connfd,pbuf,sizeof(MSG),0);
return;
}
int k=3;
printf("Result[0]:%s\n",Result[0]);
printf("Result[1]:%s\n",Result[1]);
printf("Result[2]:%s\n",Result[2]);
printf("Result[3]:%s\n",Result[3]);
printf("Result[4]:%s\n",Result[4]);
for(int i = 1;i<=hang;i++)
{
for(int j =0;j< lie;j++)
{
strcat(pbuf->data,Result[k++]);
strcat(pbuf->data," ");
}
strcat(pbuf->data,"\n");
}
send(connfd,pbuf,sizeof(MSG),0);
printf("%s",pbuf->data);
return;
}
/**
* @brief do_client 处理客户端请求
* @param connfd 客户端fd
* @param db 数据库句柄
*/
void do_client(int connfd)
{
MSG buf;
while (recv(connfd, &buf, sizeof(buf), 0) > 0)
{
switch (buf.type)
{
case R:
printf("will reg\n");
do_register(connfd, &buf);
break;
case L:
printf("will login\n");
do_login(connfd, &buf);
break;
case Q:
printf("will query\n");
do_query(connfd, &buf);
break;
case H:
printf("will history\n");
do_history(connfd, &buf);
break;
default:
break;
}
}
exit(0);
}
int main(int argc, char *argv[])
{
int listenfd, connfd;
struct sockaddr_in myaddr;
pid_t pid;
MSG buf;
if (argc < 2)
{
printf("Usage : %s <port>\n", argv[0]);
exit(-1);
}
//打开数据库
if (sqlite3_open(DATABASE, &db) < 0)
{
printf("fail to sqlite3_open : %s\n", sqlite3_errmsg(db));
return -1;
}
//创建服务器socket
listenfd = socket(PF_INET, SOCK_STREAM, 0);
if (listenfd < 0)
{
perror("fail to socket");
exit(-1);
}
bzero(&myaddr, sizeof(myaddr));
myaddr.sin_family = PF_INET;
myaddr.sin_port = htons(atoi(argv[1]));
myaddr.sin_addr.s_addr = INADDR_ANY;
if (bind(listenfd, (SA *)&myaddr, sizeof(myaddr)) < 0)
{
perror("fail to bind");
exit(-1);
}
if (listen(listenfd, 5) < 0)
{
perror("fail to listen");
exit(-1);
}
//并发服务器
while (1)
{
if ((connfd = accept(listenfd, NULL, NULL)) < 0)
{
perror("fail to accept");
exit(-1);
}
pid = fork();
if (pid == -1)
{
perror("fail to fork\n");
exit(-1);
}
else if (pid == 0)
{ //子进程
printf("a user comming\n");
do_client(connfd);
}
else
{ //父进程
close(connfd);
}
}
}
client.c
//client.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sqlite3.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define N 16
typedef struct sockaddr SA;
enum CMD
{
R = 1, //注册
L, //登录
Q, //查询
H //历史
};
typedef struct
{
int type;
char name[N];
char data[1000]; // password or word
} MSG;
/**
* @brief 客户端注册
* 用户输入用户名和密码
* 封装消息
* 发送
* 接收并输出结果
*/
void do_register(int sockfd, MSG *pbuf)
{
pbuf->type = 1;
printf("input name:");
//fgets(pbuf->name,N,stdin);
scanf("%s", pbuf->name);
getchar();
// fgets(pbuf->data,256,stdin);
// send(sockfd,pbuf,sizeof(buf),0);
printf("input password:");
scanf("%s", pbuf->data);
getchar();
send(sockfd, pbuf, sizeof(MSG), 0);
memset(pbuf->data, 0, sizeof(pbuf->data));
int ret = recv(sockfd, pbuf, sizeof(MSG), 0);
if (ret < 0)
{
perror("recv err");
return;
}
printf("%s\n", pbuf->data);
memset(pbuf->data, 0, sizeof(pbuf->data));
memset(pbuf->name, 0, sizeof(pbuf->name));
return;
}
/**
* @brief 客户端登录
* 用户输入用户名和密码
* 封装消息
* 发送
* 接收并输出结果
* @return 成功返回1 失败返回0
*/
int do_login(int sockfd, MSG *pbuf)
{
memset(pbuf->data, 0, sizeof(pbuf->data));
memset(pbuf->name, 0, sizeof(pbuf->name));
pbuf->type = 2;
printf("请输入用户名:");
scanf("%s", pbuf->name);
getchar();
printf("请输入密码:");
scanf("%s", pbuf->data);
getchar();
send(sockfd, pbuf, sizeof(MSG), 0);
memset(pbuf->data, 0, sizeof(pbuf->data));
int ret = recv(sockfd, pbuf, sizeof(MSG), 0);
if (ret < 0)
{
perror("recv err");
return 0;
}
printf("%s\n", pbuf->data);
if (!strcmp(pbuf->data, "登陆成功!"))
return 1;
else
return 0;
}
/**
* @brief 客户端查询
* 查询历史
*/
void do_history(int sockfd, MSG *pbuf)
{
pbuf->type = H;
send(sockfd, pbuf, sizeof(MSG), 0);
int ret = recv(sockfd, pbuf, sizeof(MSG), 0);
if (ret < 0)
{
perror("recv err");
return;
}
printf("%s", pbuf->data);
return;
}
/**
* @brief 客户端查询
* 输入单词或“#”
* 封装消息
* 发送
* 接收并输出结果
*/
void do_query(int sockfd, MSG *pbuf)
{
while (1)
{
memset(pbuf->data, 0, sizeof(pbuf->data));
pbuf->type = Q;
printf("请输入单词或#(#为退出):");
scanf("%s", pbuf->data);
getchar();
//printf("getchar success!\n");
if (!strcmp(pbuf->data, "#"))
break;
send(sockfd, pbuf, sizeof(MSG), 0);
memset(pbuf->data, 0, sizeof(pbuf->data));
int ret = recv(sockfd, pbuf, sizeof(MSG), 0);
if (ret < 0)
{
perror("recv err");
return;
}
// if(ret>0)
// {
// printf("recv seccuss");
// }
printf("%s", pbuf->data);
}
}
void enter_query(int sockfd, MSG *buf)
{
int input;
char cleanbuf[64];
while (1)
{
printf("***********************************************\n");
printf("* 1: query_word 2: history_record 3: quit *\n");
printf("***********************************************\n");
printf("please choose : ");
//获取用户输入
if (scanf("%d", &input) == 0)
{
fgets(cleanbuf, 64, stdin); //类型错误需要重新清除输入缓冲区
continue;
}
switch (input)
{
case 1:
printf("\n");
do_query(sockfd, buf);
printf("\n");
break;
case 2:
printf("\n");
do_history(sockfd, buf);
printf("\n");
break;
case 3:
return;
default:
break;
}
}
}
int main(int argc, char *argv[])
{
int sockfd, login = 0;
struct sockaddr_in servaddr;
MSG buf;
char clean[64];
if (argc < 3)
{
printf("Usage : %s <serv_ip> <serv_port>\n", argv[0]);
exit(-1);
}
//创建客户端socket
if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
perror("fail to socket");
exit(-1);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = PF_INET;
servaddr.sin_port = htons(atoi(argv[2]));
servaddr.sin_addr.s_addr = inet_addr(argv[1]);
//连接服务器
if (connect(sockfd, (SA *)&servaddr, sizeof(servaddr)) < 0)
{
perror("fail to connect");
exit(-1);
}
int input;
char cleanbuf[64];
while (1)
{
printf("************************************\n");
printf("* 1: register 2: login 3: quit *\n");
printf("************************************\n");
printf("please choose : ");
//获取用户输入
if (scanf("%d", &input) == 0)
{
fgets(cleanbuf, 64, stdin); //类型错误需要重新清除输入缓冲区
continue;
}
switch (input)
{
case 1:
do_register(sockfd, &buf);
break;
case 2:
if (do_login(sockfd, &buf) == 1)
{ //成功
enter_query(sockfd, &buf);
}
break;
case 3:
close(sockfd);
exit(0);
break;
default:
break;
}
}
}
项目文件夹及其单词数据库
通过网盘分享的文件:dictionary.zip 提取码: 5sr9https://pan.baidu.com/s/1DwZnggUNhxK_wgzFI0ytjw?pwd=5sr9
视频演示
标签:int,pbuf,TCP,MSG,printf,sqlite3,sizeof,data,词典 From: https://blog.csdn.net/weixin_63791423/article/details/142314074