在线英英词典

发布于:2022-12-11 ⋅ 阅读:(671) ⋅ 点赞:(0)

目录


前言

        在线英英词典项目是一款基于网络的以单词查询为主要功能的软件,具体实现功能如下: 单词查询,账号注册与登录,历史记录管理


一、项目模块

        1.网络通讯模块,服务器和客户端通基于 tcp 协议

        2.数据持久化模块,使用轻量级数据库 SQLITE3

        3.客户端菜单模块,使用命令行界面

二、实现步骤

        1.服务器:使用sqlite3 数据库,在库中创建user表和record表(user 表,用于管理用户账号信息;record 表,用于管理历史查询信息);

        2. 客户端:使用命令行界面来选择注册、登录、查询、历史记录、退出选项;

        3.客户端选择注册:客户端输入账号密码,服务器接受数据后打开user表进行比对。如果user表中没有此用户则记录到表中,发送信息到客户端,客户端可继续选择登录账号;如果user表中有此用户则报错,发送信息到客户端,客户端重新注册账号;

        4.客户端选择登录:客户端输入账号密码,服务器接受数据后打开user表进行比对,查看密码账号是否匹配。登录成功后可以进行查询;

        5.客户端选择查询:客户端输入需要查询的单词,服务器接受单词后,打开dict.txt文档,如果没有该单词,则打印错误信息发送给客户端;如果该文档中有查询的单词,则发送该单词给客户端,并且记录此时的用户、时间和单词到record表中;

        6.客户端选择查询历史记录:服务器接收到查询历史记录信息后,开始在record表中查询数据,每查到一条历史记录就给客户端发送一条记录,客户端也会一一接收;

三、功能代码

1.服务器 server.c

代码如下(示例):

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


//信息结构体
typedef struct
{
    char type;		//选择
    char name[64];	//用户名
    char result[256]; // password密码 or word单词 or remark返回数据
} MSG_t;

//系统初始化的函数//打开数据文件、尝试建表
sqlite3 *process_init();
void recv_client(int rws, sqlite3 *sql_db);
void process_register(int rws, MSG_t *msg, sqlite3 *sql_db);
void process_login(int rws, MSG_t *msg, sqlite3 *sql_db);
void process_search(int rws, MSG_t *msg, sqlite3 *sql_db);

void sigHandler(int signo);
int server_init(char *ipaddr, unsigned short port, int backlog);
int get_time(char *time_s);
void process_history(int rws, MSG_t *msg, sqlite3 *sql_db);
int callback(void *arg, int ncolumn, char **f_value, char **f_name);



int main(int argc, char *argv[])
{
    if(argc < 3)
    {
        printf("Usage:%s <ip> <port>\n",argv[0]);
        return -1;
    }

    //数据库初始化
    sqlite3 *sql_db = process_init();

    //signal(SIGCHLD, sigHandler);

    int sockfd = server_init(argv[1], atoi(argv[2]), 1024);
    if(-1 == sockfd)
    {
        printf("server_init error\n");
        return -1;
    }
    printf("listen....\n");

    struct sockaddr_in caddr;//保存客户端的信息(ip port protocol)
    bzero(&caddr, sizeof(caddr));
    socklen_t clen = sizeof(caddr);

    int ret;

    while(1)
    {
        //rws:用于服务器和连接的客户端通信
        int rws = accept(sockfd, (struct sockaddr *)&caddr, &clen);//阻塞等待客户端请求连接
        if(-1 == rws)
        {
            perror("accept");
            close(sockfd);
            return -1;

        }
        printf("IP:%s Port:%u\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));

        //创建子进程
        pid_t pid = fork();
        if(-1 == pid)
        {
            perror("fork");
            break;
        }
        else if(0 == pid)//子进程
        {
            close(sockfd);//关闭监听套接字
            recv_client(rws, sql_db);
        }
        else //父进程
        {
            signal(SIGCHLD, sigHandler);
            close(rws);
        }

    }
    close(sockfd);

    return 0;
}

void sigHandler(int signo)//自定义信号处理函数
{
    //回收子进程的资源
    if(SIGCHLD == signo)
        while(waitpid(-1, NULL, WNOHANG) > 0);//循环回收资源

}

int server_init(char *ipaddr, unsigned short port, int backlog)
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建网络通信接口
    if(-1 == sockfd)
    {
        perror("socket");
        return -1;
    }

    struct sockaddr_in saddr;//服务器的地址结构
    bzero(&saddr,sizeof(saddr));//memset()
    saddr.sin_family = AF_INET;//指定协议族ipv4
    saddr.sin_port = htons(port);//端口号:5001~65535
    saddr.sin_addr.s_addr = (ipaddr == NULL)? htonl(INADDR_ANY) : inet_addr(ipaddr);//ip地址 点分式 -> 二进制网络字节序


    int ret = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));//绑定 将服务器的ip和port与sockfd绑定
    if(-1 == ret)
    {
        perror("bind");
        close(sockfd);
        return -1;
    }

    ret = listen(sockfd, backlog);//监听是否有客户端请求连接 sockfd:监听套接字
    if(-1 == ret)
    {
        perror("listen");
        close(sockfd);
        return -1;
    }
    return sockfd;
}

