目录
一、功能要求
仿照云词典的原理,实现云词典功能,用户可以查询输入的单词的英文解释,也可以查看历史记录。
服务器
处理客户端的请求,并将数据存入数据库中,客户端请求的数据从数据库进行获取,服务器转发给客户端。
用户客户端
实现账号的注册、登录功能,当登录成功时可以查单词、查看历史记录等功能。
二、演示效果
1.登录、注册功能
2. 查单词功能
3.查看历史纪录功能
三、项目代码
1.头文件
#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>
#include <sys/select.h>
struct word_msg
{
char word[20]; // 单词
char parse[256]; // 含义
};
typedef struct
{
int acceptfd; // 链接客户端
unsigned char type; // 功能类型 R:注册 L:登录 Q:查询 H:历史记录
char name[32]; // 用户名
char password[32]; // 密码
char buf[1024]; // 调试信息
struct word_msg msgg; // 单词信息
} MSG;
2.服务器
#include "head.h"
fd_set readfds, tempfds;
sqlite3 *db;
MSG msg;
void zhuce(MSG *msg); // 注册
void denglu(MSG *msg); // 登录
void chaxun(MSG *msg); // 查询
void lishi(MSG *msg); // 历史
int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("用法:<port>\n");
return -1;
}
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
printf("sorkfd:%d\n", sockfd);
struct sockaddr_in saddr, caddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(atoi(argv[1]));
saddr.sin_addr.s_addr = INADDR_ANY;
socklen_t addrlen = sizeof(saddr);
if (bind(sockfd, (struct sockaddr *)&saddr, addrlen) < 0)
{
perror("bind失败");
close(sockfd);
return -1;
}
printf("bind成功\n");
if (listen(sockfd, 7) < 0)
{
perror("lisren失败");
close(sockfd);
return -1;
}
printf("listen成功\n");
// 打开数据库
if (sqlite3_open("./yuncidian.db", &db) < 0)
{
printf("打开数据库失败: %s\n", sqlite3_errmsg(db));
return -1;
}
FD_ZERO(&readfds);
FD_ZERO(&tempfds);
FD_SET(sockfd, &readfds);
int max = sockfd;
while (1)
{
tempfds = readfds;
int ret = select(max + 1, &tempfds, NULL, NULL, NULL);
if (ret == -1)
{
perror("select失败");
close(sockfd);
return -1;
}
if (FD_ISSET(sockfd, &tempfds))
{
int acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &addrlen);
if (acceptfd < 0)
{
perror("accept失败\n");
return -1;
}
printf("客户端ip:%s\t 端口号:%d\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));
FD_SET(acceptfd, &readfds);
if (max < acceptfd)
{
max = acceptfd;
}
}
for (int i = sockfd + 1; i <= max; i++)
{
if (FD_ISSET(i, &tempfds))
{
int ret = recv(i, &msg, sizeof(msg), 0);
msg.acceptfd = i;
if (ret < 0)
{
perror("recv失败");
close(sockfd);
return -1;
}
else if (ret == 0)
{
printf("客户端acceptfd:%d退出\n", i);
FD_CLR(i, &readfds);
close(i);
while (!FD_ISSET(max, &readfds))
{
max--;
}
}
else
{
printf("%c\n", msg.type);
switch (msg.type)
{
case 'R':
zhuce(&msg);
break;
case 'L':
denglu(&msg);
break;
case 'Q':
chaxun(&msg);
break;
case 'H':
lishi(&msg);
break;
default:
break;
}
}
}
}
}
return 0;
}
void zhuce(MSG *msg) // 注册
{
memset(msg->buf, 0, sizeof(msg->buf));
char *errmsg = NULL;
char sql[128];
char check_sql[128];
char **result = NULL;
int rows, columns;
while (1)
{ // 检查用户名是否已存在
sprintf(check_sql, "select * from user where name='%s';", msg->name);
if (sqlite3_get_table(db, check_sql, &result, &rows, &columns, &errmsg) != 0)
{
sprintf(msg->buf, "查询失败: %s", errmsg);
break;
}
if (rows > 0)
{
sprintf(msg->buf, "用户名已存在");
break;
}
// 执行插入操作
sprintf(sql, "insert into user values('%s','%s');", msg->name, msg->password);
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != 0)
{
sprintf(msg->buf, "注册失败: %s", errmsg);
break;
}
else
{
printf("%s\n", sql);
sprintf(msg->buf, "注册成功");
break;
}
}
send(msg->acceptfd, msg->buf, sizeof(msg->buf), 0);
}
void denglu(MSG *msg) // 登录
{
memset(msg->buf, 0, sizeof(msg->buf));
char sql[128];
char *errmsg = NULL;
char **result = NULL;
int rows, columns;
while (1)
{
sprintf(sql, "select * from user where name = '%s' and password = '%s';", msg->name, msg->password);
// 执行 SQL 查询
int sqlget = sqlite3_get_table(db, sql, &result, &rows, &columns, &errmsg);
if (sqlget != 0)
{
sprintf(msg->buf, "sqlite3_get_table错误%s", errmsg);
break;
}
else if (rows > 0)
{
sprintf(msg->buf, "登录成功");
break;
}
else
{
sprintf(msg->buf, "登录失败");
break;
}
}
send(msg->acceptfd, msg->buf, strlen(msg->buf), 0);
}
void chaxun(MSG *msg) // 查询
{
memset(msg->buf, 0, sizeof(msg->buf));
char sql[1024];
char sql1[1024];
char *errmsg = NULL;
char **result = NULL;
int rows, columns;
time_t t;
struct tm *timeinfo;
char date[100];
time(&t);
timeinfo = localtime(&t);
while (1)
{
snprintf(date, sizeof(date), "%d-%d-%d %d:%d:%d", timeinfo->tm_year + 1900, timeinfo->tm_mon + 1, timeinfo->tm_mday, timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec);
snprintf(sql, sizeof(sql), "select parse from dic where word='%s';", msg->msgg.word);
snprintf(sql1, sizeof(sql1), "insert into history values ('%s','%s','%s');", msg->name, date, msg->msgg.word);
if (sqlite3_exec(db, sql1, NULL, NULL, &errmsg) != 0)
{
sprintf(msg->buf, "放入历史记录失败: %s", errmsg);
break;
}
printf("%s\n", sql1);
int rc = sqlite3_get_table(db, sql, &result, &rows, &columns, &errmsg);
if (rc != 0)
{
sprintf(msg->buf, "查询失败: %s", errmsg);
}
else
{
if (rows > 0)
{
for (int i = 1; i <= rows; ++i)
{
strcat(msg->buf, "查询到的单词信息:");
strcat(msg->buf, result[i * columns]);
strcat(msg->buf, "\n");
}
printf("%s\n", sql);
break;
}
else
{
strcpy(msg->buf, "没有找到该单词信息.");
break;
}
}
}
send(msg->acceptfd, msg->buf, strlen(msg->buf) - 1, 0);
}
void lishi(MSG *msg) // 历史
{
printf("%s\n", msg->name);
char sql[1100];
char *errmsg = NULL;
char **result = NULL;
int rows, columns;
while (1)
{
sprintf(sql, "select date,word from history where name='%s';", msg->name);
memset(msg->buf, 0, sizeof(msg->buf));
int rc = sqlite3_get_table(db, sql, &result, &rows, &columns, &errmsg);
if (rc != 0)
{
sprintf(msg->buf, "查询历史记录失败: %s", errmsg);
break;
}
else
{
if (rows > 0)
{
for (int i = 1; i <= rows; ++i)
{
strcat(msg->buf, "查询到的信息:");
for (int j = 0; j < columns; ++j)
{
strcat(msg->buf, result[i * columns + j]);
strcat(msg->buf, " ");
}
strcat(msg->buf, "\n");
}
break;
}
else
{
strcpy(msg->buf, "该历史记录不存在");
break;
}
}
}
send(msg->acceptfd, msg->buf, strlen(msg->buf) - 1, 0);
}
3.用户端
#include "head.h"
int flag = 0;
fd_set readfds, tempfds;
sqlite3 *db;
MSG msg;
void denglujiemian(); // 登录界面
void yuncidianjiemian(); // 云词典界面
void zhuce(MSG *msg); // 注册
void denglu(MSG *msg); // 登录
void chaxun(MSG *msg); // 查询
void lishi(MSG *msg); // 历史
int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("用法:<port>\n");
return -1;
}
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
perror("socket err");
return -1;
}
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(atoi(argv[1]));
saddr.sin_addr.s_addr = inet_addr("192.168.50.214");
socklen_t addrlen = sizeof(saddr);
if (connect(sockfd, (struct sockaddr *)&saddr, addrlen) < 0)
{
perror("connect err");
return -1;
}
printf("connect 成功\n");
while (1)
{
memset(msg.buf, 0, sizeof(msg.buf));
msg.acceptfd = sockfd;
if (flag == 0)
{
denglujiemian(); // 登录界面
}
if (flag == 1)
{
yuncidianjiemian(); // 云词典界面
}
printf("请输入选项: ");
scanf(" %c", &msg.type);
printf("msg.type:%c\n", msg.type);
switch (msg.type)
{
case 'R':
zhuce(&msg); // 注册
break;
case 'L':
denglu(&msg); // 登录
break;
case 'Q':
chaxun(&msg); // 查询
break;
case 'H':
lishi(&msg); // 历史
break;
case 'D':
flag = 0; // 返回登录菜单
break;
default:
break;
}
}
// 6.关闭
close(sockfd);
return 0;
}
void denglujiemian() // 登录界面
{
printf("************************************\n");
printf("* R: register L: login D: quit *\n");
printf("************************************\n");
}
void yuncidianjiemian() // 云词典界面
{
printf("***********************************************\n");
printf("* Q: query_word H: history_record D: quit *\n");
printf("***********************************************\n");
}
void zhuce(MSG *msg) // 注册
{
memset(msg->name, 0, sizeof(msg->name));
memset(msg->password, 0, sizeof(msg->password));
printf("请输入用户名:");
scanf("%s", msg->name);
getchar();
printf("请输入用户密码:");
scanf("%s", msg->password);
getchar();
send(msg->acceptfd, msg, sizeof(*msg), 0);
recv(msg->acceptfd, msg->buf, sizeof(msg->buf), 0);
printf("%s\n", msg->buf);
}
void denglu(MSG *msg) // 登录
{
while (1)
{
memset(msg->name, 0, sizeof(msg->name));
memset(msg->password, 0, sizeof(msg->password));
printf("请输入用户名:");
scanf("%s", msg->name);
getchar();
printf("请输入用户密码:");
scanf("%s", msg->password);
getchar();
send(msg->acceptfd, msg, sizeof(*msg), 0);
recv(msg->acceptfd, msg->buf, sizeof(msg->buf), 0);
printf("%s\n", msg->buf);
if (strcmp(msg->buf, "登录成功") == 0)
{
flag = 1;
break;
}
if (strcmp(msg->buf, "登录失败") == 0)
{
continue;
}
}
}
void chaxun(MSG *msg) // 查询
{
while (1)
{
memset(msg->buf, 0, sizeof(msg->buf));
memset(msg->msgg.word, 0, sizeof(msg->msgg.word));
printf("请输入要查询的单词(输入#退出查询):");
scanf("%s", msg->msgg.word);
getchar();
if (strcmp(msg->msgg.word, "#") == 0)
{
break;
}
else
{
send(msg->acceptfd, msg, sizeof(*msg), 0);
recv(msg->acceptfd, msg->buf, sizeof(msg->buf), 0);
printf("%s\n", msg->buf);
continue;
}
}
}
void lishi(MSG *msg) // 历史
{
send(msg->acceptfd, msg, sizeof(*msg), 0);
recv(msg->acceptfd, msg->buf, sizeof(msg->buf), 0);
printf("%s\n", msg->buf);
}