目录
1.服务器
// 多进程服务器
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <signal.h>
#include <sqlite3.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
// 打印错误信息
#define ERR_MSG(msg) \
do { \
fprintf(stderr, "error_line:%d\n", __LINE__); \
perror(msg); \
} while (0)
// 协议
typedef struct {
char type; // '1'注册 '2'登录 '3'客户端退出 'C'查单词 'L'历史记录 'S'注销账号 'Q'退出
char name[24]; // 用户名
char pwd[24]; // 密码
char word[32]; // 单词
char text[128]; // 信息
} msg_t;
// 服务器端口号
#define POST 8080
// 服务器ip
// #define IP "192.168.2.146"
#define IP "127.0.0.1" // 使用本地回环地址
/// @brief 导入电子词典库
/// @return 成功返回数据库地址 失败NULL
sqlite3* import_dictionaries();
/// @brief 初始化用户数据库
/// @return 成功返回数据库地址 失败NULL
sqlite3* init_usrData();
/// @brief 创建服务器模型
/// @param IP 服务器IP地址
/// @param dictDb 电子词典数据库
/// @param usrDb 用户数据库
/// @return 成功0 失败-1
// int create_server(const char* IP, sqlite3* dictDb, sqlite3* usrDb);
int create_server(sqlite3* dictDb, sqlite3* usrDb);
/// @brief 客户端交互
/// @param newfd 客户端套接字
/// @param cli_addr 客户端地址信息
/// @return 成功0 失败-1
int cli_handle(int newfd, struct sockaddr_in cli_addr, sqlite3* dictDb, sqlite3* usrDb);
/// @brief 用户注册
/// @param newfd 客户端套接字
/// @param msg 客户端消息结构体
/// @param usrDb 用户数据库
void do_enroll(int newfd, msg_t msg, sqlite3* usrDb);
/// @brief 用户登录
/// @param newfd 客户端套接字
/// @param msg 客户端消息结构体
/// @param usrDb 用户数据库
void do_login(int newfd, msg_t msg, sqlite3* usrDb);
/// @brief 查单词
/// @param newfd 客户端套接字
/// @param msg 客户端消息结构体
/// @param dictDb 电子词典数据库
/// @param usrDb 用户数据库
/// @param t 当前时间秒数
/// @param tm 时间结构体
void look_up_word(int newfd, msg_t msg, sqlite3* dictDb, sqlite3* usrDb, time_t* t, struct tm* tm);
/// @brief 历史记录
/// @param newfd 客户端套接字
/// @param msg 客户端消息结构体
/// @param usrDb 用户数据库
void history(int newfd, msg_t msg, sqlite3* usrDb);
/// @brief 注销账号
/// @param newfd 客户端套接字
/// @param msg 客户端消息结构体
/// @param usrDb 用户数据库
void close_an_account(int newfd, msg_t msg, sqlite3* usrDb);
/// @brief 用户退出
/// @param msg 客户端消息结构体
/// @param usrDb 用户数据库
void do_quit(msg_t msg, sqlite3* usrDb);
/// @brief 信号处理函数
void signalhand(int sig)
{
// 回收子进程资源
while (waitpid(-1, NULL, WNOHANG) > 0)
;
}
int main(int argc, const char* argv[])
{
// 1.校验参数
// if (argc != 2) {
// printf("input error\n");
// printf("usage: ./a.out IP(192.168.9.2)\n");
// return -1;
// }
// 2.导入电子词库
sqlite3* dictDb = import_dictionaries();
// 3.初始化用户数据库
sqlite3* usrDb = init_usrData();
// 4.捕捉SIGCHLD信号
if (SIG_ERR == signal(SIGCHLD, signalhand)) {
ERR_MSG("signal");
return -1;
}
// 5.创建服务器
// create_server(arg[1], dictDb, usrDb);
create_server(dictDb, usrDb);
return 0;
}
/// @brief 导入电子词典库
sqlite3* import_dictionaries()
{
// 1.打开词汇文件
FILE* fp = fopen("./dict.txt", "r");
if (fp == NULL) {
ERR_MSG("fopen");
return NULL;
}
// 2.打开词典数据库
sqlite3* db = NULL;
if (sqlite3_open("./.dict.db", &db) != SQLITE_OK) {
fprintf(stderr, "sqlite3_open:%s error_code:%d\n", sqlite3_errmsg(db), sqlite3_errcode(db));
return NULL;
}
// 3.创建表
char sql[128] = "create table dict (word char, mean char);";
char* errmsg = NULL;
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK) {
if (strcmp(errmsg, "table dict already exists") == 0) {
printf("电子词典库已存在\n");
goto END;
} else {
fprintf(stderr, "line:%d-->sqlite3_exec:%s\n", __LINE__, errmsg);
return NULL;
}
}
if (errmsg == NULL) {
printf("正在导入电子词典库,请稍后...\n");
}
// 4.开启事务
if (sqlite3_exec(db, "begin", NULL, NULL, &errmsg) != SQLITE_OK) {
fprintf(stderr, "line:%d-->sqlite3_exec:%s\n", __LINE__, errmsg);
return NULL;
}
char buf[128] = ""; // 保存一行单词与解释
char word[30] = ""; // 保存单词
char meaning[64] = ""; // 保存单词解释
int i = 0; // 空格下标
int j = 0; // 单词解释下标
int flag = 1; // 标记所以操作是否成功
// 5.读取数据
while (fgets(buf, sizeof(buf), fp)) {
bzero(word, sizeof(word));
bzero(meaning, sizeof(meaning));
// 取出单词
i = 0;
j = 0;
while (buf[i] != ' ') {
word[i] = buf[i];
i++;
}
// 取出解释
i += 3;
while (buf[i]) {
meaning[j++] = buf[i++];
}
meaning[strlen(meaning) - 1] = '\0';
// 插入到数据库
bzero(sql, sizeof(sql));
sprintf(sql, "insert into dict values (\"%s\", \"%s\");", word, meaning);
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK) {
fprintf(stderr, "line:%d-->sqlite3_exec:%s\n", __LINE__, errmsg);
flag = 0;
return NULL;
}
}
// 6.判断所以操作是否完成
if (flag) { // 所以操作成功,提交事务
sqlite3_exec(db, "commit;", NULL, NULL, NULL); // 提交事务
} else { // 事务操作失败,回滚
sqlite3_exec(db, "rollback;", NULL, NULL, NULL); // 事务操作失败,回滚
flag = 1;
}
END:
printf("电子词典库准备完毕\n");
// 7.关闭文件指针
fclose(fp);
return db;
}
/// @brief 初始化用户数据库
sqlite3* init_usrData()
{
// 1.打开用户数据库
sqlite3* db = NULL;
if (sqlite3_open("./.usr.db", &db) != SQLITE_OK) {
fprintf(stderr, "sqlite3_open:%s error_code:%d\n", sqlite3_errmsg(db), sqlite3_errcode(db));
return NULL;
}
// 2.创建用户信息表
char sql[128] = "create table if not exists usr (name char primary key, pwd char, stage int);";
char* errmsg = NULL;
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK) {
fprintf(stderr, "line:%d-->sqlite3_exec:%s\n", __LINE__, errmsg);
return NULL;
}
// 3.创建用户历史记录表
strcpy(sql, "create table if not exists history (name char, word char, mean char, time char);");
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK) {
fprintf(stderr, "line:%d-->sqlite3_exec:%s\n", __LINE__, errmsg);
return NULL;
}
// 4.服务器每次启动,将usr表中用户登录状态设置为0(stage)
strcpy(sql, "update usr set stage=0 where stage=1;");
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK) {
fprintf(stderr, "line:%d-->sqlite3_exec:%s\n", __LINE__, errmsg);
return NULL;
}
printf("数据库初始化成功\n");
return db;
}
/// @brief 创建服务器模型
// int create_server(const char* IP, sqlite3* dictDb, sqlite3* usrDb)
int create_server(sqlite3* dictDb, sqlite3* usrDb)
{
// 1.创建套接字
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sfd) {
ERR_MSG("socket");
return -1;
}
// 2.允许端口快速重用
int reuse = 1;
if (-1 == setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))) {
ERR_MSG("setsockopt");
return -1;
}
// 3.定义实际服务器地址信息结构体
struct sockaddr_in sin_addr;
sin_addr.sin_family = AF_INET; // 地址簇
sin_addr.sin_port = htons(POST); // 端口号, 需要转网络字节序
sin_addr.sin_addr.s_addr = inet_addr(IP); // 本机ip, 需要转网络字节序
// 4.绑定ip和端口
if (-1 == bind(sfd, (struct sockaddr*)&sin_addr, sizeof(sin_addr))) {
ERR_MSG("bind");
return -1;
}
// 5.设置套接字为监听状态
if (-1 == listen(sfd, 10)) {
ERR_MSG("listfd");
return -1;
}
// 6.定义存储成功连接客户端的地址信息
struct sockaddr_in cli_addr;
socklen_t addrlen = sizeof(cli_addr);
int newfd = 0; // 成功连接的客户端
while (1) {
// 7.获取连接成功的套接字
newfd = accept(sfd, (struct sockaddr*)&cli_addr, &addrlen);
if (-1 == newfd) {
ERR_MSG("accept");
return -1;
}
printf("[%s][%d]客户端连接成功\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
// 8.创建子进程
pid_t pd = fork();
if (pd > 0) { // 父进程
close(newfd);
} else if (pd == 0) { // 子进程 负责与客户端交互
close(sfd);
// 9.与客户端交互
cli_handle(newfd, cli_addr, dictDb, usrDb);
// 子进程退出
exit(0);
} else {
ERR_MSG("fork");
return -1;
}
}
// 10.关闭套接字
close(sfd);
return 0;
}
/// @brief 客户端交互
int cli_handle(int newfd, struct sockaddr_in cli_addr, sqlite3* dictDb, sqlite3* usrDb)
{
msg_t msg; // 定义消息结构体
time_t t; // 保存当前时间秒数
struct tm* tm; // 格式化时间
ssize_t size = 0; // 保存每次接收到的数据大小
while (1) {
// 接收客户端消息
size = recv(newfd, &msg, sizeof(msg), 0);
if (size == -1) {
ERR_MSG("recv");
return -1;
} else if (size == 0) {
// 将用户登录状态值设置为0(stage)
do_quit(msg, usrDb);
break;
}
// 判断用户的消息类型
switch (msg.type) {
case '1': // 注册
do_enroll(newfd, msg, usrDb);
break;
case '2': // 登录
do_login(newfd, msg, usrDb);
break;
case 'C': // 查单词
look_up_word(newfd, msg, dictDb, usrDb, &t, tm);
break;
case 'L': // 历史记录
history(newfd, msg, usrDb);
break;
case 'S': // 注销账号
close_an_account(newfd, msg, usrDb);
break;
case 'Q': // 退出
do_quit(msg, usrDb);
break;
}
}
close(newfd);
printf("[%s][%d]客户端退出\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
}
/// @brief 用户注册
void do_enroll(int newfd, msg_t msg, sqlite3* usrDb)
{
char** pres = NULL;
int row, column;
char* errmsg = NULL;
char sql[128] = "";
// 查找表中是否有该用户
sprintf(sql, "select name from usr where name=\"%s\";", msg.name);
if (sqlite3_get_table(usrDb, sql, &pres, &row, &column, &errmsg) != SQLITE_OK) {
fprintf(stderr, "line:%d-->sqlite3_exec:%s\n", __LINE__, errmsg);
return;
}
if (row != 0) { // 该用户已注册
strcpy(msg.text, "用户已注册!");
// 发送消息给客户端
if (-1 == send(newfd, &msg, sizeof(msg), 0)) {
ERR_MSG("send");
return;
}
printf("用户已注册!\n");
} else { // 该用户未注册过
bzero(sql, sizeof(sql));
// 将用户名和密码插入表中
sprintf(sql, "insert into usr values (\"%s\", \"%s\", 0);", msg.name, msg.pwd);
if (sqlite3_exec(usrDb, sql, NULL, NULL, &errmsg) != SQLITE_OK) {
fprintf(stderr, "line:%d-->sqlite3_exec:%s\n", __LINE__, errmsg);
return;
}
strcpy(msg.text, "注册成功!");
// 发送消息给客户端
if (-1 == send(newfd, &msg, sizeof(msg), 0)) {
ERR_MSG("send");
return;
}
printf("注册成功!\n");
}
}
/// @brief 用户登录
void do_login(int newfd, msg_t msg, sqlite3* usrDb)
{
char** pres = NULL;
int row, column;
char* errmsg = NULL;
char sql[128] = "";
// 查找表中是否有该用户
sprintf(sql, "select * from usr where name=\"%s\";", msg.name);
if (sqlite3_get_table(usrDb, sql, &pres, &row, &column, &errmsg) != SQLITE_OK) {
fprintf(stderr, "line:%d-->sqlite3_exec:%s\n", __LINE__, errmsg);
return;
}
bzero(msg.text, sizeof(msg.text));
if (row != 0) { // 该用户存在
// 判断密码是否正确
if (strcmp(pres[4], msg.pwd) != 0) {
strcpy(msg.text, "密码错误!");
} else if (strcmp(pres[5], "0") != 0) { // 检查该用户是否已登录
strcpy(msg.text, "该用户已登录!");
} else { // 登录成功
bzero(sql, sizeof(sql));
// 将该用户的登录状态值设置为1(stage)
sprintf(sql, "update usr set stage=1 where name=\"%s\";", msg.name);
if (sqlite3_exec(usrDb, sql, NULL, NULL, &errmsg) != SQLITE_OK) {
fprintf(stderr, "line:%d-->sqlite3_exec:%s\n", __LINE__, errmsg);
return;
}
strcpy(msg.text, "登录成功!");
}
} else { // 该用户未注册过
strcpy(msg.text, "用户名错误!");
}
// 发送消息给客户端
if (-1 == send(newfd, &msg, sizeof(msg), 0)) {
ERR_MSG("send");
return;
}
printf("[%s]%s\n", msg.name, msg.text);
}
/// @brief 查单词
void look_up_word(int newfd, msg_t msg, sqlite3* dictDb, sqlite3* usrDb, time_t* t, struct tm* tm)
{
// 获取当前时间
time(t);
tm = localtime(t);
char** pres = NULL;
int row, column;
char* errmsg = NULL;
char sql[256] = "";
// 查找电子词库中是否有用户查找的单词
sprintf(sql, "select * from dict where word=\"%s\";", msg.word);
if (sqlite3_get_table(dictDb, sql, &pres, &row, &column, &errmsg) != SQLITE_OK) {
fprintf(stderr, "line:%d-->sqlite3_exec:%s\n", __LINE__, errmsg);
return;
}
// 单词存在
if (row != 0) {
bzero(msg.text, sizeof(msg.text));
strcpy(msg.text, pres[(row + 1) * column - 1]); // 将单词解释保存到消息包
// 修改上次历史记录时间戳
bzero(sql, sizeof(sql));
sprintf(sql, "update history set time=\"%d-%02d-%02d %02d:%02d\" where name=\"%s\" and word=\"%s\";", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, msg.name, msg.word);
if (sqlite3_exec(usrDb, sql, NULL, NULL, &errmsg) != SQLITE_OK) {
fprintf(stderr, "line:%d-->sqlite3_exec:%s\n", __LINE__, errmsg);
return;
} else { // 没有该单词的历史记录
// 插入历史记录
bzero(sql, sizeof(sql));
sprintf(sql, "insert into history values (\"%s\", \"%s\", \"%s\", \"%d-%02d-%02d %02d:%02d\");", msg.name, msg.word, msg.text, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min);
if (sqlite3_exec(usrDb, sql, NULL, NULL, &errmsg) != SQLITE_OK) {
fprintf(stderr, "line:%d-->sqlite3_exec:%s\n", __LINE__, errmsg);
return;
}
}
printf("历史记录插入成功\n");
} else { // 单词不存在
strcpy(msg.text, "NOT FOUND!");
printf("NOT FOUND!\n");
}
// 发送消息给客户端
if (-1 == send(newfd, &msg, sizeof(msg), 0)) {
ERR_MSG("send");
return;
}
}
/// @brief 历史记录
void history(int newfd, msg_t msg, sqlite3* usrDb)
{
char** pres = NULL;
int row, column;
char* errmsg = NULL;
char sql[128] = "";
// 查找表中是否有该用户name
sprintf(sql, "select word, mean, time from history where name=\"%s\";", msg.name);
if (sqlite3_get_table(usrDb, sql, &pres, &row, &column, &errmsg) != SQLITE_OK) {
fprintf(stderr, "line:%d-->sqlite3_exec:%s\n", __LINE__, errmsg);
return;
}
// row >= 1 表示有历史记录
for (int i = 1; i <= row; i++) {
bzero(msg.text, sizeof(msg.text));
// 将历史记录拼接到消息包
sprintf(msg.text, "%s\t%s\t%s", pres[i * column], pres[i * column + 1], pres[i * column + 2]);
// 发送历史记录给客户端
if (-1 == send(newfd, &msg, sizeof(msg), 0)) {
ERR_MSG("send");
return;
}
}
// 没有历史记录
if (0 == row) {
bzero(msg.text, sizeof(msg.text));
strcpy(msg.text, "NOT FOUND HISTORY!");
// 发送没有历史记录消息给客户端
if (-1 == send(newfd, &msg, sizeof(msg), 0)) {
ERR_MSG("send");
return;
}
}
// 设置历史记录发送完毕后的消息
bzero(msg.text, sizeof(msg.text));
strcpy(msg.text, "**OVER**");
// 发送结束信息
if (-1 == send(newfd, &msg, sizeof(msg), 0)) {
ERR_MSG("send");
return;
}
}
/// @brief 注销账号
void close_an_account(int newfd, msg_t msg, sqlite3* usrDb)
{
char* errmsg = NULL;
char sql[128] = "";
// 将用户为name的账号信息删除
sprintf(sql, "delete from usr where name=\"%s\";", msg.name);
if (sqlite3_exec(usrDb, sql, NULL, NULL, &errmsg) != SQLITE_OK) {
fprintf(stderr, "line:%d-->sqlite3_exec:%s\n", __LINE__, errmsg);
return;
}
// 将用户为name的历史记录删除
sprintf(sql, "delete from history where name=\"%s\";", msg.name);
if (sqlite3_exec(usrDb, sql, NULL, NULL, &errmsg) != SQLITE_OK) {
fprintf(stderr, "line:%d-->sqlite3_exec:%s\n", __LINE__, errmsg);
return;
}
// 发送历史记录给客户端
bzero(msg.text, sizeof(msg.text));
sprintf(msg.text, "账号注销成功");
if (-1 == send(newfd, &msg, sizeof(msg), 0)) {
ERR_MSG("send");
return;
}
printf("[%s]用户账号注销\n", msg.name);
}
/// @brief 用户退出
void do_quit(msg_t msg, sqlite3* usrDb)
{
char* errmsg = NULL;
char sql[128] = "";
// 将退出的用户登录状态值设置为0
sprintf(sql, "update usr set stage=0 where name=\"%s\";", msg.name);
if (sqlite3_exec(usrDb, sql, NULL, NULL, &errmsg) != SQLITE_OK) {
fprintf(stderr, "line:%d-->sqlite3_exec:%s\n", __LINE__, errmsg);
return;
}
printf("%s用户退出\n", msg.name);
}
2.客户端
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
// 打印错误信息
#define ERR_MSG(msg) \
do { \
fprintf(stderr, "error_line:%d\n", __LINE__); \
perror(msg); \
} while (0)
// 协议
typedef struct {
char type; // '1'注册 '2'登录 '3'客户端退出 'C'查单词 'L'历史记录 'S'注销账号 'Q'退出
char name[24]; // 用户名
char pwd[24]; // 密码
char word[32]; // 单词
char text[128]; // 信息
} msg_t;
// 服务器端口号
#define POST 8080
// 服务器ip
// #define IP "192.168.9.2"
#define IP "127.0.0.1"
/// @brief 创建客户端
/// @return 成功0 失败-1
int create_client();
/// @brief 注册
/// @param fd 套接字
/// @param msg 消息结构体
/// @return 成功0 失败-1
int do_enroll(int fd, msg_t msg);
/// @brief 登录
/// @param fd 套接字
/// @param msg 消息结构体
/// @return 成功0 失败-1
int do_login(int fd, msg_t msg);
/// @brief 登录后二级菜单
/// @param fd 套接字
/// @param msg 消息结构体
/// @return 成功0 失败-1
int do_menus(int fd, msg_t msg);
/// @brief 查单词
/// @param fd 套接字
/// @param msg 消息结构体
/// @return 成功0 失败-1
int look_up_word(int fd, msg_t msg);
/// @brief 历史记录
/// @param fd 套接字
/// @param msg 消息结构体
/// @return 成功0 失败-1
int history(int fd, msg_t msg);
/// @brief 注销账号
/// @param fd 套接字
/// @param msg 消息结构体
/// @return 成功0 失败-1
int close_an_account(int fd, msg_t msg);
/// @brief 退出登录
/// @param fd 套接字
/// @param msg 消息结构体
/// @return 成功0 失败-1
int do_log_out(int fd, msg_t msg);
/// @brief 客户端退出
/// @param fd 套接字
/// @param msg 消息结构体
/// @return 成功0 失败-1
int do_quit(int fd, msg_t msg);
int main(int argc, const char* argv[])
{
// 创建客户端
create_client();
return 0;
}
/// @brief 创建客户端
/// @return 成功0 失败-1
int create_client()
{
// 1.创建套接字
int skfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == skfd) {
ERR_MSG("socket");
return -1;
}
// 2.设置服务器地址信息结构体
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(POST); // 服务器端口号, 将本地字节序转网络字节序
saddr.sin_addr.s_addr = inet_addr(IP); // 服务器IP, 将本地字节序转网络字节序
// 3.获取新套接字
if (-1 == connect(skfd, (struct sockaddr*)&saddr, sizeof(saddr))) {
ERR_MSG("connect");
return -1;
}
msg_t msg; // 定义消息结构体
char option; // 定义终端输入选项
// 4.接收/发送
while (1) {
printf("******************\n");
printf("******1.注册******\n");
printf("******2.登录******\n");
printf("******3.退出******\n");
printf("******************\n");
option = getc(stdin);
getchar();
switch (option) {
case '1': // 注册
do_enroll(skfd, msg);
break;
case '2': // 登录
do_login(skfd, msg);
break;
case '3': // 退出
do_quit(skfd, msg);
break;
default:
printf("输入错误,请重新输入\n");
getchar();
break;
}
printf("按任意键清屏>>>");
getchar();
system("clear");
}
// 5.关闭套接字
close(skfd);
return 0;
}
/// @brief 注册
int do_enroll(int fd, msg_t msg)
{
// 设置注册消息包
msg.type = '1';
printf("请输入用户名>>>");
scanf("%s", msg.name);
getchar();
printf("请输入密码>>>");
scanf("%s", msg.pwd);
getchar();
// 发送注册的用户名和密码
if (-1 == send(fd, &msg, sizeof(msg), 0)) {
ERR_MSG("send");
return -1;
}
// 接收是否注册成功
if (-1 == read(fd, &msg, sizeof(msg))) {
ERR_MSG("send");
return -1;
}
// 打印服务器消息
printf("%s\n", msg.text);
return 0;
}
/// @brief 登录
int do_login(int fd, msg_t msg)
{
// 设置登录消息包
msg.type = '2';
printf("请输入用户名>>>");
scanf("%s", msg.name);
getchar();
printf("请输入密码>>>");
scanf("%s", msg.pwd);
getchar();
// 发送登录的用户名和密码
if (-1 == send(fd, &msg, sizeof(msg), 0)) {
ERR_MSG("send");
return -1;
}
// 接收是否登录成功
if (-1 == recv(fd, &msg, sizeof(msg), 0)) {
ERR_MSG("send");
return -1;
}
// 判断是否登录成功
if (strcmp(msg.text, "登录成功!") != 0) {
printf("%s\n", msg.text); // 登录失败
return -1;
}
// 登录成功
system("clear");
// 二级菜单
do_menus(fd, msg);
return 0;
}
/// @brief 二级菜单
int do_menus(int fd, msg_t msg)
{
char option;
while (1) {
printf("******************\n");
printf("****1.查单词******\n");
printf("****2.历史记录****\n");
printf("****3.注销账号****\n");
printf("****4.退出登录****\n");
printf("******************\n");
option = getc(stdin);
getchar();
switch (option) {
case '1': // 查单词
look_up_word(fd, msg);
break;
case '2': // 历史记录
history(fd, msg);
break;
case '3': // 注销账号
close_an_account(fd, msg);
return 0;
case '4': // 退出登录
do_log_out(fd, msg);
return 0;
default:
printf("输入错误,请重新输入\n");
getchar();
break;
}
printf("按任意键清屏>>>");
while (getchar() != 10)
;
system("clear");
}
}
/// @brief 查单词
int look_up_word(int fd, msg_t msg)
{
// 设置查单词消息包
msg.type = 'C';
while (1) {
// 输入单词
printf("请输入单词(输入#退出)>>>");
fgets(msg.word, sizeof(msg.word), stdin);
msg.word[strlen(msg.word) - 1] = '\0';
// 判断输入是否为#
if (strcmp(msg.word, "#") == 0) {
break;
}
// 发送消息包
if (-1 == send(fd, &msg, sizeof(msg), 0)) {
ERR_MSG("send");
break;
}
// 接收服务器消息
if (-1 == recv(fd, &msg, sizeof(msg), 0)) {
ERR_MSG("read");
break;
}
// 判断是否找到对应单词
if (strcmp(msg.text, "NOT FOUND!") == 0) {
printf("\tNOT FOUND!\t\n");
break;
}
// 打印单词解释
printf("\t%s\t\t%s\n", msg.word, msg.text);
return 0;
}
return -1;
}
/// @brief 历史记录
int history(int fd, msg_t msg)
{
// 设置历史记录消息包
msg.type = 'L';
// 发送消息包
if (-1 == send(fd, &msg, sizeof(msg), 0)) {
ERR_MSG("send");
return -1;
}
// 接收服务器消息
while (1) {
// 接收历史记录
if (-1 == recv(fd, &msg, sizeof(msg), 0)) {
ERR_MSG("send");
return -1;
}
// 判断是否接收完毕
if (strcmp(msg.text, "**OVER**") == 0) {
puts("");
break;
}
// 打印历史记录
printf("%s\n", msg.text);
}
return 0;
}
int close_an_account(int fd, msg_t msg)
{
// 设置注销账号消息包
msg.type = 'S';
// 发送消息包
if (-1 == send(fd, &msg, sizeof(msg), 0)) {
ERR_MSG("send");
return -1;
}
// 接收消息
if (-1 == recv(fd, &msg, sizeof(msg), 0)) {
ERR_MSG("send");
return -1;
}
// 打印结果
printf("%s\n", msg.text);
return 0;
}
/// @brief 退出登录
int do_log_out(int fd, msg_t msg)
{
// 设置退出登录消息包
msg.type = 'Q';
// 发送消息
if (-1 == send(fd, &msg, sizeof(msg), 0)) {
ERR_MSG("send");
return -1;
}
return 0;
}
/// @brief 客户端退出
int do_quit(int fd, msg_t msg)
{
// 设置客户端退出消息包
msg.type = '3';
// 发送客户端退出消息
if (-1 == send(fd, &msg, sizeof(msg), 0)) {
ERR_MSG("send");
return -1;
}
exit(0);
}