//系统初始化的函数
//打开数据文件、尝试建表
sqlite3 *process_init()
{
    //打开数据库文件
    sqlite3 *sql_db = NULL;
    int ret = sqlite3_open("test.db", &sql_db);
    if (ret != SQLITE_OK)
    {
        printf("open sqlite3 error  errmsg: %s \n", sqlite3_errmsg(sql_db));
        exit(-1);
    }

    //建表
    char *errstr = NULL;
    //用户-密码
    char buf[128] = "CREATE TABLE IF NOT EXISTS user(name text PRIMARY KEY, pass text)";
    ret = sqlite3_exec(sql_db, buf, NULL, NULL, &errstr);
    if (ret != SQLITE_OK)
    {
        printf("create table user error   %s\n",errstr);
        sqlite3_close(sql_db);
        exit(-1);
    }

    //用户-时间-查询记录
    char buff1[128] = "CREATE TABLE IF NOT EXISTS record(name text, date text, word text)";
    ret = sqlite3_exec(sql_db, buff1, NULL, NULL, &errstr);
    if (ret != SQLITE_OK)
    {
        printf("create table record error   %s\n",errstr);
        sqlite3_close(sql_db);
        exit(-1);
    }
    sqlite3_free(errstr);

    return sql_db;
}

void recv_client(int rws, sqlite3 *sql_db)
{
    MSG_t msg;
    while(recv(rws, &msg, sizeof(MSG_t), 0) > 0)
    {
        printf("type = %c\n", msg.type); //选择
        printf("result = %s\n", msg.result); //数据
        switch (msg.type)
        {
            case 'R': // 1. 注册
                process_register(rws, &msg, sql_db);
                break;
            case 'L': // 2. 登录
                process_login(rws, &msg, sql_db);
                break;
            case 'Q': // 3. 查询
                process_search(rws, &msg, sql_db);
                break;
            case 'H': // 4. 历史记录
                process_history(rws, &msg, sql_db);
                break;
        }
    }
    printf("client quit\n");
    exit(0);
    return;
}

// 注册
void process_register(int rws, MSG_t *msg, sqlite3 *sql_db)
{
    char sql_buf[512], *errmsg;

    sprintf(sql_buf, "INSERT INTO user VALUES('%s','%s')", msg->name, msg->result);
    int ret = sqlite3_exec(sql_db, sql_buf, NULL, NULL, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf("error: %s\n", errmsg);
        sprintf(msg->result, "用户名 %s 已经存在!!!", msg->name);
        sqlite3_free(errmsg);
    }
    else
    {
        strncpy(msg->result, "register success", 16);
    }

    //发送数据
    ret = send(rws, &msg->result, sizeof(MSG_t), 0);
    if(ret == -1)
    {
        perror("send");
    }
    return;
}

// 登录
void process_login(int rws, MSG_t *msg, sqlite3 *sql_db)
{
    char sql_buf[512], *errmsg;

    sprintf(sql_buf, "SELECT * FROM user WHERE name='%s' AND pass='%s'", msg->name, msg->result);

    char **result;//结果指针
    int nrow = 0;//满足条件的记录个数
    int ncolumn = 0; //每条记录的字段数

    int ret =sqlite3_get_table(sql_db, sql_buf, &result, &nrow, &ncolumn, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf("error: %s\n", errmsg);
        sqlite3_free(errmsg);//释放错误信息的空间 防止内存泄漏
    }
    if(0 == nrow) // 结果集没数据,行数nrow=0
    {
        sprintf(msg->result, "user name: %s passwd: %s  is inconsistency", msg->name, msg->result);
    }
    else
    {
        strcpy(msg->result, "login success");
    }
    printf("msg->result: %s\n", msg->result);/

    //发送数据
    ret = send(rws, &msg->result, sizeof(MSG_t), 0);
    if(ret == -1)
    {
        perror("send");
    }
    return ;
}

