C语言基于TCP,sqlite3的简单电子词典(含测试步骤)

项目总体要求

 

 

服务器端功能需求:

客户端功能需求:

 

 

dictionary.h

#ifndef __DICTIONARY_H__
#define __DICTIONARY_H__

#include "stdio.h"
#include "stdlib.h"
#include "unistd.h"
#include "string.h"
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <sqlite3.h>
#include <sys/stat.h>
#include <fcntl.h>

#define PORT 8888
#define IP "192.168.8.249"
#define DB_NAME "dict.db"
#define WORD_SQL "create table if not exists dictionary (word char,meaning char);"
#define HISTORY_SQL "create table if not exists history (name char,word char,meaning char,time char);"
#define HISTORY_INSERT_FORMAT "insert into history values(\"%s\",\"%s\",\"%s\",\"%s\")"
#define HISTORY_SELECT_FORMAT "select * from history where name = \"%s\""
#define USER_SQL "create table if not exists user (name char primary key,passwd char,status);"
#define USER_INSERT_FORMAT "insert into user values(\"%s\",\"%s\",%d)"
#define USER_SELECT_FORMAT "select * from user where name = \"%s\" and passwd = \"%s\""
#define USER_UPDATE_FORMAT "update user set status = %d where name = '%s'"
#define DICT_SELECT_SQL "select * from dictionary"
#define DICT_SELECT_FORMAT "select * from dictionary where word = \"%s\""
#define WORD_INSERT_FORMAT "insert into dictionary values(\"%s\",\"%s\");"
#define FILE_PATH_TEST "dict_test.txt"
#define FILE_PATH "dict.txt"
#define DICT_ROW 7987
#define DICT_TEST_ROW 64

#define ERR_MSG(msg) do {\
    fprintf(stderr,"line: __%d__ ",__LINE__);\
    perror(msg);\
} while (0)

//定义消息类型结构体,用于客户端向服务器发送消息
typedef struct _msg 
{
    char name[20];
    char passwd[20];
    char text[128];
    int status;  //用户登录状态 1.登录成功  0.退出
    int type;  //1.注册 2.登录 3.退出 4.查单词 5.查历史记录
} msg_t;


/*******************服务器部分所用函数**********************/
//获取当前系统时间
void get_system_time();

//接收及发送的函数
int deal_cli_inter(int newfd,struct sockaddr_in cin,sqlite3 *db);

//回收僵尸进程
void handler(int sig);

//数据库初始化,包括库,表的创建,以及数据的导入
void database_init(sqlite3 *db);

//数据库数据导入
int load_db(sqlite3 *db,char *filepath);

//服务器端注册
void do_server_register(int newfd, msg_t* msg, sqlite3 *db);

//服务器端登录
void do_server_login(int newfd,msg_t* msg,sqlite3 * db);

//服务器端退出
void do_server_exit(int newfd,msg_t* msg,sqlite3 * db);

//服务器查询单词
void do_server_word_query(int newfd,msg_t* msg,sqlite3 * db);

//服务器查询历史记录
void do_server_history_query(int newfd,msg_t* msg,sqlite3 * db);


/*******************客户端部分所用函数**********************/
//客户端处理 ctrl+c SIGINT信号的函数
void ctrlc_handler(int sig);

//客户端注册功能
void do_register(int cfd,msg_t* msg);

//客户端登录功能
void do_login(int cfd,msg_t* msg);

//客户端查单词
void do_query_word(int cfd,msg_t* msg);

//客户端查历史记录
void do_query_history(int cfd,msg_t* msg);

//客户端给服务器发送用户退出标识
void set_user_exit_status(int cfd,msg_t* msg);

//客户端打印一级菜单
void print_1st_level_menu();

//客户端打印二级菜单
void print_2nd_level_menu();



#endif

 dict_server.c

 

#include "dictionary.h"



