基于 TCP协议和sqlite3数据库的网络电子词典(个人项目)
一、开发环境:Ubuntu 16.04
二、项目描述:
基于TCP协议的并发服务器设计,采用sql数据库进行数据存储,文件保存历史查询数据,能满足多用户同时登陆,实现用户的注册、登录以及退出,登录成功后即可使用单词查询和查询历史记录功能。
三、代码实现思路:
- 使用sqlite3数据库,创建用户信息表和单词表。
- 服务器采用基于TCP协议的多进程技术,可以满足多用户同时登录。
- 服务器接收客户端的注册请求,将用户名和密码存入数据库的注册表中。
- 服务器接收客户端的登录请求,将登录信息与注册表中的信息进行比对,反馈对比信息。
- 客户端登录成功后,开启查询单词和历史记录功能。
- 服务器接收客户端的查询单词后,对比数据库中的单词表,反馈给客户端相应的信息。并用文件IO接收从服务器发来的单词及其释义。
- 若客服端发送查询历史记录请求后,打开历史记录文件描述符,打印出该用户查询过的所有历史记录。
四、运用技术点:
- 采用文件IO对历史数据进行读写操作。
- 采用TCP协议的并发服务器设计。
- SQLite3数据库。
五、背景
任何一种词语,使用频率高了,就会成为公共词汇。英语作为一种国际通用
性语言,部分单词已成为公共词汇,并且直接应用到汉语中来。因其表达简洁,
准确而受到人们青睐。但是,这些新引入或者比较专业词汇,对于较少关注新闻,
接触网络的群体而言,就会造成阅读困难。传统的纸质英汉词典因为更新周期久,
携带不便,价格相对昂贵而不能广泛应用。方便,高效,快捷的电子词典才能满
足现代人的需要。
参考资料
名称 |
作者 |
出版社 |
Unix网络编程 |
||
TCP/IP协议详解 |
||
Unix高级环境编程 |
六、系统概述
我们的这套系统采用的是tcp协议的并发服务器设计,可以满足多用户同时登录,用户登录后可以查询单词及历史记录,对于数据的存储我们采用的是sql数据库技术,查找快速,保密性好!
>具体功能能如下:
<1>主界面
(1) 用户登录
(2) 用户注册
(3) 用户退出
<2>登陆成功后界面
(1) 查询单词
(2) 查询历史记录
(3) 用户退出
<3>详细设计
消息类型设计
宏名设计 |
说明 |
USER_REGISTER |
用户注册 |
USER_LOGIN |
用户登陆 |
USER_WORD |
用户查询单词 |
USER_SUCCESS |
登陆成功 |
USER_FALIURE |
登陆失败 |
4.2结构体的设计
注:__attribute__((__packed__))表示取消结构体对齐
typedef struct
{
char _username[25]; //用户名
char _password[25]; //密码
} __attribute__((__packed__))user_t;
typedef struct
{
int type;
int size;
union
{
user_t uinfo; //用户信息
char _word[100];
} content;
//客户端填词,服务端填写单词解释
#define word content._word
#define username content.uinfo._username
#define password content.uinfo._passwd
}__attribute__((__packed__))mhead_t;
#define EXEC_SQL(db,sql,errmsg) do{\
if(sqlite3_exec(db,sql,NULL,NULL,&merrmsg) < 0)\
{\
fprintf(stderr,“sqlite exec [%s]error : %s\n”,sql,errmsg);\
exit(EXIT_FAILURE);
}\
}while(0);
七、服务器
#include "head.h" int do_register(int sockfd,sqlite3 *pdb,char *_username,char *_password)
{char *errmsg;char buf[1024];char **dbresult;int nrow = 0,ncolumn = 0;char sql[1024] = {0};mhead_t *head = (mhead_t *)buf; sprintf(sql,"select * from user_table where NAME='%s';",_username);if(sqlite3_get_table(pdb,sql,&dbresult,&nrow,&ncolumn,&errmsg) != 0){fprintf(stderr,"sqlite3 get table error : %s.\n",errmsg);exit(EXIT_FAILURE);}//没有这样的用户名if(nrow == 0){//录入数据库bzero(sql,sizeof(sql));sprintf(sql,"insert into user_table values('%s','%s');",_username,_password);EXEC_SQL(pdb,sql,errmsg);printf("ok ........\n");head->type = USER_SUCCESS;if(send(sockfd,buf,sizeof(mhead_t),0) < 0){perror("Fail to send");exit(EXIT_FAILURE);}//注册失败,用户名存在}else{head->type = USER_FAILURE;if(send(sockfd,buf,sizeof(mhead_t),0) < 0){perror("Fail to send");exit(EXIT_FAILURE);}//表示未知 printf("???????\n");}//插入到数据库之后,释放dbresult结果sqlite3_free_table(dbresult);return 0;
}int do_login(int sockfd,sqlite3 *pdb,char *_username,char *_password)
{int i,j,index = 0,ret;char *errmsg;char buf[1024];char **dbresult;int nrow = 0,ncolumn = 0;char sql[1024] = {0};mhead_t *head = (mhead_t *)buf; sprintf(sql,"select * from user_table where NAME='%s';",_username);if(sqlite3_get_table(pdb,sql,&dbresult,&nrow,&ncolumn,&errmsg) != 0){fprintf(stderr,"sqlite3 get table error : %s.\n",errmsg);exit(EXIT_FAILURE);}//有这样的用户名if(nrow > 0){bzero(sql,sizeof(sql));EXEC_SQL(pdb,sql,errmsg);printf("ok........\n");for(i = 0;i <= nrow;i++){for(j = 0;j < ncolumn;j++){if(strcmp(dbresult[index],_password) == 0){head->type = USER_SUCCESS;if(send(sockfd,buf,sizeof(mhead_t),0) < 0){perror("Fail to send");exit(EXIT_FAILURE);}i = nrow + 1;break;}index++;}}}else if(nrow == 0){head->type = USER_FAILURE;if(send(sockfd,buf,sizeof(mhead_t),0) < 0){perror("Fail to send");exit(EXIT_FAILURE);}//表示未知 printf("???????\n");}//插入到数据库之后,释放dbresult结果sqlite3_free_table(dbresult);return 0;
}//获取系统时间
int get_data()
{time_t t;time(&t);//获得从1970年1月1日开始到现在有多少秒printf("current time:%s\n",ctime(&t));//获得当前的时间return 0;
}int do_query(int sockfd,sqlite3 *pdb,char *_word)
{int ret;char *errmsg;char buf[1024] = {0};char **dbresult;char sql[1024] = {0};int nrow = 0,ncolumn = 0;mhead_t *head = (mhead_t *)buf;int i = 0,j = 0,index = 0;sprintf(sql,"select * from dict_table where word = '%s';",_word);printf("%s\n",_word);if(sqlite3_get_table(pdb,sql,&dbresult,&nrow,&ncolumn,&errmsg) != 0){fprintf(stderr,"sqlite3 get table error : %s.\n",errmsg);exit(EXIT_FAILURE);}if(nrow > 0){ printf("ok........\n");for(i = 0;i <= nrow;i++){for(j = 0;j < ncolumn;j++){if(strcmp(dbresult[index],_word) == 0){puts(dbresult[index+1]);sprintf(head->word,"%s:%s\n",_word,dbresult[index + 1]);i=nrow+1;break;}index++;}} head->type = USER_SUCCESS;printf("%s\n",head->word);if(send(sockfd,buf,sizeof(mhead_t),0) < 0){perror("Fail to send");exit(EXIT_FAILURE);}}else if(nrow == 0){head->type = USER_FAILURE;if(send(sockfd,buf,sizeof(mhead_t),0) < 0){perror("Fail to send");exit(EXIT_FAILURE);}else//表示未知 printf("???????\n");}sqlite3_free_table(dbresult);return 0;
}int do_client(int sockfd,sqlite3 *pdb)
{int n;int count = 0;char buf[1024];mhead_t *head = (mhead_t *)buf; while(1){count = 0;//接收协议头while(1){n = recv(sockfd,buf + count,sizeof(mhead_t) - count,0);if(n <= 0){exit(EXIT_FAILURE);}count += n;printf("count : %d mhead_t : %d\n",count,sizeof(mhead_t));if(count == sizeof(mhead_t))break;}switch(head->type){case USER_REGISTER:do_register(sockfd,pdb,head->username,head->password); break;case USER_LOGIN:do_login(sockfd,pdb,head->username,head->password);break;case USER_WORD:do_query(sockfd,pdb,head->word);break;defalut:exit(EXIT_SUCCESS);} }return 0;
}
八、客户端
#include "head.h"
//用户提示界面1
void help_info1()
{printf("\t-----------------------------------------------\n");printf("\t| 在线辞典 |\n");printf("\t|版本:0.0.1 |\n");printf("\t|作者:XXX |\n");printf("\t|功能: |\n");printf("\t| [1] 登录 |\n");printf("\t| [2] 注册 |\n");printf("\t| [3] 退出 |\n");printf("\t|注意:用户只有登录成功后才能进入查单词界面 |\n");printf("\t------------------------------------------------\n");return;
}
void help_info2()
{printf("\t-----------------------------------------------\n");printf("\t| 欢迎进入查询界面 |\n");printf("\t|版本:0.0.1 |\n");printf("\t|作者:XXX |\n");printf("\t|功能: |\n");printf("\t| [1] 查询单词 |\n");printf("\t| [2] 查询历史 |\n");printf("\t| [3] 退出 |\n");printf("\t------------------------------------------------\n");return;
}//用户输入指令,供大家选择
enum{LOGIN = 1, //登陆REGISTER = 2, //注册QUIT = 3, //退出QUERY = 1, //查询单词HISTORY = 2, //查询历史
};int init_tcp(char *ip,char *port)
{int sockfd;struct sockaddr_in server_addr;if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0){perror("Fail to socket"); exit(EXIT_FAILURE);}bzero(&server_addr,sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(atoi(port));server_addr.sin_addr.s_addr = inet_addr(ip);if(connect(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0){perror("Fail to bind"); exit(EXIT_FAILURE);}return sockfd;
}int do_register(int sockfd)
{int n = 0;int count = 0;char buf[1024] = {0};//定义发送的协议头mhead_t *head = (mhead_t *)buf;printf("\n您正在注册,请输入用户名和密码\n");head->type = USER_REGISTER;head->size = sizeof(mhead_t);printf("Input username : ");fgets(head->username,sizeof(head->username),stdin);head->username[strlen(head->username) - 1] = '\0';printf("Input password : ");fgets(head->password,sizeof(head->password),stdin);head->password[strlen(head->password) - 1] = '\0';//发给服务器端if(send(sockfd,buf,sizeof(mhead_t),0) < 0){perror("Fail to send");exit(EXIT_FAILURE);}bzero(&buf,sizeof(buf));while(1){//接收数据,TCP是可靠的连接,若是数据//未完全接收的话,可以在接收n = recv(sockfd,buf + count,sizeof(mhead_t) - count,0);if(n <= 0){perror("Fail to send");exit(EXIT_FAILURE);}//若是数据未发送完成,再次接收的时候可补充count += n;if(count == sizeof(mhead_t))break;}if(head->type == USER_SUCCESS){printf("\n恭喜您,注册成功!\n"); return 0;}else{ sprintf(buf,"%s\n",head->word);write(fd,buf,strlen(buf));printf("\n很遗憾,这个用户名已经被其它用户注册过了,请重新注册"); return -1;}
}int do_query(int sockfd)
{time_t t;time(&t);//获得从1970年1月1日开始到现在有多少秒int fd;int n = 0,count = 0;char buf[1024] = {0};char time[1024] = {0};mhead_t *head = (mhead_t *)buf;head->type = USER_WORD;head->size = sizeof(mhead_t);fd = open("history.txt",O_RDWR | O_CREAT | O_APPEND,0666);printf("\n您正在查询单词!\n");printf("请输入你想要查询的单词:");fgets(head->word,sizeof(head->word),stdin);head->word[strlen(head->word) - 1] = '\0';if(send(sockfd,buf,sizeof(mhead_t),0) < 0){perror("Fail to send");exit(EXIT_FAILURE);}bzero(&buf,sizeof(buf));while(1){//接收数据,TCP是可靠的连接,若是数据//未完全接收的话,可以在接收n = recv(sockfd,buf + count,sizeof(mhead_t) - count,0);if(n <= 0){perror("Fail to send");exit(EXIT_FAILURE);}//若是数据未发送完成,再次接收的时候可补充count += n;if(count == sizeof(mhead_t))break;}if(head->type == USER_SUCCESS){printf("%s\n",head->word); sprintf(time,"current time:%s\n%s\n",ctime(&t),head->word);//获得当前时间write(fd,time,strlen(time));printf("%s\n",time);return 0;}else{printf("\nno such word!\n");}close(fd);return 0;
}int do_history(int sockfd)
{int n = 0;char buf[1024] = {0};int fd;fd = open("history.txt",O_RDONLY);while(1){memset(buf,0,sizeof(buf));n = read(fd,buf,sizeof(buf));if(0 == n)break;printf("%s",buf);}close(fd);return 0;
}
int do_task2(int sockfd)
{int cmd;while(1){//提示界面帮助,用户选择help_info2(); printf("\n\n请选择>");scanf("%d",&cmd);//吃掉回车键getchar();switch(cmd){//查询单词函数case QUERY:if(do_query(sockfd) < 0)continue;break;//查询历史函数case HISTORY:if(do_history(sockfd) < 0)continue;break;case QUIT:exit(EXIT_SUCCESS);default:printf("Unknow cmd.\n");continue;}}return 0;
}int do_login(int sockfd)
{int n = 0;int count = 0;char buf[1024] = {0};//定义发送的协议头mhead_t *head = (mhead_t *)buf;printf("\n您正在登录,请输入用户名和密码\n");head->type = USER_LOGIN;head->size = sizeof(mhead_t);printf("Input username : ");fgets(head->username,sizeof(head->username),stdin);head->username[strlen(head->username) - 1] = '\0';printf("Input password : ");fgets(head->password,sizeof(head->password),stdin);head->password[strlen(head->password) - 1] = '\0';//发给服务器端if(send(sockfd,buf,sizeof(mhead_t),0) < 0){perror("Fail to send");exit(EXIT_FAILURE);}bzero(&buf,sizeof(buf));while(1){//接收数据,TCP是可靠的连接,若是数据//未完全接收的话,可以在接收n = recv(sockfd,buf + count,sizeof(mhead_t) - count,0);if(n <= 0){perror("Fail to send");exit(EXIT_FAILURE);}//若是数据未发送完成,再次接收的时候可补充count += n;if(count == sizeof(mhead_t))break;}if(head->type == USER_SUCCESS){printf("\n恭喜您,登录成功!\n"); return 0;}else{printf("\n登陆用户信息错误,请重试!\n"); return -1;}
}
int do_task(int sockfd)
{int cmd;while(1){//提示界面帮助,用户选择help_info1(); printf("\n\n请选择>");scanf("%d",&cmd);//吃掉回车键getchar();switch(cmd){//用户登陆case LOGIN:if(do_login(sockfd) < 0)continue;do_task2(sockfd);break;//用户注册,我们先来写注册的函数case REGISTER:if(do_register(sockfd) < 0)continue;do_task2(sockfd);break;case QUIT:exit(EXIT_SUCCESS);default:printf("Unknow cmd.\n");continue;}}return 0;
}//./client ip port
//由于后面要传递参数,故这里的const省略
int main(int argc, char *argv[])
{int sockfd; int addr_len = sizeof(struct sockaddr);struct sockaddr_in peer_addr;if(argc < 3){fprintf(stderr,"Usage : %s argv[1] argv[2]\n",argv[0]); exit(EXIT_FAILURE);}sockfd = init_tcp(argv[1],argv[2]);do_task(sockfd);return 0;
}
九、运行结果
1、先运行服务器
2、再运行客户端
3、登陆
4、 查询单词
5、查询历史记录
6、退出
7、注册
基于 TCP协议和sqlite3数据库的网络电子词典(个人项目)相关推荐
- tcp协议和udp协议区别_TCP和UDP协议有什么区别?
tcp协议和udp协议区别 TCP and UDP are two protocols that are part of the transport layer in a TCP/IP model o ...
- TCP协议和UDP协议的区别及其应用
下面是TCP和UDP的简单程序: 简单的TCP网络程序 https://blog.csdn.net/qq_37941471/article/details/80738319 简单的UDP网络程序 ht ...
- doraemon的python tcp协议和udp协议
### 8.9 tcp协议和udp协议#### 8.9.1 tcp协议 -------打电话 特点:- 可靠 慢 全双工通信 - 建立连接的时候:三次握手 - 断开连接的时候:四次挥手 - ...
- TCP协议和TCP连接
TCP协议和TCP连接 一.TCP协议的简介 二.TCP连接的简介 1.TCP连接的建立(TCP三次握手) 2.TCP连接的断开(TCP四次挥手) 一.TCP协议的简介 TCP(Transmi ...
- socket通信 tcp协议和udp协议 C++
Socket通信学习(套字接) Socket通信主要分两种:TCP协议和UDP协议 TCP:Transmission Control Protocol UDP:User Datagram Protoc ...
- php-ews发送邮件,node.js 基于 STMP 协议和 EWS 协议发送邮件
本文主要介绍 node.js 发送基于 STMP 协议和 MS Exchange Web Service(EWS) 协议的邮件的方法.文中所有参考代码均以 TypeScript 编码示例. 1 基于 ...
- 基于C语言的网络电子词典
一.概述 本文章是来自于华清远见的一个基于C语言的网络电子词典项目,使用到了tcp协议的并发服务器设计.网络编程.文件I/O.数据库等多方面的知识.可以满足多用户同时登陆,用户登陆后可以查询单词及历 ...
- 传输层TCP协议和UDP协议
传输层协议 文章目录 传输层协议 传输层: 传输层协议: UDP协议: TCP协议: UDP协议和TCP协议的比较: 适用场景: 传输层: 传输层是OSI中最重要,最关键的一层,是唯一负责总体的数据传 ...
- 网络协议和Netty——第一章 网络协议笔记
一.计算机网络体系结构 1.OSI七层模型 开放系统互连参考模型 (Open System Interconnect 简称OSI)是国际标准化组织(ISO)和国际电报电话咨询委员会(CCITT)联合制 ...
最新文章
- 独家 | 微软与哈佛大学定量社会科学研究所合作开发开放数据差异隐私平台,开启研究新征程(附链接)...
- 浙江大学计算机考研大纲,2018年浙江大学研究生入学考试《计算机学科专业基础》(878)考试大纲...
- Java 洛谷 P1308 统计单词数
- 【学习笔记】1、Python的基本介绍
- AM,DSB,SSB,FM信号调制matlab
- laravel5.6 使用指定字段作为key_MyRocks TTL使用姿势及Bugfix
- PHP结合HTML5使用FormData对象提交表单及上传图片
- Java—多线程创建详解
- 计算机桌面图标有箭头,电脑桌面图标为什么会有一个小箭头,原来没的呀,
- mysql 从库升级为主库的步骤
- Tensorflow2.0实战之Auto-Encoder
- 用户事件的存储与分析
- jasperreport报表导出excel锁定行和列
- 基于工业4g网关的危化品运输车监控方案
- 工具类-随即获取姓名-ZH
- ICC布局布线 实践课
- 安卓 输入法出现导致布局变形问题解决
- 2019 校内赛 RPG的地牢猎手(bfs+优先队列)
- 计算机应用基础模板一,计算机应用基础试卷模板(一)
- android 刷机动画,Android开机动画修改方法