云词典——基于TCP和sqlite3实现

项目需求

  功能描述   

       仿照有道云词典功能,实现一个自己的云词典。项目分为服务器端和客户端,服务器端响应客户端的注册、登录、查词和查看历史记录请求,客户端与用户交互和请求服务器。

视频演示

【基于TCP和sqlite3的云词典】icon-default.png?t=O83Ahttps://www.bilibili.com/video/BV19ctae6ELJ/?share_source=copy_web&vd_source=61c95cea209459baed5f6958120ea97e

  效果参考

云词典——基于TCP和sqlite3实现_第1张图片

功能矩阵

功能模块

功能点

功能点描述

优先级

备注

客户端

注册

可实现新用户的注册功能

A

登录

支持用户登录校验,错误给出提示,正确进入操作菜单

A

查词

可以进行单词释义查询操作

A

查词历史

可查看当前用户的查词历史

A

服务器

注册

实现用户的注册功能,并永久保存到数据库中

A

登录

实现用户登录请求的校验

B

查词

实现用户的查词请求并返回释义结果

A

历史

根据当前用户返回查词历史

A

心跳包检测

tcp的keepalive机制,确保客户端是否在线

C

选做

 流程图:

云词典——基于TCP和sqlite3实现_第2张图片

程序框架

通过网盘分享的文件:框架 提取码: pqqnicon-default.png?t=O83Ahttps://pan.baidu.com/s/1fv-svjLrQhSTZKQ2t-Gd5w?pwd=pqqn

参考

server.c

//sever.c
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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 \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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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  \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 提取码: 5sr9icon-default.png?t=O83Ahttps://pan.baidu.com/s/1DwZnggUNhxK_wgzFI0ytjw?pwd=5sr9

你可能感兴趣的:(c语言,sqlite3,tcp/ip)