失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 练习:英英词典

练习:英英词典

时间:2021-11-21 13:11:14

相关推荐

练习:英英词典

值得学习的内容:

1.网络通信TCP编程

2.多进程并发服务器

3.sqlite3轻量级数据库

4.IO多路复用编程,使用epoll族

client.c

#include "MYHEAD.h"#defineBACKLOG5//允许同时请求的客户端个数#definePORT5001//端口号#defineTRANS 4 //单词解释#defineHIST 5 //历史记录#defineLINE_INFO printf("ERR:%s,%s,%d\n",__FILE__,__FUNCTION__,__LINE__)#defineLOGIN 1 //登录#defineREGISTER 2 //注册#defineQUIT 3 //退出#defineDB_PATH"./mydictory.db"#defineDICT_PATH"./dict.txt"typedef struct MSG{int type; //消息类型char title[10];//标题char data[200];//内容}MSG;/* 单词解释 */int do_translate(int fd, MSG *msg, sqlite3 *db);/* 查询历史记录 */int do_history(int fd, MSG *msg, sqlite3 *db);/* 登录 */int do_check_login(int fd,MSG *msg,sqlite3 *db);/* 注册 */int do_register(int fd,MSG *msg,sqlite3 *db);/* 子进程 */void child_process(int fd,struct sockaddr_in client,sqlite3 *db);/* 响应函数 */void handler(int signal);/* 服务器函数 */int main(char argc,char **argv){int fd,newfd;//文件描述符pid_t pid;//进程idchar *errmsg;//sql库函数存放错误信息sqlite3 *db;//数据库char sql[250];//sql语句/* 打开数据库 */if(sqlite3_open(DB_PATH,&db)!=SQLITE_OK){LINE_INFO;}/* 创建用户ID密码表 */sprintf(sql,"create table user(id int primary key,password text)");if(sqlite3_exec(db,sql,NULL,NULL,&errmsg) != SQLITE_OK){LINE_INFO;printf("%s\n",errmsg);}/* 创建用户查询记录表 */sprintf(sql,"create table record(id int, word text, datetime text)");if(sqlite3_exec(db,sql,NULL,NULL,&errmsg) != SQLITE_OK){LINE_INFO;printf("%s\n",errmsg);}/* 绑定:信号--响应函数。子进程退出时,内核会发送SIGCHLD通知父进程 */signal(SIGCHLD,handler);/* 创建socket */if((fd=socket(AF_INET,SOCK_STREAM,0))<0){perror("socket");exit(-1);}/* 允许绑定地址快速重用 */int b_reuse = 1;setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int));struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port=htons(PORT);//字节序转换addr.sin_addr.s_addr = htonl(INADDR_ANY);//(INADDR_ANY)表示本机任意网卡地址/* 绑定服务器的ip,port给刚创建的socket */if(bind(fd,(struct sockaddr *)&addr,sizeof(addr)) < 0){perror("bind");exit(-1);}/* 监听,socket变成被动的 */if(listen(fd,BACKLOG) < 0)//BACKLOG=同时请求的客户端个数{perror("listen");exit(-1);}socklen_t client_len;//容器struct sockaddr_in client;//容器,存放客户端的地址信息printf("lstening...\n");while(1){/* 阻塞等待建立链接accept,fd属于监听类socket(被动),newfd属于连接类socket(主动) */if((newfd = accept(fd,(struct sockaddr *)&client,&client_len))<0){if(errno != EINTR){//accept属于阻塞函数,要考虑被中断信号打断的情况。perror("accept");exit(-1);}}printf("newfd:%d,fd:%d\n",newfd,fd);//每个进程都有一个独立的表,因此每个进程打印出来的fd数值可以相同if((pid=fork())<0){perror("fork");exit(-1);}if(0 == pid){//子进程close(fd);//关闭监听socket,对子进程来说不需要监听child_process(newfd,client,db);}else{//父进程,只负责acceptclose(newfd);//关闭连接socket,对父进程来说不要连接}}close(fd);//父进程关闭监听socketreturn 0;}/* 子进程函数 */void child_process(int fd,struct sockaddr_in client,sqlite3 *db){MSG msg;int n;char ip[16];char port[6];while(1){if((n=read(fd,(void *)&msg,sizeof(MSG)))<0){//阻塞等待if(n!=EINTR)//阻塞可能会被中断信号打断,并返回-1{//任何长等待机制都要考虑被中断的处理方式,用来区分异常错误perror("read");exit(-1);}}inet_ntop(AF_INET,&client.sin_addr.s_addr,ip,16);inet_ntop(AF_INET,&client.sin_port,port,6);if(0==n){printf("quit!\n%s:%s:\n",ip,port);close(fd);exit(0);}if(n>0){/* 收到客户端的用户登录信息 */switch(msg.type){case LOGIN:do_check_login(fd,&msg,db);break;case TRANS:do_translate(fd,&msg,db);break;case HIST:do_history(fd,&msg,db);break;case REGISTER:do_register(fd,&msg,db);break;}}}}/* 注册 */int do_register(int fd,MSG *msg,sqlite3 *db){char sql[250];char *errmsg,**resultp;int nrow,ncolumn;sprintf(sql,"insert into user values(%s,'%s')",msg->title,msg->data);/* 插入记录 */if(sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=SQLITE_OK){LINE_INFO;printf("%s\n",errmsg);strcpy(msg->data,errmsg);}else strcpy(msg->data,"ok");if(send(fd,(void *)msg,sizeof(MSG),0)<0){LINE_INFO;perror("send");return -1;}return 0;}/* 登录 */int do_check_login(int fd,MSG *msg,sqlite3 *db){char sql[250];char *errmsg,**resultp;int nrow,ncolumn;int ret;sprintf(sql,"select * from user where id=%s",msg->title);/* 先查询id号是否存在 */if(sqlite3_get_table(db,sql,&resultp,&nrow,&ncolumn,&errmsg)!=SQLITE_OK){LINE_INFO;printf("%s\n",errmsg);strcpy(msg->data,errmsg);}/* 表格内容实际以一维数组存放,包括column名,从左到右,从上到下 *//* nrow=行数,ncolumn=列数。如果ncolumn=0,说明数组内没有内容 */else if(0==ncolumn){/* id不存在 */ret =0 ;strcpy(msg->data,"id don't exist!\n");}else{/* 密码正确 */if(0==strcmp(msg->data,resultp[ncolumn*2-1])){ret = 1;strcpy(msg->data,"ok");}/* 密码错误 */else{ret = 0;strcpy(msg->data,"password error!\n");}}/* 返回信息 */if(send(fd,(void *)msg,sizeof(MSG),0)<0){LINE_INFO;perror("send");return -1;}printf("send!\n");return ret;}/* 单词解释 */int do_translate(int fd, MSG *msg, sqlite3 *db){char sql[250];//存放sql语句char buf[512];//存放从字典取出来的字符串char word[20];//存放用户查询的单词char *errmsg;FILE *dict;int ret,length;if((dict = fopen(DICT_PATH, "r+")) == NULL){LINE_INFO;perror("fopen");return -1;//打开字典失败}printf("recv:%d:%s:%s\n",msg->type, msg->title, msg->data);strcpy(word,msg->data);length = strlen(word);//单词长度/* 循环fgets,直到文件末尾 */while(!feof(dict)){/* 字典在txt文件中以行为单位,并且按照字母a-z排序 */if(fgets(buf, 512, dict) == NULL){LINE_INFO;perror("fgets");}/* 用data的字串减去buf的字串 */ret = strncmp(msg->data, buf, length);//匹配printf("ret=%d,length=%d,word=%s\n",ret,length,word);printf("%s\n",buf);if(ret > 0){/* 继续跟下一行比较,dog 与 a */continue;}else if(ret < 0){printf("/* 按照字母表顺序,往下已经没有能够匹配的了 */\n");strcpy(msg->data, "not found!");break;}else if(buf[length] != ' '){printf("/* 考虑 a与abc的情况。前面匹配,后面却不是空格 */\n");strcpy(msg->data, "not found!");break;}/* 最后剩下匹配的情况 */while(buf[length] == ' ')length++;//跳过空格strcpy(word,msg->data);strcpy(msg->data, &buf[length]);/* 记录保存到数据库内 */time_t ti = time(NULL);struct tm* tm = localtime(&ti);sprintf(buf, "%d-%d-%d,%d:%d:%d", \tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, \tm->tm_hour, tm->tm_min, tm->tm_sec);sprintf(sql,"insert into record values(%s,'%s','%s')",msg->title,word,buf);if(sqlite3_exec(db,sql,NULL,NULL,&errmsg) != SQLITE_OK){LINE_INFO;printf("%s\n",errmsg);}break;}fclose(dict);if(send(fd,(void *)msg,sizeof(MSG),0)<0){LINE_INFO;perror("send");return -1;}return 0;}/* 查询历史记录 */int do_history(int fd, MSG *msg, sqlite3 *db){char sql[250];//存放sql语句char *errmsg, **resultp;int nrow, ncolumn, r; msg->data[0] = '\0';//写入空字符,代表字串长度=0sprintf(sql,"select * from record where id=%s",msg->title);if(sqlite3_get_table(db,sql,&resultp,&nrow,&ncolumn,&errmsg) < 0){LINE_INFO;printf("%s",errmsg);return -1;}printf("recv:%d:%s:%s",msg->type, msg->title, msg->data);/* 如果查不到记录,nrow=0,column=0 */if(0 == nrow){strcpy(msg->data, "not found record!\n");if(send(fd,msg,sizeof(MSG),0)<0){LINE_INFO;perror("send");return -1;}/* 查不到记录 */return 1;}printf("/* 如果查到记录,nrow >= 2 column >= 1 */\n");/* 总共nrow行,第1行是column名,查询历史记录只打印最后2条即可 */if(nrow<3) r=1;//打印1条记录else r=2;//打印2条记录while(r--){/* 打印倒数1或2条记录 */sprintf(sql,"id=%s,word='%s',time:%s\n", \resultp[(nrow-r)*ncolumn],resultp[(nrow-r)*ncolumn+1],resultp[(nrow-r)*ncolumn+2]);strcat(msg->data,sql);}if(send(fd,msg,sizeof(MSG),0)<0){LINE_INFO;perror("send");return -1;}return 1;}/* 信号响应函数 */void handler(int signal){if(SIGCHLD == signal){waitpid(-1,NULL,WNOHANG);//WNOHANG代表非阻塞。}}

