【基于TCP 在线电子词典】

项目功能

1. 登录注册功能,不能重复登录,重复注册
2. 单词查询功能
3. 历史记录功能,存储单词,意思,以及查询时间
4. 基于TCP,支持多客户端连接
5. 采用数据库保存用户信息与历史记录
6. 将dict.txt的数据导入到数据库中保存。
7. 按下ctrl+c退出客户端后,注销该客户端的登录信息
8. 使用并发服务器

流程图

客户端

在这里插入图片描述

服务器端

在这里插入图片描述

功能实现

服务器

头文件:

#ifndef __SERVER_H__
#define __SERVER_H__
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sqlite3.h>
#include <time.h>
#define PORT 6666
#define IP "192.168.10.90"
#define R 1 //注册
#define L 2 //登录
#define S 3 //查单词
#define Q 4 //查记录
#define B 5 //回退
typedef struct msg
{
    char name[20];//用户名
    char data[128];//密码|单词|消息
    int option;//判断执行什么操作
    int flag;//成功或失败
}MSG;
void init_sql(sqlite3 *db);
void handler(int sig);
void init_internet(int sockfd, int port, char *addr);
void client_deal(int newfd, sqlite3 *db);
void Register(int sockfd, MSG *msg, sqlite3 *db);
void Login(int newfd, MSG *msg, sqlite3 *db);
int Search_word();
void getdata(char *date);
int Query_log(int newfd, MSG *msg, sqlite3 *db);
void off(MSG *msg, sqlite3 *db);

#endif

服务器实现代码:

#include <stdio.h>
#include "server.h"

