基于TCP的在线词典

一、项目介绍

        在线词典是基于TCP等网络协议族体系完成的一个小型的应用程序,能够完成一些简单的类似于实际生活中的登录,保存用户信息和使用历史记录一系列操作(信息都存放在事先创建好的数据库文件中)。“查询”作为该项目主要的应用价值,其他功能则为了方便用户使用。

二、功能说明

大方向一共四个功能:

        注册

        登录

        查询单词

        查询历史记录

 三、功能逻辑以及逻辑图

        该项目运用多进程实现TCP并发服务器的功能,当用户连接到该项目服务器时,反馈用户三个选项(注册,登录,退出),用户选择“注册”选项时,程序会让用户填写用户信息,并且比较新用户的信息和已经存在的用户的信息,如果已经存在,则会返回最初的三个选项让用户重新选择,否则,向数据库文件存入新用户的信息,再给用户反馈注册成功,并且再返回给用户三个选项(注册,登录,退出),如下图所示:

 在用户选择“登录”操作时,服务器会让用户依次输入用户名和密码,用户名和密码都正确则跳转到下一个选项,否则会重新让用户输入。在用户登录成功之后,服务器会给用户发送三个选项(查询,历史记录,退出),用户输入“1”之后,跳转到查询模块,进行查询,该服务器模型可让用户进行任意次数的查询,每次成功查询到的字词都会记录到历史记录中,这些记录可退出查询功能之后,使用“历史记录”功能查看,如下图所示:

 最后用户输入“#”则与服务器断开连接,退出在线词典。

         项目逻辑图如下:

客户端:

 服务器:

  四  个人认为项目中比较精髓的代码及逻辑

int do_searchword(int acceptfd, MSG *msg)
{
    FILE *fd;
    char temp[300] = {0};
    char *p=NULL;
    int len, ret;
    //单词长度
    len = strlen(msg->date);
    //打开文件
    if (NULL == (fd = fopen("dict.txt", "r")))
    {
        strcpy(msg->date, "open dictionary file error");
        if (-1 == send(acceptfd, msg, sizeof(MSG), 0))
        {
            ERRLOG("send error");
        }
    }
    
    while (fgets(temp, 300, fd) != NULL)
    {
        //比较单词
        ret = strncmp(msg->date, temp, len);
        if (ret == 0 && temp[len] == ' ')
        {
            p = temp + len;
            //移动p让*p指向解释的第一个字符
            while (*p == ' ')
            {
                p++;
            }
            //将解释的首地址给数组
            strcpy(msg->date, p);

            fclose(fd);
            return 1;
        }
    }
    fclose(fd);
    return 0;
}

在查询功能模块调用的do_searchword(int acceptfd, MSG *msg)中,fgets读取每一行内容(字典文件每一行大小不大于300字节),每读取一行就和msg里面的date比较“len”个字符,因为数组下标从0开始,而len是从一开始,所以当strncmp返回0,并且数组temp的temp[len]是' ',说明词典里的单词与用户查询的单词完全匹配(单词与解释之间存在大于零的若干个空格),temp表示数组首地址,加上len,p指针就指向了第一个空格的地址。这之后就让指针移动,直到指向的地址存放的数据不是空格,最后利用strcpy函数,让date数组的指针(数组名)指向p指针所在的地址,因为数组成员地址连续,所以首地址确认后,数组其他成员也确认了,首地址是解释的第一个字符,这样就达到了将解释内容放到msg数据包的目的了。
 

五 项目代码

客户端:

#include <head.h>
#define A 1
#define B 2
#define C 3
#define D 4
typedef struct
{
    int type;
    char name[32];
    char date[256];
} MSG;