int main(int argc, char const *argv[])
{
    //捕获17号信号,注册新的处理函数,用于回收僵尸进程
    __sighandler_t s = signal(SIGCHLD,handler);
    if (SIG_ERR == s)
    {
        printf("signal");
        return -1;
    }
    
    //1.socket  创建流式套接字  -->> TCP
    int sfd =  socket(AF_INET, SOCK_STREAM, 0);
    if (sfd < 0)
    {
        ERR_MSG("socket");
        return -1;
    }
    printf("sfd = %d\n",sfd);

    //允许端口快速被复用
    int reuse = 1;
    if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)) < 0)
    {
        ERR_MSG("setsockopt");
        return -1;
    }

    //填充地址信息结构体,真实的地址信息结构体在man 7 ip
    //AF_INET --> man 7 ip
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;   //必须要填AF_INET
    sin.sin_port = htons(PORT); //1024~49151 网络字节序
    sin.sin_addr.s_addr = inet_addr(IP);  //本机IP

    //2.bind: 功能 将IP地址和端口号绑定到指定套接字中
    if(bind(sfd, (const struct sockaddr *)&sin,sizeof(sin)) < 0)
    {
        ERR_MSG("bind");
        return -1;
    }
    printf("bind success __%d__\n",__LINE__);

    //3.listen: 将套接字设置为被动监听状态,只负责监听是否有客户端连接成功
    if(listen(sfd,10) <0)
    {
        ERR_MSG("listen");
        return -1;
    }
    printf("listen success __%d__\n",__LINE__);
    int login_fail_flag = 0; //登录失败状态标识 1:失败  0:成功

    //初始化数据库,导入数据
    //创建数据库
    sqlite3 *db = NULL;
    if(sqlite3_open(DB_NAME,&db)!= SQLITE_OK)
    {
        fprintf(stderr,"database create failed,__%d__,%d,%s\n",__LINE__,sqlite3_errcode(db),
        sqlite3_errmsg(db));
        return -1;
    }
    printf("database create success!\n");
    database_init(db);


    struct sockaddr_in cin; //存储客户端的地址信息
    socklen_t addrlen = sizeof(cin);
    int newfd = 0;
    int cpid = 0;
    while (1)
    {
        //父进程只负责连接
        newfd =  accept(sfd,(struct sockaddr *)&cin,&addrlen);
        if (newfd < 0)
        {
            ERR_MSG("accept");
            return -1;
        }
        printf("[%s | %d] newfd = %d\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);


        cpid = fork();
        if (0==cpid)
        {
            close(sfd);  //sfd对于子进程没有用
            //与客户端交互的代码

            deal_cli_inter(newfd,cin,db);

            //退出子进程
            exit(0);
        } else if(cpid > 0)
        {
            close(newfd);  //newfd对于父进程没有用
        }
    }

    close(sfd);
    return 0;
}

