目录
电子词典
电子词典是一种带有显示屏和存储功能的电子设备,主要用于查找和翻译单词、短语和句子。它可以提供多个词典、同义词、反义词、例句、发音等多种辅助功能,使用户能够更方便、快捷地学习和使用外语。电子词典通常具有轻便、易携带、方便使用等优点,已成为语言学习和交流中不可或缺的工具之一。
TCP网络编程
TCP网络编程是指使用TCP协议进行网络通信的程序设计和实现。TCP(传输控制协议)是一种面向连接的、可靠的、基于流的传输协议,广泛应用于互联网中数据传输。
TCP网络编程需要注意以下几个方面:
-
建立连接:TCP是面向连接的协议,即在通信前需要建立连接。连接建立后,才能进行数据传输。
-
发送数据:TCP数据被划分为若干个数据块(或称为段),每一段都有序号,用于保证数据的可靠性。发送数据时,需要按照一定的顺序发送。
-
接收数据:接收端需要按照一定的顺序接收数据,对于丢失的数据块,会自动请求重新发送。
-
断开连接:连接的断开需要经过一定的握手过程,确保数据传输的完整性和可靠性。
在TCP网络编程中,可以使用socket API来进行网络的编程。socket API提供了一套标准的网络编程接口,可以方便地实现网络通信。常用的编程语言如C、Java、Python都提供了socket API的封装库,可以直接使用。
Sqlite3
Sqlite3是一种软件库,提供一种轻量级的、独立于平台的数据库管理系统。它是一种自包含的、没有服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite 的设计目标是嵌入式系统、移动设备和桌面应用程序的小型数据库。SQLite是最流行的关系型数据库之一,常用于移动应用和浏览器内置数据库等场景。SQLite的优点包括文件大小小、易于学习和使用,无需独立的服务器,高效可靠,支持所有主流操作系统等。
建立两张表 usr表中保存用户的名字和密码;record表保存用户查询的单词和查询的时间。
实现功能
1.用户注册功能
服务器端
服务器将客户端发送的用户名和密码插入数据库,根据sqlite3_exec的返回值判断用户是否存在,
将返回值信息发送给客户端。
void zhuce(int newfd,MSG*msg,sqlite3*db){
char *errmsg;
char sql[200]={0};
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)
{
sprintf(msg->data,"name %s exit",msg->name);
printf("%s\n",msg->data);
}
else
{
sprintf(msg->data,"zhuce success");
printf("%s\n",msg->data);
}
if(send(newfd,msg,sizeof(MSG),0)==-1){
perror("send error\n");
exit(-1);
}
return;
}
客户端
从终端输入姓名和密码存入到结构体,将结构体发送给服务器。
void zhuce(int fd,MSG*msg){
msg->type=1;
printf("\033[1;33m输入姓名\n");
fgets(msg->name,30,stdin);
msg->name[strlen(msg->name)-1]='\0';
printf("\033[0;35m输入密码\n");
fgets(msg->data,100,stdin);
msg->data[strlen(msg->data)-1]='\0';
if(send(fd,msg,sizeof(MSG),0)==-1)
{
perror("send 失败");
exit(-1);
}
if(recv(fd,msg,sizeof(MSG),0)==-1)
{
perror("recv 失败");
exit(-1);
}
printf("%s\n",msg->data);
return;
}
2.用户登录功能
服务器端
服务器将客户端发来的用户名和密码在数据库中查询,根据row的值来判断用户账号和密码是否正确。
void denglu(int newfd,MSG*msg,sqlite3*db){
char *errmsg;
char sql[200]={0};
char **result;
int row;
int col;
printf("正在登陆\n");
sprintf(sql,"select * from usr where name = '%s' and pass = '%s'",msg->name,msg->data);
if( sqlite3_get_table(db,sql,&result,&row,&col,&errmsg)!=SQLITE_OK)
{
printf("%s",errmsg);
exit(-1);
}
else
{
if(row==0){
sprintf(msg->data,"urs or pass error");
}
else
{
sprintf(msg->data,"denglu success");
}
}
if(send(newfd,msg,sizeof(MSG),0)==-1){
perror("send error\n");
exit(-1);
}
return;
}
客户端
发送用户名和密码,接收服务器发来得数据如果是“denglu success”返回1,打印查询单词界面。
int denglu(int fd,MSG*msg){
msg->type=2;
printf("\033[1;36m输入姓名\n");
fgets(msg->name,30,stdin);
msg->name[strlen(msg->name)-1]='\0';
printf("\033[0;33m输入密码\n");
fgets(msg->data,100,stdin);
msg->data[strlen(msg->data)-1]='\0';
if(send(fd,msg,sizeof(MSG),0)==-1)
{
perror("send 失败");
exit(-1);
}
if(recv(fd,msg,sizeof(MSG),0)==-1)
{
perror("recv 失败");
exit(-1);
}
if(strncmp(msg->data,"denglu success",14)==0){
printf("\033[0;35m%s\n",msg->data);
return 1;
}else{
printf("\033[1;33m%s\n",msg->data);
return -1;
}
}
3.查询单词功能
服务器端
接收服务器的单词,使用fgets在dict.txt中查找单词(可以将单词表写入数据库,在数据库查找,找,待实现),将单词和strtok裁剪的字符串进行比较如果相等则有单词将后面的字符串发送给客户端;如果没有,将not word 返回给客户端。并将此时的时间,单词,名字插入数据库。
int find(int newfd,MSG*msg,sqlite3*db){
printf("finding\n");
char *m;
char date[100]={0};
char *errmsg;
char word[100]={0};
int len=0;
char sql[100];
char buf[100];
char temp[100];
FILE *fp=fopen("./dict.txt","r");
if(fp==NULL){
perror("open dict fail\n");
exit(-1);
}
printf("open file success\n");
strcpy(word,msg->data);
len=strlen(word);
printf("%s,%d\n",word,len);
while(1)
{
char*p=fgets(buf,100,fp);
if(p == NULL){
strcpy(msg->data,"no this word");
send(newfd,msg,sizeof(MSG),0);
break;
}
strcpy(temp,buf);
char*q= strtok(buf," ");
if(strcmp(q,msg->data)==0){
printf("strok:%s\n",q);
printf("buf:%s\n",buf);
printf("temp:%s\n",temp);
m=temp+len;
while(*m==' ')
{
m++;
}
strcpy(msg->data,m);
send(newfd,msg,sizeof(MSG),0);
time_t tm;
struct tm *tmp_ptr = NULL;
time(&tm);
tmp_ptr = localtime(&tm);
sprintf (date,"%d-%02d-%02d %02d:%02d:%02d", (1900+tmp_ptr->tm_year), (1+tmp_ptr->tm_mon), tmp_ptr->tm_mday, tmp_ptr->tm_hour, tmp_ptr->tm_min, tmp_ptr->tm_sec);
sprintf(sql,"insert into record values('%s','%s','%s')",msg->name,date,word);
printf("%s",date);
if(sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=SQLITE_OK){
printf("%s\n",errmsg);
return -1;
}
fclose(fp);
return 1;
}
}
fclose(fp);
return -1;
}
//2. 列数3.列的结果 4.列的名字
int hiscallback(void *arg,int num,char** value,char ** name){
int newfd;
MSG msg;
newfd=*(int*)arg;
sprintf(msg.data,"%s---%s---%s",value[0],value[2],value[1]);
send(newfd,&msg,sizeof(MSG),0);
return 0;
}
客户端
将查询的单词写入结构体,将结构体发送给服务器,接收服务器返回的信息打印到终端。
void find(int fd,MSG*msg){
msg->type=3;
while(1)
{
// memset(msg->data,0,100);
printf("\033[1;32m输入要查找的单词(q)结束\n");
fgets(msg->data,100,stdin);
msg->data[strlen(msg->data)-1]='\0';
if (0 == strcmp(msg->data, "q"))
{
printf("quit \n");
break;
}
if(send(fd,msg,sizeof(MSG),0) == -1)
{
perror("send 失败");
exit(-1);
}
if(recv(fd,msg,sizeof(MSG),0)==-1)
{
perror("recv 失败");
exit(-1);
}
printf("\033[0;33m%s\n",msg->data);
}
return;
}
查询历史记录功能
服务器端在record表中查询名字,使用回调函数将查询的单词和时间发送给客户端。读完时发送‘*’给客户端,解决阻塞问题否则服务器会一直读取。
//2. 列数3.列的结果 4.列的名字
int hiscallback(void *arg,int num,char** value,char ** name){
int newfd;
MSG msg;
newfd=*(int*)arg;
sprintf(msg.data,"%s---%s---%s",value[0],value[2],value[1]);
send(newfd,&msg,sizeof(MSG),0);
return 0;
}
int history(int newfd,MSG*msg,sqlite3*db){
char *errmsg;
char sql[200]={0};
sprintf(sql,"select * from record where name = '%s'",msg->name);
if( sqlite3_exec(db , sql , hiscallback , (void*)&newfd , &errmsg)!=SQLITE_OK)
{
printf("error:%s\n",errmsg);
sqlite3_free(errmsg);
exit(-1);
}
//解决阻塞
strcpy(msg->data, "*");
if (-1 == send(newfd, msg, sizeof(MSG), 0))
{
perror("fail to send");
exit(-1);
}
return 1;
}
客户端
将结构体发送给服务器,循环读取服务器发来得数据如果读到*结束循环。
int history(int fd,MSG*msg){
msg->type=4;
if(send(fd,msg,sizeof(MSG),0)==-1)
{
perror("历史失败");
exit(-1);
}
while(1){
if(recv(fd,msg,sizeof(MSG),0)==-1)
{
perror("recv 失败");
exit(-1);
}
//解决阻塞
if(0==strcmp(msg->data,"*"))
{
break;
}
printf("%s\n",msg->data);
}
return 1;
}
所有代码
服务器ser.c
/*===============================================
* 文件名称:ser.c
* 创 建 者:
* 创建日期:2023年08月26日
* 描 述:
================================================*/
#include <sys/types.h>
#i
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sqlite3.h>
#include <signal.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#define Z 1 //注册
#define D 2 //登陆
#define C 3 //查询
#define H 4 //历史
typedef struct {
int type;
char name[30];
char data[100];
}MSG;
int hiscallback(void *arg,int num,char** value,char ** name);
void zhuce(int newfd,MSG*msg,sqlite3*db);
void denglu(int newfd,MSG*msg,sqlite3*db);
int find(int newfd,MSG*msg,sqlite3*db);
int history(int newfd,MSG*msg,sqlite3*db);
int docli(int newfd,sqlite3*db);
void handler(int arg)
{
wait(NULL); //阻塞或非阻塞都可以,因为时间很短,不影响程序执行
}
void zhuce(int newfd,MSG*msg,sqlite3*db){
char *errmsg;
char sql[200]={0};
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)
{
sprintf(msg->data,"name %s exit",msg->name);
printf("%s\n",msg->data);
}
else
{
sprintf(msg->data,"zhuce success");
printf("%s\n",msg->data);
}
if(send(newfd,msg,sizeof(MSG),0)==-1){
perror("send error\n");
exit(-1);
}
return;
}
void denglu(int newfd,MSG*msg,sqlite3*db){
char *errmsg;
char sql[200]={0};
char **result;
int row;
int col;
printf("正在登陆\n");
sprintf(sql,"select * from usr where name = '%s' and pass = '%s'",msg->name,msg->data);
if( sqlite3_get_table(db,sql,&result,&row,&col,&errmsg)!=SQLITE_OK)
{
printf("%s",errmsg);
exit(-1);
}
else
{
if(row==0){
sprintf(msg->data,"urs or pass error");
}
else
{
sprintf(msg->data,"denglu success");
}
}
if(send(newfd,msg,sizeof(MSG),0)==-1){
perror("send error\n");
exit(-1);
}
return;
}
int find(int newfd,MSG*msg,sqlite3*db){
printf("finding\n");
char *m;
char date[100]={0};
char *errmsg;
char word[100]={0};
int len=0;
char sql[100];
char buf[100];
char temp[100];
FILE *fp=fopen("./dict.txt","r");
if(fp==NULL){
perror("open dict fail\n");
exit(-1);
}
printf("open file success\n");
strcpy(word,msg->data);
len=strlen(word);
printf("%s,%d\n",word,len);
while(1)
{
char*p=fgets(buf,100,fp);
if(p == NULL){
strcpy(msg->data,"no this word");
send(newfd,msg,sizeof(MSG),0);
break;
}
strcpy(temp,buf);
char*q= strtok(buf," ");
if(strcmp(q,msg->data)==0){
printf("strok:%s\n",q);
printf("buf:%s\n",buf);
printf("temp:%s\n",temp);
m=temp+len;
while(*m==' ')
{
m++;
}
strcpy(msg->data,m);
send(newfd,msg,sizeof(MSG),0);
time_t tm;
struct tm *tmp_ptr = NULL;
time(&tm);
tmp_ptr = localtime(&tm);
sprintf (date,"%d-%02d-%02d %02d:%02d:%02d", (1900+tmp_ptr->tm_year), (1+tmp_ptr->tm_mon), tmp_ptr->tm_mday, tmp_ptr->tm_hour, tmp_ptr->tm_min, tmp_ptr->tm_sec);
sprintf(sql,"insert into record values('%s','%s','%s')",msg->name,date,word);
printf("%s",date);
if(sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=SQLITE_OK){
printf("%s\n",errmsg);
return -1;
}
fclose(fp);
return 1;
}
}
fclose(fp);
return -1;
}
//2. 列数3.列的结果 4.列的名字
int hiscallback(void *arg,int num,char** value,char ** name){
int newfd;
MSG msg;
newfd=*(int*)arg;
sprintf(msg.data,"%s---%s---%s",value[0],value[2],value[1]);
send(newfd,&msg,sizeof(MSG),0);
return 0;
}
int history(int newfd,MSG*msg,sqlite3*db){
char *errmsg;
char sql[200]={0};
sprintf(sql,"select * from record where name = '%s'",msg->name);
if( sqlite3_exec(db , sql , hiscallback , (void*)&newfd , &errmsg)!=SQLITE_OK)
{
printf("error:%s\n",errmsg);
sqlite3_free(errmsg);
exit(-1);
}
//解决阻塞
strcpy(msg->data, "*");
if (-1 == send(newfd, msg, sizeof(MSG), 0))
{
perror("fail to send");
exit(-1);
}
return 1;
}
int docli(int newfd,sqlite3*db){
MSG msg;
while(1){
int rec=recv(newfd,&msg,sizeof(msg),0);
if(rec>0)
{
switch(msg.type)
{
case 1:
zhuce(newfd,&msg,db);
break;
case 2:
denglu(newfd,&msg,db);
break;
case 3:
printf("6");
find(newfd,&msg,db);
break;
case 4:
history(newfd,&msg,db);
break;
defalut:
printf("recv error\n");
}
}
}
close(newfd);
exit(0);
return 0;
}
int main(int argc, char *argv[])
{
MSG msg;
sqlite3 *db;
pid_t pid;
if(argc!=3){
perror("input addr port\n");
exit(-1);
}
if(sqlite3_open("my.db",&db)!=SQLITE_OK)
{
printf("%s\n",sqlite3_errmsg(db));
return -1;
}
else{
printf("open db success\n");
}
int fd=socket(AF_INET,SOCK_STREAM,0);
if(fd<0){
perror("sock\n");
exit(-1);
}
//允许绑定地质快速重用
int b_reuse=1;
setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int));
struct sockaddr_in sin;
memset(&sin,0,sizeof(sin));
sin.sin_family=AF_INET;
sin.sin_addr.s_addr=inet_addr(argv[1]);
sin.sin_port= htons(atoi(argv[2]));
int sin_addr_len=sizeof(sin);
int bi=bind(fd,(struct sockaddr*)&sin,sin_addr_len);
if(bi==-1){
perror("bind\n");
exit(-1);
}
if (-1 == listen(fd, 5))
{
perror("listen");
exit(-1);
}
signal(SIGCHLD,handler);
struct sockaddr_in cliaddr;
memset(&cliaddr, 0, sizeof(cliaddr));
int cliaddr_len = sizeof(cliaddr);
char buff[100] = {0};
while(1)
{
//阻塞等待客户端连接
int newfd = accept(fd, (struct sockaddr *)&cliaddr, &cliaddr_len);
if (-1 == newfd)
{
perror("accept error");
}
printf("有客户端[%s:%d]连入服务器\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));
if((pid=fork())<0){
perror("fork");
exit(-1);
}
else if(pid==0){
close(fd);
docli(newfd,db);
}
else
{
close(newfd);
}
}
return 0;
}
客户端cli.c
/*===============================================
* 文件名称:cli.c
* 创 建 者:
* 创建日期:2023年08月26日
* 描 述:
================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sqlite3.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define Z 1 //注册
#define D 2 //登陆
#define C 3 //查询
#define H 4 //历史
typedef struct {
int type;
char name[30];
char data[100];
}MSG;
void zhuce(int fd,MSG*msg){
msg->type=1;
printf("\033[1;33m输入姓名\n");
fgets(msg->name,30,stdin);
msg->name[strlen(msg->name)-1]='\0';
printf("\033[0;35m输入密码\n");
fgets(msg->data,100,stdin);
msg->data[strlen(msg->data)-1]='\0';
if(send(fd,msg,sizeof(MSG),0)==-1)
{
perror("send 失败");
exit(-1);
}
if(recv(fd,msg,sizeof(MSG),0)==-1)
{
perror("recv 失败");
exit(-1);
}
printf("%s\n",msg->data);
return;
}
int denglu(int fd,MSG*msg){
msg->type=2;
printf("\033[1;36m输入姓名\n");
fgets(msg->name,30,stdin);
msg->name[strlen(msg->name)-1]='\0';
printf("\033[0;33m输入密码\n");
fgets(msg->data,100,stdin);
msg->data[strlen(msg->data)-1]='\0';
if(send(fd,msg,sizeof(MSG),0)==-1)
{
perror("send 失败");
exit(-1);
}
if(recv(fd,msg,sizeof(MSG),0)==-1)
{
perror("recv 失败");
exit(-1);
}
if(strncmp(msg->data,"denglu success",14)==0){
printf("\033[0;35m%s\n",msg->data);
return 1;
}else{
printf("\033[1;33m%s\n",msg->data);
return -1;
}
}
void find(int fd,MSG*msg){
msg->type=3;
while(1)
{
// memset(msg->data,0,100);
printf("\033[1;32m输入要查找的单词(q)结束\n");
fgets(msg->data,100,stdin);
msg->data[strlen(msg->data)-1]='\0';
if (0 == strcmp(msg->data, "q"))
{
printf("quit \n");
break;
}
if(send(fd,msg,sizeof(MSG),0) == -1)
{
perror("send 失败");
exit(-1);
}
if(recv(fd,msg,sizeof(MSG),0)==-1)
{
perror("recv 失败");
exit(-1);
}
printf("\033[0;33m%s\n",msg->data);
}
return;
}
int history(int fd,MSG*msg){
msg->type=4;
if(send(fd,msg,sizeof(MSG),0)==-1)
{
perror("历史失败");
exit(-1);
}
while(1){
if(recv(fd,msg,sizeof(MSG),0)==-1)
{
perror("recv 失败");
exit(-1);
}
//解决阻塞
if(0==strcmp(msg->data,"*"))
{
break;
}
printf("%s\n",msg->data);
}
return 1;
}
void find_menu(int fd,MSG msg){
int n;
while(1) {
puts("\n");
printf("\033[1;35m🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵\n");
printf("\033[1;31m登陆成功!你来啦! %s !\n",msg.name);
printf("\033[1;33m 使用功能 \n");
printf("\033[1;34m1查询 \033[1;36m2历史 \033[1;32m3退出\n");
printf("\033[1;35m🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵\n");
int sc=scanf("%d",&n);
if(sc==-1){
perror("scanf\n");
exit(-1);
}
getchar();
switch(n)
{
case 1:
find(fd,&msg);
break;
case 2:
history(fd,&msg);
break;
case 3:
printf("\033[1;36m感谢使用,欢迎下次再来\n");
close(fd);
exit(0);
break;
default:
printf("\033[1;31m输入错误\n");
break;
}
}
return;
}
int main(int argc, char *argv[])
{
MSG msg;
if(argc!=3){
perror("input addr port\n");
exit(-1);
}
int fd=socket(AF_INET,SOCK_STREAM,0);
if(fd<0){
perror("sock\n");
exit(-1);
}
//允许绑定地质快速重用
int b_reuse=1;
setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int));
struct sockaddr_in sin;
memset(&sin,0,sizeof(sin));
sin.sin_family=AF_INET;
sin.sin_addr.s_addr=inet_addr(argv[1]);
sin.sin_port= htons(atoi(argv[2]));
int sin_addr_len=sizeof(sin);
int con=connect(fd,(struct sockaddr*)&sin,sin_addr_len);
if(con==-1){
perror("connect\n");
exit(-1);
}
printf("connect success\n");
int choose;
while(1)
{
puts("\n");
printf("\033[1;35m🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵\n");
printf("\033[1;33m 欢迎使用单词系统 \n");
printf("\033[1;34m 选择功能 \n");
printf("\033[1;31m1.注册 \033[1;35m2.登陆 \033[0;32m3退出\n");
printf("\033[1;35m🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵\n");
int sc=scanf("%d",&choose);
if(sc==-1){
perror("scanf\n");
exit(-1);
}
getchar();
switch(choose)
{
case 1:
zhuce(fd,&msg);
break;
case 2:
if(denglu(fd,&msg)==1){
find_menu(fd,msg);
}
break;
case 3:
printf("\033[1;34m感谢使用,欢迎下次再来\n");
close(fd);
exit(0);
default:
printf("\033[1;34m输入错了请重新输入\n");
}
}
return 0;
}
运行结果图:
(1)注册功能测试
(2)登录功能测试
(3)查询功能测试
(4)查看历史记录功能测试
(5)退出功能测试