Linux下在线词典项目学习

在线词典要实现的功能:

  • 在线词典
  • 项目功能描述
  • 用户注册和登录验证
  • 服务器端将用户信息和历史记录保存在数据库中,客户端输入用户名和密码,服务器端在数据库中查找、匹配,返回结果
  • 单次在线翻译
  • 根据客户端输入的单次在字典文件中搜索
  • 历史记录查询
  • 项目分析

词典功能展示:

  1. 注册用户
image-20220809132828483
  1. 登录

    image-20220809133018465
  2. 查询单词

    image-20220809133300150
  3. 历史记录

    image-20220809133524348
  4. 支持多客户端同时连接服务器查询单词

服务器流程图:

客户端流程图:

代码

点击查看代码
/**
 * @file client.c
 * @brief ./client serv_ip serv_port
 * @author 
 * @version 
 * @date 2022-08-09
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define N 32

#define R 1     //user - register
#define L 2     //user - login
#define Q 3     //user - query
#define H 4     //user - history

//定义通信双方的信息结构体
typedef struct {
    int type;
    char name[N];
    char data[256];
}MSG;

int do_register(int sockfd,MSG *msg);  //register
int do_login(int sockfd,MSG *msg);      //login
int do_query(int sockfd,MSG *msg);      //query
int do_history(int sockfd,MSG *msg);    //history

int main(int argc,const char *argv[])
{
    int sockfd;
    struct sockaddr_in serveraddr;
    int n;
    MSG msg;

    if (argc != 3){ //服务器IP 端口号
        printf("Usage: %s server ip port.\n",argv[0]);
        return -1;
    }
    
    // 创建socket
    if ((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0){
        perror("fail to socket.\n");
        return -1;
    }

    // 服务器地址
    bzero(&serveraddr,sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));

    // connect服务器
    if(connect(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr))){
        perror("fail to connect");
        return -1;
    }

    // 一级菜单
    while(1)
    {
        printf("****************************************\n");
        printf("*   1.register    2.login    3.quit    *\n");
        printf("****************************************\n");
        printf("Please chose:");

        scanf("%d",&n);
        while (getchar() != '\n'){}

        switch(n)
        {
        case 1:
            do_register(sockfd,&msg);
            break;
        case 2:
            if(do_login(sockfd,&msg) == 1){
                goto next;  //登录成功 跳转二级菜单
            }
            break;
        case 3:
            close(sockfd);
            exit(0);
            break;
        default:
            printf("Invalid data cmd.\n");
        }
    }

next:   //二级菜单
    while(1)
    {
        printf("************************************************\n");
        printf("*   1.query_word   2.history_record   3.quit   *\n");
        printf("************************************************\n");
        printf("Please chose:");

        scanf("%d",&n);
        while (getchar() != '\n'){}

        switch(n)
        {
        case 1:
            do_query(sockfd,&msg);
            break;
        case 2:
            do_history(sockfd,&msg);
            break;
        case 3:
            close(sockfd);
            exit(0);
            break;
        default:
            printf("Invalid data cmd.\n");
        }
        
    }

    return 0;
}

int do_register(int sockfd,MSG *msg)
{
    msg->type = R;

    printf("Input name: ");
    scanf("%s",msg->name);
    getchar();

    printf("Input passwd: ");
    scanf("%s",msg->data);
    getchar();

    if (send(sockfd,msg,sizeof(MSG),0) < 0 ){
        perror("send");
        return -1;
    }

    if (recv(sockfd,msg,sizeof(MSG),0) < 0){
        perror("recv");
        return -1;
    }

    // 打印客户端返回信息   OK! 或者 usr alread exist.
    printf("%s\n",msg->data);

    return 0;
}

int do_login(int sockfd,MSG *msg)
{
    msg->type = L;

    printf("Input name: ");
    scanf("%s",msg->name);
    getchar();

    printf("Input passwd: ");
    scanf("%s",msg->data);
    getchar();

    if (send(sockfd,msg,sizeof(MSG),0) < 0 ){
        perror("send");
        return -1;
    }

    if (recv(sockfd,msg,sizeof(MSG),0) < 0){
        perror("recv");
        return -1;
    }

    // 判断服务器 返回值是否为OK
    if(strncmp(msg->data,"OK",3) == 0){
        printf("Login OK!\n");
        return 1;
    }
    else{
        printf("%s\n",msg->data);
    }

    return 0;
}

int do_query(int sockfd,MSG *msg)
{
    msg->type = Q;
    puts("----------------------");

    while(1){
        printf("Input word(# means quit): ");
        scanf("%s",msg->data);
        getchar();

        //输入#号 返回上一级菜单
        if(strncmp(msg->data,"#",1) == 0)
            break;

        //将要查询的单词传送给服务器
        if(send(sockfd,msg,sizeof(MSG),0) <0){
            perror("send");
            return -1;
        }

        // 等待接收服务器 传回的单词注释信息
        if (recv(sockfd,msg,sizeof(MSG),0) < 0){
            perror("recv");
            return -1;
        }
        
        // 打印单词解释
        printf("%s\n",msg->data);
    }
    return 0;
}

int do_history(int sockfd,MSG *msg)
{
    msg->type = H;

    send(sockfd,msg,sizeof(MSG),0);

    // 接收服务器传递的历史记录信息
    while(1){
        recv(sockfd,msg,sizeof(MSG),0);

        if(msg->data[0] == '\0')
            break;

        // 打印历史记录信息
        printf("%s\n",msg->data);
    }
    return 0;
}


点击查看代码
/**
 * @file server.c
 * @brief ./server
 * @author 
 * @version 
 * @date 2022-08-09
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sqlite3.h>
#include <signal.h>
#include <time.h>

#define N 32

#define SERV_PORT 10000 //服务器 端口号

#define R 1     //user - register
#define L 2     //user - login
#define Q 3     //user - query
#define H 4     //user - history

#define DATABASE "my.db"

//定义通信双方的信息结构体
typedef struct {
    int type;
    char name[N];
    char data[256];
}MSG;

int do_client(int acceptfd,sqlite3 *db);    //do_client
void do_register(int acceptfd,MSG *msg,sqlite3 *db);  //register
int do_login(int acceptfd,MSG *msg,sqlite3 *db);      //login
int do_serachword(MSG *msg,char word[]);
int get_date(char *date);
int do_query(int acceptfd,MSG *msg,sqlite3 *db);      //query
int history_callback(void *arg,int f_num,char **f_value,char **f_name);
int do_history(int acceptfd,MSG *msg,sqlite3 *db);    //history

int main(int argc,const char *argv[])
{
    int sockfd;
    struct sockaddr_in serveraddr;
    int n;
    MSG msg;
    sqlite3 *db;
    int acceptfd;
    pid_t pid;
    char *errmsg;

    // 打开数据库
    if (sqlite3_open(DATABASE,&db) != SQLITE_OK){
        printf("%s\n",sqlite3_errmsg(db));
        return -1;
    }
    else
        printf("Open DATABASE success.\n");

    // 创建usr表
    if (sqlite3_exec(db,"create table if not exists usr(name text primary key,pass text);",NULL,NULL,&errmsg) != SQLITE_OK){
        printf("%s\n",errmsg);
    }

    // 创建record表
    if (sqlite3_exec(db,"create table if not exists record(name text,data text,word text);",NULL,NULL,&errmsg) != SQLITE_OK){
        printf("%s\n",errmsg);
    }


    
    // 创建socket

    if ((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0){
        perror("socket");
        return -1;
    }

    // 服务器地址
    bzero(&serveraddr,sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); //服务器IP INADDR_ANY获取
    serveraddr.sin_port = htons(SERV_PORT);    //端口

    // bind()
    if(bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr)) < 0){
        perror("bind");
        return -1;
    }

    // listen
    if (listen(sockfd,5) < 0)
    {
        perror("listen");
        return -1;
    }
    
    //处理僵尸进程
    signal(SIGCHLD,SIG_IGN);    //忽略子进程状态信息,自动回收僵尸进程

    while(1)
    {
        if((acceptfd = accept(sockfd,NULL,NULL)) < 0){
            perror("accept");
            return -1;
        }
        
        if ((pid = fork()) < 0){
            perror("fork");
            return -1;
        }
        else if (pid == 0){ //子进程 处理客户端具体请求
            close(sockfd);
            do_client(acceptfd,db);
        }
        else    //父进程 接收客户端请求
        {
            close(acceptfd);
        }
    }

    return 0;
}

//do_client函数
int do_client(int acceptfd,sqlite3 *db)
{
    MSG msg;
    while (recv(acceptfd,&msg,sizeof(MSG),0) > 0){
        printf("type: %d\n",msg.type);
        switch(msg.type)
        {
        case R:
            do_register(acceptfd,&msg,db);
            break;
        case L:
            do_login(acceptfd,&msg,db);
            break;
        case Q:
            do_query(acceptfd,&msg,db);
            break;
        case H:
            do_history(acceptfd,&msg,db);
            break;
        default:
            printf("Invalid data msg.\n");
        }
    }
    
    printf("Client exit.\n");
    close(acceptfd);
    exit(0);

    return 0;
}

void do_register(int acceptfd,MSG *msg,sqlite3 *db)
{
    char *errmsg;
    char sql[sizeof(MSG)+128];

    sprintf(sql,"insert into usr values('%s','%s');",msg->name,msg->data);
    printf("%s\n",sql);

    if (sqlite3_exec(db,sql,NULL,NULL,&errmsg) != SQLITE_OK){
        printf("%s\n",errmsg);
        strcpy(msg->data,"usr name already exist.\n");
    }
    else{
        printf("Client register ok!\n");
        strcpy(msg->data,"OK!");
    }

    if(send(acceptfd,msg,sizeof(MSG),0) < 0){
        perror("send");
        return;
    }

    return;
}

int do_login(int acceptfd,MSG *msg,sqlite3 *db)
{
    char sql[sizeof(MSG)+128] = {};
    char *errmsg;
    int nrow;
    int ncloumn;
    char **resultp;

    // 根据 用户名和密码 查询数据库usr
    sprintf(sql,"select * from usr where name = '%s' and pass ='%s';",msg->name,msg->data);
    printf("%s\n",sql);

    if (sqlite3_get_table(db,sql,&resultp,&nrow,&ncloumn,&errmsg) != SQLITE_OK){
        printf("%s\n",errmsg);
        return -1;
    }

    // 查询成功,数据库中拥有此用户 发送OK
    //nrow只可能是 0或者1
    if(nrow == 1){
        strcpy(msg->data,"OK");
        send(acceptfd,msg,sizeof(MSG),0);
        return 1;
    }

    // 密码或者用户名错误
    if (nrow == 0){
        strcpy(msg->data,"usr or passwd is wrong.");
        send(acceptfd,msg,sizeof(MSG),0);
    }

    return 0;
}

//do_serachword函数
int do_serachword(MSG *msg,char word[])
{
    FILE *fp;
    int len = 0;
    char temp[512] = {};
    int result;
    char *p;

    //打开词典文件
    if((fp = fopen("dict.txt","r")) == NULL){
        perror("fopen");
        strcpy(msg->data,"Failed to open dict.txt\n");
        return -1;
    }

    len = strlen(word);
    printf("%s,len=%d\n",word,len);

    //按行读取词典文件,比对查询单词
    while(fgets(temp,512,fp) != NULL){
        result = strncmp(temp,word,len); //词典中单词出现的具有先后顺序 比较词典和传入单词

        if(result < 0)   //temp < word 证明word还在temp单词的下面 继续查找
            continue;
        if((result > 0) || (result ==0 && temp[len] != ' '))    //两种情况证明没有这个word 1是temp > word   2是 如temp为anc 要查询单词为an,此时result为0,但显然没有找到该单词
            break;

        // 找到了要查询单词所在的行,取出注释 如abide   v.遵守
        p = temp + len; //   v.遵守
        while(*p == ' ')    //去除空格 v.遵守
            p++;
        
        strcpy(msg->data,p);
        printf("%s\n",msg->data);

        //注释拷贝完成后 关闭文件
        fclose(fp);
        return 1;
    }

    fclose(fp);
    return 0;
}

//获取本地时间
int get_date(char *date)
{
    time_t t;
    struct tm *tp;

    time(&t);
    tp = localtime(&t);

    // 格式化输出时间到date
    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);

    return 0;
}

int do_query(int acceptfd,MSG *msg,sqlite3 *db)
{
    char word[64];
    int found = 0;
    char date[128];
    char sql[512] = {};
    char *errmsg;

    strcpy(word,msg->data);

    found = do_serachword(msg,word);    //具体查找单词 在do_searchword中实现

    // found=1 表示找到了单词,此时需保存 用户名,查询时间,单词 到历史记录表中
    if (found ==1){
        get_date(date); //获取时间

        sprintf(sql,"insert into record values('%s','%s','%s');",msg->name,date,word);
        printf("%s\n",sql);

        if(sqlite3_exec(db,sql,NULL,NULL,&errmsg) != SQLITE_OK){
            printf("%s\n",errmsg);
            return -1;
        }
    }
    else    //没有查到单词
        strcpy(msg->data,"Not found word!\n");

    // 发送查询结果到客户端
    send(acceptfd,msg,sizeof(MSG),0);
    
    return 0;
}

// 利用回调函数 得到查询结果 并将历史记录发送个客户端
int history_callback(void *arg,int f_num,char **f_value,char **f_name)
{
    // record表中有 name date word
    int acceptfd;
    MSG msg;

    acceptfd = *(int *)arg;

    sprintf(msg.data,"%s , %s",f_value[1],f_value[2]);

    send(acceptfd,&msg,sizeof(MSG),0);

    return 0;
}

int do_history(int acceptfd,MSG *msg,sqlite3 *db)
{
    char sql[128] = {};
    char *errmsg;

    sprintf(sql,"select * from record where name = '%s';",msg->name);

    // 查询数据库
    if (sqlite3_exec(db,sql,history_callback,(void *)&acceptfd,&errmsg) != SQLITE_OK){
        printf("%s\n",errmsg);
    }
    else
        printf("Query record done.\n");
    
    // 所有记录查询完成后给客户端发送一个结束信息
    msg->data[0] = '\0';
    send(acceptfd,msg,sizeof(MSG),0);

    return 0;
}


代码附件下载

posted @ 2022-08-09 13:54  ray963  阅读(456)  评论(0)    收藏  举报