Linux系统开发 | 在线词典
做了一个小级别的实验项目,对之前所学过的知识有个综合性的总结。在线词典实验涉及有,Linux C语言的开发、网络套接字编程、多进程开发、sqlite3数据库接口调用、文件读写等操作,make编译及Makefile文件的编写。。
项目介绍
项目总体划分为客户端、和服务端两个部分。客户端(也是APP)一方面为使用的用户提供简单的注册、登录、查询等操作,另一方面负责与服务端进行TCP通信,向服务器发送请求。而真正提供服务的是服务端进程,它不仅能和数据库进行交互,而且要接收用户的请求,把服务提供给用户。
用户通过客户端程序访问部署在服务端的词典,具体可以实现的功能有:
- 注册用户/用户登录
- 用户查询词典(查询英文单词的解释信息)
- 用户查询自己的历史查询记录
具体设计流程
运行客户端,首先出现一个主菜单,用户输入‘1’为注册,‘2‘为登录,‘3’为退出。当用户输入‘2’,登录成功后,又会进入二级服务菜单。这时用户输入‘1’为查询单词,‘2‘为查询历史,‘3’为退出。
以上是客户端应用的实现,底层调用的网络接口免不了要跟服务端交互。至于请求的类型和数据,需要我们自己根据需求来设计,与服务端统一保持一致即可。
服务端,主要负责接收客户端的请求信息,另一方面与数据库(调用相关函数创建)进行交互。由于客户端不只一个,我们用多进程的方法来处理多个客户端的请求;分析不同的请求信息,比如查询历史,服务端接收到这一命令后,就向数据库的历史记录表查询相关信息,再将结果处理,再返回到客户端。
虽然可以将词典的内容做成一个数据表,载入数据库,这样查询起来也十分方便,但是太过麻烦。所以我们将单词与相关解释信息放到一个文件中,查询单词时,到这个文件中匹配信息即可。
代码实现(代码量较大,只展示部分)
共用头文件 common.h
#ifndef __COMMON_H__
#define __COMMON_H__#include
#include
#include /* See NOTES */
#include
#include
#include
#include
#include
#include
#include
#include #define N 32
typedef struct {int type;char name[N];char data[256];
}MSG;#define R 1 // user - register
#define L 2 // user - login
#define Q 3 // user - query
#define H 4 // user - history#define DATABASE "my.db" //创建的数据库#define SERADDR "127.0.0.1" //这里笔者偷个懒,使用网络回送地址,就不进行远程通信了
#define SERPORT 5001 //服务端占用 5001 这个端口#endif
客户端文件 client.c
#include "common.h"int do_register(int sockfd, MSG *msg); //注册用户
int do_login(int sockfd, MSG *msg); //用户登录
int do_query(int sockfd, MSG *msg); //查询单词
int do_history(int sockfd, MSG *msg); //查询历史
int SubMenu(int sockfd); //二级子菜单int main(int argc, const char *argv[])
{int sockfd;struct sockaddr_in serveraddr;int n;MSG msg;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(SERADDR);serveraddr.sin_port = htons(SERPORT);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(">");scanf("%d", &n);getchar();switch(n){case 1:do_register(sockfd, &msg);break;case 2:if(do_login(sockfd, &msg) == 1){SubMenu(sockfd); }break;case 3:close(sockfd);exit(0);break;default:printf("Invalid data cmd.\n");}}return 0;
}int SubMenu(int sockfd)
{int n;MSG msg;while(1){printf("-----------------------------------------------------\n");printf("- 1.query_word 2.history_record 3.quit -\n");printf("-----------------------------------------------------\n");printf(">");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;
}///
//省略一堆底层函数实现
//
服务端文件 server.c
#include "common.h"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);//获取时间int main(int argc, const char *argv[])
{int sockfd;struct sockaddr_in serveraddr;int acceptfd;sqlite3 *db;pid_t pid;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(SERADDR);serveraddr.sin_port = htons(SERPORT);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) {//son processclose(sockfd);do_client(acceptfd, db);}else {//father processclose(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;
}///
//省略一堆底层函数实现
//
实验结果展示
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
