在线词典介绍
- 流程图如下:
- 项目的功能介绍
- 在线英英词典
- 项目功能描述
- 用户注册和登录验证
- 服务器端将用户信息和历史记录保存在数据中。客户端输入用户和密码,服务器端在数据库中查找、匹配,返回结果
- 单词在线翻译
- 根据客户端输入输入的单词在字典文件中搜索
- 历史记录查询
- 项目分析
- 项目流程
- 定义数据库中表的结构
- 定义消息结构体
- 分析服务端和客户端流程
- 编码实现
- 客户端流程图分析
- 服务器和客户端通信的结构体
- 客户端流程图如下:
- 服务器端流程图如下: ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/64b2312c006e4298a3f382597ff62558.png
- 服务器和客户端通信的结构体
在线词典的代码编写
- 客户端代码的编写:
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #define N 32 #define R 1 // user - register #define L 2 // user - login #define Q 3 // user - query #define H 4 // user - history // 定义通信双方的信息结构体 typedef struct { int type; char name[N]; char data[256]; }MSG; int do_register(int sockfd, MSG *msg) { msg->type = R; printf("Input name:"); scanf("%s", msg->name); getchar(); printf("Input passwd:"); scanf("%s", msg->data); if(send(sockfd, msg, sizeof(MSG),0) < 0) { printf("fail to send.\n"); return -1; } if(recv(sockfd, msg, sizeof(MSG), 0) < 0) { printf("Fail to recv.\n"); return -1; } // ok ! or usr alread exist. printf("%s\n", msg->data); return 0; } int do_login(int sockfd, MSG *msg) { msg->type = L; printf("Input name:"); scanf("%s", msg->name); getchar(); printf("Input passwd:"); scanf("%s", msg->data); if(send(sockfd, msg, sizeof(MSG),0) < 0) { printf("fail to send.\n"); return -1; } if(recv(sockfd, msg, sizeof(MSG), 0) < 0) { printf("Fail to recv.\n"); return -1; } if(strncmp(msg->data, "OK", 3) == 0) { printf("Login ok!\n"); return 1; } else { printf("%s\n", msg->data); } return 0; } int do_query(int sockfd, MSG *msg) { msg->type = Q; puts("--------------"); while(1) { printf("Input word:"); scanf("%s", msg->data); getchar(); //客户端,输入#号,返回到上一级菜单 if(strncmp(msg->data, "#", 1) == 0) break; //将要查询的单词发送给服务器 if(send(sockfd,msg, sizeof(MSG), 0) < 0) { printf("Fail to send.\n"); return -1; } // 等待接受服务器,传递回来的单词的注释信息 if(recv(sockfd, msg,sizeof(MSG), 0) < 0) { printf("Fail to recv.\n"); return -1; } printf("%s\n", msg->data); } return 0; } int do_history(int sockfd, MSG *msg) { msg->type = H; send(sockfd, msg, sizeof(MSG), 0); // 接受服务器,传递回来的历史记录信息 while(1) { recv(sockfd, msg, sizeof(MSG), 0); if(msg->data[0] == '\0') break; //输出历史记录信息 printf("%s\n", msg->data); } return 0; } // ./server 192.168.3.196 10000 int main(int argc, const char *argv[]) { int sockfd; struct sockaddr_in serveraddr; int n; MSG msg; if(argc != 3) { printf("Usage:%s serverip port.\n", argv[0]); return -1; } if((sockfd = socket(AF_INET, SOCK_STREAM,0)) < 0) { perror("fail to socket.\n"); return -1; } bzero(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = inet_addr(argv[1]); serveraddr.sin_port = htons(atoi(argv[2])); if(connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) { perror("fail to connect"); return -1; } while(1) { printf("*****************************************************************\n"); printf("* 1.register 2.login 3.quit *\n"); printf("*****************************************************************\n"); printf("Please choose:"); scanf("%d", &n); getchar(); 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); break; default: printf("Invalid data cmd.\n"); } } next: while(1) { printf("*****************************************************\n"); printf("* 1.query_word 2.history_record 3.quit *\n"); printf("*****************************************************\n"); printf("Please choose:"); scanf("%d", &n); getchar(); switch(n) { case 1: do_query(sockfd, &msg); break; case 2: do_history(sockfd, &msg); break; case 3: close(sockfd); exit(0); break; default : printf("Invalid data cmd.\n"); } } return 0;
}
```
- 服务器端代码编写如下:
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <unistd.h> #include <sqlite3.h> #include <signal.h> #include <time.h> #define N 32 #define R 1 // user - register #define L 2 // user - login #define Q 3 // user - query #define H 4 // user - history #define DATABASE "my.db" // 定义通信双方的信息结构体 typedef struct { int type; char name[N]; char data[256]; }MSG; int do_client(int acceptfd, sqlite3 *db); void do_register(int acceptfd, MSG *msg, sqlite3 *db); int do_login(int acceptfd, MSG *msg, sqlite3 *db); int do_query(int acceptfd, MSG *msg, sqlite3 *db); int do_history(int acceptfd, MSG *msg, sqlite3 *db); int history_callback(void* arg,int f_num,char** f_value,char** f_name); int do_searchword(int acceptfd, MSG *msg, char word[]); int get_date(char *date); // ./server 192.168.3.196 10000 int main(int argc, const char *argv[]) { int sockfd; struct sockaddr_in serveraddr; int n; MSG msg; sqlite3 *db; int acceptfd; pid_t pid; if(argc != 3) { printf("Usage:%s serverip port.\n", argv[0]); return -1; } //打开数据库 if(sqlite3_open(DATABASE, &db) != SQLITE_OK) { printf("%s\n", sqlite3_errmsg(db)); return -1; } else { printf("open DATABASE success.\n"); } if((sockfd = socket(AF_INET, SOCK_STREAM,0)) < 0) { perror("fail to socket.\n"); return -1; } bzero(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = inet_addr(argv[1]); serveraddr.sin_port = htons(atoi(argv[2])); if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) { perror("fail to bind.\n"); return -1; } // 将套接字设为监听模式 if(listen(sockfd, 5) < 0) { printf("fail to listen.\n"); return -1; } //处理僵尸进程 signal(SIGCHLD, SIG_IGN); while(1) { if((acceptfd = accept(sockfd, NULL, NULL)) < 0) { perror("fail to accept"); return -1; } if((pid = fork()) < 0) { perror("fail to fork"); return -1; } else if(pid == 0) // 儿子进程 { //处理客户端具体的消息 close(sockfd); do_client(acceptfd, db); } else // 父亲进程,用来接受客户端的请求的 { close(acceptfd); } } return 0; } int do_client(int acceptfd, sqlite3 *db) { MSG msg; while(recv(acceptfd, &msg, sizeof(msg), 0) > 0) { printf("type:%d\n", msg.type); switch(msg.type) { case R: do_register(acceptfd, &msg, db); break; case L: do_login(acceptfd, &msg, db); break; case Q: do_query(acceptfd, &msg, db); break; case H: do_history(acceptfd, &msg, db); break; default: printf("Invalid data msg.\n"); } } printf("client exit.\n"); close(acceptfd); exit(0); return 0; } void do_register(int acceptfd, MSG *msg, sqlite3 *db) { char * errmsg; char sql[128]; sprintf(sql, "insert into usr values('%s', %s);", msg->name, msg->data); printf("%s\n", sql); if(sqlite3_exec(db,sql, NULL, NULL, &errmsg) != SQLITE_OK) { printf("%s\n", errmsg); strcpy(msg->data, "usr name already exist."); } else { printf("client register ok!\n"); strcpy(msg->data, "OK!"); } if(send(acceptfd, msg, sizeof(MSG), 0) < 0) { perror("fail to send"); return ; } return ; } int do_login(int acceptfd, MSG *msg , sqlite3 *db) { char sql[128] = {}; char *errmsg; int nrow; int ncloumn; char **resultp; sprintf(sql, "select * from usr where name = '%s' and pass = '%s';", msg->name, msg->data); printf("%s\n", sql); if(sqlite3_get_table(db, sql, &resultp, &nrow, &ncloumn, &errmsg)!= SQLITE_OK) { printf("%s\n", errmsg); return -1; } else { printf("get_table ok!\n"); } // 查询成功,数据库中拥有此用户 if(nrow == 1) { strcpy(msg->data, "OK"); send(acceptfd, msg, sizeof(MSG), 0); return 1; } if(nrow == 0) // 密码或者用户名错误 { strcpy(msg->data,"usr/passwd wrong."); send(acceptfd, msg, sizeof(MSG), 0);: } return 0; } int do_searchword(int acceptfd, MSG *msg, char word[]) { FILE * fp; int len = 0; char temp[512] = {}; int result; char *p; //打开文件,读取文件,进行比对 if((fp = fopen("dict.txt", "r")) == NULL) { perror("fail to fopen.\n"); strcpy(msg->data, "Failed to open dict.txt"); send(acceptfd, msg, sizeof(MSG), 0); return -1; } //打印出,客户端要查询的单词 len = strlen(word); printf("%s , len = %d\n", word, len); //读文件,来查询单词 while(fgets(temp, 512, fp) != NULL) { // printf("temp:%s\n", temp); // abandon ab result = strncmp(temp,word,len); if(result < 0) { continue; } if(result > 0 || ((result == 0) && (temp[len]!=' '))) { break; } // 表示找到了,查询的单词 p = temp + len; // abandon v.akdsf dafsjkj // printf("found word:%s\n", p); while(*p == ' ') { p++; } // 找到了注释,跳跃过所有的空格 strcpy(msg->data, p); printf("found word:%s\n", msg->data); // 注释拷贝完毕之后,应该关闭文件 fclose(fp); return 1; } fclose(fp); return 0; } int get_date(char *date) { time_t t; struct tm *tp; time(&t); //进行时间格式转换 tp = localtime(&t); sprintf(date, "%d-%d-%d %d:%d:%d", tp->tm_year + 1900, tp->tm_mon+1, tp->tm_mday, tp->tm_hour, tp->tm_min , tp->tm_sec); printf("get date:%s\n", date); return 0; } int do_query(int acceptfd, MSG *msg , sqlite3 *db) { char word[64]; int found = 0; char date[128] = {}; char sql[128] = {}; char *errmsg; //拿出msg结构体中,要查询的单词 strcpy(word, msg->data); found = do_searchword(acceptfd, msg, word); printf("查询一个单词完毕.\n"); // 表示找到了单词,那么此时应该将 用户名,时间,单词,插入到历史记录表中去。 if(found == 1) { // 需要获取系统时间 get_date(date); sprintf(sql, "insert into record values('%s', '%s', '%s')", msg->name, date, word); if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK) { printf("%s\n", errmsg); return -1; } else { printf("Insert record done.\n"); } } else //表示没有找到 { strcpy(msg->data, "Not found!"); } // 将查询的结果,发送给客户端 send(acceptfd, msg, sizeof(MSG), 0); return 0; } // 得到查询结果,并且需要将历史记录发送给客户端 int history_callback(void* arg,int f_num,char** f_value,char** f_name) { // record , name , date , word int acceptfd; MSG msg; acceptfd = *((int *)arg); sprintf(msg.data, "%s , %s", f_value[1], f_value[2]); send(acceptfd, &msg, sizeof(MSG), 0); return 0; } int do_history(int acceptfd, MSG *msg, sqlite3 *db) { char sql[128] = {}; char *errmsg; sprintf(sql, "select * from record where name = '%s'", msg->name); //查询数据库 if(sqlite3_exec(db, sql, history_callback,(void *)&acceptfd, &errmsg)!= SQLITE_OK) { printf("%s\n", errmsg); } else { printf("Query record done.\n"); } // 所有的记录查询发送完毕之后,给客户端发出一个结束信息 msg->data[0] = '\0'; send(acceptfd, msg, sizeof(MSG), 0); return 0; }