网络编程-电子词典项目

该代码实现了一个C/S架构的电子词典系统,包括服务器端和客户端。服务器端负责管理用户注册、登录、单词查询、历史记录等功能,使用SQLite数据库存储用户数据和电子词典。客户端则提供用户界面,进行注册、登录和查询操作。系统支持用户交互和历史记录管理。
摘要由CSDN通过智能技术生成

目录

1.服务器

2.客户端

3.电子词库附件


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);
}

3.电子词库附件

https://download.csdn.net/download/liu319293960_/87722277

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CG Liu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值