基于tcp协议及数据库sqlite3的云词典项目

这个小项目是一个网络编程学习过程中一个阶段性检测项目,过程中可以有效检测我们对于服务器客户端搭建的能力,以及一些bug查找能力。项目的一个简单讲解我发在了b站上,没啥心得,多练就好。
https://t.bilibili.com/865244702526406675?share_source=pc_native

数据库创建及导入单词表

复制代码
#include <stdio.h>`
`#include <sqlite3.h>`
`#include <string.h>`
`#include <stdlib.h>`
`#include <unistd.h>`
`#include <sys/types.h>`
`#include <sys/stat.h>`
`#include <fcntl.h>`
`int` `main(int argc,` `char` `const` `*argv[])`
`{`
    `// 1.打开或创建数据库`
`    sqlite3 *db =` `NULL;`
    `int rc;`
    `if` `(sqlite3_open("./word.db",` `&db)` `!= SQLITE_OK)//打开或创建库`
    `{`
        `printf("sqlite3_open err:%s\n",` `sqlite3_errmsg(db));`
        `return` `-1;`
    `}`
    `printf("sqlite3_open success\n");`
    `// 2.创建表`
    `char` `*errmsg =` `NULL;//返回创建数据库表的错误`
    `//创建一个两列的单词表,用于存储单词及注释`
    `if` `(sqlite3_exec(db,` `"create table if not exists wd1 (word char, annotation char);",` `NULL,` `NULL,` `&errmsg)` `!= SQLITE_OK)`
    `{`
        `printf("create err: %sn", errmsg);`
        `sqlite3_close(db);`  
        `return` `-1;`
    `}`
    `//创建一个两列的账户表,用于存储账户名及对应密码`
    `if` `(sqlite3_exec(db,` `"create table if not exists user (name char, password char);",` `NULL,` `NULL,` `&errmsg)` `!= SQLITE_OK)`
    `{`
        `printf("create err: %sn", errmsg);`
        `sqlite3_close(db);`
        `return` `-1;`
    `}`
    `printf("create success\n");`
    `// 3.向表中插入数据`
`    FILE *fp =` `fopen(argv[1],` `"r");//打开要插入的文件流`
    `if` `(fp ==` `NULL)`
    `{`
        `printf("failed to open file\n");`
        `sqlite3_close(db);`
        `return` `-1;`
    `}`
    `char buf[1024];`  `//读取的一行`
    `char word[32];`   `//单词`
    `char ant[1024];`  `//保存注释`
    `while` `(fgets(buf,` `sizeof(buf), fp)` `!=` `NULL)` `//读一行`
    `{`
        `sscanf(buf,` `"%99[^ ] %256[^\n]", word, ant);`  `//将第一个单词放到单词数组中,后面的内容放到注释数组中`
        `char sql[1024];//存放命令内容`
        `sprintf(sql,` `"insert into wd1 values(\"%s\", \"%s\");", word, ant);`  `// 构造插入语句,将单词及注释插入到单词表中`
`        rc =` `sqlite3_exec(db, sql,` `NULL,` `NULL,` `&errmsg);`
        `if` `(rc != SQLITE_OK)`
        `{`
            `printf("insert err: %s\n", errmsg);`
            `return` `-1;`
        `}`
    `}`
    `fclose(fp);//关闭文件描述符`
    `// 5.关闭数据库连接`
    `sqlite3_close(db);`
    `return` `0;`
`}`

`

头函数及传输协议

复制代码
#ifndef __HEAD_H__`
`#define __HEAD_H__//防止重包含`
`#include <sys/socket.h>`
`#include <netinet/in.h>`
`#include <netinet/ip.h>`
`#define N 32`
`enum type_t //运行命令`
`{`
`    R=4, //register注册`
`    L, //login登录`
`    Q, //query搜索`
`    H, //history历史`
`};`
`typedef struct //数据包结构体`
`{`
`    int type;//执行命令类型`
`    char name[N];   //用户名`
`    char data[1024]; //密码或要查询的单词`
`} MSG_t;`
`typedef struct node_t`
`{`
`    struct sockaddr_in addr; //ip地址`
`    struct node_t *next;     //链表下一个地址`
`} list_t;`
`#endif`