server.c

#include "MYHEAD.h"//使用示例:./client 192.168.1.100 5001#defineTRANS4//单词解释#defineHIST5//历史记录#defineLINE_INFOprintf("%s,%s,%d\n",__FILE__,__FUNCTION__,__LINE__)#defineLOGIN1//登录#defineREGISTER2//注册#defineQUIT3//退出typedef struct MSG{int type;//消息类型char title[10];//标题char data[255];//内容}MSG;/* 单词解释 */int do_translate(int sockfd, MSG *msg);/* 历史记录 */int do_history(int sockfd, MSG *msg);/* 注册模块 */int do_register(int sockfd, MSG *msg);/* 打印主菜单 */int mainmenu(void);/* 打印用户菜单 */int usermenu(void);/* 登录 */int do_login(int sockfd, MSG *msg);/******************************************************************//* 客户端主程序 */int main(char argc,char **argv){/* 判断输入参数个数 */if(argc<3){printf("Usage:%s <ip> <port>\n",argv[0]);exit(-1);}MSG msg;int fd, cmd;/* 创建socket */if((fd=socket(AF_INET,SOCK_STREAM,0))<0){perror("socket");exit(-1);}uint32_t ip;//容器/* 192.xxx.xxx.xxx字符串形式,转换,32位网络字节序 */if(inet_pton(AF_INET,argv[1],(void *)&ip) != 1){perror("inet_pton");exit(-1);}struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_addr.s_addr = ip;addr.sin_port = htons(atoi(argv[2]));//网络字节序/* 链接connect */if(connect(fd,(struct sockaddr *)&addr,sizeof(addr))<0){perror("connect");exit(-1);}before_login:while(1){mainmenu();//打印菜单printf("input:");/* 阻塞等待输入 */cmd=0;scanf("%d",&cmd);getchar();//过滤回车键switch(cmd){case LOGIN://登录if(do_login(fd, &msg)>0)/* 登录成功,跳转 */goto after_login;break;case REGISTER://注册do_register(fd, &msg);break;case QUIT:/* 退出 */close(fd);//关闭sockfdprintf("quit sucessed!\n");exit(0);break;default:printf("undefined cmd!\n");}}/* 用户登录成功后 */after_login:while(1){usermenu();printf("input:");cmd=0;scanf("%d",&cmd);getchar();switch(cmd){case TRANS:/* 单词解释 */do_translate(fd, &msg);break;case HIST:/* 历史记录 */do_history(fd, &msg);break;case QUIT:/* 返回上级菜单 */goto before_login;break;default:printf("undefined cmd!\n");}}return 0;}/* 打印主菜单 */int mainmenu(void){printf("**************************************************\n");printf("\t\t主菜单\n");printf("1:登录\t2:注册\t3:退出\n");printf("**************************************************\n");return 0;}/* 打印用户菜单 */int usermenu(void){printf("**************************************************\n");printf("\t\t用户菜单\n");printf("4:单词解释\t5:历史记录\t3:退出\n");printf("**************************************************\n");return 0;}/* 登录模块 */int do_login(int sockfd,MSG *msg){pid_t pid;char buf[100];msg->type=LOGIN;//登录类型printf("input id:");scanf("%s",msg->title);getchar();printf("input password:");scanf("%s",msg->data);getchar();printf("wait for login...\n");/* IO多路复用,同时监听socket与stdin */int ret;int epfd=epoll_create(2);struct epoll_event event;//注册结构体struct epoll_event ready[2];//就绪结构体event.events=EPOLLIN;//监听可读信号event.data.fd=sockfd;//socket的fd/* 注册sockfd */if(epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&event)<0){LINE_INFO;perror("epoll_ctl");}event.data.fd=0;/* 注册stdin */if(epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event)<0){LINE_INFO;perror("epoll_ctl");}/* 发送数据给服务器 */if(send(sockfd,(void *)msg,sizeof(MSG),0)<0){LINE_INFO;//输出位置信息perror("send");//输出errno信息close(epfd);return -1;}printf("/* 开始监听 */\n");int n;while((ret=epoll_wait(epfd,ready,2,-1))>0){/* ret存放的是fd个数,需要配合遍历匹配 */while(ret--){if((0 == ready[ret].data.fd) && (EPOLLIN & ready[ret].events)){printf("/* 标准输入fd有可读信号 */\n");if(read(0,buf,100)<0){LINE_INFO;perror("read");}/* 比较1个字符 */if(strncmp("#",buf,1)==0){close(epfd);return 0;//退出login模块}}if((sockfd == ready[ret].data.fd) && (EPOLLIN & ready[ret].events)){printf("/* sockfd有可读信号 */\n");if((n=read(sockfd,(void *)msg,sizeof(MSG))>0)){printf("recv:%s\n",msg->data);if(0==strcmp("ok",msg->data)){/* 登录成功 */printf("login successed!\n");printf("recv->type:%d,title:%s,data:%s\n",\msg->type,msg->title,msg->data);close(epfd);return 1;//退出login模块}close(epfd);return -1;//退出login模块}}}}/* 登录失败 */LINE_INFO;perror("epoll_wait");close(epfd);return 0;}/* 注册模块 */int do_register(int sockfd, MSG *msg){pid_t pid;char buf[100];msg->type=REGISTER;//注册类型printf("input id:");scanf("%s",msg->title);getchar();printf("input password:");scanf("%s",msg->data);getchar();printf("wait for register...\n");/* IO多路复用,同时监听socket与stdin */int ret;int epfd=epoll_create(2);struct epoll_event event;//注册结构体struct epoll_event ready[2];//就绪结构体event.events=EPOLLIN;//监听可读信号event.data.fd=sockfd;//socket的fd/* 注册sockfd */if(epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&event)<0){LINE_INFO;perror("epoll_ctl");}event.data.fd=0;/* 注册stdin */if(epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event)<0){LINE_INFO;perror("epoll_ctl");}/* 发送数据给服务器 */if(send(sockfd,(void *)msg,sizeof(MSG),0)<0){LINE_INFO;//输出位置信息perror("send");//输出errno信息close(epfd);return -1;}printf("/* 开始监听 */\n");int n;while((ret=epoll_wait(epfd,ready,2,-1))>0){/* ret存放的是fd个数,需要配合遍历匹配 */while(ret--){if((0 == ready[ret].data.fd) && (EPOLLIN & ready[ret].events)){printf("/* 标准输入fd有可读信号 */\n");if((n=read(0,buf,100))<=2){/* 防止用户可能不止输入了1个字符 *//* buf[0]='#' buf[1]=回车 */if('#'==buf[0]){/* login过程中,用户想要返回主菜单 */close(epfd);//关闭io句柄return 0;//中止}printf("undefined cmd!\n");//输如了#以外的命令}}if((sockfd == ready[ret].data.fd) && (EPOLLIN & ready[ret].events)){printf("/* sockfd有可读信号 */\n");if((n=read(sockfd,(void *)msg,sizeof(MSG))>0)){printf("recv:%s\n",msg->data);if(0==strcmp("ok",msg->data)){/* 注册成功 */printf("register successed!\n");close(epfd);return 1;}close(epfd);return -1;}}}}/* 注册失败 */LINE_INFO;perror("epoll_wait");return 0;}/* 单词解释*/int do_translate(int sockfd, MSG *msg){char buf[100];int ret;//存放返回值int epfd=epoll_create(2);//监听集合struct epoll_event event;//注册结构体struct epoll_event ready[2];//就绪结构体/* 读取用户输入word */msg->type = TRANS;//单词解释printf("input word:");scanf("%s",msg->data);getchar();/* IO多路复用,同时监听socket与stdin */event.events=EPOLLIN;//监听可读信号event.data.fd=sockfd;//socket/* 注册sockfd */if(epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&event)<0){LINE_INFO;perror("epoll_ctl");}event.data.fd=0;/* 注册stdin */if(epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event)<0){LINE_INFO;perror("epoll_ctl");}/* 发送数据给服务器 */if(send(sockfd,(void *)msg,sizeof(MSG),0)<0){LINE_INFO;//输出位置信息perror("send");//输出errno信息close(epfd);return -1;}printf("send->type:%d,title:%s,data:%s\n",msg->type,msg->title,msg->data);printf("/* 开始监听 */\n");while((ret=epoll_wait(epfd,ready,2,-1))>0){/* ret存放的是fd个数,需要配合遍历匹配 */while(ret--){if((0 == ready[ret].data.fd) && (EPOLLIN & ready[ret].events)){printf("/* 标准输入fd有可读信号 */\n");if(read(0, buf, 100)<0){LINE_INFO;perror("read");}/* 识别用户的输入 */if(0 == strncmp("#",buf,1)){close(epfd);//关闭监听集合return 0;//退出单词解释模块}}if((sockfd == ready[ret].data.fd) && (EPOLLIN & ready[ret].events)){printf("/* sockfd有可读信号 */\n");if(read(sockfd,(void *)msg,sizeof(MSG))>0){printf("%s\n",msg->data);close(epfd);return 1;//收到单词解释,退出}}}}close(epfd);return 0;}/* 历史记录 */int do_history(int sockfd, MSG *msg){char buf[100];int ret;//存放返回值int epfd;//监听集合struct epoll_event event;//注册结构体struct epoll_event ready[2];//就绪结构体msg->type = HIST;//查询历史记录if((epfd = epoll_create(2))<0){LINE_INFO;perror("epoll_create\n");}/* IO多路复用,同时监听socket与stdin */event.events=EPOLLIN;//监听可读信号event.data.fd=sockfd;//socket/* 注册sockfd */if(epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&event)<0){LINE_INFO;perror("epoll_ctl");}event.data.fd=0;/* 注册stdin */if(epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event)<0){LINE_INFO;perror("epoll_ctl");}/* 发送数据给服务器 */if(send(sockfd,(void *)msg,sizeof(MSG),0)<0){LINE_INFO;//输出位置信息perror("send");//输出errno信息close(epfd);return -1;}printf("/* 开始监听 */\n");while((ret=epoll_wait(epfd,ready,2,-1))>0){/* ret存放的是fd个数,需要配合遍历匹配 */while(ret--){if((0 == ready[ret].data.fd) && (EPOLLIN & ready[ret].events)){printf("/* 标准输入fd有可读信号 */\n");if(read(0, buf, 100)<0){LINE_INFO;perror("read");}/* 识别用户的输入 */if(0 == strncmp("#",buf,1)){close(epfd);//关闭监听集合return 0;//退出模块}}if((sockfd == ready[ret].data.fd) && (EPOLLIN & ready[ret].events)){printf("/* sockfd有可读信号 */\n");if(read(sockfd,(void *)msg,sizeof(MSG))>0){printf("%s\n",msg->data);close(epfd);return 1;//收到回复,退出}}}}close(epfd);return 0;}

如果觉得《练习:英英词典》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。