项目要求:
- 登录注册功能,不能重复登录,重复注册
- 单词查询功能
- 历史记录功能,存储单词,意思,以及查询时间
- 基于TCP,支持多客户端连接
- 采用数据库保存用户信息与历史记录
- 将dict.txt的数据导入到数据库中保存。
- 按下ctrl+c退出客户端后,注销该客户端的登录信息;
客户端:
头文件(dic_cli.h):
#ifndef __C_DIC_H__
#define __C_DIC_H__
//=============================================================
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
//=============================================================
// 错误信息
#define ERR_MSG(msg) \
do \
{ \
fprintf(stderr, "line:__%d__", __LINE__); \
perror(msg); \
} while (0)
// IP地址和端口号
#define PORT 8888
#define IP "192.168.13.1"
// 封装协议结构体
struct data
{
char type; // 消息类型
char name[128]; // 账户
char password[128]; // 密码
char word[128]; // 单词
char translate[128]; // 翻译
char time[256]; // 时间
} encapsulation;
//=============================================================
int ser();
void menu_one();
int login(int sfd);
int my_register(int sfd);
int find_word(int sfd);
void menu_tow();
int find_history(int sfd);
int quit(int sfd);
//=============================================================
#include "C_Dic_txt.c"
#endif
主函数:
#include "dic_cli.h"
int main(int argc, const char *argv[])
{
// 创建套接字
int sfd=ser();
// 选择菜单变量
char select_one=0;
// 子菜单
char select_tow=0;
while(1)
{
START:
system("clear");
// 主菜单
menu_one();
printf("请输入功能对应的编号>>>\n");
select_one=getchar();
// 吸收垃圾字符
while(getchar()!=10);
switch(select_one)
{
case '1':
//注册
my_register(sfd);
break;
case '2':
//登录
if(login(sfd)>0)
{
while(1)
{
// 词典功能菜单
system("clear");
menu_tow();
printf("请输入相应功能的编号>>>\n");
select_tow=getchar();
while(getchar()!=10);
switch(select_tow)
{
case '4':
//查询单词
find_word(sfd);
break;
case '5':
//历史记录查询
find_history(sfd);
break;
case '6':
//退出登录
quit(sfd);
goto START;
default:
fprintf(stdout, "输入有误请重新输入>>>\n");
break;
}
printf("\n输入任意字符清屏>>>\n");
while(getchar()!=10);
}
case '3':
//退出
goto END;
break;
default:
fprintf(stdout, "输入有误请重新输入>>>\n");
break;
}
}
printf("输入任意字符清屏>>>\n");
while(getchar()!=10);
}
END:
close(sfd); //关闭套接字
return 0;
}
执行函数体:
#include "dic_cli.h"
// 创建流式套接字
// socket(),bind(),connect
int ser()
{
//创建流式套接字
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
printf("socket success\n");
//填充地址信息结构体
struct sockaddr_in sin; //定义结构体变量
sin.sin_family=AF_INET; // 必须填写AF_INER
sin.sin_port=htons(PORT); //端口号
sin.sin_addr.s_addr=inet_addr(IP); // 本地IP地址
//连接服务器
if(connect(sfd,(struct sockaddr *)&sin,sizeof(sin))<0)
{
ERR_MSG("connect");
return -1;
}
return sfd;
}
//创建菜单
void menu_one()
{
printf("---------欢迎进入电子词典--------\n");
printf("-----------1.注册----------------\n");
printf("-----------2.登录----------------\n");
printf("-----------3.退出----------------\n");
printf("---------------------------------\n");
}
//注册功能
int my_register(int sfd)
{
//协议
encapsulation.type='1';
//清空账户和密码的零时存写数据
bzero(encapsulation.name,sizeof(encapsulation.name));
bzero(encapsulation.password,sizeof(encapsulation.password));
//从终端获账户和密码
printf("请输入注册的用户名>>>\n");
fgets(encapsulation.name,sizeof(encapsulation.name),stdin);
encapsulation.name[strlen(encapsulation.name)-1]=0;
printf("请输入注册的密码>>>\n");
fgets(encapsulation.password,sizeof(encapsulation.password),stdin);
encapsulation.password[strlen(encapsulation.password)-1]=0;
//将数据包发送给服务器
if(send(sfd,&encapsulation,sizeof(encapsulation),0)<0)
{
ERR_MSG("send");
return -1;
}
//接收服务器的回复
if(recv(sfd,&encapsulation,sizeof(encapsulation),0)<0)
{
ERR_MSG("recv");
return -1;
}
if(encapsulation.type=='S')
{
fprintf(stderr,"注册成功\n");
}
else if(encapsulation.type=='E')
{
fprintf(stderr,"注册失败请更换账号\n");
}
return 0;
}
//登录
int login(int sfd)
{
//协议
encapsulation.type='2';
printf("请输入登录用户名>>>\n");
fgets(encapsulation.name,sizeof(encapsulation.name),stdin);
encapsulation.name[strlen(encapsulation.name)-1]=0;
printf("请输入登录密码>>>\n");
fgets(encapsulation.password,sizeof(encapsulation.password),stdin);
encapsulation.password[strlen(encapsulation.password)-1]=0;
//将数据包发送给服务器
if(send(sfd,&encapsulation,sizeof(encapsulation),0)<0)
{
ERR_MSG("send");
return -1;
}
//接收服务器的回复
if(recv(sfd,&encapsulation,sizeof(encapsulation),0)<0)
{
ERR_MSG("recv");
return -1;
}
if(encapsulation.type=='S')
{
fprintf(stderr,"登录成功\n");
return 1;
}
else if(encapsulation.type=='E')
{
fprintf(stderr,"登录失败\n");
return -1;
}
else if(encapsulation.type=='R')
{
fprintf(stderr,"重复登录\n");
return -1;
}
return 1;
}
//词典功能菜单
void menu_tow()
{
printf("---------欢迎来到电子词典--------\n");
printf("-----------4.查询单词------------\n");
printf("-----------5.历史查询记录--------\n");
printf("-----------6.退出----------------\n");
printf("---------------------------------\n");
}
//查找单词
int find_word(int sfd)
{
//封装协议
encapsulation.type='4';
printf("请输入要查询的单词>>>\n");
fgets(encapsulation.word,sizeof(encapsulation.word),stdin);
encapsulation.word[strlen(encapsulation.word)-1]=0;
//将数据包发送给服务器
if(send(sfd,&encapsulation,sizeof(encapsulation),0)<0)
{
ERR_MSG("send");
return -1;
}
//接收服务器的回复
if(recv(sfd,&encapsulation,sizeof(encapsulation),0)<0)
{
ERR_MSG("recv");
return -1;
}
if(encapsulation.type=='S')
{
fprintf(stderr,"%s",encapsulation.translate);
}
else if(encapsulation.type=='E')
{
fprintf(stderr,"查询失败,请检查单词的拼写\n");
return -1;
}
return 0;
}
//历史记录查找
int find_history(int sfd)
{
//协议
encapsulation.type='5';
//将数据包发送给服务器
if(send(sfd,&encapsulation,sizeof(encapsulation),0)<0)
{
ERR_MSG("send");
return -1;
}
while(1)
{
//接收服务器的回复
if(recv(sfd,&encapsulation,sizeof(encapsulation),0)<0)
{
ERR_MSG("recv");
return -1;
}
if(encapsulation.type=='S')
break;
if(encapsulation.type=='F')
{
fprintf(stdout,"没有查询记录");
break;
}
fprintf(stdout,"%s %s %s %s\n",encapsulation.name,encapsulation.word,encapsulation.translate,encapsulation.time);
}
return 1;
}
//退出登录
int quit(int sfd)
{
//协议
encapsulation.type='6';
//将数据包发送给服务器
if(send(sfd,&encapsulation,sizeof(encapsulation),0)<0)
{
ERR_MSG("send");
return -1;
}
return 0;
}
服务器:
头文件(dic_ser.h):
#ifndef __S_Dic_H__
#define __S_Dic_H__
//==========================================================
#include <stdio.h>
#include <sqlite3.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <strings.h>
#include <unistd.h>
#include <sys/wait.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
//==========================================================
// 端口号
#define PORT 8888
// IP地址
#define IP "192.168.13.1"
// 错误信息提示
#define ERR_MSG(msg) \
do \
{ \
fprintf(stderr, "line:__%d__\n", __LINE__); \
perror(msg); \
} while (0)
struct data
{
char type; // 消息类型
char name[128]; // 账户
char password[128]; // 密码
char translate[128]; // 翻译
char time[256]; // 时间
char word[128]; // 单词
} encapsulation;
//==========================================================
sqlite3* sqlite3_database();
int Cli();
void handler(int sig);
int sqlite3_delete(sqlite3 *db);
int user_register(int newfd);
int sqlite3_close(sqlite3* db);
int sqlite3_word(sqlite3* db);
int history(int newfd);
int login(int newfd);
int my_recv(int newfd,struct sockaddr_in cin);
int find(int newfd);
int quit(int newfd);
//==========================================================
#include "S_Dic_txt.c"
#endif
主函数:
#include "dic_ser.h"
int main(int argc, const char *argv[])
{
// 僵尸进程信号
singnalhander sig = signal(SIGCHLD, handler);
if(SIG_ERR == sig)
{
perror("signal");
return -1;
}
// 创建套接字
int sfd=Cli();
// 创建数据库(导入单词)
sqlite3*db=sqlite3_database();
// 存储接收客户端的结构体信息地址
struct sockaddr_in cin;
socklen_t addrlen=sizeof(cin);
// 创建父子进程
pid_t pid=0;
while(1)
{
// 阻塞函数,从自己完成连接的队列头中获取一个客户端信息,生成一个新的文件描述符
// 该文件描述符才是与客户端通信的文件描述符
int newfd=accept(sfd,(struct sockaddr *)&cin,&addrlen);
if(newfd<0)
{
ERR_MSG("accept");
return -1;
}
printf("[%sig:%d] newfd=%d 连接成功\n",\
inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
//创建子进程
pid=fork();
//子进程
if(pid==0)
{
close(sfd);
my_recv(newfd,cin);
close(newfd);
exit(0);
}
else if(pid>0)
{
}
else
{
ERR_MSG("fork");
return -1;
}
}
//关闭数据库
sqlite3_close(db);
return 0;
}
执行函数:
#include "dic_ser.h"
// 僵尸信号
void handler(int sig)
{
while (waitpid(-1, NULL, WNOHANG) > 0)
;
return;
}
// 创建套接字
int Cli()
{
// 创建套接字
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if (sfd < 0)
{
ERR_MSG("socket");
return -1;
}
// 允许端口快速重用
int reuse = 1;
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
{
ERR_MSG("setsockopt");
return -1;
}
// 填充地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET; // 必须填写AF_INER
sin.sin_port = htons(PORT); // 端口
sin.sin_addr.s_addr = inet_addr(IP);// IP地址
// 将IP和端口绑定到套字节上
if (bind(sfd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
ERR_MSG("bind");
return -1;
}
// 将套节字设为监听状态,监听是否有客户端链接成功
if (listen(sfd, 5) < 0)
{
ERR_MSG("listen");
return -1;
}
return sfd;
}
//================================================================================
// 数据库功能
sqlite3 *db = NULL;
char *errmsg = NULL;
sqlite3 *sqlite3_database()
{
// 如果数据库不存在,则创建后打开
// 如果存在则直接打开
if (sqlite3_open("./dict.db", &db) != SQLITE_OK)
{
fprintf(stderr, "line:%d sqlite3_open failed:%d:%s\n",
__LINE__, sqlite3_errcode(db), sqlite3_errmsg(db));
return NULL;
}
// 删除单词表
sqlite3_delete(db);
// 创建一个表
// 注意:C代码中编写的SQL语句与在数据库中编写的一样
char sql[128] = "create table if not exists dict(English char,chainese char);";
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "line:%d sqlite3_exec:%s\n", __LINE__, errmsg);
return NULL;
}
// 创建保存注册账户信息的表
bzero(sql, sizeof(sql));
sprintf(sql, "create table if not exists user_register(user char primary key,password char);");
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "line:%d sqlite3_exec:%s\n", __LINE__, errmsg);
return NULL;
}
// 创建保存登录用户信息的表
bzero(sql, sizeof(sql));
sprintf(sql, "create table if not exists user_login(user char primary key);");
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "line:%d sqlite3_exec:%s\n", __LINE__, errmsg);
return NULL;
}
// 创建存储历史记录
bzero(sql, sizeof(sql));
sprintf(sql, "create table if not exists user_history(user char,word char,translate char,time char);");
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "line:%d sqlite3_exec:%s\n", __LINE__, errmsg);
return NULL;
}
// 导入单词函数
sqlite3_word(db);
return db;
}
// 关闭数据库
int sqlite3_close(sqlite3 *db)
{
if (sqlite3_close(db) != SQLITE_OK)
{
fprintf(stderr, "line:%d sqlite3_close failed:%d:%s\n",
__LINE__, sqlite3_errcode(db), sqlite3_errmsg(db));
return -1;
}
return 0;
}
// 导入单词
int sqlite3_word(sqlite3 *db)
{
// 打开文件
FILE *fd = fopen("./dict.txt", "r");
if (NULL == fd)
{
perror("fopen");
return -1;
}
printf("电子词典正在导入......\n");
char buf[512] = "";
char get_word[128] = "";
char get_explain[128] = "";
char sql[128] = "";
char *errmsg = NULL;
while (1)
{
bzero(get_word, sizeof(get_word));
bzero(get_explain, sizeof(get_explain));
if (fgets(buf, sizeof(buf), fd) == NULL)
{
printf("单词导入完毕等待用户接入......\n");
return -1;
}
buf[strlen(buf) - 1] = '\0';
for (int i = 0; i < buf[i + 2]; i++)
{
if (buf[i] != ' ' && buf[i + 1] == ' ' && buf[i + 2] == ' ')
{
strncpy(get_word, buf, i + 1);
}
else if (buf[i] == ' ' && buf[i + 1] == ' ' && buf[i + 2] != ' ')
{
strcpy(get_explain, buf + i + 2);
break;
}
}
bzero(sql, sizeof(sql));
sprintf(sql, "insert into dict values(\"%s\",\"%s\");", get_word, get_explain);
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "line:%d sqlite3_exec:%s\n", __LINE__, errmsg);
return -1;
}
}
// 关闭文件描述符
fclose(fd);
return 0;
}
// 接收客户端信息
int my_recv(int fd, struct sockaddr_in cin)
{
int newfd = fd;
ssize_t res = 0;
while (1)
{
res = recv(newfd, &encapsulation, sizeof(encapsulation), 0);
struct data encapsulation_t;
strcpy(encapsulation_t.name, encapsulation.name);
printf("%s\n", encapsulation_t.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);
// 删除下线客户的信息
char sql[521];
char *errmsg = NULL;
sprintf(sql, "delete from user_login where user=\"%s\";", encapsulation_t.name);
// printf("%s\n",sql);
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "line:%d sqlite3_exec:%s\n", __LINE__, errmsg);
return 0;
}
return 0;
}
char choose = encapsulation.type;
switch (choose)
{
// 注册
case '1':
user_register(newfd);
break;
// 登录
case '2':
login(newfd);
break;
// 查询
case '4':
find(newfd);
break;
// 查询历史记录
case '5':
history(newfd);
break;
// 退出
case '6':
quit(newfd);
break;
}
}
return 0;
}
// 注册
int user_register(int newfd)
{
char sql[364];
char *errmsg = NULL;
sprintf(sql, "insert into user_register values(\"%s\",\"%s\");",
encapsulation.name, encapsulation.password);
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
encapsulation.type = 'E';
fprintf(stderr, "newfd = %d 注册失败\n", newfd);
if (send(newfd, &encapsulation, sizeof(encapsulation), 0) < 0)
{
ERR_MSG("send");
return -1;
}
fprintf(stderr, "line:%d sqlite3_exec:%s\n", __LINE__, errmsg);
return -1;
}
encapsulation.type = 'S';
fprintf(stderr, "newfd = %d 注册成功\n", newfd);
if (send(newfd, &encapsulation, sizeof(encapsulation), 0) < 0)
{
ERR_MSG("send");
return -1;
}
return 1;
}
// 查询单词回调函数
int find_callBack(void *arg, int column, char **column_text, char **column_name) // void* arg = &flag
{
strcat(encapsulation.translate, column_text[1]);
//strcat(encapsulation.translate, "\n");
encapsulation.type = 'S';
fprintf(stderr, "查询单词成功\n");
(*(int *)arg)++;
return 0;
}
//查询单词
int find(int newfd)
{
// 清空翻译
bzero(encapsulation.translate, sizeof(encapsulation.translate));
char sql[512];
char *errmsg = NULL;
int flag = 0;
sprintf(sql, "select * from dict where English=\"%s\";", encapsulation.word);
if (sqlite3_exec(db, sql, find_callBack, &flag, &errmsg) != SQLITE_OK)
{
encapsulation.type = 'E';
fprintf(stderr, "newfd = %d 查询单词失败\n", newfd);
// 将数据包发送给客户端
if (send(newfd, &encapsulation, sizeof(encapsulation), 0) < 0)
{
ERR_MSG("send");
return -1;
}
fprintf(stderr, "line:%d sqlite3_exec:%s\n", __LINE__, errmsg);
return -1;
}
// 将数据包发送给客户端
if (send(newfd, &encapsulation, sizeof(encapsulation), 0) < 0)
{
ERR_MSG("send");
return -1;
}
// 当前时间
time_t t1;
struct tm *timeinfo = NULL;
time(&t1);
timeinfo = localtime(&t1);
char time1[256] = "";
sprintf(time1, "%d-%d-%d %d:%d:%d", timeinfo->tm_year + 1900, timeinfo->tm_mon + 1, timeinfo->tm_mday,
timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec);
// 插入历史记录表
bzero(sql, sizeof(sql));
errmsg = NULL;
sprintf(sql, "insert into user_history values(\"%s\",\"%s\",\"%s\",\"%s\");",
encapsulation.name, encapsulation.word, encapsulation.translate, time1);
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
return 0;
}
return 0;
}
//历史记录
int history(int newfd)
{
//发送数据包给客户端
char sql[256] = "";
sprintf(sql, "select *from user_history where user=\"%s\";", encapsulation.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;
}
bzero(encapsulation.name, sizeof(encapsulation.name));
bzero(encapsulation.word, sizeof(encapsulation.word));
bzero(encapsulation.translate, sizeof(encapsulation.translate));
bzero(encapsulation.time, sizeof(encapsulation.time));
int i = 0;
for (i = 0; i < (row + 1) * column; i++)
{
if (i % column == column - 4)
sprintf(encapsulation.name, "%s", pres[i]);
else if (i % column == column - 3)
sprintf(encapsulation.word, "%s", pres[i]);
else if (i % column == column - 2)
sprintf(encapsulation.translate, "%s", pres[i]);
else if (i % column == column - 1)
{
sprintf(encapsulation.time, "%s", pres[i]);
if (send(newfd, &encapsulation, sizeof(encapsulation), 0) < 0)
{
ERR_MSG("send");
return -1;
}
}
}
if (i == (row + 1) * column)
{
encapsulation.type = 'S';
if (send(newfd, &encapsulation, sizeof(encapsulation), 0) < 0)
{
ERR_MSG("send");
return -1;
}
}
if (i == 0)
{
encapsulation.type = 'F';
if (send(newfd, &encapsulation, sizeof(encapsulation), 0) < 0)
{
ERR_MSG("send");
return -1;
}
}
// 释放内存空间
sqlite3_free_table(pres);
return 0;
}
//退出
int quit(int newfd)
{
// 删除登录信息
char sql[256];
char *errmsg = NULL;
sprintf(sql, "delete from user_login where user=\"%s\";", encapsulation.name);
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "line:%d sqlite3_exec:%s\n", __LINE__, errmsg);
return 0;
}
return 0;
}
int sqlite3_delete(sqlite3 *db) // 删除单词表函数
{
char sql[128] = "drop table dict;";
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
// fprintf(stderr,"line:%d sqlite3_exec:%s\n",__LINE__,errmsg);
return -1;
}
printf("worddelete success\n");
return 0;
}
//登录回调函数
int login_callBack(void *arg, int column, char **column_text, char **column_name) // void* arg = &flag
{
if (strcmp(column_text[0], encapsulation.name) == 0 && strcmp(column_text[1], encapsulation.password) == 0)
{
encapsulation.type = 'S';
*(int *)arg = 1;
}
return 0;
}
//登录
int login(int newfd)
{
char sql[512];
char *errmsg = NULL;
int flag = 0;
sprintf(sql, "select * from user_register where user=\"%s\";", encapsulation.name);
if (sqlite3_exec(db, sql, login_callBack, &flag, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "line:%d sqlite3_exec:%s\n", __LINE__, errmsg);
return -1;
}
if (flag == 0)
{
//保存登录用户信息
encapsulation.type = 'E';
fprintf(stderr, "newfd = %d 登录失败\n", newfd);
if (send(newfd, &encapsulation, sizeof(encapsulation), 0) < 0)
{
ERR_MSG("send");
return -1;
}
}
else if (flag == 1)
{
// 将登陆信息保存到登录信息表中
char sql[364];
char *errmsg = NULL;
sprintf(sql, "insert into user_login values(\"%s\");", encapsulation.name);
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
encapsulation.type = 'R';
fprintf(stderr, "登录重复\n");
bzero(encapsulation.name, sizeof(encapsulation.name));
// 将数据包发送给客户端
if (send(newfd, &encapsulation, sizeof(encapsulation), 0) < 0)
{
ERR_MSG("send");
return -1;
}
fprintf(stderr, "line:%d sqlite3_exec:%s\n", __LINE__, errmsg);
return 0;
}
fprintf(stdout, "%s 登录成功\n", encapsulation.name);
if (send(newfd, &encapsulation, sizeof(encapsulation), 0) < 0)
{
ERR_MSG("send");
return -1;
}
}
return 0;
}
//================================================================================