void signal_handler(int signum);
void do_clident(int acceptfd, sqlite3 *my_db);
void do_register(int acceptfd, MSG *msg, sqlite3 *my_db);
void do_login(int acceptfd, MSG *msg, sqlite3 *my_db);
void do_query(int acceptfd, MSG *msg, sqlite3 *my_db);
void do_history(int acceptfd, MSG *msg, sqlite3 *my_db);
int do_searchword(int acceptfd, MSG *msg);
void getdate(char *date);
int history_callback(void *arg, int f_num, char **f_value, char **f_name);
int main(int argc, const char *argv[])
{
    int acceptfd;
    pid_t pid;
    sqlite3 *my_db = NULL;
    char *errstr = NULL;
    //打开数据库文件
    int ret = sqlite3_open("my.db", &my_db);
    if (ret != SQLITE_OK)
    {
        printf("errcode = [%d], errstr = [%s]\n", ret, sqlite3_errmsg(my_db));
        exit(-1);
    }
    printf("打开数据文件成功\n");
    //入参合理性检查
    if (3 != argc)
    {
        printf("Usage : %s <IP> <PORT>\n", argv[0]);
        exit(-1);
    }
    //创建套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd)
    {
        ERRLOG("socket error");
    }
    //填充服务器网络信息结构体
    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[2]));
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);

    socklen_t serveraddr_len = sizeof(serveraddr);

    // bind绑定
    if (-1 == bind(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len))
    {
        ERRLOG("bind error");
    }
    //将套接字设置为监听状态
    if (-1 == listen(sockfd, 5))
    {
        ERRLOG("listen error");
    }

    signal(SIGUSR1, signal_handler);
    //定义一个结构体保存客户端信息
    struct sockaddr_in clientaddr;
    memset(&clientaddr, 0, sizeof(clientaddr));
    socklen_t clientaddr_len = sizeof(clientaddr);
    while (1)
    {
        if (-1 == (acceptfd = accept(sockfd, NULL, NULL)))
        {
            ERRLOG("accept error");
        }
        if (-1 == (pid = fork()))
        {
            ERRLOG("fork error");
        }
        else if (pid == 0)
        {
            do_clident(acceptfd, my_db);
        }
        if (pid > 0)
        {
            close(acceptfd);
        }
    }
    return 0;
}

void signal_handler(int signum)
{
    if (signum == SIGUSR1)
    {
        wait(0);
    }
}
void do_clident(int acceptfd, sqlite3 *my_db)
{
    MSG msg;
    while (recv(acceptfd, &msg, sizeof(MSG), 0) > 0)
    {
        printf("type = %d\n", msg.type);
        printf("date = %s\n", msg.date);
        switch (msg.type)
        {
        case A:
            do_register(acceptfd, &msg, my_db);
            break;
        case B:
            do_login(acceptfd, &msg, my_db);
            break;
        case C:
            do_query(acceptfd, &msg, my_db);
            break;
        case D:
            do_history(acceptfd, &msg, my_db);
            break;
        }
    }
    printf("client quit\n");
    exit(0);
    return;
}

