当前位置:   article > 正文

网络电子词典

网络电子词典

一、项目要求:

1. 登录注册功能,不能重复登录,重复注册

2. 单词查询功能

3. 历史记录功能,存储单词,意思,以及查询时间

4. 基于TCP,支持多客户端连接

5. 采用数据库保存用户信息与历史记录

格式要求: 1. main函数只跑逻辑,不允许跑功能代码 2. 功能代码封装成函数 

二、流程示意图

服务器:

客户端:

 

 三、实现代码

server.c代码部分:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <string.h>
  5. #include <sqlite3.h>
  6. #include <signal.h>
  7. #include <time.h>
  8. #include <sys/socket.h>
  9. #include <netinet/in.h>
  10. #include <arpa/inet.h>
  11. #define DATABASE "my.db"
  12. #define N 16
  13. enum CMD{
  14. R=1,
  15. L,
  16. Q,
  17. H
  18. };
  19. typedef struct sockaddr SA;
  20. typedef struct
  21. {
  22. int type;
  23. char name[N];
  24. char data[256]; // password or word
  25. } MSG;
  26. static sqlite3 *db; //数据库句柄指针
  27. void do_register(int connfd, MSG *pbuf) //用户注册模块
  28. {
  29. char sqlstr[128];
  30. char *errmsg;
  31. sprintf(sqlstr, "insert into usr values ('%s', '%s')", pbuf->name, pbuf->data);
  32. printf("%s\n", sqlstr);
  33. if (sqlite3_exec(db, sqlstr, NULL, NULL, &errmsg) != SQLITE_OK) //执行SQL语句并处理结果
  34. {
  35. sqlite3_free(errmsg);
  36. sprintf(pbuf->data, "user %s already exist!!!", pbuf->name);
  37. }
  38. else
  39. {
  40. strncpy(pbuf->data, "OK", 256);
  41. }
  42. send(connfd, pbuf, sizeof(MSG), 0);
  43. }
  44. void do_login(int connfd, MSG *pbuf) //用户登录模块
  45. {
  46. char sqlstr[128]; //拼接后的查询语句
  47. char *errmsg, **result; //存放错误信息和查询到的结果
  48. int nrow, ncolumn; //查询到的行数和列数
  49. sprintf(sqlstr, "select * from usr where name = '%s' and pass = '%s'", pbuf->name, pbuf->data);
  50. if (sqlite3_get_table(db, sqlstr, &result, &nrow, &ncolumn, &errmsg) != SQLITE_OK)
  51. {
  52. printf("error : %s\n", errmsg);
  53. sqlite3_free(errmsg);
  54. }
  55. if (nrow == 0) //查询到的条数为0条
  56. {
  57. strncpy(pbuf->data, "name or password is wrong!!!", 256);
  58. }
  59. else //查询到的所需信息
  60. {
  61. strncpy(pbuf->data, "OK", 256);
  62. }
  63. send(connfd, pbuf, sizeof(MSG), 0);
  64. sqlite3_free_table(result);
  65. return;
  66. }
  67. #if 0
  68. int do_searchword(int connfd, MSG *pbuf) //查询单词模块调用的查询单词函数方式一
  69. {
  70. FILE *fp;
  71. char line[300];
  72. char *p;
  73. int len, result;
  74. len = strlen(pbuf->data); //要查询的单词长度
  75. if ((fp = fopen("dict.txt", "r")) == NULL) //打开保存单词文件
  76. {
  77. strcpy(pbuf->data, "dict on server can't be opened :(");
  78. send(connfd, pbuf, sizeof(MSG), 0);
  79. return 0;
  80. }
  81. printf("query word is %s\n", pbuf->data); //打印一下客户端要查询的单词
  82. while (fgets(line, 300, fp) != NULL) //读文件,每次读300个字符存放到字符数组line中,来查询单词
  83. {
  84. result = strncmp(pbuf->data, line, len); //比较一下要查询的单词和line中读取到的文件中每行的单词信息进行比较,比较的长度大小为要查询的单词大小
  85. if (result > 0) continue; //大于0,说明要查询的单词大小大于读取的单词大小,则继续下一行,比如abandon ,查询abc,则跳入下一行
  86. if (result < 0 || line[len] != ' ') break;//小于0,则说明要查找的单词小于读取的单词大小,但之前的都已比较完毕,则查询失败。不是空格也不行,比如查询ab,而读取的是abandon.
  87. p = line + len; //指针指到单词末尾
  88. while (*p == ' ') p++; //指针移动到注释之前
  89. strcpy(pbuf->data, p); //拷贝单词注释
  90. fclose(fp); //
  91. return 1;
  92. }
  93. fclose(fp);
  94. return 0;
  95. }
  96. #else
  97. int do_searchword(int connfd, MSG *pbuf) //查询单词模块调用的查询单词函数方式二
  98. {
  99. char buf[128] = {0};
  100. printf("query word is %s\n", pbuf->data);
  101. sprintf(buf, "select * from dic where word='%s'", pbuf->data);
  102. char *errmsg;
  103. char **resultp;
  104. int n_row, n_cloum;
  105. if (sqlite3_get_table(db, buf, &resultp, &n_row, &n_cloum, &errmsg) != SQLITE_OK)
  106. {
  107. printf("err:%s\n", errmsg);
  108. return -1;
  109. }
  110. if(n_row != 1)
  111. {
  112. return 0;
  113. }
  114. char **p;
  115. p = resultp + n_cloum;
  116. // printf("data=%s\n", p[1]);
  117. strcpy(pbuf->data, p[1]);
  118. return 1;
  119. }
  120. #endif
  121. void get_date(char date[]) //获取系统时间
  122. {
  123. time_t t;
  124. struct tm *tp;
  125. time(&t); //获取到一个时间秒数
  126. tp = localtime(&t);//进行时间格式转换
  127. /*
  128. //字符串拼接
  129. sprintf(date, "%d-%02d-%02d %02d:%02d:%02d", tp->tm_year+1900,
  130. tp->tm_mon+1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec);
  131. */
  132. strftime(date, 64, "%Y-%m-%d %H:%M:%S", tp);
  133. return;
  134. }
  135. void do_query(int connfd, MSG *pbuf) //查询单词模块
  136. {
  137. char sqlstr[128], *errmsg;
  138. int len, result, found = 0;
  139. char date[64], word[64];
  140. strcpy(word, pbuf->data); //拿出pbuf结构体中要查询的单词
  141. found = do_searchword(connfd, pbuf); //调用do_searchword函数模块去查询
  142. if(found) //返回值非0,表示找到了该单词,则将用户查询信息(包括用户名,时间和单词)插入到历史记录表中
  143. {
  144. get_date(date);//调用时间函数获取时间
  145. sprintf(sqlstr, "insert into record values ('%s', '%s', '%s')", pbuf->name, date, word);
  146. if (sqlite3_exec(db, sqlstr, NULL, NULL, &errmsg) != SQLITE_OK)
  147. {
  148. printf("error : %s\n", errmsg);
  149. sqlite3_free(errmsg);
  150. }
  151. }
  152. else //返回值为0,表示没有找到,发送没到到提示信息
  153. {
  154. strcpy(pbuf->data, "not found\n");
  155. }
  156. send(connfd, pbuf, sizeof(MSG), 0); //把查找到的信息发送给客户端
  157. return;
  158. }
  159. //得到查询结果,并将历史记录发送给客户端
  160. int history_callback(void *arg, int f_num, char **f_value, char **f_name)
  161. {
  162. int connfd;
  163. MSG buf;
  164. connfd = *(int *)arg;
  165. sprintf(buf.data, "%s : %s", f_value[1], f_value[2]);
  166. send(connfd, &buf, sizeof(buf), 0);
  167. return 0;
  168. }
  169. void do_history(int connfd, MSG *pbuf) //查询历史记录模块
  170. {
  171. char sqlstr[128], *errmsg;
  172. sprintf(sqlstr, "select * from record where name = '%s'", pbuf->name);
  173. if (sqlite3_exec(db, sqlstr, history_callback, (void *)&connfd, &errmsg) != SQLITE_OK) //查询数据库
  174. {
  175. printf("error : %s\n", errmsg);
  176. sqlite3_free(errmsg);
  177. }
  178. pbuf->data[0] = '\0'; //所有的记录发送完毕之后,给客户端发送一个结束信息
  179. send(connfd, pbuf, sizeof(MSG), 0);
  180. return;
  181. }
  182. void do_client(int connfd) //处理客户端请求主模块,调用函数完成所需功能
  183. {
  184. MSG buf;
  185. while(recv(connfd, &buf, sizeof(buf), 0) > 0){
  186. switch (buf.type) {
  187. case R:
  188. printf("will reg\n");
  189. do_register(connfd, &buf);
  190. break;
  191. case L:
  192. printf("will login\n");
  193. do_login(connfd, &buf);
  194. break;
  195. case Q:
  196. printf("will query\n");
  197. do_query(connfd, &buf);
  198. break;
  199. case H:
  200. printf("will history\n");
  201. do_history(connfd, &buf);
  202. break;
  203. default:
  204. break;
  205. }
  206. }
  207. exit(0);
  208. }
  209. int main(int argc, char *argv[]) //主函数
  210. {
  211. int listenfd, connfd;
  212. struct sockaddr_in myaddr;
  213. pid_t pid;
  214. MSG buf;
  215. if (argc < 3)
  216. {
  217. printf("Usage : %s <ip> <port>\n", argv[0]);
  218. exit(-1);
  219. }
  220. //打开数据库
  221. if(sqlite3_open(DATABASE, &db) < 0){
  222. printf("fail to sqlite3_open : %s\n", sqlite3_errmsg(db));
  223. return -1;
  224. }
  225. //创建服务器socket
  226. listenfd = socket(PF_INET, SOCK_STREAM, 0);
  227. if(listenfd < 0){
  228. perror("fail to socket");
  229. exit(-1);
  230. }
  231. bzero(&myaddr, sizeof(myaddr));
  232. myaddr.sin_family = PF_INET;
  233. myaddr.sin_port = htons(atoi(argv[2]));
  234. myaddr.sin_addr.s_addr = inet_addr(argv[1]);
  235. if (bind(listenfd, (SA *)&myaddr, sizeof(myaddr)) < 0)
  236. {
  237. perror("fail to bind");
  238. exit(-1);
  239. }
  240. // 将套接字设为监听模式
  241. if (listen(listenfd, 5) < 0)
  242. {
  243. perror("fail to listen");
  244. exit(-1);
  245. }
  246. //并发服务器模型
  247. while(1){
  248. if((connfd = accept(listenfd, NULL, NULL)) < 0){
  249. perror("fail to accept");
  250. exit(-1);
  251. }
  252. pid = fork();
  253. if(pid == -1){
  254. perror("fail to fork\n");
  255. exit(-1);
  256. }
  257. else if(pid == 0){ //子进程处理客户端具体的消息
  258. printf("a user comming\n");
  259. do_client(connfd);
  260. }else{ //父进程用于接收客户端的请求
  261. close(connfd);
  262. }
  263. }
  264. }

