功能:
注册:添加用户名和密码到数据库。
登录:检索数据库,查询是否符合。
查询:检索字典文本文件,查询结果存入数据库中。
历史:检索数据库,将登录账户的查询历史打印。
TCP服务器:
声明与定义
#define ERRLOG(msg) \
do \
{ \
printf("%s %s(%d):", __FILE__, __func__, __LINE__); \
perror(msg); \
exit(-1); \
} while (0)
//消息结构体封装
typedef struct msg
{
char code;
char name[20];
char txt[128];
} msg_t;
sqlite3 *sql_init();//数据库初始化
int log_sql(msg_t *clientmsg, sqlite3 *my_sql, int clientfd);//注册
int select_sql(msg_t *clientmsg, sqlite3 *my_sql, int clientfd);//登录
int printf_sql(msg_t *clientmsg, sqlite3 *my_sql, int clientfd);//检索单词
int history_sql(msg_t *clientmsg, sqlite3 *my_sql, int clientfd);//查看历史
int str_deal(char *buf);//字符串处理
int time_get(char *timestr);//获取时间
主函数体
//检查输入
if (3 != argc)
{
printf("Usage: %s <IP> <port>\n", argv[0]);
return -1;
}
1、第一步配置服务器结构体和套接字
//套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd)
{
ERRLOG("socket error");
}
//配置服务器结构体
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[2]));
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t serveraddr_len = sizeof(serveraddr);
if (-1 == bind(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len))
{
ERRLOG("bind error");
}
if (-1 == listen(sockfd, 5))
{
ERRLOG("listen error");
}
2、配置select函数的文件句柄
int maxfd = 0;
fd_set readfds;
fd_set readfdstemp;
FD_ZERO(&readfds);
FD_ZERO(&readfdstemp);
//将套接字描述符添加到句柄中
FD_SET(sockfd, &readfds);
maxfd = maxfd > sockfd ? maxfd : sockfd;
3、初始化数据库
sqlite3 *my_sql = sql_init();
4、通过select函数检索句柄实现的逻辑结构
int ret = 0;//保存有几个文件描述符就绪
int i = 0;//在循环中代表了文件描述符
int clientfd;//客户端连接创建的套接字
int nbyte = 0;//接受多少字节
while (1)
{
FD_ZERO(&readfdstemp);
readfdstemp = readfds;
//阻塞等待有1个或者多个文件描述符就绪
ret = select(maxfd + 1, &readfdstemp, NULL, NULL, NULL);
if (ret == -1)
{
PRINT_ERR("select init error");
}
else
{//遍历句柄,区分有客户端连接还是客户端发送消息
for (i = 3; i < (maxfd + 1) && ret != 0; i++)
{
if (FD_ISSET(i, &readfdstemp))
{
if (i == sockfd)
{
if ((clientfd = accept(i, NULL, NULL)) == -1)
{
PRINT_ERR("accept error");
}
printf("新用户已连接\n");
FD_SET(clientfd, &readfds);
maxfd = maxfd > clientfd ? maxfd : clientfd;
}
else
{
memset(&severmsg, 0, sizeof(severmsg));
if ((nbyte = recv(i, &severmsg, sizeof(severmsg), 0)) == -1)
{
PRINT_ERR("recv error");
}
else if (nbyte == 0)
{
printf("用户断开连接\n");
FD_CLR(i, &readfds);
close(i);
continue;
}
//根据结构体中的操作码.code判断客户端需求
switch (severmsg.code)
{
case '1':
log_sql(&severmsg, my_sql, i);
break;
case '2':
select_sql(&severmsg, my_sql, i);
break;
case '3':
printf_sql(&severmsg, my_sql, i);
break;
case '4':
history_sql(&severmsg, my_sql, i);
break;
}
}
//每次处理完一个就绪,已就绪的文件描述符就减少了一个
ret--;
}
}
}
}
功能模块
数据库初始化:
sqlite3 *sql_init()
{
sqlite3 *my_sql = NULL;
int ret = sqlite3_open("dictionary.db", &my_sql);
if (ret != SQLITE_OK)
{
printf("error:%d , errstr:%s", ret, sqlite3_errmsg(my_sql));
exit(-1);
}
char *errstr = NULL;
char user[128] = "CREATE TABLE IF NOT EXISTS usr (name txt primary key, pass txt);";
char hissql[128] = "CREATE TABLE IF NOT EXISTS history (name txt, date txt, word txt,jieshi char);";
if ((ret = sqlite3_exec(my_sql, user, NULL, NULL, &errstr)) != SQLITE_OK)
{
printf("errcode:[%d] errstr:[%s]\n", ret, errstr);
exit(-1);
}
if ((ret = sqlite3_exec(my_sql, hissql, NULL, NULL, &errstr)) != SQLITE_OK)
{
printf("errcode:[%d] errstr:[%s]\n", ret, errstr);
exit(-1);
}
sqlite3_free(errstr);
return my_sql;
}
2、注册用户名
int log_sql(msg_t *clientmsg, sqlite3 *my_sql, int clientfd)
{
int ret = 0;
char sqlstr[200] = {0};
sprintf(sqlstr, "INSERT INTO usr VALUES('%s' , '%s')", clientmsg->name, clientmsg->txt);
if ((ret = sqlite3_exec(my_sql, sqlstr, NULL, NULL, NULL)) != SQLITE_OK)
{
printf("errcode:[%d] errstr:[%s]\n", ret, sqlite3_errmsg(my_sql));
}
if (send(clientfd, clientmsg, sizeof(msg_t), 0) == -1)
{
PRINT_ERR("send error");
}
return 0;
}
3、登录用户名和密码
int select_sql(msg_t *clientmsg, sqlite3 *my_sql, int clientfd)
{
int ret = 0;
char sqlstr[200] = {0};
char **result;
int row;
int col;
sprintf(sqlstr, "SELECT * FROM usr WHERE name='%s' AND pass='%s'", clientmsg->name, clientmsg->txt);
if ((ret = sqlite3_get_table(my_sql, sqlstr, &result, &row, &col, NULL)) != SQLITE_OK)
{
printf("errcode:[%d] errstr:[%s]\n", ret, sqlite3_errmsg(my_sql));
}
if (row == 0)
{
//若无符合的用户名和密码,操作码置5。告诉客户端用户名或密码错误
clientmsg->code = '5';
}
sqlite3_free_table(result);
if (send(clientfd, clientmsg, sizeof(msg_t), 0) == -1)
{
PRINT_ERR("send error");
}
printf("%c,%s,%s\n", clientmsg->code, clientmsg->name, clientmsg->txt);
return 0;
}
4、检索词典
int printf_sql(msg_t *clientmsg, sqlite3 *my_sql, int clientfd)
{
//打开文件
FILE *fp;
char buf[300] = {0};
if ((fp = fopen("dict.txt", "r")) == NULL)
{
PRINT_ERR("open dictionary error");
}
int flag = 0;
while (fgets(buf, sizeof(buf), fp) != NULL)
{
if (strncmp(buf, clientmsg->txt, strlen(clientmsg->txt)) == 0)
{
if (buf[strlen(clientmsg->txt)] == ' ')
{
str_deal(buf);
flag = 1;
break;
}
}
}
if (flag == 1)
{
//处理查询结果
char word[30] = {0};
strcpy(word, clientmsg->txt);
char *p = buf + (strlen(clientmsg->txt) + 1);
strcpy(clientmsg->txt, p);
//获取时间
char timestr[50] = {0};
time_get(timestr);
//保存结果到历史中
int ret = 0;
char sqlstr[200] = {0};
sprintf(sqlstr, "INSERT INTO history VALUES('%s' ,'%s', '%s','%s')", clientmsg->name, timestr, word, p);
if ((ret = sqlite3_exec(my_sql, sqlstr, NULL, NULL, NULL)) != SQLITE_OK)
{
printf("errcode:[%d] errstr:[%s]\n", ret, sqlite3_errmsg(my_sql));
}
}
else
{
strcpy(clientmsg->txt, "词库中无此单词,请检查");
}
if (send(clientfd, clientmsg, sizeof(msg_t), 0) == -1)
{
PRINT_ERR("send error");
}
fclose(fp);
return 0;
}
5、历史记录查询
int history_sql(msg_t *clientmsg, sqlite3 *my_sql, int clientfd)
{
int ret = 0;
char sqlstr[200] = {0};
char **result;
int row;
int col;
sprintf(sqlstr, "SELECT * FROM history WHERE name='%s'", clientmsg->name);
if ((ret = sqlite3_get_table(my_sql, sqlstr, &result, &row, &col, NULL)) != SQLITE_OK)
{
printf("errcode:[%d] errstr:[%s]\n", ret, sqlite3_errmsg(my_sql));
}
//一行发送一次
int i = 0;
int j = 0;
for (i = 0; i < col; i++);//函数结果的数组中前四个为表头
int index = i;
for (i = 0; i < row; i++)
{
for (j = 0; j < col;)
{ //结果:用户名、时间、单词、单词解释……
//根据get_table函数的结果 +1为时间、+2为单词、+3为单词解释
sprintf(clientmsg->txt, "%s %s : %s", result[index + 1], result[index + 2], result[index+3]);
if (send(clientfd, clientmsg, sizeof(msg_t), 0) == -1)
{
PRINT_ERR("send error");
}
index += 4;
j += 4;
}
}
//发送结束符
strcpy(clientmsg->txt, "**over**");
if (send(clientfd, clientmsg, sizeof(msg_t), 0) == -1)
{
PRINT_ERR("send error");
}
return 0;
}
6、字符串处理,删除多余的空格。每个空格处,只保留一个空格
int str_deal(char *buf)
{
for (int i = 0; buf[i] != '\0';)
{
if (buf[i] == ' ' && buf[i + 1] == ' ')
{
for (int j = i + 1; buf[j] != '\0'; j++)
{
buf[j] = buf[j + 1];
}
}
else
{
i++;
}
}
return 0;
}
7、获取系统时间
int time_get(char *timestr)
{
time_t time_history;
struct tm *tm;
if ((time_history = time(NULL)) == -1)
PRINT_ERR("get time error");
if ((tm = localtime(&time_history)) == NULL)
PRINT_ERR("change time error");
sprintf(timestr, "%d-%02d-%02d %02d:%02d:%02d", tm->tm_year + 1900,
tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
return 0;
}
客户端
声明与定义
#define ERRLOG(msg) \
do \
{ \
printf("%s %s(%d):", __FILE__, __func__, __LINE__); \
perror(msg); \
exit(-1); \
} while (0)
typedef struct msg
{
char code;
char name[20];
char txt[128];
} msg_t;
int log_sql(msg_t *clientmsg, int sockfd);
int select_sql(msg_t *clientmsg, int sockfd);
int printf_sql(msg_t *clientmsg, int sockfd);
int history_sql(msg_t *clientmsg, int sockfd);
主函数体
if (3 != argc)
{
printf("Usage: %s <IP> <port>\n", argv[0]);
return -1;
}
1、配置服务器和服务器结构体、连接服务器
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd)
{
ERRLOG("socket error");
}
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[2]));
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t serveraddr_len = sizeof(serveraddr);
if (-1 == connect(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len))
{
ERRLOG("connect error");
}
2、逻辑及实现
msg_t clientmsg;
memset(&clientmsg, 0, sizeof(clientmsg));
while (1)
{
printf("************************************************\n");
printf("**************1、注册 2、登录 *******************\n");
printf("************************************************\n");
scanf(" %c", &(clientmsg.code));
printf("请输入用户名和密码:");
scanf("%s %s", clientmsg.name, clientmsg.txt);
switch (clientmsg.code)
{
case '1':
log_sql(&clientmsg, sockfd);
break;
case '2':
select_sql(&clientmsg, sockfd);
if (clientmsg.code == '2')
{ //如果登陆失败,服务器传回错误码5
goto NEXT;
}
break;
}
memset(clientmsg.txt, 0, sizeof(clientmsg.txt));
clientmsg.code = 0;
}
NEXT:
while (1)
{
printf("************************************************\n");
printf("**********1、查询 2、历史 3、登出***************\n");
printf("************************************************\n");
scanf(" %c", &(clientmsg.code));
switch (clientmsg.code)
{
case '1':
clientmsg.code = '3';
printf("输入单词:");
scanf("%s", clientmsg.txt);
printf_sql(&clientmsg, sockfd);
break;
case '2':
clientmsg.code = '4';
history_sql(&clientmsg, sockfd);
break;
}
memset(clientmsg.txt, 0, sizeof(clientmsg.txt));
clientmsg.code = 0;
}
功能模块
1、注册模块
int log_sql(msg_t *clientmsg, int sockfd)
{
int nbyte = 0;
if (send(sockfd, clientmsg, sizeof(msg_t), 0) == -1)
{
PRINT_ERR("send error");
}
if ((nbyte = recv(sockfd, clientmsg, sizeof(msg_t), 0)) == -1)
{
PRINT_ERR("recv error");
}
if (clientmsg->code == '5')
{
printf("注册成功\n");
}
else
{
printf("用户名已被注册\n");
}
return 0;
}
2、登录
int select_sql(msg_t *clientmsg, int sockfd)
{
int nbyte = 0;
if (send(sockfd, clientmsg, sizeof(msg_t), 0) == -1)
{
PRINT_ERR("send error");
}
if ((nbyte = recv(sockfd, clientmsg, sizeof(msg_t), 0)) == -1)
{
PRINT_ERR("recv error");
}
if (clientmsg->code == '5')
{
printf("用户名或密码不正确,请确认用户名和密码或注册\n");
}
else
{
printf("登录成功\n");
}
return 0;
}
3、查询
int printf_sql(msg_t *clientmsg, int sockfd)
{
int nbyte = 0;
if (send(sockfd, clientmsg, sizeof(msg_t), 0) == -1)
{
PRINT_ERR("send error");
}
if ((nbyte = recv(sockfd, clientmsg, sizeof(msg_t), 0)) == -1)
{
PRINT_ERR("recv error");
}
printf("%s\n", clientmsg->txt);
return 0;
}
4、历史查看
int history_sql(msg_t *clientmsg, int sockfd)
{
int nbyte = 0;
if (send(sockfd, clientmsg, sizeof(msg_t), 0) == -1)
{
PRINT_ERR("send error");
}
//判断是否接收完毕
while (strcmp(clientmsg->txt,"**over**"))
{
if ((nbyte = recv(sockfd, clientmsg, sizeof(msg_t), 0)) == -1)
{
PRINT_ERR("recv error");
}
printf("%s\n", clientmsg->txt);
}
return 0;
}