`

云词典服务器端

复制代码
/*服务器创建代码 */`
`#include <stdio.h>`
`#include <sys/types.h> /* See NOTES */`
`#include <sys/socket.h>`
`#include <netinet/in.h>`
`#include <netinet/ip.h> /* superset of previous */`
`#include <arpa/inet.h>`
`#include <unistd.h>`
`#include <stdlib.h>`
`#include <string.h>`
`#include <sqlite3.h>`
`#include <signal.h>`
`#include <sys/select.h>`
`#include <time.h>`
`#include <sys/time.h>`
`#include "head.h"`
`MSG_t msg;`
`int n;`
`sqlite3 *db = NULL;  //命令输入`
`char *errmsg = NULL; //错误码`
`int hang, lie;       //数据库行和列`
`int k = 0;`
`int Register(sqlite3 *db, int sockfd) //注册函数`
`{`
`    char **result = NULL; //数据库返回内容`
`    //3.向表中插入数据`
`    //(1)执行函数`
`    char sq1[128];                                                     //保存命令`
`    sprintf(sq1, "select * from user where name = \"%s\";", msg.name); `
`    sqlite3_get_table(db, sq1, &result, &hang, &lie, &errmsg);//判断数据库中是否已经存在该账户`
`    if (hang != 0)`
`    {`
`        sprintf(msg.data, "账户已存在;\n");`
`        send(sockfd, &msg, sizeof(msg), 0);`
`        return -1;`
`    }`
`    else //成功注册`
`    {`
`        sprintf(sq1, "insert into user values(\"%s\",\"%s\");", msg.name, msg.data);`
`        if (sqlite3_exec(db, sq1, NULL, NULL, &errmsg) == SQLITE_OK) //""需要用\转意,注册成功插入用户表内`
`        {`
`            sprintf(sq1, "create table if not exists \"%s\" (word char, time char);", msg.name); //每注册一个用户创建一个新表`
`            if (sqlite3_exec(db, sq1, NULL, NULL, &errmsg) != SQLITE_OK)                         //创建新表`
`            {`
`                printf("create err: %s", errmsg);`
`                sqlite3_close(db);`
`                return -1;`
`            }`
`            else`
`            {`
`                printf("creat %s success\n", msg.name);`
`            }`
`            sprintf(msg.data, "OK");`
`            send(sockfd, &msg, sizeof(msg), 0); //注册成功发送消息`
`            memset(msg.data, 0, sizeof(msg.data));`
`            return 0;`
`        }`
`        else //否则插入失败`
`        {`
`            printf("insert value err;%s\n", errmsg);`
`            return -1;`
`        }`
`    }`
`}`
`//用户登录`
`int loginclient(sqlite3 *db, int sockfd)`
`{`
`    char **result = NULL; //数据库返回内容`
`    char sq1[128];        //保存命令`
`    sprintf(sq1, "select * from user where name = \"%s\";", msg.name);`
`    sqlite3_get_table(db, sq1, &result, &hang, &lie, &errmsg); //判断数据库中是否已经存在该账户`
`    if (hang != 0)                                             //如果能读出内容则行数不为0`
`    {`
`        if (strcmp(result[3], msg.data) == 0) //判断密码是否正确`
`        {`
`            sprintf(msg.data, "OK");`
`            send(sockfd, &msg, sizeof(msg), 0);`
`            return 0;`
`        }`
`        else //密码错误`
`        {`
`            sprintf(msg.data, "password err\n");`
`            send(sockfd, &msg, sizeof(msg), 0);`
`            return -1;`
`        }`
`    }`
`    else //反之未注册`
`    {`
`        sprintf(msg.data, "no register\n"); //未注册`
`        send(sockfd, &msg, sizeof(msg), 0);`
`        return -1;`
`    }`
`}`
`//查询单词注释`
`int chatclient(sqlite3 *db, int sockfd) //查询单词函数`
`{`
`    char **result = NULL; //数据库返回内容`
`    char sq1[128];        //保存命令`
`    sprintf(sq1, "select * from wd1 where word = \"%s\";", msg.data);`
`    sqlite3_get_table(db, sq1, &result, &hang, &lie, &errmsg); //判断是否查到该单词`
`    if (hang != 0)`
`    {`
`        sprintf(msg.data, "%s", result[3]); //将注释内容发送到客户端`
`        send(sockfd, &msg, sizeof(msg), 0); //发送该单词注释`
`        time_t th;`
`        time(&th); //获取当前时间`
`        char times[128];`
`        struct tm *ts;`
`        ts = localtime(&th); //将当时间转化为标准时间`
`        sprintf(times, "%4d-%2d-%2d %2d:%2d:%2d", ts->tm_year + 1900, ts->tm_mon + 1, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec);`
`        //先将时间放到一个字符串中,再放到命令语句中`
`        sprintf(sq1, "insert into \"%s\" values(\"%s\", \"%s\");", msg.name, result[2], times); // 构造插入语句`
`        int rc = sqlite3_exec(db, sq1, NULL, NULL, &errmsg);                                    //将查询时间保存到数据库中`
`        if (rc != SQLITE_OK)`
`        {`
`            printf("insert err: %s\n", errmsg);`
`            return -1;`
`        }`
`        return 0;`
`    }`
`    else //未找到该单词`
`    {`
`        sprintf(msg.data, "word unfund\n");`
`        send(sockfd, &msg, sizeof(msg), 0);`
`        return -1;`
`    }`
`}`
`int history(sqlite3 *db, int sockfd) //查询历史记录`
`{`
`    char **result = NULL;                            //数据库返回内容`
`    char sq1[128];                                   //保存命令`
`    sprintf(sq1, "select * from \"%s\";", msg.name); //查询单词查询历史`
`    sqlite3_get_table(db, sq1, &result, &hang, &lie, &errmsg);`
`    if (hang != 0)`
`    {`
`        for (int j = 0; j < hang; j++) //拼接表内内容到字符数组中`
`        {`
`            strcat(msg.data, result[j * lie + 2]);`
`            strcat(msg.data, " "); //两个表内内容间添加空格间隔`
`            strcat(msg.data, result[j * lie + 3]);`
`            strcat(msg.data, "\n"); //两行间换行`
`        }`
`        send(sockfd, &msg, sizeof(msg), 0); //发送该单词查询结果`
`        return 0;`
`    }`
`    else if (hang == 0) //历史记录为空`
`    {`
`        sprintf(msg.data, "history is void");`
`        send(sockfd, &msg, sizeof(msg), 0); //发送该单词查询结果`
`        return 0;`
`    }`
`}`
`int main(int argc, char const *argv[])`
`{`
`    // 1.打开或创建数据库`
`    int rc;`
`    if (sqlite3_open("./word.db", &db) != SQLITE_OK)`
`    {`
`        printf("sqlite3_open err:%s\n", sqlite3_errmsg(db));`
`        return -1;`
`    }`
`    printf("sqlite3_open success\n"); //打开数据库成功`
`    if (argc < 2)                     //行传参正确`
`    {`
`        printf("plase input <file><port>\n");`
`        return -1;`
`    }`
`    //1.创建套接字,用于链接`
`    int sockfd;`
`    int acceptfd; //接收套接字`
`    sockfd = socket(AF_INET, SOCK_STREAM, 0);`
`    if (sockfd < 0) //容错`
`    {`
`        perror("socket err");`
`        return -1;`
`    }`
`    //2.绑定 ip+port 填充结构体`
`    struct sockaddr_in saddr;`
`    saddr.sin_family = AF_INET;                   //协议族ipv4`
`    saddr.sin_port = htons(atoi(argv[1]));        //端口号,htons将无符号短整数hostshort从主机字节顺序到网络字节顺序。`
`    saddr.sin_addr.s_addr = inet_addr("0.0.0.0"); //ip地址,转化为16进制表示`
`    socklen_t len = sizeof(saddr);                //结构体大小`
`    //bind绑定ip和端口`
`    if (bind(sockfd, (struct sockaddr *)&saddr, len) < 0)`
`    {`
`        perror("bind err");`
`        return -1;`
`    }`
`    //3.启动监听,把主动套接子变为被动套接字`
`    if (listen(sockfd, 6) < 0)`
`    {`
`        perror("listen err");`
`        return -1;`
`    }`
`    //4.创建表`
`    fd_set readfds;    //原表`
`    fd_set tempfds;    //创建一个临时表,用来保存新表`
`    FD_ZERO(&readfds); //原表置空`
`    //5.填表`
`    FD_SET(sockfd, &readfds); //将要监测的文件描述符插入到表中`
`    int maxfd = sockfd;       //表内最大描述符`
`    int ret;`
`    //6.循环监听 select`
`    while (1)`
`    {`
`        tempfds = readfds;                                       //每次循环前重新赋值一次`
`        int ret = select(maxfd + 1, &tempfds, NULL, NULL, NULL); //监测`
`        if (ret < 0)`
`        {`
`            perror("select err");`
`            return -1;`
`        }`
`        if (FD_ISSET(sockfd, &tempfds)) //监听是否有客户端链接`
`        {`
`            //阻塞等待客户端的链接请求`
`            acceptfd = accept(sockfd, (struct sockaddr *)&saddr, &len);`
`            //获取客户端的ip和端口,(struct sockaddr *)&saddr:用来存放返回的ip,和端口`
`            if (acceptfd < 0)`
`            {`
`                perror("accept err");`
`                return -1;`
`            }`
`            printf("client ip:%s ,port:%d:connect success\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));`
`            //打印已经接入的客户端IP`
`            FD_SET(acceptfd, &readfds); //将新接入的客户端文件描述符插入到原表中`
`            if (acceptfd > maxfd)       //如果新插入的文件描述符大于已知最大文件描述符,则更新表内最大文件描述符`
`            {`
`                maxfd = acceptfd;`
`            }`
`        }`
`        for (int i = 5; i <= maxfd; i++) //遍历判断是否有信号传输`
`        {`
`            if (FD_ISSET(i, &tempfds)) //监测客户端文件描述符`
`            {`
`                int ret = recv(i, &msg, sizeof(msg), 0); //接收的信号`
`                if (ret < 0)                             //接收错误`
`                {`
`                    perror("recv err.");`
`                    return -1;`
`                }`
`                else if (ret == 0)`
`                {`
`                    printf("%d client exit\n", i); //客户端退出`
`                    close(i);                      //关闭描述符`
`                    FD_CLR(i, &readfds);           //删除文件描述符`
`                }`
`                else`
`                {`
`                    switch (msg.type)`
`                    {`
`                    case R: //注册`
`                        Register(db, i);`
`                        break;`
`                    case L: //登录`
`                        loginclient(db, i);`
`                        break;`
`                    case Q: //搜索`
`                        chatclient(db, i);`
`                        break;`
`                    case H: //历史`
`                        history(db, i);`
`                        break;`
`                    default:`
`                        break;`
`                    }`
`                }`
`            }`
`        }`
`    }`
`    close(sockfd);`
`    close(acceptfd);`
`    return 0;`
`}`