int main(int argc, const char *argv[])
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    int newfd;
    pid_t pid;
    if (sockfd < 0)
    {
        perror("socket");
        return -1;
    }
    init_internet(sockfd, PORT, IP);
    signal(SIGCHLD, handler);
    sqlite3 *db = NULL;
    if (sqlite3_open("./my.db", &db) != SQLITE_OK)
    {
        fprintf(stderr, "line:%d sqlite_open:%s\n", __LINE__, sqlite3_errmsg(db));
        return -1;
    }
    init_sql(db);
    struct sockaddr_in cin;
    socklen_t cin_len = sizeof(cin);
    while (1)
    {
        if ((newfd = accept(sockfd, (struct sockaddr *)&cin, &cin_len)) < 0)
        {
            perror("accept");
            return -1;
        }
        printf("[%s:%d]连接到服务器..\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));
        if ((pid = fork()) < 0)
        {
            perror("fork");
            return -1;
        }
        else if (0 == pid) //子进程执行处理代码
        {
            client_deal(newfd, db);
        }
        else //父进程负责连接
        {
            close(newfd);
        }
    }
    return 0;
}
void init_sql(sqlite3 *db)
{

    printf("正在初始化...\n");
    //创建表

    char sql[256] = "";
    char *errmsg = NULL;
    strcpy(sql, "create table if not exists usr (name char PRIMARY KEY,passwd char,status char);");
    if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
    {
        printf("sqlite3_exec error:%s\n", errmsg);
        return;
    }
    strcpy(sql, "create table if not exists log (name char,word char,explain char,time char);");
    if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
    {
        printf("sqlite3_exec error:%s\n", errmsg);
        return;
    }
    strcpy(sql, "create table if not exists dict (Word char,Explain char);");
    if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
    {
        printf("sqlite3_exec error:%s\n", errmsg);
        return;
    }
    //判断词库存不存在
    char **result = NULL;
    int rows = 1;
    int columns = 0;
    // char sql[128] = "select * from stu";
    strcpy(sql, "select * from dict");
    if (sqlite3_get_table(db, sql, &result, &rows, &columns, &errmsg) != SQLITE_OK)
    {
        printf("sqlite3_get_table error:%s line=%d\n", errmsg, __LINE__);
        return;
    }
    sqlite3_free_table(result);
    if (rows < 7987)
    {
        printf("正在导入词库...\n");
        FILE *fp = fopen("./dict.txt", "r");
        if (NULL == fp)
        {
            perror("fopen");
            return;
        }
        char buff[300];
        char Word[64];
        char Explain[256];

        char *p = NULL;

        while (NULL != fgets(buff, sizeof(buff), fp))
        {
            p = buff;
            while (1)
            {
                if (*p != ' ' || (*p == ' ' && *(p + 1) != ' '))
                    p++;
                else
                    break;
            }
            *p = '\0';
            p++;
            //获取单词
            strcpy(Word, buff);
            //跳过空格
            while (*p == ' ')
            {
                p++;
            }
            //截取解释
            strcpy(Explain, p);
            sprintf(sql, "insert into dict values(\"%s\",\"%s\")", Word, Explain);
            if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
            {
                printf("sqlite3_exec error:%s line=%d\n", errmsg, __LINE__);
                return;
            }
        }
    }
    printf("单词库导入成功\n");
}
void handler(int sig) //处理僵尸进程
{
    while (waitpid(-1, NULL, WNOHANG) > 0)
        ;
}
void init_internet(int sockfd, int port, char *addr)
{
    int reuse = 1;
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
    {
        perror("setsockopt");
        return;
    }
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr(addr);
    sin.sin_port = htons(port);
    if (bind(sockfd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
    {
        perror("bind");
        return;
    }
    if (listen(sockfd, 10) < 0)
    {
        perror("listen");
        return;
    }
}
//客户端处理函数
void client_deal(int newfd, sqlite3 *db)
{
    MSG msg;
    while (1) // 接收客户端发来的请求
    {
        char sql[512];
        char *errmsg = NULL;
        int res = recv(newfd, &msg, sizeof(MSG), 0);
        if (res < 0)
        {
            perror("recv");
            return;
        }
        else if (0 == res)
        {
            printf("client quit\n");
            sprintf(sql, "update usr set status = 'no' where name = '%s'", msg.name); //退出后登录状态为No
            if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
            {
                printf("sqlite3_exec error:%s line=%d\n", errmsg, __LINE__);
            }
            exit(0);
        }
        else
        {
            switch (msg.option)
            {
            case R:
                Register(newfd, &msg, db);
                break;
            case L:
                Login(newfd, &msg, db);
                break;
            case S:
                Search_word(newfd, &msg, db);
                break;
            case Q:
                Query_log(newfd, &msg, db);
                break;
            case B:
                off(&msg, db);
            }
        }
    }
    return;
}
//注册功能
void Register(int sockfd, MSG *msg, sqlite3 *db)
{
    char sql[512] = {0};
    char *errmsg;
    sprintf(sql, "insert into usr values(\"%s\",\"%s\",'no');", msg->name, msg->data);
    // name为主键,插入失败则用户名已经存在
    if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
    {
        // printf("sqlite3_exec error:%s line=%d\n", errmsg, __LINE__);
        sprintf(msg->data, "用户名 %s 已存在!!", msg->name);
    }
    else
    {   printf("注册了一个新用户为: %s\n",msg->name);
        strcpy(msg->data, "注册成功!!");
    }
    send(sockfd, msg, sizeof(MSG), 0);
    return;
}
//登录功能
void Login(int newfd, MSG *msg, sqlite3 *db)
{
    char sql[512] = {0};
    char *errmsg, **result;
    int rows, columns;

    //通过sqlite3_get_table函数查询记录是否存在
    sprintf(sql, "select * from usr where name = '%s' and passwd = '%s'", msg->name, msg->data);
    if (sqlite3_get_table(db, sql, &result, &rows, &columns, &errmsg) != SQLITE_OK)
    {
        printf("sqlite3_get_table error:%s line=%d\n", errmsg, __LINE__);
    }
    //通过row参数判断是否能够查询到疾记录,如果值为0,则查询不到,如果值为非0,则查询到
    if (rows == 0)
    {
        strcpy(msg->data, "用户名或密码错误");
        msg->flag = 0; //失败
    }
    else
    {
        if (strcmp("no", result[5]) == 0)
        {
            strcpy(msg->data, "登录成功");
            sprintf(sql, "update usr set status = 'yes' where name = '%s'", msg->name); //登录之后状态设置为yes;
            if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
            {
                printf("sqlite3_exec error:%s line=%d\n", errmsg, __LINE__);
            }
            msg->flag = 1; //成功
            printf("%s 登录了\n", msg->name);
        }
        else
        {
            strcpy(msg->data, "不允许重复登录\n");
            msg->flag = 0;
        }
    }

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

    return;
}
//查询单词功能
int Search_word(int newfd, MSG *msg, sqlite3 *db)
{
    char sql[512] = "", *errmsg = NULL;
    int found = 0;
    char date[128];
    char **result = NULL;
    int rows = 0;
    int columns = 0;
    sprintf(sql, "select * from dict where Word='%s'", msg->data);
    if (sqlite3_get_table(db, sql, &result, &rows, &columns, &errmsg) != SQLITE_OK)
    {
        printf("sqlite3_get_table error:%s line=%d\n", errmsg, __LINE__);
        return -1;
    }
    if (0 == rows)//没有查到
    {
        strcpy(msg->data, "Not Found!!!");
        send(newfd, msg, sizeof(MSG), 0);
    }
    else
    {
        printf("%s\t\t%s\n", result[2], result[3]);
        strcpy(msg->data, result[3]);
        //如果执行成功,还需要保存历史记录
        //获取时间
        getdata(date);
        //通过sqlite3_exec函数插入数据
        bzero(sql, sizeof(sql));
        sprintf(sql, "insert into log values('%s', '%s', '%s', '%s')", msg->name, result[2], result[3], date);
        if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
        {
            printf("sqlite3_exec error:%s line=%d\n", errmsg, __LINE__);
        }
        printf("%s 查询并插入记录成功!\n",msg->name);
        send(newfd, msg, sizeof(MSG), 0);
    }
    return 0;
}
//获取系统当前时间
void getdata(char *date)
{
    time_t t;
    struct tm *tp;
    time(&t);
    tp = localtime(&t);
    sprintf(date, "%d-%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 Query_log(int newfd, MSG *msg, sqlite3 *db)
{
    char sql[512] = "", *errmsg = NULL;
    int found = 0;
    char info[512];
    char **result = NULL;
    int rows = 0;
    int columns = 0;
    printf("%s 查询了记录...\n", msg->name);
    sprintf(sql, "select * from log where name='%s'", msg->name);
    if (sqlite3_get_table(db, sql, &result, &rows, &columns, &errmsg) != SQLITE_OK)
    {
        printf("sqlite3_get_table error:%s line=%d\n", errmsg, __LINE__);
        return -1;
    }
    //将记录逐条发送给客户端
    for (int i = 1; i <= rows; i++)
    {
        sprintf(info, "%s\t%s\t%s\t%s\n", result[i * columns], result[i * columns + 1], result[i * columns + 2], result[i * columns + 3]);
        send(newfd, info, sizeof(info), 0);
    }
    strcpy(info, "query end");
    send(newfd, info, sizeof(info), 0);
    return 1;
}
//注销
void off(MSG *msg, sqlite3 *db) //注销登录后,设置状态为no
{
    char sql[512];
    char *errmsg = NULL;
    sprintf(sql, "update usr set status = 'no' where name = '%s'", msg->name); //登录之后状态设置为no;
    if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
    {
        printf("sqlite3_exec error:%s line=%d\n", errmsg, __LINE__);
    }
    printf("%s 退出了登录\n",msg->name);
}

客户端

头文件

#ifndef __SERVER_H__
#define __SERVER_H__
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sqlite3.h>
#include <time.h>
#define PORT 6666
#define IP "192.168.10.90"
#define R 1 //注册
#define L 2 //登录
#define S 3 //查单词
#define Q 4 //查记录
#define B 5 //回退
typedef struct msg
{
    char name[20];//用户名
    char data[128];//密码|单词|消息
    int option;//判断执行什么操作
    int flag;//成功或失败
}MSG;
void init_sql(sqlite3 *db);
void handler(int sig);
void init_internet(int sockfd, int port, char *addr);
void client_deal(int newfd, sqlite3 *db);
void Register(int sockfd, MSG *msg, sqlite3 *db);
void Login(int newfd, MSG *msg, sqlite3 *db);
int Search_word();
void getdata(char *date);
int Query_log(int newfd, MSG *msg, sqlite3 *db);
void off(MSG *msg, sqlite3 *db);

#endif

客户端实现代码:

#include <stdio.h>
#include "client.h"

char name[20];
int flag=0;//页面跳转使用
int main(int argc, const char *argv[])
{
    MSG msg;
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket");
        return -1;
    }
    init_internet(sockfd, PORT, IP);
START:
    main_chose(sockfd, msg);
    if (flag == 1)
    {
        chose(sockfd, msg);
        goto START;
    }
    if(flag==0)
    {
        goto END;
    }
END:
    close(sockfd);
    return 0;
}
void main_chose(int sockfd, MSG msg)
{
    int res = 0;
    while (1)
    {
        main_menu();
        int chose;
        printf("请选择>>>");
        scanf("%d", &chose);
        while (getchar() != 10)
            ;
        switch (chose)
        {
        case 1:
            Register(sockfd, &msg);
            break;
        case 2:
            res = Login(sockfd, &msg);
            break;
        case 3:
            return;
        default:
            printf("输入错误\n");
            break;
        }
        if (1 == res)
        {
            flag = 1; //跳下一个页面;
            printf("按任意键进入下一页面>>>");
            while (getchar() != 10)
                ;
            break;
        }
        printf("按任意键清屏>>>");
        while (getchar() != 10)
            ;
    }
    return;
}
void chose(int sockfd, MSG msg)
{
    while (1)
    {
        menu();
        int chose;
        printf("请选择>>>");
        scanf("%d", &chose);
        while (getchar() != 10)
            ;
        switch (chose)
        {
        case 1:
            Search_word(sockfd, &msg);
            break;
        case 2:
            Query_log(sockfd, &msg);
            break;
        case 3:
            Back(sockfd,&msg);
            flag=0;
            return;
        default:
            printf("输入错误\n");
            break;
        }
        printf("按任意键清屏>>>");
        while (getchar() != 10)
            ;
    }
    return;
}
void main_menu()
{
    system("clear");
    printf("=======================电子词典============================\n");
    printf("------------------------主界面-----------------------------\n");
    printf("\t\t\t1.注册\n");
    printf("\t\t\t2.登录\n");
    printf("\t\t\t3.退出\n");
    printf("-----------------------------------------------------------\n");
}
void menu()
{
    system("clear");
    printf("=======================电子词典============================\n");
    printf("-----------------------功能菜单----------------------------\n");
    printf("\t\t\t1.查单词\n");
    printf("\t\t\t2.查询记录\n");
    printf("\t\t\t3.返回上级\n");
    printf("-----------------------------------------------------------\n");
}
//连接服务器
void init_internet(int sockfd, int port, char *addr)
{
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr(addr);
    sin.sin_port = htons(port);
    if (-1 == connect(sockfd, (struct sockaddr *)&sin, sizeof(sin)))
    {
        perror("connect");
        return;
    }
    printf("connect success\n");
}
//注册功能
void Register(int sockfd, MSG *msg)
{
    msg->option = R;
    printf("请输入要注册的用户名>>>");
    scanf("%s", msg->name);
    while (getchar() != 10)
        ;
    printf("请输入密码>>>");
    scanf("%s", msg->data);
    while (getchar() != 10)
        ;
    //将用户名及密码发送给服务器,判断是否存在
    send(sockfd, msg, sizeof(MSG), 0);
    recv(sockfd, msg, sizeof(MSG), 0);
    //接收服务器发回来的消息来判断是否成功
    printf("Register: %s\n", msg->data);
    return;
}
//登录功能
int Login(int sockfd, MSG *msg)
{
    //设置操作码
    msg->option = L;
    //输入用户名
    printf("请输入用户名>>>");
    scanf("%s", msg->name);
    while (getchar() != 10)
        ;
    //输入密码
    printf("请输入密码>>>");
    scanf("%s", msg->data);
    while (getchar() != 10)
        ;
    //发送数据给服务器
    send(sockfd, msg, sizeof(MSG), 0);
    //接收服务器发送的数据
    recv(sockfd, msg, sizeof(MSG), 0);

    //判断是否登录成功
    printf("%s\n", msg->data);
    if (msg->flag == 0)
    {
        return 0;
    }
    else
    {
        strcpy(name, msg->name);
        return 1;
    }
}
//查找单词
int Search_word(int sockfd, MSG *msg)
{
    msg->option = S;
    strcpy(msg->name, name);
    printf("---------查询界面---------\n");
    while (1)
    {
        printf("请输入单词 (输入#退出): ");
        scanf("%s", msg->data);
        while (getchar() != 10)
            ;
        //如果输入的是#,返回
        if (strcmp(msg->data, "#") == 0)
        {
            break;
        }

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

        recv(sockfd, msg, sizeof(MSG), 0);
        printf("EXPLANTION %s\n", msg->data);
    }
    return 0;
}
//查找记录
int Query_log(int sockfd, MSG *msg)
{
    char info[512];
    msg->option = Q;
    strcpy(msg->name, name);
    send(sockfd, msg, sizeof(MSG), 0);
    while (1)
    {
        recv(sockfd, info, sizeof(info), 0);
        printf("%s\n", info);
        if (0 == strcmp(info, "query end"))
        {
            break;
        }
    }
    return 0;
}
//返回上一级,退出登录
void Back(int sockfd,MSG*msg)
{
    msg->option = B;
    strcpy(msg->name, name);
    send(sockfd, msg, sizeof(MSG), 0);
    main_menu();
}

功能演示

注册功能

1.客户端
在这里插入图片描述

2.服务器
在这里插入图片描述

登录功能

1.客户端
在这里插入图片描述

2.服务器

在这里插入图片描述

查询单词功能

1.客户端
在这里插入图片描述
2.服务器
在这里插入图片描述

查询记录

1.客户端
在这里插入图片描述
2.服务器
在这里插入图片描述

注销登录(返回上级)

1.客户端
2.服务器
在这里插入图片描述

不允许重复登录

客户端
在这里插入图片描述

Ctrl + C注销登录

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Holy meat

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值