// 查询
void process_search(int rws, MSG_t *msg, sqlite3 *sql_db)
{
    char sql_buf[512], *errmsg, *p;
    char s[300], time_s[128];
    int ret;

    FILE *fp = fopen("dict.txt", "r"); // 打开字典
    if (fp == NULL)
    {
        perror("fopen");
        strcpy(msg->result, "dict can not open");

        ret = send(rws, &msg, sizeof(MSG_t), 0);
        if(ret == -1)
        {
            perror("send");
            return ;
        }
    }

    char word[256]; //单词
    strcpy(word, msg->result);
    int len = strlen(msg->result);//单词长度
    while (fgets(s, sizeof(s), fp) != NULL)
    {
        //比较单词
        ret = strncmp(word, s, len);
        if (ret == 0 && s[len] == ' ') //相等 且 下一位是空格
        {
            p = &s[len]; //指向第一个空格
            while (*p == ' ')
            {
                p++;
            }                     //跳到解释的第一个字母
            strcpy(msg->result, p); //解释赋值
            //解释发送
            goto TOcli;
        }
        else
        {
            strcpy(msg->result, "no exist");
        }
    }
TOcli:
    //存在-记录-查询单词成功
    if (0 != strcmp(msg->result, "no exist"))
    {
        get_time(time_s); // 当前时间
        //使用sqlite3_exec函数调用插入记录
        sprintf(sql_buf, "INSERT INTO record VALUES('%s','%s','%s')", msg->name, time_s, word);
        int ret = sqlite3_exec(sql_db, sql_buf, NULL, NULL, &errmsg);
        if (ret != SQLITE_OK)
        {
            printf("error: %s\n", errmsg);
            sqlite3_free(errmsg);//释放错误信息的空间 防止内存泄漏
        }
    }
    //发送数据
    ret = send(rws, &msg->result, sizeof(MSG_t), 0);
    if(ret == -1)
    {
        perror("send");
    }

    return ;
}

//时间
int get_time(char *time_s)
{
    time_t t;
    time(&t); //秒钟数

    struct tm *tp;      // tm的结构体
    tp = localtime(&t); //将秒钟转化为年月日时分秒的格式
    sprintf(time_s, "%d-%d-%d %d:%d:%d", 1900 + tp->tm_year, 1 + tp->tm_mon, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec);
}

//历史
void process_history(int rws, MSG_t *msg, sqlite3 *sql_db)
{
    char sql_buf[512], *errmsg;
    int ret;

    sprintf(sql_buf, "SELECT * from record WHERE name='%s'", msg->name);
    ret = sqlite3_exec(sql_db, sql_buf, callback, (void *)&rws, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf("error: %s\n", errmsg);
        sprintf(msg->result, "用户名 %s 无记录!!!", msg->name);
        sqlite3_free(errmsg);//释放错误信息的空间 防止内存泄漏
    }

    //回复结束标志
    strcpy(msg->result, "OVER");
    //发送数据
    ret = send(rws, &msg->result, sizeof(MSG_t), 0);
    if(ret == -1)
    {
        perror("send");
    }
    return ;
}

// callback 回调函数
int callback(void *arg, int ncolumn, char **f_value, char **f_name)
{
    //每查询到一条记录 这个函数就会被调用一次!!!!
    MSG_t msg;
    char buf[512], *errmsg;
    int rws = *(int *)arg; // 接收传递参数 强制类型转换
    sprintf(msg.result, "%s : %s - %s", f_value[0], f_value[1], f_value[2]); // 时间 | 单词 ;f_value[0]=用户名
    //发送数据
    int ret = send(rws, &msg.result, sizeof(MSG_t), 0);
    if(ret == -1)
    {
        perror("send");
    }

    return 0;
}

2.客户端 client.c

代码如下(示例):

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


//信息结构体
typedef struct
{
    char type;		//选择
    char name[64];	//用户名
    char result[256]; // password密码 or word单词 or remark返回数据
} MSG_t;


int process_Register(int sockfd, MSG_t *msg);
int process_Login(int sockfd, MSG_t *msg);
int process_Search(int sockfd, MSG_t *msg);
int process_History(int sockfd, MSG_t *msg);

