在线词典--(一、流程分析)
在线词典实现框架
应用综合项目:
1、在线英英词典
2、项目功能描述
3、用户注册和登录验证
4、服务器端将用户信息和历史记录保存在数据库中,客户端输入用户名和密码,服务器端在数据库中查找、匹配,返回结果
5、单次在线翻译
6、根据客户端输入的单次在字典文件中搜索
7、历史记录查询
8、项目分析
项目流程框架:
客户端
服务器端:
实现:
在终端,创建数据库,dic.db
创建记录table
create table user (name char , passwd char , date char);
create table record (name char , date char , word char);
在原有基础上增加实现
1、在搭建的框架中实现并发,实现多客户端的注册登录
2、同时支持管理员(用户名:root,密码:1)和普通用户
3、管理员可以查询所有用户的使用记录
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <sys/types.h> 4 #include <sys/socket.h> 5 #include <sys/socket.h> 6 #include <netinet/in.h> 7 #include <arpa/inet.h> 8 #include <string.h> 9 10 #define N 32 11 12 #define R 1 //usr--register 13 #define L 2 //usr--login 14 #define Q 3 //usr--query 15 #define H 4 //root--history 16 #define Y 5 //root--login 17 #define X 6 //root--user record 18 19 //定义通信双方的MSG信息结构体 20 typedef struct { 21 int type; 22 char name[N]; 23 char data[256]; 24 }MSG; 25 26 int do_regiser(int sockfd, MSG *msg); 27 int do_login(int sockfd, MSG *msg); 28 int do_login_root(int sockfd, MSG *msg); 29 int do_query(int sockfd, MSG *msg); 30 int do_history(int sockfd, MSG *msg); 31 int do_user_record(int sockfd, MSG *msg); 32 33 34 int main(int argc, const char *argv[]) 35 { 36 int sockfd; 37 struct sockaddr_in serveraddr; 38 int num; 39 MSG msg; 40 41 if(argc != 3) 42 { 43 printf("Usage :%s serverip port.\n", argv[0]); 44 return -1; 45 } 46 47 if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) 48 { 49 perror("fail to socket!\n"); 50 return -1; 51 } 52 53 bzero(&serveraddr, sizeof(serveraddr)); 54 serveraddr.sin_family = AF_INET; 55 serveraddr.sin_addr.s_addr = inet_addr(argv[1]); //点分形式转为二进制数据 56 serveraddr.sin_port = htons(atoi(argv[2])); //先将字符型转为int型数据,再HBD->NBD 57 58 if(connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) 59 { 60 perror("connect error!\n"); 61 return -1; 62 } 63 64 printf("Client starting...\n"); 65 66 while(1) 67 { 68 printf("***************************************************\n"); 69 printf("* 1.regiser 2.login 3.root login 4.quit *\n"); 70 printf("***************************************************\n"); 71 printf("Please choose:"); 72 73 scanf("%d",&num); 74 getchar(); //每次输入完毕会产生垃圾字符,清除 75 76 switch(num) 77 { 78 case 1: 79 do_regiser(sockfd, &msg); 80 break; 81 case 2: 82 if(do_login(sockfd,&msg) == 1) 83 { 84 goto next; //登录成功,跳到二级菜单 85 } 86 break; 87 case 3: 88 if(do_login_root(sockfd, &msg) == 1) 89 { 90 goto next_root; 91 } 92 case 4: 93 close(sockfd); 94 exit(0); 95 break; 96 default: 97 printf("Invalid data cmd.\n"); 98 } 99 } 100 101 next: //二级菜单 102 while(1) 103 { 104 printf("***************************************************\n"); 105 printf("* 1.query word 2.quit \n"); 106 printf("***************************************************\n"); 107 printf("Please choose:"); 108 109 scanf("%d",&num); 110 getchar(); //每次输入完毕会产生垃圾字符,清除 111 112 switch(num) 113 { 114 case 1: 115 do_query(sockfd, &msg); 116 break; 117 case 2: 118 close(sockfd); 119 exit(1); 120 break; 121 default: 122 printf("Invalid data cmd.\n"); 123 } 124 125 } 126 127 128 next_root: //管理员二级菜单 129 while(1) 130 { 131 printf("***************************************************\n"); 132 printf("* 1.query word 2.history 3.user record 4.quit *\n"); 133 printf("***************************************************\n"); 134 printf("Please choose:"); 135 136 scanf("%d",&num); 137 getchar(); //每次输入完毕会产生垃圾字符,清除 138 139 switch(num) 140 { 141 case 1: 142 do_query(sockfd, &msg); 143 break; 144 case 2: 145 do_history(sockfd, &msg); 146 break; 147 case 3: 148 do_user_record(sockfd, &msg); 149 break; 150 case 4: 151 close(sockfd); 152 exit(1); 153 break; 154 default: 155 printf("Invalid data cmd.\n"); 156 } 157 158 } 159 return 0; 160 } 161 162 int do_regiser(int sockfd, MSG *msg) 163 { 164 msg->type = R; 165 printf("Intput name:"); 166 scanf("%s",msg->name); 167 getchar(); 168 169 printf("Intput password:"); 170 scanf("%s",msg->data); 171 172 if(send(sockfd, msg, sizeof(MSG), 0) < 0) 173 { 174 printf("fail to send.\n"); 175 return -1; 176 } 177 178 if(recv(sockfd, msg, sizeof(MSG), 0) < 0) 179 { 180 printf("fail to recv.\n"); 181 exit(1); 182 } 183 184 //ok! or usr already exist 185 printf("%s\n",msg->data); 186 187 return 0; 188 189 } 190 191 int do_login(int sockfd, MSG *msg) 192 { 193 msg->type = L; 194 195 printf("Intput name:"); 196 scanf("%s",msg->name); 197 getchar(); 198 199 printf("Intput password:"); 200 scanf("%s",msg->data); 201 202 if(send(sockfd, msg, sizeof(MSG), 0) < 0) 203 { 204 printf("fail to send.\n"); 205 exit(1); 206 } 207 208 if(recv(sockfd, msg, sizeof(MSG), 0) < 0) 209 { 210 printf("fail to recv.\n"); 211 return -1; 212 } 213 214 if(strncmp(msg->data, "ok", 3) == 0) 215 { 216 printf("Login ok!\n"); 217 return 1; 218 } 219 else 220 { 221 printf("%s\n",msg->data); 222 } 223 224 225 return 0; 226 } 227 228 229 int do_login_root(int sockfd, MSG *msg) 230 { 231 msg->type = Y; 232 233 printf("name: root"); 234 sprintf(msg->name,"root"); 235 getchar(); 236 237 printf("Intput password:"); 238 scanf("%s",msg->data); 239 240 if(send(sockfd, msg, sizeof(MSG), 0) < 0) 241 { 242 printf("fail to send.\n"); 243 exit(1); 244 } 245 246 if(recv(sockfd, msg, sizeof(MSG), 0) < 0) 247 { 248 printf("fail to recv.\n"); 249 return -1; 250 } 251 252 if(strncmp(msg->data, "ok", 3) == 0) 253 { 254 printf("ROOT Login ok!\n"); 255 return 1; 256 } 257 else 258 { 259 printf("%s\n",msg->data); 260 } 261 262 263 return 0; 264 } 265 266 int do_query(int sockfd, MSG *msg) 267 { 268 msg->type = Q; 269 puts("--------------------------\n"); 270 271 while(1) 272 { 273 printf("Intput word:"); 274 scanf("%s",msg->data); 275 276 //客户端输入结束并返回上一层 277 if(strncmp(msg->data,"#",1) == 0) //比较前1个字符,相同返0 278 break; 279 280 //将要查询的单词发送给服务器 281 if(send(sockfd, msg, sizeof(MSG), 0) < 0) 282 { 283 printf("file to send.\n"); 284 return -1; 285 } 286 287 //等待接收服务器传递回来的注释单词信息 288 if(recv(sockfd, msg, sizeof(MSG),0) < 0) 289 { 290 printf("file to recv.\n"); 291 return -1; 292 } 293 printf("%s\n",msg->data); 294 } 295 296 return 0; 297 } 298 299 int do_history(int sockfd, MSG *msg) 300 { 301 msg->type = H; 302 303 printf("Intput the user name:\n"); 304 printf("** name * query date * word **\n"); 305 scanf("%s",msg->name); 306 send(sockfd, msg, sizeof(MSG), 0); 307 308 //接收服务器传递回来的历史记录 309 while(1) 310 { 311 recv(sockfd, msg, sizeof(MSG), 0); 312 313 if(msg->data[0] == '\0') 314 break; 315 316 //输出历史记录信息 317 printf("%s\n", msg->data); 318 } 319 return 0; 320 } 321 322 //查询用户的登录与密码记录 323 int do_user_record(int sockfd, MSG *msg) 324 { 325 msg->type = X; 326 printf("Intput the user name:\n"); 327 printf("** name * password * register date **\n"); 328 scanf("%s",msg->name); 329 send(sockfd, msg, sizeof(MSG), 0); 330 331 //接收服务器传递回来的历史记录 332 while(1) 333 { 334 recv(sockfd, msg, sizeof(MSG), 0); 335 336 if(msg->data[0] == '\0') 337 break; 338 339 //输出历史记录信息 340 printf("%s\n", msg->data); 341 } 342 return 0; 343 344 }
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <sys/types.h> 4 #include <sys/socket.h> 5 #include <sys/socket.h> 6 #include <netinet/in.h> 7 #include <arpa/inet.h> 8 #include <string.h> 9 #include <unistd.h> 10 #include <sqlite3.h> 11 #include <signal.h> 12 #include <time.h> 13 14 #define N 32 15 #define DATABASE "dic.db" 16 17 #define R 1 //usr--register 18 #define L 2 //usr--login 19 #define Q 3 //usr--query 20 #define H 4 //root--history 21 #define Y 5 //root--login 22 #define X 6 //root--user record 23 24 //定义通信双方的MSG信息结构体 25 typedef struct { 26 int type; 27 char name[N]; 28 char data[256]; 29 }MSG; 30 31 int do_client(int acceptfd, sqlite3 *db); 32 void do_regiser(int acceptfd, MSG *msg, sqlite3 *db); 33 int do_login(int acceptfd, MSG *msg, sqlite3 *db); 34 int do_login_root(int acceptfd, MSG *msg, sqlite3 *db); 35 int do_query(int acceptfd, MSG *msg, sqlite3 *db); 36 int do_history(int acceptfd, MSG *msg, sqlite3 *db); 37 int history_callback(void *arg, int f_num,char** f_value ,char** f_name); 38 int do_searchword(int acceptfd,MSG *msg, char word[]); 39 int get_date(char *date); 40 int do_user_record(int acceptfd, MSG *msg, sqlite3 *db); 41 int user_record_callback(void *arg, int f_num,char** f_value ,char** f_name); 42 43 44 // ./server 192.168.31.123 5001 45 int main(int argc, const char *argv[]) 46 { 47 int sockfd; 48 int acceptfd; 49 struct sockaddr_in serveraddr; 50 int num; 51 MSG msg; 52 sqlite3 *db; 53 pid_t pid; 54 char *errmsg; 55 char sql[128]; 56 57 if(argc != 3) 58 { 59 printf("Usage :%s server ip port.\n", argv[0]); 60 return -1; 61 } 62 63 /*打开数据库 */ 64 if(sqlite3_open(DATABASE, &db) != SQLITE_OK) 65 { 66 printf("%s\n", sqlite3_errmsg(db)); 67 return -1; 68 } 69 else 70 { 71 printf("open DATABASE success.\n"); 72 } 73 74 //在dic,db 里面创建表格用户密码 user 与单词记录表 record 75 //创建套接字 76 if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) 77 { 78 perror("fail to socket!\n"); 79 return -1; 80 } 81 82 bzero(&serveraddr, sizeof(serveraddr)); 83 serveraddr.sin_family = AF_INET; 84 serveraddr.sin_addr.s_addr = inet_addr(argv[1]); //点分形式转为二进制数据 85 serveraddr.sin_port = htons(atoi(argv[2])); //先将字符型转为int型数据,再HBD->NBD 86 87 /*地址快速重用 */ 88 int b_reuse = -1; 89 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int)); 90 91 /*绑定 */ 92 if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) 93 { 94 perror("file to bind.\n"); 95 return -1; 96 } 97 98 /*将套接字设置为监听模式*/ 99 if(listen(sockfd, 5) < 0) 100 { 101 perror("fail to listen.\n"); 102 return -1; 103 } 104 105 //处理僵尸进程 106 signal(SIGCHLD, SIG_IGN); 107 //SIG_IGN,信号处理方式设置为忽略,内核就会将僵尸子进程交给init处理 108 109 printf("Server starting ... OK!\n"); 110 111 while(1) 112 { 113 if((acceptfd = accept(sockfd, NULL, NULL)) < 0) 114 { 115 perror("fail to accept.\n"); 116 return -1; 117 } 118 119 printf("Connect success!\n"); 120 121 if((pid = fork()) < 0) 122 { 123 perror("fail to fork."); 124 return -1; 125 } 126 else if(pid == 0) //子进程 127 { 128 //处理客户端的具体消息 129 close(sockfd); //关闭不需要的文件描述符,节省资源 130 do_client(acceptfd, db); 131 return 0; 132 } 133 else //父进程,用来接收客户端的请求 134 { 135 close(acceptfd); 136 } 137 } 138 close(acceptfd); 139 return 0; 140 } 141 142 int do_client(int acceptfd, sqlite3 *db) 143 { 144 MSG msg; 145 //客户端关闭也会关闭套接字,进而跳出while循环 146 while(recv(acceptfd, &msg, sizeof(msg), 0) > 0) //接收成功 147 { 148 printf("type:%d\n", msg.type); 149 switch(msg.type) 150 { 151 case R: 152 do_regiser(acceptfd, &msg, db); 153 break; 154 case L: 155 do_login(acceptfd, &msg, db); 156 break; 157 case Y: 158 do_login_root(acceptfd, &msg, db); 159 break; 160 case Q: 161 do_query(acceptfd, &msg, db); 162 break; 163 case H: 164 do_history(acceptfd, &msg, db); 165 break; 166 case X: 167 do_user_record(acceptfd, &msg, db); 168 default: 169 printf("Invalid data msg.\n"); 170 } 171 } 172 173 printf("client exit.\n"); 174 close(acceptfd); 175 exit(0); 176 177 return 0; 178 } 179 180 void do_regiser(int acceptfd, MSG *msg, sqlite3 *db) 181 { 182 char *errmsg; 183 char sql[128]; 184 char date[128]; 185 //获取系统时间 186 get_date(date); 187 188 sprintf(sql,"insert into user values('%s','%s' ,'%s');", msg->name, msg->data, date); 189 printf("%s\n",sql); 190 191 if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK) 192 { 193 printf("%s\n",errmsg); 194 strcpy(msg->data, "user name already exit."); 195 } 196 else 197 { 198 printf("client register ok!\n"); 199 strcpy(msg->data, "ok"); //将OK拷到data中,发给client 200 } 201 202 if(send(acceptfd, msg, sizeof(MSG), 0)<0) 203 { 204 perror("fail to send"); 205 return; 206 } 207 208 return; 209 } 210 211 int do_login(int acceptfd, MSG *msg, sqlite3 *db) 212 { 213 char sql[128]; 214 char *errmsg; 215 int nrow; 216 int ncloumn; 217 char **resultp; 218 219 sprintf(sql,"select * from user where name = '%s' and passwd ='%s';",msg->name, msg->data); 220 printf("%s\n",sql); 221 222 if(sqlite3_get_table(db, sql,&resultp, &nrow, &ncloumn, &errmsg) != SQLITE_OK) 223 { 224 printf("%s\n", errmsg); 225 return -1; 226 } 227 else 228 { 229 printf("get_table ok!\n"); 230 } 231 232 //查询成功,数据库拥有此用户 233 if(nrow == 1) 234 { 235 strcpy(msg->data, "ok"); 236 send(acceptfd, msg, sizeof(MSG),0); 237 return 1; 238 } 239 else //密码或用户名错误 240 { 241 strcpy(msg->data,"usr/passwd wrong."); 242 send(acceptfd, msg, sizeof(MSG),0); 243 } 244 return 0; 245 } 246 247 int do_login_root(int acceptfd, MSG *msg, sqlite3 *db) 248 { 249 char sql[128]; 250 char *errmsg; 251 int nrow; 252 int ncloumn; 253 char **resultp; 254 255 sprintf(sql,"select * from user where name = 'root' and passwd = '%s';", msg->data); 256 printf("%s\n",sql); 257 258 if(sqlite3_get_table(db, sql,&resultp, &nrow, &ncloumn, &errmsg) != SQLITE_OK) 259 { 260 printf("%s\n", errmsg); 261 return -1; 262 } 263 else 264 { 265 printf("get_table ok!\n"); 266 } 267 268 //查询成功,数据库拥有此用户 269 if(nrow == 1) 270 { 271 strcpy(msg->data, "ok"); 272 send(acceptfd, msg, sizeof(MSG),0); 273 return 1; 274 } 275 else //密码或用户名错误 276 { 277 strcpy(msg->data,"passwd wrong."); 278 send(acceptfd, msg, sizeof(MSG),0); 279 } 280 281 return 0; 282 } 283 284 int do_query(int acceptfd, MSG *msg, sqlite3 *db) 285 { 286 char word[64]; 287 int found = 0; 288 char date[128]; 289 char sql[128]; 290 char *errmsg; 291 292 //拿出msg结构体中,要查询的单词 293 strcpy(word, msg->data); 294 295 found = do_searchword(acceptfd,msg, word); 296 297 //表找到了单词,此时应将 name/date/word 插入history.text 298 if(found == 1) 299 { 300 //获取系统时间 301 get_date(date); 302 303 //添加历史记录 304 sprintf(sql,"insert into record values('%s','%s','%s')", msg->name, date, word); 305 306 307 if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK) 308 { 309 printf("%s\n",errmsg); 310 return -1; 311 } 312 else 313 { 314 printf("Insert record done.\n"); 315 } 316 } 317 else //未找到 318 { 319 strcpy(msg->data,"Not found!"); 320 } 321 322 //将查询的结果,发送给客户端 323 send(acceptfd, msg, sizeof(MSG), 0); 324 325 return 0; 326 } 327 328 int do_history(int acceptfd, MSG *msg, sqlite3 *db) 329 { 330 char sql[128]; 331 char *errmsg; 332 333 334 sprintf(sql, "select * from record where name = '%s';",msg->name); 335 336 //查询数据库 337 if(sqlite3_exec(db, sql, history_callback, (void *)&acceptfd, &errmsg) != SQLITE_OK) 338 { 339 printf("%s\n",errmsg); 340 } 341 else 342 { 343 printf("Query record done.\n"); 344 } 345 346 //所有记录查询发送完毕,给客户端发送结束信息 347 msg->data[0] = '\0'; 348 349 send(acceptfd, msg, sizeof(MSG),0); 350 351 return 0; 352 } 353 354 int history_callback(void *arg, int f_num,char** f_value ,char** f_name) 355 { 356 //record , name , date , word 357 int acceptfd; 358 MSG msg; //声明结构体变量 359 acceptfd = *((int *)arg); 360 361 sprintf(msg.data,"%s, %s , %s",f_value[0], f_value[1], f_value[2]); //不是msg->data 362 363 send(acceptfd, &msg, sizeof(MSG), 0); 364 365 return 0; 366 } 367 368 int do_searchword(int acceptfd,MSG *msg, char word[]) 369 { 370 FILE * fp; 371 int len = 0; 372 char temp[512] = {}; 373 int result; 374 char *p; 375 376 //打开文件,读取文件,进行对比 377 if((fp = fopen("dict.txt", "r")) == NULL) 378 { 379 perror("fail to fopen.\n"); 380 strcpy(msg->data,"Failed to open dict.txt"); 381 send(acceptfd, msg, sizeof(MSG), 0); 382 return -1; 383 } 384 385 //打印出,客户端要查询的单词 386 len = strlen(word); 387 printf("%s, len = %d\n", word, len); 388 389 //读文件,来查询单词 390 while(fgets(temp,512,fp) != NULL) 391 { 392 393 // printf("temp:%s\n", temp); //打印从dict.txt获取的字符 394 result = strncmp(temp ,word, len); 395 396 if(result < 0) //eg:查询Apple,app<apple,appble<pple 397 { 398 continue; 399 } 400 if(result > 0 || ((result == 0) && (temp[len] != ' '))) 401 { 402 break; 403 } 404 405 //表示找到查询的单词 406 p = temp + len; //指针移动到单词的末尾 407 while(*p == ' ') 408 { 409 p++; 410 } 411 412 //找到了注释,跳跃过所有的空格 413 strcpy(msg->data, p); 414 printf("found word:%s\n",msg->data); 415 416 //注释拷贝完毕,关闭文件 417 fclose(fp); 418 419 return 1; 420 } 421 422 fclose(fp); 423 424 return 0; 425 } 426 427 int get_date(char *date) 428 { 429 time_t t; 430 struct tm *tp; 431 432 time(&t); 433 434 //进行时间格式转换 435 tp = localtime(&t); 436 437 sprintf(date, "%d-%d-%d %d:%d:%d",tp->tm_year+1900, tp->tm_mon+1, tp->tm_mday, 438 tp->tm_hour, tp->tm_min, tp->tm_sec); 439 440 441 return 0; 442 } 443 444 445 //查询用户的注册时间与账户及密码 446 int do_user_record(int acceptfd, MSG *msg, sqlite3 *db) 447 { 448 char sql[128]; 449 char *errmsg; 450 451 452 sprintf(sql, "select * from user where name = '%s';",msg->name); 453 454 //查询数据库 455 if(sqlite3_exec(db, sql, user_record_callback, (void *)&acceptfd, &errmsg) != SQLITE_OK) 456 { 457 printf("%s\n",errmsg); 458 } 459 else 460 { 461 printf("Query user record done.\n"); 462 } 463 464 //所有记录查询发送完毕,给客户端发送结束信息 465 msg->data[0] = '\0'; 466 467 send(acceptfd, msg, sizeof(MSG),0); 468 469 return 0; 470 } 471 472 int user_record_callback(void *arg, int f_num,char** f_value ,char** f_name) 473 { 474 //record , name , passwd , date 475 int acceptfd; 476 MSG msg; //声明结构体变量 477 acceptfd = *((int *)arg); 478 479 sprintf(msg.data,"%s, %s , %s",f_value[0], f_value[1], f_value[2]); //不是msg->data 480 481 send(acceptfd, &msg, sizeof(MSG), 0); 482 483 return 0; 484 }