void do_register(int acceptfd, MSG *msg, sqlite3 *my_db)
{
    char sqlbuf[512] = {0};
    char *errmsg = NULL;
    //组装sql语句
    sprintf(sqlbuf, "INSERT INTO usr VALUES('%s','%s')", msg->name, msg->date);
    if (SQLITE_OK != sqlite3_exec(my_db, sqlbuf, NULL, NULL, &errmsg))
    {
        sprintf(msg->date, "usr[%s] already exist!", msg->name);
    }
    else
    {
        strcpy(msg->date, "OK");
    }
    if (-1 == send(acceptfd, msg, sizeof(MSG), 0))
    {
        ERRLOG("send error");
    }
    sqlite3_free(errmsg);
}
void do_login(int acceptfd, MSG *msg, sqlite3 *my_db)
{
    char sqlbuf[512] = {0};
    char *errmsg = NULL;
    char **result = NULL;
    int nrow, column;
    //组装sql语句
    sprintf(sqlbuf, "SELECT * FROM usr WHERE name='%s' AND date='%s' ", msg->name, msg->date);
    if (sqlite3_get_table(my_db, sqlbuf, &result, &nrow, &column, &errmsg) != SQLITE_OK)
    {
        printf("error:%s\n", errmsg);
    }
    //通过nrow参数判断是否能够查询到疾记录,如果值为0,则查询不到,如果值为非0,则查询到
    if (nrow == 0)
    {
        strcpy(msg->date, "name or password is wrony!!!");
    }
    else
    {
        strncpy(msg->date, "OK", 256);
    }
    sqlite3_free_table(result);
    sqlite3_free(errmsg);
    if (-1 == send(acceptfd, msg, sizeof(MSG), 0))
    {
        printf("send error\n");
    }
    return;
}
void do_query(int acceptfd, MSG *msg, sqlite3 *my_db)
{
    char sqlbuf[128] = {0};
    char *errmsg;
    char word[128] = {0};
    char time[128] = {0};
    int falg = 0;
    strcpy(word, msg->date);


    falg = do_searchword(acceptfd, msg);
    if (0 == falg)
    {
        strcpy(msg->date, "the word don't exist");
        if (-1 == send(acceptfd, msg, sizeof(MSG), 0))
        {
            printf("send error");
        }
        return ;
    }
    if (1 == falg)
    {
        getdate(time);
        //组装sql语句
        sprintf(sqlbuf, "INSERT INTO record VALUES('%s','%s','%s')", msg->name, time, word);
        if (sqlite3_exec(my_db, sqlbuf, NULL, NULL, &errmsg) != SQLITE_OK)
        {
            printf("errmsg:%s\n", errmsg);
            sqlite3_free(errmsg);
        }
    }
    if (-1 == send(acceptfd, msg, sizeof(MSG), 0))
    {
        printf("send error\n");
    }
    return;
}
void do_history(int acceptfd, MSG *msg, sqlite3 *my_db)
{
    char sqlbuf[128] = {0};

    char *errmsg;
    
    //查询历史表
    sprintf(sqlbuf, "SELECT * FROM record WHERE name='%s'", msg->name);
    if (SQLITE_OK != sqlite3_exec(my_db, sqlbuf, history_callback, (void *)&acceptfd, &errmsg))
    {
        printf("errmsg:%s\n", errmsg);
        sqlite3_free(errmsg);
    }

    //发送结束语
    strcpy(msg->date, "**over**");
    if (-1 == send(acceptfd, msg, sizeof(MSG), 0))
    {
        printf("send over error");
    }
    return;
}
int do_searchword(int acceptfd, MSG *msg)
{
    FILE *fd;
    char temp[300] = {0};
    char *p=NULL;
    int len, ret;
    //单词长度
    len = strlen(msg->date);
    //打开文件
    if (NULL == (fd = fopen("dict.txt", "r")))
    {
        strcpy(msg->date, "open dictionary file error");
        if (-1 == send(acceptfd, msg, sizeof(MSG), 0))
        {
            ERRLOG("send error");
        }
    }
    
    while (fgets(temp, 300, fd) != NULL)
    {
        //比较单词
        ret = strncmp(msg->date, temp, len);
        if (ret == 0 && temp[len] == ' ')
        {
            p = temp + len;
            //移动p让*p指向解释的第一个字符
            while (*p == ' ')
            {
                p++;
            }
            //将解释的首地址给数组
            strcpy(msg->date, p);

            fclose(fd);
            return 1;
        }
    }
    fclose(fd);
    return 0;
}
void getdate(char *date)
{
    time_t t;
    struct tm *tp;
    time(&t);
    tp = localtime(&t);
    sprintf(date, "%04d-%02d-%02d %02d:%02d:%02d", 1900 + tp->tm_year, 1 + tp->tm_mon, tp->tm_mday,\
                     tp->tm_hour, tp->tm_min, tp->tm_sec);
}
int history_callback(void *arg, int f_num, char **f_value, char **f_name)
{
    int acceptfd;
    MSG msg;
    acceptfd = *(int *)arg;
    sprintf(msg.date, "%s :%s", f_value[1], f_value[2]);
    if (-1 == send(acceptfd, &msg, sizeof(MSG), 0))
    {
        ERRLOG("send error");
    }
    return 0;
}

服务器:

#include <head.h>
#define A 1
#define B 2
#define C 3
#define D 4
typedef struct
{
    int type;
    char name[32];
    char date[256];
} MSG;
void do_register(int sockfd, MSG *msg);
int do_login(int sockfd, MSG *msg);
void do_query(int sockfd, MSG *msg);
void do_history(int sockfd, MSG *msg);