int main(int argc, char *argv[])
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建网络通信接口
    if(-1 == sockfd)
    {
        perror("socket");
        return -1;
    }

    printf("sockfd=%d\n",sockfd);

    // 2.填充服务器网络信息结构体
    struct sockaddr_in addr;//服务器的地址结构
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;//指定协议族ipv4
    addr.sin_port = htons(atoi(argv[2]));//端口号
    addr.sin_addr.s_addr = inet_addr(argv[1]);//ip地址 点分式 -> 二进制网络字节序

    int ret = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr));//绑定 将服务器的ip和port与sockfd绑定
    if(-1 == ret)
    {
        perror("connect");
        close(sockfd);
        return -1;
    }
    printf("connect success\n");

    MSG_t msg;
    char n;

    while (1)
    {
        printf("\n************************************\n");
        printf("* R:  注册     L: 登录     #: 退出 *\n");
        printf("************************************\n");
        printf("please choose : ");

        if(scanf("%c", &n) <= 0)
        {
            perror("scanf");
            exit(-1);
        }
        switch (n)
        {
            case 'R': //注册
                process_Register(sockfd, &msg);
                break;
            case 'L'://登录
                if( process_Login(sockfd, &msg) ==1 )
                    goto next;
                break;
            case '#':
                close(sockfd);
                exit(0);
            default:
                break;
        }
    getchar();

    }
next:
    while (1)
    {
        printf("\n************************************\n");
        printf("*Q: 查询     H: 历史记录     #: 退出 *\n");
        printf("************************************\n");
        printf("please choose : ");

        getchar();
        if(scanf("%c", &n) <= 0)
        {
            perror("scanf");
            exit(-1);
        }
       
        
        switch (n)
        {
            case 'Q': //查询
                process_Search(sockfd, &msg);
                break;
            case 'H': //历史记录
                process_History(sockfd, &msg);
                break;
            case '#':
                close(sockfd);
                exit(0);
            default:
                break;
        }

    }
    close(sockfd);

    return 0;
}

int process_Register(int sockfd, MSG_t *msg)
{
    msg->type = 'R';
    printf("please input user name: ");
    scanf("%s", msg->name);
    printf("please input user passwd: ");
    scanf("%s", msg->result);

    int ret = send(sockfd, msg, sizeof(MSG_t), 0);
    if(ret <= 0)
    {
        perror("send");
        return -1;
    }

    ret = recv(sockfd, msg->result, sizeof(MSG_t), 0);
    if(ret <= 0)
    {
        perror("recv");
        return -1;
    }
    printf("%s\n", msg->result);
    return 0;

}

int process_Login(int sockfd, MSG_t *msg)
{
    msg->type = 'L';
    printf("please input user name: ");
    scanf("%s", msg->name);
    printf("please input user passwd: ");
    scanf("%s", msg->result);

    int ret = send(sockfd, msg, sizeof(MSG_t), 0);
    if(ret <= 0)
    {
        perror("send");
        return -1;
    }

    ret = recv(sockfd, msg->result, sizeof(MSG_t), 0);
    if(ret <= 0)
    {
        perror("recv");
        return -1;
    }
    printf("msg->result: %s\n", msg->result);

    if(strcmp(msg->result, "login success") == 0)
    {
        return 1;
    }

    return 0;
}

int process_Search(int sockfd, MSG_t *msg)
{
    msg->type = 'Q';
    while(1)
    {
        printf("please input search word: ");
        scanf("%s", msg->result);

        if(strcmp(msg->result, "$") == 0)
            break;

        int ret = send(sockfd, msg, sizeof(MSG_t), 0);
        if(ret == 0)
        {
            perror("send");
            return -1;
        }

        ret = recv(sockfd, msg->result, sizeof(MSG_t), 0);
        if(ret == -1)
        {
            perror("recv");
            return -1;
        }

        printf("%s\n", msg->result);
    }

    return 0;
}

int process_History(int sockfd, MSG_t *msg)
{
    msg->type = 'H';
    int ret = send(sockfd, msg, sizeof(MSG_t), 0);
    if(ret == -1)
    {
        perror("send");
        return -1;
    }

    while(1)
    {
        ret = recv(sockfd, msg->result, sizeof(MSG_t), 0);
        if(ret == -1)
        {
            perror("recv");
            return -1;
        }

        if(strcmp(msg->result, "OVER") == 0)
            break;

        printf("%s\n", msg->result);
    }

    return 0;
}

四、结果

本文含有隐藏内容,请 开通VIP 后查看