1.搭建客户端与服务器,客户端使用多线程方式,主线程跑发送信息给服务器功能,分支线程跑循环接收服务器信息功能
客户端代码:
//TCP客户端的搭建
int tcp_kehuduan()
{
//创建字节流套接字
cfd =socket(AF_INET,SOCK_STREAM,0);
if(cfd <0)
{
ERR_MSG("socket");
return -1;
}
// printf("cfd =%d\n",cfd );//打印套接字的文件描述符
// 允许端口快速重用 客户端有没有都一样
int reuse=1;
if(setsockopt(cfd ,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
{
ERR_MSG("setsockopt");
return -1;
}
//填充地址信息结构体
//真实的地址信息结构体根据地址族指定 AF_INET:man 7 ip查看数据库中的账户一列是否有与之匹配的
struct sockaddr_in sin1;
sin1.sin_family=AF_INET;//必须填AF_INET
sin1.sin_port=htons(PORT);//端口号:1024~49151
sin1.sin_addr.s_addr=inet_addr(IP);//本机IP
//判定客户端的地址信息结构体,非必须绑定
//如果不绑定,则有操作系统自动选择一个端口号以及本机可用ip绑定到套接字上
//连接服务器
if(connect(cfd,(struct sockaddr*)&sin1,sizeof(sin1))<0)
{
ERR_MSG("connect");
return -1;
}
printf("connect success __%d__\n",__LINE__);
pthread_t tid;
//能运行到当前位置,则代表客户端连接成功,则需要创建一个分支线程用于与客户端通信
if(pthread_create(&tid,NULL,deal_cli_msg,NULL)!=0)
{
fprintf(stderr,"line:%d pthread_create failed\n",__LINE__);
return -1;
}
pthread_detach(tid);//分离线程
}
客户端分支线程实现循环接收服务器信息代码:
void* deal_cli_msg(void *arg)//支线线程负责接收数据
{
char buf[64]="";//接收服务器的信息
while(1)
{
memset(buf,0,sizeof(buf));//清空字符串
res=recv(cfd,buf,sizeof(buf),0);//接收服务器判断完后的信息
if(res<0)
{
ERR_MSG("recv");
return NULL;
}
printf("%s\n",buf);
if(0==strcmp(buf,"登录成功")) //判定为登录成功
k=1; //去到主函数判断是否登录成功
}
}
2.服务器采用多进程的方式,父进程循环连接新的客户端,子进程循环接收客户端的信息
int TCPfuwuqi() //服务器的搭建
{
signal(17,handler);
//创建字节流套接字
sfd=socket(AF_INET,SOCK_STREAM,0);
if(sfd<0)
{
ERR_MSG("socket");
return -1;
}
// printf("sfd=%d\n",sfd);//打印套接字的文件描述符
// 允许端口快速重用
int reuse=1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
{
ERR_MSG("setsockopt");
return -1;
}
//填充地址信息结构体
//真实的地址信息结构体根据地址族指定 AF_INET:man 7 ip查看
struct sockaddr_in sin;
sin.sin_family=AF_INET;//必须填AF_INET
sin.sin_port=htons(PORT);//端口号:1024~49151
sin.sin_addr.s_addr=inet_addr(IP);//本机IP
//将ip和端口号绑定到套接字上
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("bind");
return -1;
}
printf("bind success __%d__\n",__LINE__);
//将套接字设置为被动监听状态,监听是否有客户端连接成功
if(listen(sfd,64)<0)
{
ERR_MSG("listen");
return -1;
}
printf("listen success __%d__",__LINE__);
struct sockaddr_in cin;//存储连接成功的客户端地址信息
socklen_t addrlen=sizeof(cin);
newfd=-1;
//阻塞函数,从已经完成连接的队列头中获取一个客户端信息,生成一个新的套接字
//该文件描述符才是与客户端通信的文件描述符
while(1)
{
//父进程负责连接
newfd=accept(sfd,(struct sockaddr*)&cin,&addrlen);
if(newfd<0)
{
ERR_MSG("accept");
return -1;
}
printf("newfd=%d,连接成功__%d__\n",newfd,__LINE__);
pid_t cpid=fork();//创建紫禁城
if(cpid>0) //父进程
close(newfd);
else if(0==cpid) //紫禁城
{
close(sfd);
deal_cli_msg();//跑功能代码
close(newfd);
exit(0);
}
}
close(sfd);
}
子进程:
3.服务器实现电子词典的导入
//导入电子词典
int daoru_cidian()
{
//如果数据库不存在则创建后打开
//如果存在则直接打开
sqlite3* db=NULL;
if(sqlite3_open("./my.db",&db)!=0)
{
fprintf(stderr,"errcode:%d sqlite3_open:%s\n",sqlite3_errcode(db),sqlite3_errmsg(db));
return -1;
}
printf("sqlite3_open successs\n");
//创建一个表
//注意:c代码中的sql语句与在数据库中编写的一致
char sql[128]="create table if not exists cihui (单词 char,意思 char);";
char* errmsg=NULL;
if(sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=0)
{
fprintf(stderr,"line:%d sqlite3_exec:%s\n",__LINE__,errmsg);
return -1;
}
printf("create table stu successs\n");
//打开单词表
FILE* fd=fopen("./dict.txt","r");
char buf[32]="";
char buf1[32]="";
char buf2[32]="";
char c[3]="";
printf("开始导入电子词典\n");
while(1)
{
bzero(buf,sizeof(buf));
bzero(buf1,sizeof(buf1));
bzero(c,sizeof(c));
fscanf(fd,"%s",buf);//获取每一行第一个单词
if(buf[0]=='\0')//读到结尾退出
break;
fgets(c,3,fd);//读取后面2个字符
if(strcmp(c," ")!=0)//如果其中有不为空格的
{
while(1)
{
bzero(buf2,sizeof(buf2));
fseek(fd,-1,SEEK_CUR);//向前偏移一个单位
fscanf(fd,"%s",buf2);//获取第二个单词
strcat(buf," ");
strcat(buf,buf2);//拼接2个单词为一个单词
fgets(c,3,fd);//继续读取后面2个字符
if(strcmp(c," ")==0)//如果后面2个字符都为空格则退出循环
break;
}
}
fseek(fd,1,SEEK_CUR);//向后偏移一个单位
fgets(buf1,32,fd); //获取单词的意思
buf1[strlen(buf1)-1]='\0';
char sql[128]="insert into cihui values (\""; //sql语句
strcat(sql,buf);
strcat(sql,"\",\"");
strcat(sql,buf1);
strcat(sql,"\");"); //拼接sql语句
char* errmsg1=NULL;
// fprintf(stderr,"%s\n",sql);
if(sqlite3_exec(db,sql,NULL,NULL,&errmsg1)!=0) //执行sql语句
{
fprintf(stderr,"line:%d sqlite3_exec:%s\n",__LINE__,errmsg1);
return -1;
}
}
printf("导入完成!!!\n");
fclose(fd);
//关闭数据库
if(sqlite3_close(db)!=0)
{
fprintf(stderr,"errcode:%d sqlite3_open:%s\n",sqlite3_errcode(db),sqlite3_errmsg(db));
return -1;
}
}
4.客户端发送登录信息,服务器接收到后做出对应的处理
//用户登录功能
int denglv(int cfd)
{
bzero(&denglu,sizeof(denglu));
denglu.a=1;//为登录信息
printf("请输入你的账号");
scanf("%s",denglu.zhanghao);
getchar();
printf("请输入你的密码");
scanf("%s",denglu.mima);
getchar();
if(send(cfd,&denglu,sizeof(denglu),0)<0)//向服务器发送登录请求
{
ERR_MSG("send");
return -1;
}
}
if(1==denglu.a)//判断为登录信息
{
//如果数据库不存在则创建后打开
//如果存在则直接打开
if(sqlite3_open("./my.db",&db)!=0)
{
fprintf(stderr,"errcode:%d sqlite3_open:%s\n",sqlite3_errcode(
return NULL;
}
//创建一个账户密码表名字为poot
//注意:c代码中的sql语句与在数据库中编写的一致
strcpy(sql,"create table if not exists poot (account char,password
if(sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=0)
{
fprintf(stderr,"line:%d sqlite3_exec:%s\n",__LINE__,errmsg);
return NULL;
}
//查看数据库中的账户一列是否有与之匹配的
strcpy(sql1,"select account from poot;");
if(sqlite3_get_table(db,sql1,&pres,&row,&column,&errmsg)!=SQLITE_O
{
fprintf(stderr,"line:%d sqlite3_exec:%s\n",__LINE__,errmsg);
return NULL;
}
for(int i=0;i<((row+1)*column);i++) //遍历账户
{
if(i>0 && 0==strcmp(pres[i],denglu.zhanghao)) //找到对应账户
{
k=1;//用来判断是否有对应的账户
strcpy(bzz,pres[i]);//账户复制给bzz
strcpy(sql1,"select password from poot;");
if(sqlite3_get_table(db,sql1,&pres,&row,&column,&errmsg)!=SQLITE_OK)
{
fprintf(stderr,"line:%d sqlite3_exec:%s\n",__LINE__,errmsg);
return NULL;
}
if(0==strcmp(pres[i],denglu.mima)) //找到对应密码
{
strcpy(sql1,"select zhuangtai from poot;");//去寻找对应的状态栏是否为a,a为已登录
if(sqlite3_get_table(db,sql1,&pres,&row,&column,&errmsg)!=SQLITE_OK)
{
fprintf(stderr,"line:%d sqlite3_exec:%s\n",__LINE__,errmsg);
return NULL;
}
if(0==strcmp("a",pres[i]))//对应的状态栏为a
{
sqlite3_close(db);
strcpy(c,"用户已登录\n");
if(send(newfd,c,sizeof(c),0)<0)
{
ERR_MSG("send");
return NULL;//跑到开头跳出本次循环
}
goto star;
}
// 如果状态为0 数据库的状态栏一列至a
strcpy(sql1,"update poot set zhuangtai=\'a\' where account=\'");
strcat(sql1,bzz);
strcat(sql1,"\';");
if(sqlite3_exec(db,sql1,NULL,NULL,&errmsg)!=0)
{
fprintf(stderr,"line:%d sqlite3_exec:%s\n",__LINE__,errmsg);
return NULL;
}
sqlite3_close(db);
strcpy(c,"登录成功");
if(send(newfd,c,sizeof(c),0)<0)
{
ERR_MSG("send");
return NULL;//跑到开头跳出本次循环
}
goto star
}
else //账户对密码不对
{
sqlite3_close(db);
strcpy(c,"密码错误!!!");
if(send(newfd,c,sizeof(c),0)<0)
{
ERR_MSG("send");
return NULL;//跑到开头跳出本次循环
}
goto star;
}
}
}
sqlite3_close(db);
strcpy(c,"账户错误!!!");
if(send(newfd,c,sizeof(c),0)<0)
{
ERR_MSG("send");
return NULL;//跑到开头跳出本次循环
}
goto star;
}
5.客户端发送注册信息,服务器做出相应的操作
//用户注册功能
120 int zhuce(int cfd)
121 {
122 bzero(&denglu,sizeof(denglu));
123 printf("请输入要注册的用户名");
124 scanf("%s",denglu.zhanghao);
125 getchar();
126 printf("请输入要注册的密码");
127 scanf("%s",denglu.mima);
128 getchar();
129 denglu.a=2;//判定为注册
130
131 if(send(cfd,&denglu,sizeof(denglu),0)<0)//向服务器发送注册请求
132 {
133 ERR_MSG("send");
134 return -1;
135 }
136 }
else if(2==denglu.a) //判定为注册信息
{
//如果数据库不存在则创建后打开
//如果存在则直接打开
if(sqlite3_open("./my.db",&db)!=0)
{
fprintf(stderr,"errcode:%d sqlite3_open:%s\n",sqlite3_errcode(db),sqlite3_errmsg(
return NULL;
}
//创建一个账户密码表名字为poot
//注意:c代码中的sql语句与在数据库中编写的一致
strcpy(sql,"create table if not exists poot (account char,password char,zhuangtai cha
if(sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=0)
{
fprintf(stderr,"line:%d sqlite3_exec:%s\n",__LINE__,errmsg);
return NULL;
}
//查看数据库中的账户一列是否有与之匹配的
strcpy(sql1,"select account from poot;");
if(sqlite3_get_table(db,sql1,&pres,&row,&column,&errmsg)!=SQLITE_OK)
{
fprintf(stderr,"line:%d sqlite3_exec:%s\n",__LINE__,errmsg);
return NULL;
}
for(int i=0;i<((row+1)*column);i++) //遍历账户
{
if(i>0 && 0==strcmp(pres[i],denglu.zhanghao)) //找到对应账户 i=0时为accoun
{
bzero(c,sizeof(c));
strcpy(c,"用户已存在请重新注册!!!");
if(send(newfd,c,sizeof(c),0)<0)
{
ERR_MSG("send");
return NULL;
}
sqlite3_close(db);//关闭数据库
}
}
//如果没有找到对应账户,则在数据库中增加这条语句
strcpy(sql,"insert into poot values (\'");
strcat(sql,denglu.zhanghao);
strcat(sql,"\',\'");
strcat(sql,denglu.mima);
strcat(sql,"\',\'0\');");
sqlite3_exec(db,sql,NULL,NULL,&errmsg);//执行sql语句
bzero(c,sizeof(c));
strcpy(c,"注册成功");
send(newfd,c,sizeof(c),0);
sqlite3_close(db);//关闭数据库
continue;
}
6.客户端发送用户退出信息,服务器接收并作相应处理
//用户退出
int exit1(int cfd)
{
denglu.a=3;//判定为退出
if(send(cfd,&denglu,sizeof(denglu),0)<0)//向服务器发送退出请求
{
ERR_MSG("send");
return -1;
}
}
else if(3==denglu.a) //判定为退出信息
{
//如果数据库不存在则创建后打开
//如果存在则直接打开
if(sqlite3_open("./my.db",&db)!=0)
{
fprintf(stderr,"errcode:%d sqlite3_open:%s\n",sqlite3_errcode(db),sqlite3_errmsg(db));
return NULL;
}
strcpy(sql1,"update poot set zhuangtai=\'0\' where account=\'");//状态栏设置为0
strcat(sql1,bzz);//
strcat(sql1,"\';");
if(sqlite3_exec(db,sql1,NULL,NULL,&errmsg)!=0) //执行sql语句
{
fprintf(stderr,"line:%d sqlite1_exec:%s\n",__LINE__,errmsg);
return NULL;
}
sqlite3_close(db);//关闭数据库
goto star;
}
7.客户端发送查询单词意思信息,服务器接收并作出相应的处理
//查询单词意思
int search_danci(int cfd)
{
denglu.a=4;//判定为查询单词意思
if(send(cfd,&denglu,sizeof(denglu),0)<0)//向服务器发送查询信息请求
{
ERR_MSG("send");
return -1;
}
}
else if(4==denglu.a) //判定为查询单词信息
{
//如果数据库不存在则创建后打开
//如果存在则直接打开
if(sqlite3_open("./my.db",&db)!=0)
{
fprintf(stderr,"errcode:%d sqlite3_open:%s\n",sqlite3_errcode(db),sqlite3_errmsg(db));
return NULL;
}
//查看数据库中的单词一列是否有与之匹配的
strcpy(sql1,"select 单词 from cihui;");
if(sqlite3_get_table(db,sql1,&pres,&row,&column,&errmsg)!=SQLITE_OK)
{
fprintf(stderr,"line:%d sqlite3_exec:%s\n",__LINE__,errmsg);
return NULL;
}
for(int i=0;i<((row+1)*column);i++) //遍历账户
{
if(i>0 && 0==strcmp(pres[i],denglu.zhanghao)) //找到对应单词
{
flag=1; //找到对应单词 flag为1
strcpy(sql1,"select 意思 from cihui;");//去寻找对应的意思
if(sqlite3_get_table(db,sql1,&pres,&row,&column,&errmsg)!=SQLITE_OK)
{
fprintf(stderr,"line:%d sqlite3_exec:%s\n",__LINE__,errmsg);
return NULL;
}
strcpy(c,"查询的结果是:");
strcat(c,pres[i]);//复制对应的单词意思
strcpy(danci_mean,pres[i]);
//printf("%s\n",c); //验证结果是否有误
send(newfd,c,sizeof(c),0);//向客户端发送查询的结果
}
}
if(0==flag)//没有找到对应的单词的话
{
strcpy(danci_mean,"没有找到对应的单词");
send(newfd,danci_mean,sizeof(danci_mean),0);
}
FILE* t=fopen("./time.txt","a+");//标准io
time(&ti); //设置时间相关操作
info=localtime(&ti);
fprintf(t,"%d-%02d-%02d-%02d:%02d:%02d ",\
info->tm_year+1900,info->tm_mon+1,info->tm_mday,\
info->tm_hour,info->tm_min,info->tm_sec); //将时间存入文件中
fflush(t);//刷新缓冲区
bzero(c,sizeof(c));//清空字符串c
fseek(t,0,SEEK_SET);
fscanf(t,"%s",c);//把时间从time.txt中读出来放到字符串中
fclose(t);//关闭文件
system("rm time.txt");//执行终端命令删除此文件
//printf("%s\n",c);//验证字符串中是否存有时间
//创建一个名为账户加history的表,表中有id 时间 查询的单词 以及意思
//注意:c代码中的sql语句与在数据库中编写的一致
strcpy(sql,"create table if not exists ");
strcat(sql,bzz);
strcat(sql,"history (time char,danci char,mean char);");
if(sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=0)
{
fprintf(stderr,"line:%d sqlite3_exec:%s\n",__LINE__,errmsg);
return NULL;
}
strcpy(sql,"insert into ");
strcat(sql,bzz);
strcat(sql,"history values (\'");
strcat(sql,c);
strcat(sql,"\',\'");
strcat(sql,denglu.zhanghao); //添加一条历史记录
strcat(sql,"\',\'");
strcat(sql,danci_mean);
strcat(sql,"\');");
sqlite3_exec(db,sql,NULL,NULL,&errmsg);//执行sql语句
sqlite3_close(db);//关闭数据库
}
8.客户端发送查询历史记录信息,服务器接收并作出相应的操作
//查询历史记录
int search_history(int cfd)
{
denglu.a=5;//判定为查询历史记录
if(send(cfd,&denglu,sizeof(denglu),0)<0)//向服务器发送查询信息请求
{
ERR_MSG("send");
return -1;
}
}
else if(5==denglu.a)//判断为查询历史记录信息
{
strcpy(c1,"查询的历史记录信息为下:");
send(newfd,c1,sizeof(c1),0); //回应客户端
//如果数据库不存在则创建后打开
//如果存在则直接打开
if(sqlite3_open("./my.db",&db)!=0)
{
fprintf(stderr,"errcode:%d sqlite3_open:%s\n",sqlite3_errcode(db),sqlite3_errmsg(db));
return NULL;
}
strcpy(sql1,"select * from ");
strcat(sql1,bzz);
strcat(sql1,"history;");
if(sqlite3_get_table(db,sql1,&pres,&row,&column,&errmsg)!=SQLITE_OK)//查询对应的历史记录表
{
fprintf(stderr,"line:%d sqlite3_exec:%s\n",__LINE__,errmsg);
return NULL;
}
for(int i=0;i<((row+1)*column);) //遍历账户
{
strcpy(c,pres[i]);
strcat(c," ");
i++;
strcat(c,pres[i]);
strcat(c," ");
i++;
strcat(c,pres[i]);
strcat(c," ");
i++;
send(newfd,c,sizeof(c),0); //一行行发送历史记录
}
sqlite3_close(db);//关闭数据库
}
9.客户端发送删除历史记录信息,服务器接收并作出相应的操作
//删除历史记录信息
int delete_history(int cfd)
{
denglu.a=6;//判定为删除历史记录
if(send(cfd,&denglu,sizeof(denglu),0)<0)//向服务器发送查询信息请求
{
ERR_MSG("send");
return -1;
}
}
else if(6==denglu.a) //判定为删除历史记录信息
{
//如果数据库不存在则创建后打开
//如果存在则直接打开
if(sqlite3_open("./my.db",&db)!=0)
{
fprintf(stderr,"errcode:%d sqlite3_open:%s\n",sqlite3_errcode(db),sqlite3_errmsg(db));
return NULL;
}
strcpy(sql,"delete from "); //清空数据库
strcat(sql,bzz);
strcat(sql,"history;");
if(sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=0)
{
fprintf(stderr,"line:%d sqlite3_exec:%s\n",__LINE__,errmsg);
return NULL;
}
strcpy(c,"清除历史记录成功");
send(newfd,c,sizeof(c),0); //回应客户端
sqlite3_close(db);//关闭数据库
}
效果图:
注意:以下代码是服务器与客户端都有的
#define ERR_MSG(msg) do{\
fprintf(stderr,"line:%d", __LINE__);\
perror(msg);\
}while(0)
#define IP "192.168.121.128" //本机IP ifconfig查看
#define PORT 8888 //1024~49151
int sfd; //文件描述符
int newfd; //用来和客户端进行交互的文件描述符
//需要传递给线程的处理参数
struct node
{
int a;//1为登录 2为注册 3为退出 4为查询单词意思 5为查看历史记录 6为删除历史记录
char zhanghao[32];
char mima[32];
};