int deal_cli_inter(int newfd,struct sockaddr_in cin,sqlite3 *db)
{
    //通过文件描述符,读取数据
    //原型 ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    msg_t msg;
    bzero(&msg,sizeof(msg));
    ssize_t  ret = 0;
    while (1)
    {
        
        ret = recv(newfd,&msg,sizeof(msg),0);
        if (ret < 0)
        {
            ERR_MSG("recv");
            break;
        } else if (0 == ret)
        {
            //当用户名存在的时候,将用户表status置0
            /*
                在一级菜单界面,按下退出,由于还未登录,所以不能将状态位置0
            */
            printf("[%s:%d] 断开连接\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
            break;
        }
        printf("[%s:%d] 连接成功\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
        /*******************************************************/
        switch (msg.type)
        {
            case 1:
                do_server_register(newfd, &msg, db);
                break;
            case 2:
                do_server_login(newfd, &msg, db);
                break;
            case 3:
                do_server_exit(newfd, &msg, db);
                break;
            case 4:
                do_server_word_query(newfd, &msg, db);
                break;
            case 5:
                do_server_history_query(newfd,&msg, db);
                break;
            
            default:
                break;
        }
    }
    close(newfd);
    return 0;
}







//数据库初始化,创建数据库及建表
void database_init(sqlite3 *db)
{
    //创建表
    //1.创建单词及释义表
    char *errmsg = NULL;
    if (sqlite3_exec(db,WORD_SQL,NULL,NULL,&errmsg)!= SQLITE_OK)
    {
        fprintf(stderr,"dictionary table create failed,__%d__,%d,%s\n",__LINE__,sqlite3_errcode(db),errmsg);
        return;
    }
    printf("dictionary table create success!\n");

    //2.创建历史记录表
    if (sqlite3_exec(db,HISTORY_SQL,NULL,NULL,&errmsg)!= SQLITE_OK)
    {
        fprintf(stderr,"history table create failed,__%d__,%d,%s\n",__LINE__,sqlite3_errcode(db),errmsg);
        return;
    }
    printf("history table create success!\n");

    //3.创建用户表
    if (sqlite3_exec(db,USER_SQL,NULL,NULL,&errmsg)!= SQLITE_OK)
    {
        fprintf(stderr,"user table create failed,__%d__,%d,%s\n",__LINE__,sqlite3_errcode(db),errmsg);
        return;
    }
    printf("user table create success!\n");
    load_db(db,FILE_PATH);
}

//调入词典数据
int load_db(sqlite3 *db,char *filepath)
{
    //首先检查词典数据库中有无数据,有数据就阻止导入
    char **pres = NULL;
    int row,column;
    char *errmsg = NULL;
    if(sqlite3_get_table(db,DICT_SELECT_SQL,&pres,&row,&column,&errmsg)!= SQLITE_OK)
    {
        fprintf(stderr,"server load_db query dictionary failed,__%d__,%d,%s\n",__LINE__,sqlite3_errcode(db),errmsg);
        return -1;
    }
    printf("server load_db query dictionary,row = %d,row =%d\n",row,column);
    if (row >= DICT_ROW)
    {
        printf("词典数据库中数据已存在!\n");
        return -1;
    }
    //1.以只读的方式打开文件
    FILE *fp = fopen(filepath,"r");
    if (NULL == fp)
    {
        ERR_MSG("dict source file open");
        return -1;
    }
    char buf[128] = {0};
    int16_t ret = 0;
    //读取数据,写入数据库
    char sql[128] = {0};
    
    if (NULL == fp)
    {
        ERR_MSG("dict source file open");
        return -1;
    }
    
    int num = 1;
    char word[64] = {0};
    char meaning[64] = {0};
    while (1)
    {
        bzero(word,sizeof(word));
        bzero(meaning,sizeof(meaning));
        bzero(buf,sizeof(buf));
        int m_index = 0;
        int j = 0;
        if (NULL == fgets(buf,sizeof(buf),fp))
        {
            printf("所有数据导入完毕,准备退出\n");
            sleep(1);
            break;
        } else
        {
            buf[strlen(buf)-1] = 0;
            //printf("[%d] %s\n",num,buf);
            //遍历buf,找到第一个空格
            for (int i = 0; i < strlen(buf); i++)
            {   
                word[i] = buf[i];
                if (buf[i] == ' ' && buf[i+1] == ' ' && buf[i+2] == ' ')
                {
                    word[i] = 0;
                    m_index = i+3;
                }
                if (0!=m_index)
                {
                    if (j < sizeof(meaning))
                    {
                        meaning[j] = buf[m_index];
                        m_index++;
                        j++;
                    }
                }
            }
            //构建sql插入数据
            sprintf(sql,WORD_INSERT_FORMAT,word,meaning);
            printf("[%d] sql=%s\n",num,sql);
            if (sqlite3_exec(db,sql,NULL,NULL,&errmsg)!= SQLITE_OK)
            {
                fprintf(stderr,"dict insert failed,__%d__,%d,%s\n",__LINE__,sqlite3_errcode(db),errmsg);
                return -1;
            }
            printf("[%d] %s  %s inserted ok\n",num,word,meaning);
            num++;
        }  
    }
     
    return 0;
}


void do_server_register(int newfd, msg_t *msg, sqlite3 *db)
{
    char sql[128] = {0};
    msg->status = 0;  //注册成功后用户处于未登录状态
    sprintf(sql,USER_INSERT_FORMAT,msg->name,msg->passwd,msg->status);
    printf("register sql = %s \n",sql);

    char *errmsg = NULL;
    if (sqlite3_exec(db,sql,NULL,NULL,&errmsg)!= SQLITE_OK)
    {
        fprintf(stderr,"insert user failed,__%d__,%d,%s\n",__LINE__,sqlite3_errcode(db),errmsg);
        //插入失败,返回用户名已存在
        strcpy(msg->text,"注册失败,该账户已经被注册!");
        ssize_t sret = send(newfd,msg,sizeof(msg_t),0);
        if (sret<0)
        {
            ERR_MSG("server do_server_register send");
            return;
        }
        return;
    } else
    {
        //插入成功,返回   返回插入成功
        //2. 发送消息给客户端
        strcpy(msg->text,"用户注册成功");
        ssize_t sret = send(newfd,msg,sizeof(msg_t),0);
        if (sret<0)
        {
            ERR_MSG("server do_server_register send");
            return;
        }
    } 
    return;
}


void do_server_login(int newfd, msg_t* msg, sqlite3 *db)
{
    char sql[128] = {0};
    sprintf(sql,USER_SELECT_FORMAT,msg->name,msg->passwd) ;
    printf("server do_server_login sql = %s \n",sql);
    char **pres = NULL;
    int row,column;
    char *errmsg = NULL;
    if(sqlite3_get_table(db,sql,&pres,&row,&column,&errmsg)!= SQLITE_OK)
    {
        fprintf(stderr,"sqlite3_get_table failed,__%d__,%d,%s\n",__LINE__,sqlite3_errcode(db),errmsg);
        return;
    }
    printf("server do_server_login,row = %d,row =%d\n",row,column);
    if (row <=0)
    {
        //用户不存在 ,提示 登录失败,账号/密码错误!
        strcpy(msg->text,"登录失败,账号/密码错误!");
        msg->status = 0; //登录成功状态码
        ssize_t sret = send(newfd,msg,sizeof(msg_t),0);
        if (sret<0)
        {
            ERR_MSG("server do_server_login send");
            return;
        }
    } else
    {
        //用户存在
        //查询数据库status=1 提示,请勿重复登录  status=0 登录成功,并将status置为1
        printf("server do_server_login pres[5]=%s\n",pres[5]);
        if (0==strcmp("1",pres[5]))
        {
            strcpy(msg->text,"登录失败,重复登录!!");
        } else if (0==strcmp("0",pres[5]))
        {
            strcpy(msg->text,"登录成功!");
            msg->status = 1; //登录成功状态码
            //更新数据库status字段
            char sql[128] = {0};
            sprintf(sql, USER_UPDATE_FORMAT,msg->status, msg->name);
            printf("server do_server_login sql = %s \n",sql);
            char *errmsg = NULL;
            if (sqlite3_exec(db,sql,NULL,NULL,&errmsg)!= SQLITE_OK)
            {
                fprintf(stderr,"server do_server_login failed,__%d__,%d,%s\n",__LINE__,sqlite3_errcode(db),errmsg);
                msg->status = 0;
                strcpy(msg->text,errmsg);
                ssize_t sret = send(newfd,msg,sizeof(msg_t),0);
                if (sret<0)
                {
                    ERR_MSG("server do_server_login send");
                    return;
                }
                return;
            }
            printf("%s server do_server_login 用户设置退出状态成功\n",msg->name);

        }
        ssize_t sret = send(newfd,msg,sizeof(msg_t),0);
        if (sret<0)
        {
            ERR_MSG("server do_server_login send");
            return;
        }
    }
}

void do_server_exit(int newfd,msg_t* msg,sqlite3 * db)
{
    char sql[128] = {0};
    sprintf(sql, USER_UPDATE_FORMAT,msg->status, msg->name);
    printf("server do_server_exit sql = %s \n",sql);
    char *errmsg = NULL;
    if (sqlite3_exec(db,sql,NULL,NULL,&errmsg)!= SQLITE_OK)
    {
        fprintf(stderr,"server do_server_exit failed,__%d__,%d,%s\n",__LINE__,sqlite3_errcode(db),errmsg);
        return;
    }
    printf("%s server do_server_exit 用户设置退出状态成功\n",msg->name);
}



//服务器查询单词
void do_server_word_query(int newfd,msg_t* msg,sqlite3 * db)
{
    char sql[256] = {0};
    sprintf(sql,DICT_SELECT_FORMAT,msg->text) ;
    printf("server do_server_word_query sql = %s \n",sql);
    char **pres = NULL;
    int row,column;
    char *errmsg = NULL;
    if(sqlite3_get_table(db,sql,&pres,&row,&column,&errmsg)!= SQLITE_OK)
    {
        fprintf(stderr,"server do_server_word_query failed,__%d__,%d,%s\n",__LINE__,sqlite3_errcode(db),errmsg);
        return;
    }
    printf("server do_server_word_query,row = %d,row =%d\n",row,column);
    if (row <=0)
    {
        //单词未查到 回复 NOT FOUND!!
        printf("单词 %s 的释义未查到\n",msg->text);
        strcpy(msg->text,"NOT FOUND!!");
        ssize_t sret = send(newfd,msg,sizeof(msg_t),0);
        if (sret<0)
        {
            ERR_MSG("server do_server_word_query send");
            return;
        }
    } else
    {
        //单词查到   
        //回复单词+释义
        strcat(msg->text,"\t");
        strcat(msg->text,pres[3]);
        printf("word and def to be sent to client==>%s\n",msg->text);
        ssize_t sret = send(newfd,msg,sizeof(msg_t),0);
        if (sret<0)
        {
            ERR_MSG("server do_server_word_query send");
            return;
        }
        //发送成功后插入历史记录表
        char sql[128] = {0};
        char time_buf[128] = {0}; 
        get_system_time(time_buf);
        sprintf(sql,HISTORY_INSERT_FORMAT,msg->name,pres[2],pres[3],time_buf);
        printf("do_server_word_query sql = %s \n",sql);

        char *errmsg = NULL;
        if (sqlite3_exec(db,sql,NULL,NULL,&errmsg)!= SQLITE_OK)
        {
            fprintf(stderr,"do_server_word_query insert history failed,__%d__,%d,%s\n",__LINE__,sqlite3_errcode(db),errmsg);
            return;
        }
        printf("用户 %s,单词 %s 插入历史记录成功\n",msg->name,pres[2]);
    }
}

//服务器查询历史记录
void do_server_history_query(int newfd,msg_t* msg,sqlite3 * db)
{
    
    char **pres = NULL;
    int row,column;
    char *errmsg = NULL;
    char sql[256] = {0};
    sprintf(sql,HISTORY_SELECT_FORMAT,msg->name);
    printf("server do_server_history_query sql = %s \n",sql);
    if(sqlite3_get_table(db,sql,&pres,&row,&column,&errmsg)!= SQLITE_OK)
    {
        fprintf(stderr,"server do_server_history_query failed,__%d__,%d,%s\n",__LINE__,sqlite3_errcode(db),errmsg);
        return;
    }
    printf("server do_server_history_query row = %d,column =%d\n",row,column);

    int count = 0;
    //row中不包含表头的一行,但是结果中有表头的内容,所以需要将表头的一行加上
    for (int i = 0; i < row+1; i++)
    {
        for (int j = 0; j < column; j++)
        {
            if(i>0)
            {
                if (j>0)
                {
                    bzero(msg->text,sizeof(msg->text));
                    sprintf(msg->text,"%s\t",pres[count]);
                    //发送给客户端
                    ssize_t sret = send(newfd,msg,sizeof(msg_t),0);
                    if (sret<0)
                    {
                        ERR_MSG("server do_server_history_query send");
                        return;
                    }
                }
            }
            count++;
        }
        if(i > 0)
        {
            bzero(msg->text,sizeof(msg->text));
            strcpy(msg->text,"\n");
            //发送给客户端
            ssize_t sret = send(newfd,msg,sizeof(msg_t),0);
            if (sret<0)
            {
                ERR_MSG("server do_server_history_query send");
                return;
            }
        }
        
    }
    //发送结束标识给客户端
    strcpy(msg->text,"**OVER**\n");
    //发送给客户端
    ssize_t sret = send(newfd,msg,sizeof(msg_t),0);
    if (sret<0)
    {
        ERR_MSG("server do_server_history_query send");
        return;
    }

}

//获取当前系统时间
void get_system_time(char *time_buf)
{
    time_t cur_systime_milli = time(NULL);
    struct tm *t = localtime(&cur_systime_milli);
    sprintf(time_buf,"%4d-%02d-%02d %02d-%02d-%02d",
    t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,
    t->tm_min,t->tm_sec);
}

//回收僵尸进程
void handler(int sig)
{
    while (waitpid(-1,NULL,WNOHANG)>0); //循环回收僵尸进程
}

dict_client.c

 

#include "dictionary.h"

int cfd = 0;
char name[20] = {0};
int main(int argc, char const *argv[])
{
    //捕获ctrl + c SIGINT信号,向用户发送退出请求
    __sighandler_t s = signal(SIGINT,ctrlc_handler);
    if (SIG_ERR == s)
    {
        printf("signal");
        return -1;
    }

    /**********连接服务器部分 begin***************************/
    //1. 创建流式套接字
    cfd = socket(AF_INET,SOCK_STREAM,0);
    if (cfd < 0)
    {
        ERR_MSG("socket");
        return -1;
    }
    //填充要连接的服务器的ip和端口
    struct sockaddr_in sin;
    sin.sin_family = AF_INET; //必须填AF_INET
    sin.sin_port = htons(PORT); //服务器绑定的端口号
    sin.sin_addr.s_addr = inet_addr(IP); //服务器绑定的IP
    //3. 连接服务器   int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
    if(connect(cfd,(struct sockaddr *)&sin,sizeof(sin)) < 0)
    {
        ERR_MSG("connect");
        return -1;
    }
    printf("connect success\n");
/*******************连接服务器部分 end***************************/

/*********************主逻辑部分 begin*********************************/
    char choose = 0;
    while (1)
    {
        msg_t msg;
        print_1st_level_menu();
        choose = getchar();
        while (getchar()!=10);
        switch (choose)
        {
            case '1':
                do_register(cfd,&msg);
                break;
            case '2':
                do_login(cfd,&msg);
                break;
            case '3':
                goto END; //服务器提示断开连接
                break;
            default:
                printf("输入错误,请重新输入\n");
        }
        //请按任意键清屏
        printf("请按任意键清屏\n");
        while (getchar()!=10);
        system("clear");
    }
/*********************主逻辑部分 end*********************************/
END:
    close(cfd);
    return 0;
}


void print_1st_level_menu()
{
    printf("..................主界面.....................\n");
    printf(".................1.注册.......................\n");
    printf(".................2.登录.......................\n");
    printf(".................3.退出.......................\n");
    printf(".............................................\n");
    printf("请输入你的选择>>");
}

void print_2nd_level_menu()
{
    system("clear");
    printf("........................................\n");
    printf("............1.查单词.......................\n");
    printf("............2.历史记录.......................\n");
    printf("............3.返回上一级.......................\n");
    printf(".............................................\n");
    printf("请输入你的选择>>");
}


//客户端注册功能
void do_register(int cfd,msg_t* msg)
{
     //1. 构造发送消息结构体
    bzero(msg,sizeof(msg));
    msg->type = 1; //注册
    printf("请输入账户名>>>");
    fscanf(stdin,"%s",msg->name);
    while (getchar()!=10);
    printf("请输入密码>>>");
    fscanf(stdin,"%s",msg->passwd);
    while (getchar()!=10);
    //2. 发送消息给服务器
    ssize_t sret = send(cfd,msg,sizeof(msg_t),0);
    if (sret<0)
    {
        ERR_MSG("client do_register send");
        return;
    }
    //3.接收服务器返回的消息
    ssize_t rret = recv(cfd,msg,sizeof(msg_t),0);
    if (rret<0)
    {
        ERR_MSG("client do_register recv");
        return;
    }
    //4.返回的消息判断是否注册成功
    printf("%s\n",msg->text);
}

//客户端登录功能
void do_login(int cfd,msg_t *msg)
{
    //1. 构造发送消息结构体
    bzero(msg,sizeof(msg_t));
    msg->type = 2; //登录
    printf("请输入登录账户名>>>");
    fscanf(stdin,"%s",msg->name);
    while (getchar()!=10);
    printf("请输入登录密码>>>");
    fscanf(stdin,"%s",msg->passwd);
    while (getchar()!=10);
    //2. 发送消息给服务器
    ssize_t sret = send(cfd,msg,sizeof(msg_t),0);
    if (sret<0)
    {
        ERR_MSG("client do_login send");
        return;
    }
    //3.接收服务器返回的消息
    ssize_t rret = recv(cfd,msg,sizeof(msg_t),0);
    if (rret<0)
    {
        ERR_MSG("client do_login recv");
        return;
    }
    //根据返回消息结构体判断登录是否成功
    //失败,提示错误原因,返回一级菜单==> 继续往下走
    if (0==msg->status)
    {
        printf("%s\n",msg->text);
        return;
    } else if (1==msg->status)
    {
        strcpy(name,msg->name);
        //成功进入二级菜单
        char choose = 0;
        while (1)
        {
            print_2nd_level_menu();
            choose = getchar();
            while (getchar()!=10);
            switch (choose)
            {
                case '1':   //==> 4
                    do_query_word(cfd,msg);
                    break;
                case '2':   //==> 5
                    do_query_history(cfd,msg);
                    break;
                case '3':     //6.返回上一级
                    //给服务发送用户退出标识
                    set_user_exit_status(cfd,msg);
                    return;
                default:
                    printf("输入错误,请重新输入\n");
            }
            //请按任意键清屏
            printf("请按任意键清屏\n");
            while (getchar()!=10);
            system("clear");
        }
    }
}

void set_user_exit_status(int cfd,msg_t *msg)
{
    //把登录状态位置为0
    msg->status = 0;
    msg->type = 3;
    //2. 发送消息给服务器
    ssize_t sret = send(cfd,msg,sizeof(msg_t),0);
    if (sret<0)
    {
        ERR_MSG("client set_user_exit_status send");
        return;
    }
    /*
    //3.接收服务器返回的消息
    ssize_t rret = recv(cfd,&msg,sizeof(msg),0);
    if (rret<0)
    {
        ERR_MSG("client do_login recv");
        return;
    }
    */
}

//客户端查单词
void do_query_word(int cfd,msg_t *msg)
{
    while (1)
    {
        bzero(msg->text,sizeof(msg->text));
        printf("请输入单词(输入#退出)>>>");
        fscanf(stdin,"%s",msg->text);
        while (getchar()!=10);
        if (strcmp(msg->text,"#")==0)
        {
            return;
        }
        msg->type = 4;
        //2. 发送消息给服务器
        ssize_t sret = send(cfd,msg,sizeof(msg_t),0);
        if (sret<0)
        {
            ERR_MSG("client do_login send");
            return;
        }
        //3.接收服务器返回的消息
        bzero(msg->text,sizeof(msg->text));
        ssize_t rret = recv(cfd,msg,sizeof(msg_t),0);
        if (rret<0)
        {
            ERR_MSG("client do_login recv");
            return;
        }
        printf("%s\n",msg->text);
    }
}


//客户端查历史记录
void do_query_history(int cfd,msg_t *msg)
{
    msg->type = 5;
    //2. 发送消息给服务器
    ssize_t sret = send(cfd,msg,sizeof(msg_t),0);
    if (sret<0)
    {
        ERR_MSG("client do_query_history send");
        return;
    }
    //3.循环接收服务器返回的消息
    while (1)
    {
        ssize_t rret = recv(cfd,msg,sizeof(msg_t),0);
        if (rret<0)
        {
            ERR_MSG("client do_login recv");
            return;
        }
        printf("%s",msg->text);
        if (0==strcmp("**OVER**\n",msg->text))
        {
            break;
        }
    }
}


void ctrlc_handler(int sig)
{
    //把登录状态位置为0
    msg_t msg;
    bzero(&msg,sizeof(msg_t));
    msg.status = 0;
    msg.type = 3;
    strcpy(msg.name,name);
    //2. 发送消息给服务器
    ssize_t sret = send(cfd,&msg,sizeof(msg_t),0);
    if (sret<0)
    {
        ERR_MSG("client ctrlc_handler send");
        return;
    }
    printf("ctrl+c signal sent,name=%s\n",msg.name);
    kill(getpid(),9);
}

测试过程:

  1. 单词数据导入及表的创建 (见 dict.db)

数据库结构

词典数据库中内容

注:首次初始化成功之后,后续关闭服务器无需再重新导入单词及初始化其他表格

  1. 客户端注册功能
  1. 注册成功的情况

注册前:

注册后: (注册完毕,用户处于未登录状态 status = 0)

按下清屏键后

此时数据库中

2)重复注册的情况

  1. 登录功能

1)登录成功后跳到二级菜单界面,同时status置为1

2)此时再次启动一个client,输入同样的用户名和密码,提示已登录,请勿重复登录

数据库中的status字段不受影响,仍为1,处于登录状态

在重复登录界面,点击3正常退出或者按ctrl+c强制退出,均对已登录的用户的status无影响

3)在已经登录成功的用户界面,按返回上一级,用户退出,退出状态status被置为0

在登录成功的二级菜单界面按ctrl+c强制结束客户端,已登录用户的status会被置为0

4) 登录密码或者姓名错误的情况

正确用户名和密码:

重新登录,输入正确用户名和密码,登录成功

  1. 点击1,查询单词

输入一个不存在的单词,显示未查询到

输入一个存在的单词,显示单词+释义 (同时插入查询历史表格中)

按下 # 键的效果

  1. 查询历史记录

再次查询一个单词

再次查看历史

  1. 按下返回上一级,用户退出,登录状态置0

按下清屏键后

查看数据库status字段,用户退出

  1. 同时登录多用户,每个用户查询不同的单词,查看历史记录

查看数据库

两个客户端退出登录

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值