完整代码如下:
有真心想学习的,需要库的朋友,底下留言哦!
server.c:
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <signal.h>
#include <sqlite3.h>
#include <time.h>
#define N 64
#define M 512
typedef struct //消息结构体
{
int type;
char name[N];
char data[M];
}MSG;
int client(int connfd,sqlite3 *db); //客户端命令选择函数
int login(int connfd, MSG *msg,sqlite3 *db); //登录函数
int register_z(int connfd, MSG *msg,sqlite3 *db); //注册函数
int search_word(int connfd, MSG *msg,sqlite3*db); //查询单词函数
int history(int connfd, MSG *msg,sqlite3*db); //查询历史记录函数
int history_date(char *date);
void handler(int signo) //对僵尸进程收尸
{
while(waitpid(-1,NULL,WNOHANG) > 0);
}
int main()
{
int n;//
sqlite3 *db;
int connfd;//定义通信套接字
signal(SIGCHLD, handler);
/*1.创建套接字*/
int sockfd;
if((sockfd=socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket");
return -1;
}
printf("socket success!\n");
int on=1;
if(0 > setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))//设置重复端口号使用
{
perror("setsockopt");
return -1;
}
if(0 != sqlite3_open("my.db",&db))//打开当前目录下的数据库
{
fprintf(stderr,"sqlite3_open:%s\n",sqlite3_errmsg(db));
return -1;
}
/*2.绑定本机地址和端口*/
struct sockaddr_in myaddr;
memset(&myaddr, 0, sizeof(myaddr));
myaddr.sin_family =AF_INET;
myaddr.sin_port =htons(8888);
myaddr.sin_addr.s_addr=inet_addr("127.0.0.1");//采用本机静态IP地址
if(bind(sockfd, (struct sockaddr *)&myaddr,sizeof(myaddr))<0)
{
perror("bind");
return -1;
}
printf("bind success!\n");
/*3.监听套接字*/
if(listen(sockfd, 8)<0)
{
perror("listen");
return -1;
}
printf("listen success!\n");
while(1)
{
/*4.接受tcp连接*/
connfd=accept(sockfd, NULL, NULL);
if(connfd < 0)
{
perror("accept");
return -1;
}
printf("accept success!\n");
pid_t pid=fork(); //采用并发服务器,利用产生子进程进行通信
if(pid < 0)
{
perror("fork");
exit(EXIT_FAILURE);
}
else if(pid == 0) //子进程进行通信
{
close(sockfd); //关闭监听套接字
client(connfd,db);//选择页面
}
close(connfd);
}
if(0 != sqlite3_close(db)) //关闭数据库
{
fprintf(stderr,"sqlite3_close:%s\n",sqlite3_errmsg(db));
return -1;
}
/*6.关闭套接字*/
close(sockfd);
return 0;
}
int client(int connfd,sqlite3 *db) //页面选择函数
{
MSG msg; //定义一个新的消息结构体用来接受客户端发给服务器的所有消息
loop1: while(0 < recv(connfd, &msg, sizeof(MSG),0)) //接受到客户端发的消息
{
//printf("msg->name:%s\n",msg.name);
switch(msg.type)
{
case 1:
if(1 == login(connfd,&msg,db)) //调用登录函数
{
client(connfd,db); //如返回值为1,又返回到选择函数
}
break;
case 2:
register_z(connfd,&msg,db); //注册用户函数
break;
case 3:
search_word(connfd,&msg,db); //查询单词函数
break;
case 4:
history(connfd,&msg,db); //查询历史记录函数
break;
case 5:
goto loop1;//当客户端返回上一级时服务器也需要返回上一级
default:
printf("input error!\n");
}
}
close(connfd); //关闭通信套接字
exit(EXIT_FAILURE); //关闭子进程
return 0;
}
int login(int connfd,MSG *msg,sqlite3 *db)//登录函数
{
char buff[M]; //定义查询用户名及密码的缓存空间
char **resultp; //定义函数之行后返回的结果空间
char *errmsg=NULL; //定义函数执行后返回的错误信息
int nrow,ncolumn; //定义数据库中的表的行数及列数
int ret;
memset(buff,0,sizeof(buff));
sprintf(buff,"select * from user where username = '%s' and password = '%s'",msg->name,msg->data);//将从客户端接受到的用户名及密码放入缓存中
if(0 != sqlite3_get_table(db,buff,&resultp,&nrow,&ncolumn,&errmsg))//执行SQL指令
{
printf("%s\n",errmsg);
return -1;
}
else
{
printf("client login success!\n");
}
if(1 == nrow) //根据指定的条件查找完成后成功找到数据,返回有>0的行数
{
strcpy(msg->name,"OK");
if (0 > (ret = send(connfd,msg,sizeof(MSG),0))) //将成功的数据发送给客户端
{
perror("send1");
}
return 1;
}
if(0 == nrow ) //查询没有结果
{
strcpy(msg->data," 登录失败\n密码或者用户名错误!\n");
if (0 > (ret = send(connfd,msg,sizeof(MSG),0))) //将查询结果失败的数据返回给客户端
{
perror("send1");
return -1;
}
printf("login fail!\n");
}
else
printf("login success!\n");
return 0;
}
int register_z(int connfd,MSG *msg,sqlite3 *db)//注册函数
{
char *errmsg=NULL;
char buff[M];
char **resultp;
int nrow,ncolumn;
memset(buff,0,sizeof(buff));
sprintf(buff,"select * from user where username='%s'",msg->name); //将按照查找指定的用户名SQL命令缓存到buff中
if(0 != sqlite3_get_table(db,buff,&resultp,&nrow,&ncolumn,&errmsg)) //将buff中的命令执行
{
printf("%s\n",errmsg);
return -1;
}
if(nrow >= 1) //查询的行数返回值>=1
{
strcpy(msg->data,"用户已存在!\n");
}
else //没有相同用户名情况下再去注册新的用户
{
memset(buff,0,sizeof(buff));
sprintf(buff,"insert into user values('%s','%s')",msg->name,msg->data); //将要添加新的用户名和密码缓存在buff中
if(0 != sqlite3_exec(db,buff,NULL,NULL,&errmsg)) //使用函数执行SQL命令
{
printf("%s\n",errmsg);
return -1;
}
else
{
printf("client register success!\n");
strcpy(msg->data,"注册成功!\n");
}
}
if(0 > send(connfd,msg,sizeof(MSG),0)) //发送查询成功与否的数据
{
perror("send");
return -1;
}
printf("register success!\n");
return 0;
}
int search_word(int connfd, MSG *msg, sqlite3 *db)//查询单词函数
{
int i,j;
char *errmsg=NULL;
char buff[M]; //定义查找的单词和解释缓存区
char order[M]; //定义装入历史记录的缓存区
char date[M];
char **resultp;
int nrow,ncolumn;
memset(buff,0,sizeof(buff)); //对缓存区清空
memset(date,0,sizeof(date)); //对时间缓冲区清空
sprintf(buff,"select * from word where word='%s'",msg->name); //将要查询的单词名字SQL命令放入缓存中
if(0 != sqlite3_get_table(db,buff,&resultp,&nrow,&ncolumn,&errmsg)) //执行SQL命令
{
printf("error: %s\n",errmsg);
return -1;
}
int count=2;
for(i=0; i < nrow; i++) //行
{
for(j=1; j < ncolumn; j++) //列
{
history_date(date);
strcpy(msg->name,resultp[count]); //单词
strcpy(msg->data,resultp[count+1]); //单词解释
count +=2; //只有两列,每次的单词解释都需要跳过单词名那一列
sprintf(order,"insert into history values('%s','%s')",date,msg->name); //将单词名和解释放入历史记录的缓存中
if(0 != sqlite3_exec(db,order,NULL,NULL,&errmsg)) //执行SQL命令
{
printf("error: %s\n",errmsg);
return -1;
}
printf("%s %s\n",date,msg->name);
}
}
if(0 == nrow)
{
printf("没有这个单词!\n");
strcpy(msg->data,"NO"); //给客户端发一个没有找到单词的信号
if(0 > send(connfd,msg,sizeof(MSG),0))
{
perror("send");
return -1;
}
}
else
{
printf("word search success!\n");
if(0 > send(connfd, msg, sizeof(MSG), 0)) //给客户端发送查询到的解释
{
perror("send");
return -1;
}
}
return 0;
}