项目要求:
1.登录注册功能,不能重复登录,重复注册。用户信息也存储在数据库中
2.单词查询功能
3.历史记录功能,存储单词,意思,以及查询时间,存储在数据库
4基于TCP,支持多客户端连接
采用数据库保存用户信息与历史记录
将dict.txt的数据导入到数据库中保存
7.返回上级、按下ctrl+c退出客户端后,该客户端退出登录
服务器头文件
#ifndef __H_H__
#define __H_H__
#include <sqlite3.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sqlite3.h>
#include <signal.h>
#include <time.h>
#define PORT 6666
#define IP "192.168.50.10"
typedef void (*sighandler_t)(int);
#define ERR_MSG(msg) do{\
fprintf(stderr,"line:__%d__\n",__LINE__);\
perror(msg);\
}while(0)
struct msg{
char type; //消息类型
char name[128]; //名字
char password[128]; //密码
char word[128]; //单词
char mean[128]; //翻译
char time[128]; //时间
}sermsg;
sqlite3* sqlite3_database(); //创建数据库表
int cli_net(); //创建套接字.链接客户端.绑定信息结构体
int sqlite3_close(sqlite3* db); //关闭数据库
int callBack(void *arg, int column, char **colunm_text, char **colunm_name);
int find_callBack(void *arg, int column, char **column_text, char **column_name);
int sqlite3_worddelete(sqlite3 *db); //删除单词表函数
int ser_recv(int newfd, struct sockaddr_in cin,sqlite3 *db); //接收函数
int do_login(int newfd,sqlite3 *db); //登录函数
int do_register(int newfd, sqlite3 *db); //注册函数
int do_find(int newfd,sqlite3 *db); //查询单词
int do_history(int newfd, sqlite3 *db); //查询历史记录
int do_quit(int newfd,sqlite3 *db); //退出
void handler(int sig); //捕获僵尸进程
#endif
服务器代码
#include "h.h"
int main(int argc, char const *argv[])
{
int sfd = cli_net();
sqlite3 *db = NULL;
if (sqlite3_open("dict.db", &db) != SQLITE_OK)
{
fprintf(stderr, "sqlite3_open failed:%d %s__%d__\n", sqlite3_errcode(db), sqlite3_errmsg(db), __LINE__);
return -1;
}
printf("sqlite3_open success\n");
//注册储存表
char sql[128] = "create table if not exists stu1(name char PRIMARY KEY,password char)";
char *errmsg = NULL;
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "sqlite3_exec failed:%d %s__%d__\n", sqlite3_errcode(db), sqlite3_errmsg(db), __LINE__);
return -1;
}
printf("创建stu1表格成功 __%d__\n", __LINE__);
//登录储存表
char sql1[128] = "create table if not exists stu2(name char PRIMARY KEY)";
if (sqlite3_exec(db, sql1, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "sqlite3_exec failed:%d %s__%d__\n", sqlite3_errcode(db), sqlite3_errmsg(db), __LINE__);
return -1;
}
printf("创建stu2表格成功 __%d__\n", __LINE__);
//单词查询记录表
char sql2[128] = "create table if not exists stu(name char,word char,mean char,time char)";
if (sqlite3_exec(db, sql2, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "sqlite3_exec failed:%d %s__%d__\n", sqlite3_errcode(db), sqlite3_errmsg(db), __LINE__);
return -1;
}
printf("创建stu表格成功 __%d__\n", __LINE__);
struct sockaddr_in cin; //储存客户端地址信息
socklen_t addrlen = sizeof(cin);
//生成新的文件描述符与客户端通信
int newfd = -1;
pid_t cpid = 0;
while (1)
{
int newfd = accept(sfd, (struct sockaddr *)&cin, &addrlen);
if (newfd < 0)
{
ERR_MSG("accept");
return -1;
}
printf("[%s,%d]newfd=%d 客户端链接成功 __%d__\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd, __LINE__);
cpid = fork();
if (0 == cpid)
{
close(sfd);
ser_recv(newfd, cin, db);
close(newfd);
exit(0);
}
else if (cpid > 0)
{
close(newfd);
}
else
{
ERR_MSG("fork");
return -1;
}
if (signal(17, handler) == SIG_ERR)
{
ERR_MSG("signal");
return -1;
}
}
return 0;
}
int do_cli_msg(int newfd, struct sockaddr_in cin)
{
char buf[128] = "";
ssize_t res = 0;
while (1)
{
bzero(buf, sizeof(buf));
res = recv(newfd, buf, sizeof(buf), 0);
if (res < 0)
{
ERR_MSG("recv");
return -1;
}
else if (0 == res)
{
printf("[%s,%d]newfd=%d 客户端下线 __%d__\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd, __LINE__);
break;
}
printf("[%s,%d]newfd=%d :%s __%d__\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd, buf, __LINE__);
strcat(buf, "*_*");
if (send(newfd, buf, sizeof(buf), 0) < 0)
{
ERR_MSG("send");
return -1;
}
printf("send success __%d__\n", __LINE__);
}
return 0;
}
void handler(int sig)
{
while (waitpid(-1, NULL, WNOHANG) > 0)
;
return;
}
int cli_net()
{
//创建流式套接字
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if (sfd < 0)
{
ERR_MSG("socket");
return -1;
}
printf("socket success\n");
//允许端口快速被覆盖重用。
int reuse = 1;
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
{
ERR_MSG("setsockopt");
return -1;
}
printf("允许端口快速被覆盖重用成功\n");
//填充地址信息结构体,真实的地址信息结构体AF_INEF:man 7 IP
struct sockaddr_in sin;
sin.sin_family = AF_INET; //必须填AF_INEF
sin.sin_port = htons(PORT); //端口号是网络字节序1024~49151
sin.sin_addr.s_addr = inet_addr(IP);
//绑定服务器的地址信息
if (bind(sfd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
ERR_MSG("bind");
return -1;
}
printf("bind success __%d__\n", __LINE__);
//将套接字设置为被动监听状态
if (listen(sfd, 128) < 0)
{
ERR_MSG("listen");
return -1;
}
printf("listen success __%d__\n", __LINE__);
return sfd;
}
int ser_recv(int newfd, struct sockaddr_in cin, sqlite3 *db) //接收函数
{
ssize_t res = 0;
while (1)
{
res = recv(newfd, &sermsg, sizeof(sermsg), 0);
struct msg sermsg1;
strcpy(sermsg1.name, sermsg.name);
if (res < 0)
{
return -1;
}
else if (0 == res)
{
fprintf(stderr, "[%s:%d]newfd = %d 客户端下线\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd);
return 0;
}
char choose = sermsg.type;
switch (choose)
{
case '1': //注册
do_register(newfd, db);
break;
case '2': //登录
do_login(newfd, db);
break;
case '4': //查询
do_find(newfd, db);
break;
case '5':
do_history(newfd, db); //查询历史记录
break;
case '6':
do_quit(newfd, db);
break;
}
}
return 0;
}
int do_register(int newfd, sqlite3 *db)
{
char sql[128] = "";
sprintf(sql, "insert into stu1 values (\"%s\",\"%s\")", sermsg.name, sermsg.password);
char *errmsg = NULL;
bzero(&sermsg.type, sizeof(sermsg.type));
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
sermsg.type = 'E';
fprintf(stderr, "newfd = %d 注册失败\n", newfd);
//将数据包发送给客户端
if (send(newfd, &sermsg, sizeof(sermsg), 0) < 0)
{
ERR_MSG("send");
return -1;
}
fprintf(stderr, "line:%d sqlite3_exec:%s\n", __LINE__, errmsg);
return -1;
}
sermsg.type = 'S';
fprintf(stdout, "newfd = %d 注册成功\n", newfd);
//将数据包发送给客户端
if (send(newfd, &sermsg, sizeof(sermsg), 0) < 0)
{
ERR_MSG("send");
return -1;
}
return 0;
}
int callBack(void *arg, int column, char **column_text, char **column_name)
{
if (strcmp(column_text[0], sermsg.name) == 0 && strcmp(column_text[1], sermsg.password) == 0)
{
sermsg.type = 'S';
*(int *)arg = 1;
}
return 0;
}
int do_login(int newfd, sqlite3 *db) //登录函数
{
char sql[128];
char *errmsg = NULL;
int flag = 0;
sprintf(sql, "select * from stu1 where name=\"%s\";", sermsg.name);
if (sqlite3_exec(db, sql, callBack, &flag, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "line:%d sqlite3_exec:%s\n", __LINE__, errmsg);
return -1;
}
if (flag == 0)
{
sermsg.type = 'E';
fprintf(stderr, "newfd = %d 登录失败\n", newfd);
//将数据包发送给客户端
if (send(newfd, &sermsg, sizeof(sermsg), 0) < 0)
{
ERR_MSG("send");
return -1;
}
return 0;
}
else if (flag == 1)
{
//将登陆信息保存到登录信息表中
sprintf(sql, "insert into stu2 values(\"%s\");", sermsg.name);
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
sermsg.type = 'R';
fprintf(stderr, "登录重复\n");
bzero(sermsg.name, sizeof(sermsg.name));
//将数据包发送给客户端
if (send(newfd, &sermsg, sizeof(sermsg), 0) < 0)
{
ERR_MSG("send");
return -1;
}
fprintf(stderr, "line:%d sqlite3_exec:%s\n", __LINE__, errmsg);
return 0;
}
}
fprintf(stdout, "%s 登录成功\n", sermsg.name);
//设置为登录状态,登陆成功,将数据包发送给客户端
if (send(newfd, &sermsg, sizeof(sermsg), 0) < 0)
{
ERR_MSG("send");
return -1;
}
return 0;
}
int find_callBack(void *arg, int column, char **column_text, char **column_name)
{
strcat(sermsg.mean, column_text[1]);
strcat(sermsg.mean, "\n");
sermsg.type = 'S';
fprintf(stderr, "查询单词成功\n");
(*(int *)arg)++;
return 0;
}
int do_find(int newfd, sqlite3 *db) //查询单词
{
bzero(sermsg.mean, sizeof(sermsg.mean));
char sql[128];
char *errmsg = NULL;
int flag = 0;
sprintf(sql, "select * from dict where word=\"%s\";", sermsg.word);
if (sqlite3_exec(db, sql, find_callBack, &flag, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "line:%d sqlite3_exec:%s\n", __LINE__, errmsg);
return -1;
}
if (flag == 0)
{
sermsg.type = 'E';
fprintf(stderr, "newfd = %d 查询失败\n", newfd);
if (send(newfd, &sermsg, sizeof(sermsg), 0) < 0)
{
ERR_MSG("send");
return -1;
}
}
else
{
fprintf(stdout, "%s 查询成功\n", sermsg.word);
if (send(newfd, &sermsg, sizeof(sermsg), 0) < 0)
{
ERR_MSG("send");
return -1;
}
}
time_t t;
time(&t);
struct tm *tm_t;
tm_t = localtime(&t);
sprintf(sermsg.time, "%d-%d-%d %d:%d:%d\n",tm_t->tm_year + 1900, tm_t->tm_mon + 1, tm_t->tm_mday, tm_t->tm_hour, tm_t->tm_min, tm_t->tm_sec);
sprintf(sql, "insert into stu values (\"%s\",\"%s\",\"%s\",\"%s\")", sermsg.name, sermsg.word, sermsg.mean, sermsg.time);
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "sqlite3_exec failed:%d %s__%d__\n", sqlite3_errcode(db), sqlite3_errmsg(db), __LINE__);
return -1;
}
return 0;
}
int do_history(int newfd, sqlite3 *db) //查看历史记录
{
char sql[256] = "";
sprintf(sql, "select *from stu where name=\"%s\";", sermsg.name);
char **pres = NULL;
int row, column;
char *errmsg = NULL;
if (sqlite3_get_table(db, sql, &pres, &row, &column, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "line:%d sqlite3_get_table:%s\n", __LINE__, errmsg);
return -1;
}
printf("row=%d column=%d\n", row, column);
if (row == 0)
{
sermsg.type = 'F';
if (send(newfd, &sermsg, sizeof(sermsg), 0) < 0)
{
ERR_MSG("send");
return -1;
}
return 0;
}
bzero(sermsg.name, sizeof(sermsg.name));
bzero(sermsg.word, sizeof(sermsg.word));
bzero(sermsg.mean, sizeof(sermsg.mean));
bzero(sermsg.time, sizeof(sermsg.time));
int i = 0;
for (i = 0; i < (row + 1) * column; i++)
{
if (i % column == column - 4)
sprintf(sermsg.name, "%s", pres[i]);
else if (i % column == column - 3)
sprintf(sermsg.word, "%s", pres[i]);
else if (i % column == column - 2)
sprintf(sermsg.mean, "%s", pres[i]);
else if (i % column == column - 1)
{
sprintf(sermsg.time, "%s", pres[i]);
if (send(newfd, &sermsg, sizeof(sermsg), 0) < 0)
{
ERR_MSG("send");
return -1;
}
}
}
if (i == (row + 1) * column)
{
printf("%s", sermsg.name);
sermsg.type = 'S';
if (send(newfd, &sermsg, sizeof(sermsg), 0) < 0)
{
ERR_MSG("send");
return -1;
}
}
//释放内存空间
sqlite3_free_table(pres);
return 0;
}
int do_quit(int newfd, sqlite3 *db)
{
char sql[256];
char *errmsg = NULL;
sprintf(sql, "delete from stu2 where name=\"%s\";", sermsg.name);
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "line:%d sqlite3_exec:%s\n", __LINE__, errmsg);
return 0;
}
printf("退出成功\n");
return 0;
}
客户端头文件
#ifndef __H2_H__
#define __H2_H__
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#define PORT 6666
#define IP "192.168.50.10"
#define ERR_MSG(msg) do{\
fprintf(stderr,"line:__%d__\n",__LINE__);\
perror(msg);\
}while(0)
struct msg{
char type; //消息类型
char name[128]; //名字
char password[128]; //密码
char word[128]; //单词
char mean[128]; //翻译
char time[128]; //时间
}climsg;
void holp1(); //主菜单
void holp2(); //查询菜单
int cli_login(int cfd); //登录
int cli_register(int cfd); //注册
int cli_find(int cfd); //查找单词
int cli_history(int cfd); //查询历史记录
int cli_quit(int cfd); //退出函数
#endif
客户端代码
#include "h2.h"
int main(int argc, char const *argv[])
{
//创建流式套接字
int cfd = socket(AF_INET, SOCK_STREAM, 0);
if (cfd < 0)
{
ERR_MSG("socket");
return -1;
}
//填充服务器的地址信息,给connect使用
struct sockaddr_in sin;
sin.sin_family = AF_INET; //必须填AF_INEF
sin.sin_port = htons(6666); //端口号是网络字节序1024~49151
sin.sin_addr.s_addr = inet_addr(IP);
//链接服务器
if (connect(cfd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
ERR_MSG("connect");
return -1;
}
char n;
char a;
while (1)
{
AND:
holp1();
printf("请输入指令:");
scanf("%c", &n);
while (getchar() != 10)
;
switch (n)
{
case '1':
cli_register(cfd);
break;
case '2':
if (cli_login(cfd) == 0)
{
while (1)
{
holp2();
printf("请输入指令:");
scanf("%c", &a);
while (getchar() != 10)
;
switch (a)
{
case '4':
cli_find(cfd);
break;
case '5':
cli_history(cfd);
break;
case '6':
cli_quit(cfd);
goto AND;
default:
printf("输入错误,请重新输入\n");
break;
}
}
};
break;
case '3':
goto END;
break;
default:
printf("输入错误,请重新输入\n");
break;
}
}
END:
if (close(cfd) < 0)
{
ERR_MSG("close");
return -1;
}
printf("退出词典\n");
return 0;
}
void holp1()
{
printf("****************************\n");
printf("**********1.注册************\n");
printf("**********2.登录************\n");
printf("**********3.退出************\n");
printf("****************************\n");
}
void holp2()
{
printf("*******************************\n");
printf("**********4.查询***************\n");
printf("**********5.历史记录***********\n");
printf("**********6.返回上一级菜单*****\n");
printf("*******************************\n");
}
int cli_register(int cfd)
{
climsg.type = '1';
bzero(climsg.name, sizeof(climsg.name));
bzero(climsg.password, sizeof(climsg.password));
printf("请输入用户名:\n");
fgets(climsg.name, sizeof(climsg.name), stdin);
climsg.name[strlen(climsg.name) - 1] = 0;
printf("请输入用户密码:\n");
fgets(climsg.password, sizeof(climsg.password), stdin);
climsg.password[strlen(climsg.password) - 1] = 0;
//发送
if (send(cfd, &climsg, sizeof(climsg), 0) < 0)
{
ERR_MSG("send");
return -1;
}
ssize_t res = 0;
bzero(&climsg.type, sizeof(climsg.type));
res = recv(cfd, &climsg, sizeof(climsg), 0);
if (res < 0)
{
ERR_MSG("recv");
return -1;
}
printf("%c",climsg.type);
if ('E' == climsg.type)
{
printf("注册失败\n");
}
else if ('S' == climsg.type)
{
printf("注册成功\n");
}
}
int cli_login(int cfd)
{
climsg.type = '2';
bzero(climsg.name, sizeof(climsg.name));
bzero(climsg.password, sizeof(climsg.password));
printf("请输入用户名:\n");
fgets(climsg.name, sizeof(climsg.name), stdin);
climsg.name[strlen(climsg.name) - 1] = 0;
printf("请输入用户密码:\n");
fgets(climsg.password, sizeof(climsg.password), stdin);
climsg.password[strlen(climsg.password) - 1] = 0;
//发送
if (send(cfd, &climsg, sizeof(climsg), 0) < 0)
{
ERR_MSG("send");
return -1;
}
ssize_t res = 0;
res = recv(cfd, &climsg, sizeof(climsg), 0);
if (res < 0)
{
ERR_MSG("recv");
return -1;
}
if ('E' == climsg.type)
{
printf("登录失败\n");
return -1;
}
else if ('R' == climsg.type)
{
printf("登录重复\n");
return -1;
}
else if ('S' == climsg.type)
{
printf("登录成功\n");
}
return 0;
}
int cli_find(int cfd)
{
climsg.type = '4';
bzero(climsg.word, sizeof(climsg.word));
printf("请输入查询单词:\n");
fgets(climsg.word, sizeof(climsg.word), stdin);
climsg.word[strlen(climsg.word) - 1] = 0;
//发送
if (send(cfd, &climsg, sizeof(climsg), 0) < 0)
{
ERR_MSG("send");
return -1;
}
ssize_t res = 0;
bzero(climsg.mean, sizeof(climsg.mean));
res = recv(cfd, &climsg, sizeof(climsg), 0);
if (res < 0)
{
ERR_MSG("recv");
return -1;
}
if ('E' == climsg.type)
{
printf("查询失败\n");
return -1;
}
else if ('S' == climsg.type)
{
printf("查询成功\n");
printf("%s\n", climsg.mean);
}
return 0;
}
int cli_history(int cfd)
{
climsg.type = '5';
if (send(cfd, &climsg, sizeof(climsg), 0) < 0)
{
ERR_MSG("send");
return -1;
}
ssize_t res = 0;
while (1)
{
res = recv(cfd, &climsg, sizeof(climsg), 0);
if (res < 0)
{
ERR_MSG("recv");
return -1;
}
if (climsg.type == 'S')
break;
if (climsg.type == 'F')
{
fprintf(stdout, "没有查询记录!\n");
break;
}
printf("%s %s %s %s\n", climsg.name, climsg.word, climsg.mean,climsg.time);
}
return 0;
}
int cli_quit(int cfd)
{
climsg.type = '6';
if (send(cfd, &climsg, sizeof(climsg), 0) < 0)
{
ERR_MSG("send");
return -1;
}
printf("返回上一级\n");
return 0;
}