`

云词典客户端

复制代码
/*客户端创建代码 */`
`#include <stdio.h>`
`#include <sys/types.h> /* See NOTES */`
`#include <sys/socket.h>`
`#include <netinet/in.h>`
`#include <netinet/ip.h> /* superset of previous */`
`#include <arpa/inet.h>`
`#include <unistd.h>`
`#include <stdlib.h>`
`#include <string.h>`
`#include "head.h"`
`#define N 32`
`MSG_t msg;`
`int n; //命令输入`
`//注册操作`
`void do_register(int sockfd)`
`{`
`    while (1)`
`    {`
`        msg.type = R; //注册状态`
`        printf("请输入您的用户名:");`
`        scanf("%s", msg.name);`
`        getchar();                         //回收一个垃圾字符`
`        if (memcmp(msg.name, "#", 1) == 0) //注册过程中输入#退出注册页面`
`        {`
`            printf("退出注册\n");`
`            break;`
`        }`
`        printf("请输入您的密码:");`
`        scanf("%s", msg.data);`
`        getchar();`
`        send(sockfd, &msg, sizeof(msg), 0);`
`        recv(sockfd, &msg, sizeof(msg), 0); //接收客户端消息,判断是否注册成功`
`        printf("register:%s\n", msg.data);`
`        if (memcmp(msg.data, "OK", 2) == 0) //服务器返回的msg.data == OK注册成功`
`        {`
`            break;`
`        }`
`    }`
`}`
`//登录操作`
`int do_login(int sockfd)`
`{`
`    while (1)`
`    {`
`        msg.type = L; //登录状态`
`        printf("请输入您的用户名:");`
`        scanf("%s", msg.name);`
`        getchar();`
`         if (memcmp(msg.data, "#", 1) == 0) //退出查询页面,先判断退出后发送内容`
`        {`
`            printf("退出查询\n");`
`            msg.type = 0;//当发送给服务器为结束内容,让服务器端读出且无法识别type,继续等待事件`
`            memset(msg.data,0,sizeof(msg.data));//将msg.data中的#清空`
`            send(sockfd, &msg, sizeof(msg), 0);`
`            break;`
`        }`
`        printf("请输入您的密码:");`
`        scanf("%s", msg.data);`
`        getchar();`
`        send(sockfd, &msg, sizeof(msg), 0);`
`        recv(sockfd, &msg, sizeof(msg), 0);`
`        printf("login:%s\n", msg.data);`
`        if (memcmp(msg.data, "OK", 2) == 0) //服务器返回的msg.data=OK登录成功`
`        {`
`            return 1;`
`        }`
`    }`
`}`
`//查询操作`
`void do_query(int sockfd)`
`{`
`    msg.type = Q;`
`    while (1)`
`    {`
`        printf("请输入你要查询的单词内容:");`
`        scanf("%s", msg.data);`
`        getchar();`
`        if (memcmp(msg.data, "#", 1) == 0) //退出查询页面,先判断退出后发送内容`
`        {`
`            printf("退出查询\n");`
`            msg.type = 0;//当发送给服务器为结束内容,让服务器端读出且无法识别type,继续等待事件`
`            memset(msg.data,0,sizeof(msg.data));`
`            send(sockfd, &msg, sizeof(msg), 0);`
`            break;`
`        }`
`        send(sockfd, &msg, sizeof(msg), 0);`
`        recv(sockfd, &msg, sizeof(msg), 0);`
`        printf("annotation:%s\n", msg.data); //打印接收的信息/注释`
`    }`
`}`
`//查询历史记录`
`void do_history(int sockfd)`
`{`
`    // memset(msg.data, 0, sizeof(msg.data));`
`    msg.type = H;`
`    send(sockfd, &msg, sizeof(msg), 0);`
`    recv(sockfd, &msg, sizeof(msg), 0); //接收信息,接一次打一次.`
`    printf("%s\n", msg.data);`
`}`
`//主函数`
`int main(int argc, char const *argv[])`
`{`
`    if (argc < 3)`
`    {`
`        printf("plase input <ip><port>\n");`
`        return -1;`
`    }`
`    //1.创建套接字,用于链接`
`    int sockfd;`
`    sockfd = socket(AF_INET, SOCK_STREAM, 0);`
`    if (sockfd < 0)`
`    {`
`        perror("socket err");`
`        return -1;`
`    }`
`    //文件描述符 0 -> 标准输入  1->标准输出  2->标准出错  3->socket`
`    printf("sockfd:%d\n", sockfd);`
`    //2.绑定 ip+port 填充结构体`
`    struct sockaddr_in saddr;`
`    saddr.sin_family = AF_INET;                 //协议族ipv4`
`    saddr.sin_port = htons(atoi(argv[2]));      //端口号,htons将无符号短整数hostshort从主机字节顺序到网络字节顺序。`
`    saddr.sin_addr.s_addr = inet_addr(argv[1]); //ip地址,转化为16进制表示`
`    socklen_t len = sizeof(saddr);              //结构体大小`
`    //3用于连接服务器;`
`    if (connect(sockfd, (struct sockaddr *)&saddr, len) < 0)`
`    {`
`        perror("connect err");`
`        return -1;`
`    }`
`    int flag = 0;`
`    while (1)`
`    {`
`        printf("*******************************************************\n");`
`        printf("*                      <BUG词典>                       *\n");`
`        printf("*               1: 注册  2: 登录  3:  退出              *\n");`
`        printf("*******************************************************\n");`
`        printf("请输入命令:");`
`        scanf("%d", &n);`
`        getchar();`
`        switch (n)`
`        {`
`        case 1:`
`            do_register(sockfd);`
`            break;`
`        case 2:`
`            if (do_login(sockfd) == 1)`
`            {`
`                while (1)`
`                {`
`                    if (flag == 1)`
`                    {`
`                        flag = 0;`
`                        break;`
`                    }`
`                    printf("*******************************************************\n");`
`                    printf("*                      <BUG词典>                       *\n");`
`                    printf("*              1: 查询单词  2: 历史记录 3:  退出          *\n");`
`                    printf("*******************************************************\n");`
`                    printf("请输入命令:");`
`                    scanf("%d", &n);`
`                    getchar();`
`                    switch (n)`
`                    {`
`                    case 1:`
`                        do_query(sockfd);`
`                        break;`
`                    case 2:`
`                        printf("search history\n");`
`                        do_history(sockfd);`
`                        break;`
`                    case 3:`
`                        flag = 1;`
`                        break;`
`                    default:`
`                        break;`
`                    }`
`                }`
`            }`
`            break;`
`        case 3:`
`            return 0;`
`        default:`
`            break;`
`        }`
`    }`
`    close(sockfd);`
`    return 0;`
`}`

`
相关推荐
热爱嵌入式的小许14 分钟前
Linux基础项目开发1:量产工具——显示系统
linux·运维·服务器·韦东山量产工具
小堃学编程22 分钟前
计算机网络(十) —— IP协议详解,理解运营商和全球网络
网络·tcp/ip·计算机网络
吾爱星辰2 小时前
Kotlin 处理字符串和正则表达式(二十一)
java·开发语言·jvm·正则表达式·kotlin
ChinaDragonDreamer2 小时前
Kotlin:2.0.20 的新特性
android·开发语言·kotlin
IT良2 小时前
c#增删改查 (数据操作的基础)
开发语言·c#
vvvae12342 小时前
分布式数据库
数据库
Kalika0-03 小时前
猴子吃桃-C语言
c语言·开发语言·数据结构·算法
_.Switch3 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
雪域迷影3 小时前
PostgreSQL Docker Error – 5432: 地址已被占用
数据库·docker·postgresql
代码雕刻家3 小时前
课设实验-数据结构-单链表-文教文化用品品牌
c语言·开发语言·数据结构