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


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

    数据库创建及导入单词表

    1. #include <stdio.h>
    2. #include <sqlite3.h>
    3. #include <string.h>
    4. #include <stdlib.h>
    5. #include <unistd.h>
    6. #include <sys/types.h>
    7. #include <sys/stat.h>
    8. #include <fcntl.h>
    9. int main(int argc, char const *argv[])
    10. {
    11. // 1.打开或创建数据库
    12.     sqlite3 *db = NULL;
    13. int rc;
    14. if (sqlite3_open("./word.db", &db) != SQLITE_OK)//打开或创建库
    15. {
    16. printf("sqlite3_open err:%s\n", sqlite3_errmsg(db));
    17. return -1;
    18. }
    19. printf("sqlite3_open success\n");
    20. // 2.创建表
    21. char *errmsg = NULL;//返回创建数据库表的错误
    22. //创建一个两列的单词表,用于存储单词及注释
    23. if (sqlite3_exec(db, "create table if not exists wd1 (word char, annotation char);", NULL, NULL, &errmsg) != SQLITE_OK)
    24. {
    25. printf("create err: %sn", errmsg);
    26. sqlite3_close(db);
    27. return -1;
    28. }
    29. //创建一个两列的账户表,用于存储账户名及对应密码
    30. if (sqlite3_exec(db, "create table if not exists user (name char, password char);", NULL, NULL, &errmsg) != SQLITE_OK)
    31. {
    32. printf("create err: %sn", errmsg);
    33. sqlite3_close(db);
    34. return -1;
    35. }
    36. printf("create success\n");
    37. // 3.向表中插入数据
    38.     FILE *fp = fopen(argv[1], "r");//打开要插入的文件流
    39. if (fp == NULL)
    40. {
    41. printf("failed to open file\n");
    42. sqlite3_close(db);
    43. return -1;
    44. }
    45. char buf[1024]; //读取的一行
    46. char word[32]; //单词
    47. char ant[1024]; //保存注释
    48. while (fgets(buf, sizeof(buf), fp) != NULL) //读一行
    49. {
    50. sscanf(buf, "%99[^ ] %256[^\n]", word, ant); //将第一个单词放到单词数组中,后面的内容放到注释数组中
    51. char sql[1024];//存放命令内容
    52. sprintf(sql, "insert into wd1 values(\"%s\", \"%s\");", word, ant); // 构造插入语句,将单词及注释插入到单词表中
    53.         rc = sqlite3_exec(db, sql, NULL, NULL, &errmsg);
    54. if (rc != SQLITE_OK)
    55. {
    56. printf("insert err: %s\n", errmsg);
    57. return -1;
    58. }
    59. }
    60. fclose(fp);//关闭文件描述符
    61. // 5.关闭数据库连接
    62. sqlite3_close(db);
    63. return 0;
    64. }

    头函数及传输协议

    1. #ifndef __HEAD_H__
    2. #define __HEAD_H__//防止重包含
    3. #include <sys/socket.h>
    4. #include <netinet/in.h>
    5. #include <netinet/ip.h>
    6. #define N 32
    7. enum type_t //运行命令
    8. {
    9.     R=4//register注册
    10.     L, //login登录
    11.     Q, //query搜索
    12.     H, //history历史
    13. };
    14. typedef struct //数据包结构体
    15. {
    16.     int type;//执行命令类型
    17.     char name[N];   //用户名
    18.     char data[1024]; //密码或要查询的单词
    19. } MSG_t;
    20. typedef struct node_t
    21. {
    22.     struct sockaddr_in addr; //ip地址
    23.     struct node_t *next;     //链表下一个地址
    24. } list_t;
    25. #endif

    云词典服务器端

    1. /*服务器创建代码 */
    2. #include <stdio.h>
    3. #include <sys/types.h> /* See NOTES */
    4. #include <sys/socket.h>
    5. #include <netinet/in.h>
    6. #include <netinet/ip.h> /* superset of previous */
    7. #include <arpa/inet.h>
    8. #include <unistd.h>
    9. #include <stdlib.h>
    10. #include <string.h>
    11. #include <sqlite3.h>
    12. #include <signal.h>
    13. #include <sys/select.h>
    14. #include <time.h>
    15. #include <sys/time.h>
    16. #include "head.h"
    17. MSG_t msg;
    18. int n;
    19. sqlite3 *db = NULL;  //命令输入
    20. char *errmsg = NULL//错误码
    21. int hang, lie;       //数据库行和列
    22. int k = 0;
    23. int Register(sqlite3 *db, int sockfd) //注册函数
    24. {
    25.     char **result = NULL//数据库返回内容
    26.     //3.向表中插入数据
    27.     //(1)执行函数
    28.     char sq1[128];                                                     //保存命令
    29.     sprintf(sq1"select * from user where name = \"%s\";", msg.name); 
    30.     sqlite3_get_table(db, sq1&result, &hang, &lie, &errmsg);//判断数据库中是否已经存在该账户
    31.     if (hang != 0)
    32.     {
    33.         sprintf(msg.data"账户已存在;\n");
    34.         send(sockfd, &msg, sizeof(msg), 0);
    35.         return -1;
    36.     }
    37.     else //成功注册
    38.     {
    39.         sprintf(sq1"insert into user values(\"%s\",\"%s\");", msg.name, msg.data);
    40.         if (sqlite3_exec(db, sq1NULLNULL&errmsg) == SQLITE_OK) //""需要用\转意,注册成功插入用户表内
    41.         {
    42.             sprintf(sq1"create table if not exists \"%s\" (word char, time char);", msg.name); //每注册一个用户创建一个新表
    43.             if (sqlite3_exec(db, sq1NULLNULL&errmsg) != SQLITE_OK)                         //创建新表
    44.             {
    45.                 printf("create err: %s", errmsg);
    46.                 sqlite3_close(db);
    47.                 return -1;
    48.             }
    49.             else
    50.             {
    51.                 printf("creat %s success\n", msg.name);
    52.             }
    53.             sprintf(msg.data"OK");
    54.             send(sockfd, &msg, sizeof(msg), 0); //注册成功发送消息
    55.             memset(msg.data0, sizeof(msg.data));
    56.             return 0;
    57.         }
    58.         else //否则插入失败
    59.         {
    60.             printf("insert value err;%s\n", errmsg);
    61.             return -1;
    62.         }
    63.     }
    64. }
    65. //用户登录
    66. int loginclient(sqlite3 *db, int sockfd)
    67. {
    68.     char **result = NULL//数据库返回内容
    69.     char sq1[128];        //保存命令
    70.     sprintf(sq1"select * from user where name = \"%s\";", msg.name);
    71.     sqlite3_get_table(db, sq1&result, &hang, &lie, &errmsg); //判断数据库中是否已经存在该账户
    72.     if (hang != 0)                                             //如果能读出内容则行数不为0
    73.     {
    74.         if (strcmp(result[3], msg.data== 0//判断密码是否正确
    75.         {
    76.             sprintf(msg.data"OK");
    77.             send(sockfd, &msg, sizeof(msg), 0);
    78.             return 0;
    79.         }
    80.         else //密码错误
    81.         {
    82.             sprintf(msg.data"password err\n");
    83.             send(sockfd, &msg, sizeof(msg), 0);
    84.             return -1;
    85.         }
    86.     }
    87.     else //反之未注册
    88.     {
    89.         sprintf(msg.data"no register\n"); //未注册
    90.         send(sockfd, &msg, sizeof(msg), 0);
    91.         return -1;
    92.     }
    93. }
    94. //查询单词注释
    95. int chatclient(sqlite3 *db, int sockfd) //查询单词函数
    96. {
    97.     char **result = NULL//数据库返回内容
    98.     char sq1[128];        //保存命令
    99.     sprintf(sq1"select * from wd1 where word = \"%s\";", msg.data);
    100.     sqlite3_get_table(db, sq1&result, &hang, &lie, &errmsg); //判断是否查到该单词
    101.     if (hang != 0)
    102.     {
    103.         sprintf(msg.data"%s", result[3]); //将注释内容发送到客户端
    104.         send(sockfd, &msg, sizeof(msg), 0); //发送该单词注释
    105.         time_t th;
    106.         time(&th); //获取当前时间
    107.         char times[128];
    108.         struct tm *ts;
    109.         ts = localtime(&th); //将当时间转化为标准时间
    110.         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);
    111.         //先将时间放到一个字符串中,再放到命令语句中
    112.         sprintf(sq1"insert into \"%s\" values(\"%s\", \"%s\");", msg.name, result[2], times); // 构造插入语句
    113.         int rc = sqlite3_exec(db, sq1NULLNULL&errmsg);                                    //将查询时间保存到数据库中
    114.         if (rc != SQLITE_OK)
    115.         {
    116.             printf("insert err: %s\n", errmsg);
    117.             return -1;
    118.         }
    119.         return 0;
    120.     }
    121.     else //未找到该单词
    122.     {
    123.         sprintf(msg.data"word unfund\n");
    124.         send(sockfd, &msg, sizeof(msg), 0);
    125.         return -1;
    126.     }
    127. }
    128. int history(sqlite3 *db, int sockfd) //查询历史记录
    129. {
    130.     char **result = NULL;                            //数据库返回内容
    131.     char sq1[128];                                   //保存命令
    132.     sprintf(sq1"select * from \"%s\";", msg.name); //查询单词查询历史
    133.     sqlite3_get_table(db, sq1&result, &hang, &lie, &errmsg);
    134.     if (hang != 0)
    135.     {
    136.         for (int j = 0; j < hang; j++//拼接表内内容到字符数组中
    137.         {
    138.             strcat(msg.data, result[j * lie + 2]);
    139.             strcat(msg.data" "); //两个表内内容间添加空格间隔
    140.             strcat(msg.data, result[j * lie + 3]);
    141.             strcat(msg.data"\n"); //两行间换行
    142.         }
    143.         send(sockfd, &msg, sizeof(msg), 0); //发送该单词查询结果
    144.         return 0;
    145.     }
    146.     else if (hang == 0//历史记录为空
    147.     {
    148.         sprintf(msg.data"history is void");
    149.         send(sockfd, &msg, sizeof(msg), 0); //发送该单词查询结果
    150.         return 0;
    151.     }
    152. }
    153. int main(int argc, char const *argv[])
    154. {
    155.     // 1.打开或创建数据库
    156.     int rc;
    157.     if (sqlite3_open("./word.db"&db) != SQLITE_OK)
    158.     {
    159.         printf("sqlite3_open err:%s\n", sqlite3_errmsg(db));
    160.         return -1;
    161.     }
    162.     printf("sqlite3_open success\n"); //打开数据库成功
    163.     if (argc < 2)                     //行传参正确
    164.     {
    165.         printf("plase input \n");
    166.         return -1;
    167.     }
    168.     //1.创建套接字,用于链接
    169.     int sockfd;
    170.     int acceptfd; //接收套接字
    171.     sockfd = socket(AF_INET, SOCK_STREAM, 0);
    172.     if (sockfd < 0//容错
    173.     {
    174.         perror("socket err");
    175.         return -1;
    176.     }
    177.     //2.绑定 ip+port 填充结构体
    178.     struct sockaddr_in saddr;
    179.     saddr.sin_family = AF_INET;                   //协议族ipv4
    180.     saddr.sin_port = htons(atoi(argv[1]));        //端口号,htons将无符号短整数hostshort从主机字节顺序到网络字节顺序。
    181.     saddr.sin_addr.s_addr = inet_addr("0.0.0.0"); //ip地址,转化为16进制表示
    182.     socklen_t len = sizeof(saddr);                //结构体大小
    183.     //bind绑定ip和端口
    184.     if (bind(sockfd, (struct sockaddr *)&saddr, len) < 0)
    185.     {
    186.         perror("bind err");
    187.         return -1;
    188.     }
    189.     //3.启动监听,把主动套接子变为被动套接字
    190.     if (listen(sockfd, 6< 0)
    191.     {
    192.         perror("listen err");
    193.         return -1;
    194.     }
    195.     //4.创建表
    196.     fd_set readfds;    //原表
    197.     fd_set tempfds;    //创建一个临时表,用来保存新表
    198.     FD_ZERO(&readfds); //原表置空
    199.     //5.填表
    200.     FD_SET(sockfd, &readfds); //将要监测的文件描述符插入到表中
    201.     int maxfd = sockfd;       //表内最大描述符
    202.     int ret;
    203.     //6.循环监听 select
    204.     while (1)
    205.     {
    206.         tempfds = readfds;                                       //每次循环前重新赋值一次
    207.         int ret = select(maxfd + 1&tempfds, NULLNULLNULL); //监测
    208.         if (ret < 0)
    209.         {
    210.             perror("select err");
    211.             return -1;
    212.         }
    213.         if (FD_ISSET(sockfd, &tempfds)) //监听是否有客户端链接
    214.         {
    215.             //阻塞等待客户端的链接请求
    216.             acceptfd = accept(sockfd, (struct sockaddr *)&saddr, &len);
    217.             //获取客户端的ip和端口,(struct sockaddr *)&saddr:用来存放返回的ip,和端口
    218.             if (acceptfd < 0)
    219.             {
    220.                 perror("accept err");
    221.                 return -1;
    222.             }
    223.             printf("client ip:%s ,port:%d:connect success\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
    224.             //打印已经接入的客户端IP
    225.             FD_SET(acceptfd, &readfds); //将新接入的客户端文件描述符插入到原表中
    226.             if (acceptfd > maxfd)       //如果新插入的文件描述符大于已知最大文件描述符,则更新表内最大文件描述符
    227.             {
    228.                 maxfd = acceptfd;
    229.             }
    230.         }
    231.         for (int i = 5; i <= maxfd; i++//遍历判断是否有信号传输
    232.         {
    233.             if (FD_ISSET(i, &tempfds)) //监测客户端文件描述符
    234.             {
    235.                 int ret = recv(i, &msg, sizeof(msg), 0); //接收的信号
    236.                 if (ret < 0)                             //接收错误
    237.                 {
    238.                     perror("recv err.");
    239.                     return -1;
    240.                 }
    241.                 else if (ret == 0)
    242.                 {
    243.                     printf("%d client exit\n", i); //客户端退出
    244.                     close(i);                      //关闭描述符
    245.                     FD_CLR(i, &readfds);           //删除文件描述符
    246.                 }
    247.                 else
    248.                 {
    249.                     switch (msg.type)
    250.                     {
    251.                     case R: //注册
    252.                         Register(db, i);
    253.                         break;
    254.                     case L: //登录
    255.                         loginclient(db, i);
    256.                         break;
    257.                     case Q: //搜索
    258.                         chatclient(db, i);
    259.                         break;
    260.                     case H: //历史
    261.                         history(db, i);
    262.                         break;
    263.                     default:
    264.                         break;
    265.                     }
    266.                 }
    267.             }
    268.         }
    269.     }
    270.     close(sockfd);
    271.     close(acceptfd);
    272.     return 0;
    273. }

    云词典客户端

    1. /*客户端创建代码 */
    2. #include <stdio.h>
    3. #include <sys/types.h> /* See NOTES */
    4. #include <sys/socket.h>
    5. #include <netinet/in.h>
    6. #include <netinet/ip.h> /* superset of previous */
    7. #include <arpa/inet.h>
    8. #include <unistd.h>
    9. #include <stdlib.h>
    10. #include <string.h>
    11. #include "head.h"
    12. #define N 32
    13. MSG_t msg;
    14. int n; //命令输入
    15. //注册操作
    16. void do_register(int sockfd)
    17. {
    18.     while (1)
    19.     {
    20.         msg.type = R; //注册状态
    21.         printf("请输入您的用户名:");
    22.         scanf("%s", msg.name);
    23.         getchar();                         //回收一个垃圾字符
    24.         if (memcmp(msg.name, "#"1== 0//注册过程中输入#退出注册页面
    25.         {
    26.             printf("退出注册\n");
    27.             break;
    28.         }
    29.         printf("请输入您的密码:");
    30.         scanf("%s", msg.data);
    31.         getchar();
    32.         send(sockfd, &msg, sizeof(msg), 0);
    33.         recv(sockfd, &msg, sizeof(msg), 0); //接收客户端消息,判断是否注册成功
    34.         printf("register:%s\n", msg.data);
    35.         if (memcmp(msg.data"OK"2== 0//服务器返回的msg.data == OK注册成功
    36.         {
    37.             break;
    38.         }
    39.     }
    40. }
    41. //登录操作
    42. int do_login(int sockfd)
    43. {
    44.     while (1)
    45.     {
    46.         msg.type = L; //登录状态
    47.         printf("请输入您的用户名:");
    48.         scanf("%s", msg.name);
    49.         getchar();
    50.          if (memcmp(msg.data"#"1== 0//退出查询页面,先判断退出后发送内容
    51.         {
    52.             printf("退出查询\n");
    53.             msg.type = 0;//当发送给服务器为结束内容,让服务器端读出且无法识别type,继续等待事件
    54.             memset(msg.data,0,sizeof(msg.data));//将msg.data中的#清空
    55.             send(sockfd, &msg, sizeof(msg), 0);
    56.             break;
    57.         }
    58.         printf("请输入您的密码:");
    59.         scanf("%s", msg.data);
    60.         getchar();
    61.         send(sockfd, &msg, sizeof(msg), 0);
    62.         recv(sockfd, &msg, sizeof(msg), 0);
    63.         printf("login:%s\n", msg.data);
    64.         if (memcmp(msg.data"OK"2== 0//服务器返回的msg.data=OK登录成功
    65.         {
    66.             return 1;
    67.         }
    68.     }
    69. }
    70. //查询操作
    71. void do_query(int sockfd)
    72. {
    73.     msg.type = Q;
    74.     while (1)
    75.     {
    76.         printf("请输入你要查询的单词内容:");
    77.         scanf("%s", msg.data);
    78.         getchar();
    79.         if (memcmp(msg.data"#"1== 0//退出查询页面,先判断退出后发送内容
    80.         {
    81.             printf("退出查询\n");
    82.             msg.type = 0;//当发送给服务器为结束内容,让服务器端读出且无法识别type,继续等待事件
    83.             memset(msg.data,0,sizeof(msg.data));
    84.             send(sockfd, &msg, sizeof(msg), 0);
    85.             break;
    86.         }
    87.         send(sockfd, &msg, sizeof(msg), 0);
    88.         recv(sockfd, &msg, sizeof(msg), 0);
    89.         printf("annotation:%s\n", msg.data); //打印接收的信息/注释
    90.     }
    91. }
    92. //查询历史记录
    93. void do_history(int sockfd)
    94. {
    95.     // memset(msg.data0, sizeof(msg.data));
    96.     msg.type = H;
    97.     send(sockfd, &msg, sizeof(msg), 0);
    98.     recv(sockfd, &msg, sizeof(msg), 0); //接收信息,接一次打一次.
    99.     printf("%s\n", msg.data);
    100. }
    101. //主函数
    102. int main(int argc, char const *argv[])
    103. {
    104.     if (argc < 3)
    105.     {
    106.         printf("plase input \n");
    107.         return -1;
    108.     }
    109.     //1.创建套接字,用于链接
    110.     int sockfd;
    111.     sockfd = socket(AF_INET, SOCK_STREAM, 0);
    112.     if (sockfd < 0)
    113.     {
    114.         perror("socket err");
    115.         return -1;
    116.     }
    117.     //文件描述符 0 -> 标准输入  1->标准输出  2->标准出错  3->socket
    118.     printf("sockfd:%d\n", sockfd);
    119.     //2.绑定 ip+port 填充结构体
    120.     struct sockaddr_in saddr;
    121.     saddr.sin_family = AF_INET;                 //协议族ipv4
    122.     saddr.sin_port = htons(atoi(argv[2]));      //端口号,htons将无符号短整数hostshort从主机字节顺序到网络字节顺序。
    123.     saddr.sin_addr.s_addr = inet_addr(argv[1]); //ip地址,转化为16进制表示
    124.     socklen_t len = sizeof(saddr);              //结构体大小
    125.     //3用于连接服务器;
    126.     if (connect(sockfd, (struct sockaddr *)&saddr, len) < 0)
    127.     {
    128.         perror("connect err");
    129.         return -1;
    130.     }
    131.     int flag = 0;
    132.     while (1)
    133.     {
    134.         printf("*******************************************************\n");
    135.         printf("*                                             *\n");
    136.         printf("*               1: 注册  2: 登录  3:  退出              *\n");
    137.         printf("*******************************************************\n");
    138.         printf("请输入命令:");
    139.         scanf("%d"&n);
    140.         getchar();
    141.         switch (n)
    142.         {
    143.         case 1:
    144.             do_register(sockfd);
    145.             break;
    146.         case 2:
    147.             if (do_login(sockfd) == 1)
    148.             {
    149.                 while (1)
    150.                 {
    151.                     if (flag == 1)
    152.                     {
    153.                         flag = 0;
    154.                         break;
    155.                     }
    156.                     printf("*******************************************************\n");
    157.                     printf("*                                             *\n");
    158.                     printf("*              1: 查询单词  2: 历史记录 3:  退出          *\n");
    159.                     printf("*******************************************************\n");
    160.                     printf("请输入命令:");
    161.                     scanf("%d"&n);
    162.                     getchar();
    163.                     switch (n)
    164.                     {
    165.                     case 1:
    166.                         do_query(sockfd);
    167.                         break;
    168.                     case 2:
    169.                         printf("search history\n");
    170.                         do_history(sockfd);
    171.                         break;
    172.                     case 3:
    173.                         flag = 1;
    174.                         break;
    175.                     default:
    176.                         break;
    177.                     }
    178.                 }
    179.             }
    180.             break;
    181.         case 3:
    182.             return 0;
    183.         default:
    184.             break;
    185.         }
    186.     }
    187.     close(sockfd);
    188.     return 0;
    189. }

  • 相关阅读:
    docker容器内调用宿主机docker执行
    win10恢复注册表MMC文件夹
    springboot基于javaweb的社区留守儿童帮扶系统毕业设计源码101603
    Ansible初识以及安装
    un9.2:创建springboot的两种方式。
    3474. 坠落的蚂蚁
    【Redis】基础数据结构-ziplist压缩列表
    在Web中搜索(Searching the Web, ACM/ICPC Beijing 2004, UVa1597)rust解法
    智慧养殖方案:浅谈视频监控与AI智能识别技术助力奶牛高效、智慧养殖
    实现简易负载式均衡在线判题系统
  • 原文地址:https://blog.csdn.net/m0_74937538/article/details/134511485