client.c代码部分:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <string.h>
  5. #include <sqlite3.h>
  6. #include <sys/socket.h>
  7. #include <netinet/in.h>
  8. #include <arpa/inet.h>
  9. #define N 16
  10. typedef struct sockaddr SA;
  11. enum CMD{
  12. R=1,
  13. L,
  14. Q,
  15. H
  16. };
  17. typedef struct
  18. {
  19. int type;
  20. char name[N];
  21. char data[256]; // password or word
  22. } MSG;
  23. void do_register(int sockfd, MSG *pbuf) //一级菜单用户注册模块
  24. {
  25. pbuf->type = R;
  26. printf("input name : ");
  27. scanf("%s", pbuf->name);
  28. printf("input password : ");
  29. scanf("%s", pbuf->data);
  30. send(sockfd, pbuf, sizeof(MSG), 0);
  31. recv(sockfd, pbuf, sizeof(MSG), 0);
  32. printf("register : %s\n\n", pbuf->data);
  33. }
  34. int do_login(int sockfd, MSG *pbuf) //一级菜单用户登录模块
  35. {
  36. pbuf->type = L;
  37. printf("input name : ");
  38. scanf("%s", pbuf->name);
  39. printf("input password : ");
  40. scanf("%s", pbuf->data);
  41. send(sockfd, pbuf, sizeof(MSG), 0);
  42. recv(sockfd, pbuf, sizeof(MSG), 0);
  43. if(strncmp(pbuf->data, "OK", 3) == 0){ //3个字节:O K /0
  44. printf("login sucess\n\n");
  45. return 1;
  46. }else{
  47. printf("login : %s\n\n", pbuf->data);
  48. return 0;
  49. }
  50. }
  51. void do_history(int sockfd, MSG *pbuf) //二级菜单查询单词历史记录模块
  52. {
  53. pbuf->type = H;
  54. send(sockfd, pbuf, sizeof(MSG), 0);
  55. while ( 1 )
  56. {
  57. recv(sockfd, pbuf, sizeof(MSG), 0);
  58. if (pbuf->data[0] == '\0') break;
  59. printf("%s\n", pbuf->data);
  60. }
  61. return;
  62. }
  63. void do_query(int sockfd, MSG *pbuf) //二级菜单查询单词模块
  64. {
  65. pbuf->type = Q;
  66. while ( 1 )
  67. {
  68. printf("input word(# to quit) : ");
  69. scanf("%s", pbuf->data);
  70. if (strcmp(pbuf->data, "#") == 0) break; //输入#返回上一级菜单
  71. send(sockfd, pbuf, sizeof(MSG), 0); //将要查询的单词发送给服务器
  72. recv(sockfd, pbuf, sizeof(MSG), 0); //等待接收服务器传递回来的单词信息
  73. printf("%s\n", pbuf->data);
  74. printf("\n");
  75. }
  76. return;
  77. }
  78. void enter_query(int sockfd, MSG *buf) //二级菜单主页面
  79. {
  80. int input;
  81. char cleanbuf[64];
  82. while (1) {
  83. printf("***********************************************\n");
  84. printf("* 1: query_word 2: history_record 3: quit *\n");
  85. printf("***********************************************\n");
  86. printf("please choose : ");
  87. //获取用户输入
  88. if(scanf("%d", &input) == 0){
  89. fgets(cleanbuf, 64, stdin); //类型错误需要重新清除输入缓冲区
  90. continue;
  91. }
  92. switch (input) {
  93. case 1:
  94. printf("\n");
  95. do_query(sockfd, buf);
  96. printf("\n");
  97. break;
  98. case 2:
  99. printf("\n");
  100. do_history(sockfd, buf);
  101. printf("\n");
  102. break;
  103. case 3:
  104. return;
  105. default:
  106. break;
  107. }
  108. }
  109. }
  110. int main(int argc, char *argv[]) //主函数
  111. {
  112. int sockfd, login = 0;
  113. struct sockaddr_in servaddr;
  114. MSG buf;
  115. char clean[64];
  116. if (argc < 3)
  117. {
  118. printf("Usage : %s <serv_ip> <serv_port>\n", argv[0]);
  119. exit(-1);
  120. }
  121. //创建客户端socket
  122. if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
  123. {
  124. perror("fail to socket");
  125. exit(-1);
  126. }
  127. bzero(&servaddr, sizeof(servaddr));
  128. servaddr.sin_family = PF_INET;
  129. servaddr.sin_port = htons(atoi(argv[2]));
  130. servaddr.sin_addr.s_addr = inet_addr(argv[1]);
  131. //连接服务器
  132. if (connect(sockfd, (SA *)&servaddr, sizeof(servaddr)) < 0)
  133. {
  134. perror("fail to connect");
  135. exit(-1);
  136. }
  137. int input;
  138. char cleanbuf[64];
  139. while(1){
  140. printf("************************************\n");
  141. printf("* 1: register 2: login 3: quit *\n");
  142. printf("************************************\n");
  143. printf("please choose : ");
  144. //获取用户输入
  145. if(scanf("%d", &input) == 0){
  146. fgets(cleanbuf, 64, stdin); //类型错误需要重新清除输入缓冲区
  147. continue;
  148. }
  149. switch (input) {
  150. case 1:
  151. do_register(sockfd, &buf);
  152. break;
  153. case 2:
  154. if(do_login(sockfd, &buf) == 1){ //成功
  155. enter_query(sockfd, &buf);
  156. }
  157. break;
  158. case 3:
  159. close(sockfd);
  160. exit(0);
  161. break;
  162. default:
  163. break;
  164. }
  165. }
  166. }

Makefile代码部分:

  1. all:server client
  2. server:server.c
  3. gcc $< -o $@ -l sqlite3
  4. client:client.c
  5. gcc $< -o $@
  6. clean:
  7. rm server client

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/654592
推荐阅读
相关标签
  

闽ICP备14008679号