int main(int argc, const char *argv[])
{
    //入参合理性检查
    if (3 != argc)
    {
        printf("Usage : %s <IP> <PORT>\n", argv[0]);
        exit(-1);
    }

    // 1.创建流式套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd)
    {
        ERRLOG("socket error");
    }

    // 2.填充服务器的网络信息结构体
    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[2]));
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);

    socklen_t serveraddr_len = sizeof(serveraddr);

    // 3.尝试与服务器建立连接
    if (-1 == connect(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len))
    {
        ERRLOG("connect error");
    }
    printf("与服务器建立连接成功..\n");
    int n;
    MSG msg;
    while (1)
    {
        printf("****************************************\n");
        printf("*1: register    2: login  3: quit      *\n");
        printf("****************************************\n");
        printf("plese  choose:");
        if (scanf("%d", &n) <= 0)
        {
            printf("输入有误\n");
            return -1;
        }
        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);
        default:
            break;
        }
    }
next:
    while(1)
    {
        printf("****************************************\n");
        printf("*1: query    2: history   3: quit      *\n");
        printf("****************************************\n");
        printf("plese  choose:");
        if (scanf("%d", &n) <= 0)
        {
            printf("输入有误\n");
            return -1;
        }
        switch (n)
        {
        case 1:
            do_query(sockfd, &msg);
            break;
        case 2:
            do_history(sockfd, &msg);
            break;
        case 3:
            close(sockfd);
            exit(0);
        }
    }
    return 0;
}
void do_register(int sockfd, MSG *msg)
{
    //确认操作码
    msg->type = A;
    //输入用户名和密码
    printf("请输入用户名:");
    scanf("%s", msg->name);
    printf("请输入密码:");
    scanf("%s", msg->date);
    //收发数据
    if (-1 == send(sockfd, msg, sizeof(MSG), 0))
    {
        ERRLOG("send error");
    }
    if (-1 == recv(sockfd, msg, sizeof(MSG), 0))
    {
        ERRLOG("recv error");
    }
    printf("register [%s]:[%s]\n", msg->name, msg->date);
}
int do_login(int sockfd, MSG *msg)
{
    msg->type = B;
    printf("请输入用户名:");
     scanf("%s", msg->name);
    printf("请输入密码:");
    scanf("%s", msg->date);
    if(-1 ==send(sockfd,msg,sizeof(MSG),0)){
        ERRLOG("send error");
    }    
    if(-1 ==recv(sockfd,msg,sizeof(MSG),0)){
        ERRLOG("recv error");
    }
    if(0 == strncmp(msg->date,"OK",3)){
        printf("login:OK\n");
        return 1;
    }
    printf("login:[%s]\n",msg->date);
    return 0;
}
void do_query(int sockfd, MSG *msg)
{
	 //确认操作码
    msg->type = C;
    puts("-----------------------------");
    //输入要查询的信息
    while (1)
    {
        printf("请输入要查询的信息(输入*退出):");
        scanf("%s", msg->date);
        //输入#就退出查询
        if (0 == strcmp(msg->date, "*"))
        {
            break;
        }
        //与服务器进行交流
        if (-1 == send(sockfd, msg, sizeof(MSG), 0))
        {
            printf("send error");
        }
        if (-1 == recv(sockfd, msg, sizeof(MSG), 0))
        {
            printf("recv error");
        }
        //打印服务器返回的信息
        printf(":%s\n", msg->date);
    }
    return;
}
void do_history(int sockfd, MSG *msg)
{
    //确认操作码
    msg->type = D;
    if (-1 == send(sockfd, msg, sizeof(MSG), 0))
    {
        printf("send error");
    }
    while (1)
    {
        if (-1 == recv(sockfd, msg, sizeof(MSG), 0))
        {
            printf("recv error");
        }
        if (0 == strcmp(msg->date, "**over**"))
        {
            break;
        }
        printf("%s\n", msg->date);
    }
}

head.h

#ifndef __HEAD_H__
#define __HEAD_H__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <sys/wait.h>
#include <dirent.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/un.h>
#include <sqlite3.h>


#define ERRLOG(msg) \
    do {               \
        printf("%s %s %d\n", __FILE__, __func__, __LINE__);\
        perror(msg);   \
        exit(-1);     \
    } while (0)

#endif

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值