1.功能说明
一共四个功能:
注册
登录
查询单词
查询历史记录
单词和解释保存在文件中,单词和解释只占一行,
一行最多300个字节,单词和解释之间至少有一个空格。
2.功能演示
3、分阶段完成各个功能
3.1 完成服务器和客户端的连接
service.c
#include <head.h>
int main(int argc, const char *argv[])
{
// 入参合理性检查
if (3 != argc)
{
printf("usage error:%s <port> <ip>\n", argv[0]);
exit(-1);
}
// 创建套接字
int sockfd;
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("sockfd error");
exit(-1);
}
// 填充服务器网络信息结构体
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);
struct sockaddr_in clientaddr;
socklen_t clientaddr_len = sizeof(clientaddr);
memset(&clientaddr, 0, sizeof(clientaddr));
// 绑定
if (-1 == bind(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len))
{
perror("bind error");
exit(-1);
}
// 监听
printf("%d\n", sockfd);
if (listen(sockfd, 5) < 0)
{
perror("fail to listen");
exit(-1);
}
int acceptfd;
while (1)
{
acceptfd = 0;
printf("阻塞等待客户端连接...\n");
if (-1 == (acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &clientaddr_len)))
{
perror("accept errror");
exit(-1);
}
printf("111111111\n");
printf("客户端[%s][%d]连接成功...\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
pid_t pid;
pid = fork();
if (pid == -1)
{
perror("fork error");
exit(-1);
}
else if (pid == 0)
{
// 子进程
}
else
{
// 父进程
wait(NULL);
}
}
return 0;
}
client.c
#include <head.h>
void print_menu();
int main(int argc, const char *argv[])
{
// 入参合理性检查
if (3 != argc)
{
printf("usage error:%s <ip> <port>\n", argv[1]);
exit(-1);
}
// 创建套接字
int sockfd;
if (-1 == (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
{
perror("socket error");
exit(-1);
}
// 填充服务器网络信息结构体
struct sockaddr_in serveraddr;
socklen_t serveraddr_len = sizeof(serveraddr);
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
serveraddr.sin_port = htons(atoi(argv[2]));
if (-1 == connect(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len))
{
perror("connect error");
exit(-1);
}
printf("连接服务器成功...\n");
while (1)
{
print_menu();
sleep(10);
}
return 0;
}
void print_menu()
{
printf("-------------------------------------\n");
printf("| 1.regist 2.login 3.quit|\n");
printf("-------------------------------------\n");
return;
}
3.2 新增完成注册和退出功能
service.c
#include <head.h>
#include <sqlite3.h> //sqlite3
#define R 1
#define L 2
#define Q 3
#define H 4
#define DATABASE "test.db"
typedef struct _MSG
{
int type;
char name[32];
char data[128];
} msg_t;
void do_regist(msg_t *msg, int acceptfd, sqlite3 *my_db);
void do_quit(msg_t *msg, int acceptfd, sqlite3 *my_db);
int main(int argc, const char *argv[])
{
// 入参合理性检查
if (3 != argc)
{
printf("usage error:%s <port> <ip>\n", argv[0]);
exit(-1);
}
// 创建数据库
sqlite3 *my_db;
if (SQLITE_OK != sqlite3_open(DATABASE, &my_db))
{
printf("sqlite3 open error:%s\n", sqlite3_errmsg(my_db));
exit(-1);
}
// 创建套接字
int sockfd;
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("sockfd error");
exit(-1);
}
// 填充服务器网络信息结构体
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);
struct sockaddr_in clientaddr;
socklen_t clientaddr_len = sizeof(clientaddr);
memset(&clientaddr, 0, sizeof(clientaddr));
// 绑定
if (-1 == bind(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len))
{
perror("bind error");
exit(-1);
}
// 监听
printf("%d\n", sockfd);
if (listen(sockfd, 5) < 0)
{
perror("fail to listen");
exit(-1);
}
int acceptfd;
msg_t msg;
while (1)
{
acceptfd = 0;
printf("阻塞等待客户端连接...\n");
if (-1 == (acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &clientaddr_len)))
{
perror("accept errror");
exit(-1);
}
printf("111111111\n");
printf("客户端[%s][%d]连接成功...\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
pid_t pid;
pid = fork();
if (pid == -1)
{
perror("fork error");
exit(-1);
}
else if (pid == 0)
{
while (1)
{
if (-1 == recv(acceptfd, &msg, sizeof(msg), 0))
{
perror("recv error");
exit(-1);
}
// printf("msg_type = %d\n", msg.type);
// printf("msg.name = %s\n", msg.name);
// printf("msg.data = %s\n", msg.data);
switch (msg.type)
{
case R:
do_regist(&msg, acceptfd, my_db);
break;
case L:
break;
case Q:
printf("客户端[%s][%d]退出了...\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
do_quit(&msg, acceptfd, my_db);
break;
case H:
break;
}
}
// 子进程
}
else
{
// 父进程
close(acceptfd);
wait(NULL);
}
}
return 0;
}
void do_regist(msg_t *msg, int acceptfd, sqlite3 *my_db)
{
char sqlstr[512] = {0};
char *errmsg;
// printf("msg]msg.type = %d\n", msg->type);
// printf("[msg]msg.name = %s\n", msg->name);
// printf("[msg]msg.data = %s\n", msg->data);
sprintf(sqlstr, "insert into user values('%s','%s')", msg->name, msg->data);
// printf("[do_regist]sqlstr = %s\n", sqlstr);
if (sqlite3_exec(my_db, sqlstr, NULL, NULL, &errmsg) != SQLITE_OK)
{
printf("errmsg = %s\n", errmsg);
sprintf(msg->data, "user already exit,regist fail");
}
else
{
strcpy(msg->data, "OK");
}
// printf("[do_regist_send]msg.type = %d\n", msg->type);
// printf("[do_regist_send]msg.name = %s\n", msg->name);
// printf("[do_regist_send]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
return;
}
void do_quit(msg_t *msg, int acceptfd, sqlite3 *my_db)
{
exit(-1);
}
client.c
#include <head.h>
#include <sqlite3.h> //sqlite3
#define R 1
#define L 2
#define Q 3
#define H 4
#define DATABASE "test.db"
typedef struct _MSG
{
int type;
char name[32];
char data[128];
} msg_t;
void print_menu();
void login_user(msg_t msg, int sockfd);
void exit_system(msg_t *msg, int sockfd);
int regist_user(msg_t *msg, int sockfd);
int check_user(msg_t msg, int sockfd);
int main(int argc, const char *argv[])
{
// 入参合理性检查
if (3 != argc)
{
printf("usage error:%s <ip> <port>\n", argv[1]);
exit(-1);
}
// 创建套接字
sqlite3 *my_db;
if (SQLITE_OK != sqlite3_open(DATABASE, &my_db))
{
printf("sqlite3 open error:%s\n", sqlite3_errmsg(my_db));
exit(-1);
}
int sockfd;
if (-1 == (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
{
perror("socket error");
exit(-1);
}
// 填充服务器网络信息结构体
struct sockaddr_in serveraddr;
socklen_t serveraddr_len = sizeof(serveraddr);
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
serveraddr.sin_port = htons(atoi(argv[2]));
int choose = 0;
msg_t msg;
if (-1 == connect(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len))
{
perror("connect error");
exit(-1);
}
printf("连接服务器成功...\n");
while (1)
{
print_menu();
printf("请输入您的选择:");
scanf("%d", &choose);
switch (choose)
{
case R:
regist_user(&msg, sockfd);
case L:
login_user(msg, sockfd);
break;
case Q:
exit_system(&msg, sockfd);
break;
}
}
return 0;
}
void print_menu()
{
printf("-------------------------------------\n");
printf("| 1.regist 2.login 3.quit|\n");
printf("-------------------------------------\n");
return;
}
void exit_system(msg_t *msg, int sockfd)
{
system("clear");
msg->type = Q;
if (-1 == send(sockfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
close(sockfd);
printf("欢迎下次使用基于TCP的在线词典系统...\n");
exit(-1);
}
int check_user(msg_t msg, int sockfd)
{
printf("[check_user_send]msg.type = %d\n", msg.type);
printf("[check_user_send]msg.name = %s\n", msg.name);
printf("[check_user_send]msg.data = %s\n", msg.data);
if (-1 == send(sockfd, &msg, sizeof(msg), 0))
{
perror("send error");
exit(-1);
}
memset(&msg, 0, sizeof(msg));
if (-1 == recv(sockfd, &msg, sizeof(msg), 0))
{
perror("recv error");
exit(-1);
}
printf("[check_user_recv]msg.type = %d\n", msg.type);
printf("[check_user_recv]msg.name = %s\n", msg.name);
printf("[check_user_recv]msg.data = %s\n", msg.data);
if (strcmp(msg.data, "user already exit,regist fail") == 0)
{
return -1;
}
else
{
return 0;
}
}
int regist_user(msg_t *msg, int sockfd)
{
memset(msg, 0, sizeof(msg_t));
msg->type = R;
printf("[注册]请输入用户名:");
scanf("%s", msg->name);
printf("[注册]请输入密码:");
scanf("%s", msg->data);
// printf("[regist_user]msg.type = %d\n", msg->type);
// printf("[regist_user]msg.name = %s\n", msg->name);
// printf("[regist_user]msg.data = %s\n", msg->data);
if (-1 == send(sockfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
// printf("[regist_send]msg.type = %d\n", msg->type);
// printf("[regist_send]msg.name = %s\n", msg->name);
// printf("[regist_send]msg.data = %s\n", msg->data);
memset(msg, 0, sizeof(msg));
if (-1 == recv(sockfd, msg, sizeof(msg_t), 0))
{
perror("recv error");
exit(-1);
}
// printf("[regist_recv]msg.type = %d\n", msg->type);
// printf("[regist_recv]msg.name = %s\n", msg->name);
// printf("[regist_recv]msg.data = %s\n", msg->data);
if (strncmp(msg->data, "OK", 3) == 0)
{
printf("注册成功...\n");
return 1;
}
printf("用户名已存在,注册失败...\n");
return 0;
}
void login_user(msg_t msg, int sockfd)
{
return;
}
结果图:
3.3 新增完成登录功能(密码和用户名都正确才能登录成功)
service.c
#include <head.h>
#include <sqlite3.h> //sqlite3
#define R 1
#define L 2
#define Q 3
#define S 4
#define H 5
#define E 6
#define DATABASE "test.db"
#define FILEPATH "./dict.txt"
typedef struct _MSG
{
int type;
char name[256];
char data[128];
} msg_t;
void do_regist(msg_t *msg, int acceptfd, sqlite3 *my_db);
void do_quit(msg_t *msg, int acceptfd, sqlite3 *my_db);
int do_login(msg_t *msg, int acceptfd, sqlite3 *my_db);
int do_search(msg_t *msg, int acceptfd, sqlite3 *my_db, FILE *fp);
void getdata(char *data);
void do_history(msg_t *msg, int acceptfd, sqlite3 *my_db);
int flafs = 0; // 全局变量 使得系统只有在第一次查询时 打开一次文件,第二次不用再打开
int main(int argc, const char *argv[])
{
// 入参合理性检查
if (3 != argc)
{
printf("usage error:%s <port> <ip>\n", argv[0]);
exit(-1);
}
// 创建数据库
sqlite3 *my_db;
if (SQLITE_OK != sqlite3_open(DATABASE, &my_db))
{
printf("sqlite3 open error:%s\n", sqlite3_errmsg(my_db));
exit(-1);
}
FILE *fp; // 传入do_search();
// 创建套接字
int sockfd;
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("sockfd error");
exit(-1);
}
// 填充服务器网络信息结构体
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);
struct sockaddr_in clientaddr;
socklen_t clientaddr_len = sizeof(clientaddr);
memset(&clientaddr, 0, sizeof(clientaddr));
// 绑定
if (-1 == bind(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len))
{
perror("bind error");
exit(-1);
}
// 监听
printf("%d\n", sockfd);
if (listen(sockfd, 5) < 0)
{
perror("fail to listen");
exit(-1);
}
int acceptfd;
msg_t msg;
while (1)
{
acceptfd = 0;
printf("阻塞等待客户端连接...\n");
if (-1 == (acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &clientaddr_len)))
{
perror("accept errror");
exit(-1);
}
printf("111111111\n");
printf("客户端[%s][%d]连接成功...\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
pid_t pid;
pid = fork();
if (pid == -1)
{
perror("fork error");
exit(-1);
}
else if (pid == 0)
{
while (1)
{
if (-1 == recv(acceptfd, &msg, sizeof(msg), 0))
{
perror("recv error");
exit(-1);
}
printf("[main]msg_type = %d\n", msg.type);
printf("[main]msg.name = %s\n", msg.name);
printf("[main]msg.data = %s\n", msg.data);
switch (msg.type)
{
case R:
do_regist(&msg, acceptfd, my_db);
break;
case L:
do_login(&msg, acceptfd, my_db);
break;
case Q:
printf("客户端[%s][%d]退出了...\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
do_quit(&msg, acceptfd, my_db);
break;
case S:
do_search(&msg, acceptfd, my_db, fp);
break;
case H:
do_history(&msg, acceptfd, my_db);
break;
case E:
break;
}
}
// 子进程
}
else
{
// 父进程
close(acceptfd);
wait(NULL);
}
}
return 0;
}
void getdata(char *data)
{
time_t t;
struct tm *tp;
time(&t);
tp = localtime(&t);
sprintf(data, "%d-%d-%d %d:%d:%d", 1900 + tp->tm_year, 1 + tp->tm_mon, tp->tm_mday,
tp->tm_hour, tp->tm_min, tp->tm_sec);
}
void do_history(msg_t *msg, int acceptfd, sqlite3 *my_db)
{
char databuff[128] = {0};
getdata(databuff);
memset(msg, 0, sizeof(msg_t));
msg->type = H;
char sqlbuff[256] = {0};
sprintf(sqlbuff, "select * from record");
return;
}
int do_search(msg_t *msg, int acceptfd, sqlite3 *my_db, FILE *fp)
{
char buff[256] = {0};
printf("要查询的单词为:%s\n", msg->data);
int word_len = 0;
char *p = NULL;
word_len = strlen(msg->data);
printf("要查询的单词%s的长度%d\n", msg->data, word_len);
// if (flafs == 0)
// {
// printf("第一次查询,单词文件打开中...\n");
if (NULL == (fp = fopen(FILEPATH, "r")))
{
printf("[第一次]打开单词文件失败...\n");
strcpy(msg->data, "open error or EOF");
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
}
p = NULL;
fgets(buff, sizeof(buff), fp);
while (strncmp(msg->data, buff, word_len) != 0)
{
if (fgets(buff, sizeof(buff), fp) == NULL)
{
strcpy(msg->data, "NO_WORLD");
// printf("11111111111111111\n");
printf("[do_search]msg.type = %d\n", msg->type);
printf("[do_search]msg.name = %s\n", msg->name);
printf("[do_search]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
return 0;
}
}
printf("[strncmp]msg.data = %s\n", msg->data);
printf("[strncmp]buff=%s\n", buff);
p = buff;
p += word_len;
if (*p != ' ')
{
// printf("22222222222\n");
strcpy(msg->data, "NO_WORLD");
printf("[do_search]msg.type = %d\n", msg->type);
printf("[do_search]msg.name = %s\n", msg->name);
printf("[do_search]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
return 0;
}
while (*p == ' ')
{
p++;
}
strcpy(msg->data, p);
printf("[do_search]msg.type = %d\n", msg->type);
printf("[do_search]msg.name = %s\n", msg->name);
printf("[do_search]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
fclose(fp);
return 1;
}
void do_regist(msg_t *msg, int acceptfd, sqlite3 *my_db)
{
char sqlstr[512] = {0};
char *errmsg;
printf("[msg]msg.type = %d\n", msg->type);
printf("[msg]msg.name = %s\n", msg->name);
printf("[msg]msg.data = %s\n", msg->data);
sprintf(sqlstr, "insert into user values('%s','%s')", msg->name, msg->data);
printf("[do_regist]sqlstr = %s\n", sqlstr);
if (sqlite3_exec(my_db, sqlstr, NULL, NULL, &errmsg) != SQLITE_OK)
{
printf("errmsg = %s\n", errmsg);
strcpy(msg->data, "user already exit,regist fail");
}
else
{
strcpy(msg->data, "OK");
}
printf("[do_regist_send]msg.type = %d\n", msg->type);
printf("[do_regist_send]msg.name = %s\n", msg->name);
printf("[do_regist_send]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
return;
}
// int callback(void *arg, int ncolumn, char **f_value, char **f_name)
// {
// int i = 0;
// int flag = 0;
// msg_t *msg_callback = (msg_t *)arg;
// printf("[msg_callbackmsg_callback.type = %d\n", msg_callback->type);
// printf("[msg_callback]msg_callback.name = %s\n", msg_callback->name);
// printf("[msg_callback]msg_callback.data = %s\n", msg_callback->data);
// if (0 == flag)
// {
// // 先打印字段名
// for (i = 0; i < ncolumn; i++)
// {
// // strcpy(msg_callback->data, "user or passwd ereror");
// printf("\n");
// }
// flag = 1;
// }
// // 再打印字段的值
// for (i = 0; i < ncolumn; i++)
// {
// if ((f_value[i] != msg_callback->name) || (f_value[i] != msg_callback->data))
// {
// strcpy(msg_callback->data, "user or passwd ereror");
// }
// else
// {
// strcpy(msg_callback->data, "OK");
// }
// }
// return 0; // 这里的回调函数要求必须有返回值 如果没有会报错 query aborted
// }
int do_login(msg_t *msg, int acceptfd, sqlite3 *my_db)
{
char sqlstr[512] = {0};
printf("[msg]msg.type = %d\n", msg->type);
printf("[msg]msg.name = %s\n", msg->name);
printf("[msg]msg.data = %s\n", msg->data);
sprintf(sqlstr, "select * from user where name='%s' and passwd='%s'", msg->name, msg->data);
printf("[do_login]sqlstr = %s\n", sqlstr);
char *errmsg, **result;
int nrow, ncolumn;
// 通过sqlite3_get_table函数查询记录是否存在
sprintf(sqlstr, "select * from user where name = '%s' and passwd = '%s'", msg->name, msg->data);
if (sqlite3_get_table(my_db, sqlstr, &result, &nrow, &ncolumn, &errmsg) != SQLITE_OK)
{
printf("error : %s\n", errmsg);
}
// 通过nrow参数判断是否能够查询到疾记录,如果值为0,则查询不到,如果值为非0,则查询到
if (nrow == 0)
{
strcpy(msg->data, "user or passwd ereror");
printf("[do_login_send]msg.type = %d\n", msg->type);
printf("[do_login_send]msg.name = %s\n", msg->name);
printf("[do_login_send]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
return 0;
}
else
{
strncpy(msg->data, "OK", 256);
printf("[do_login_send]msg.type = %d\n", msg->type);
printf("[do_login_send]msg.name = %s\n", msg->name);
printf("[do_login_send]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
return 1;
}
}
void do_quit(msg_t *msg, int acceptfd, sqlite3 *my_db)
{
exit(-1);
}
client.c
#include <head.h>
#include <sqlite3.h> //sqlite3
#define R 1
#define L 2
#define Q 3
#define S 4
#define H 5
#define E 6
#define DATABASE "test.db"
#define FILEPATH "./dict.txt"
typedef struct _MSG
{
int type;
char name[256];
char data[128];
} msg_t;
void print_menu();
int login_user(msg_t *msg, int sockfd, sqlite3 *my_db);
void exit_system(msg_t *msg, int sockfd);
int regist_user(msg_t *msg, int sockfd);
int check_user(msg_t msg, int sockfd);
int search_world(msg_t *msg, int sockfd, sqlite3 *my_db);
int exit_search(msg_t *msg, int sockfd);
void query_menu();
void printf_history(msg_t *msg, int sockfd, sqlite3 *my_db);
int main(int argc, const char *argv[])
{
// 入参合理性检查
if (3 != argc)
{
printf("usage error:%s <ip> <port>\n", argv[1]);
exit(-1);
}
// 创建套接字
sqlite3 *my_db;
if (SQLITE_OK != sqlite3_open(DATABASE, &my_db))
{
printf("sqlite3 open error:%s\n", sqlite3_errmsg(my_db));
exit(-1);
}
int sockfd;
if (-1 == (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
{
perror("socket error");
exit(-1);
}
// 填充服务器网络信息结构体
struct sockaddr_in serveraddr;
socklen_t serveraddr_len = sizeof(serveraddr);
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
serveraddr.sin_port = htons(atoi(argv[2]));
int choose = 0;
msg_t msg;
if (-1 == connect(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len))
{
perror("connect error");
exit(-1);
}
printf("连接服务器成功...\n");
NEXT2:
while (1)
{
print_menu();
printf("请输入您的选择(1-3):");
scanf("%d", &choose);
switch (choose)
{
case R:
regist_user(&msg, sockfd);
break;
case L:
if (login_user(&msg, sockfd, my_db) == 1)
{
goto NEXT;
}
break;
case Q:
close(sockfd);
// system("clear");
printf("欢迎下次使用基于TCP的在线词典系统...\n");
exit(0);
}
}
NEXT:
while (1)
{
// system("clear");
query_menu();
printf("请输入您的选择(4-6):");
scanf("%d", &choose);
switch (choose)
{
case S:
search_world(&msg, sockfd, my_db);
break;
case H:
printf_history(&msg, sockfd, my_db);
break;
case E:
goto NEXT2;
break;
}
}
return 0;
}
void print_menu()
{
printf("-------------------------------------\n");
printf("| 1.regist 2.login 3.quit |\n");
printf("-------------------------------------\n");
return;
}
void query_menu()
{
printf("-------------------------------------\n");
printf("| 4.search 5.history 6.quit |\n");
printf("-------------------------------------\n");
return;
}
void exit_system(msg_t *msg, int sockfd)
{
system("clear");
msg->type = Q;
if (-1 == send(sockfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
close(sockfd);
printf("欢迎下次使用基于TCP的在线词典系统...\n");
exit(-1);
}
void printf_history(msg_t *msg, int sockfd, sqlite3 *my_db)
{
// memset(msg, 0, sizeof(msg_t));
// printf("[printf_history]msg.type = %d\n", msg->type);
// printf("[printf_history]msg.name = %s\n", msg->name); // 单词
// printf("[printf_history]msg.data = %s\n", msg->data); // OK
msg->type = H;
printf("<---历史记录如下--->\n");
while (1)
{
if (-1 == recv(sockfd, msg, sizeof(msg_t), 0))
{
perror("recv error");
exit(-1);
}
if (strcmp(msg->data, "***OVER***") == 0)
{
break;
}
printf("%s:%s\n", msg->name, msg->data);
}
return;
}
int regist_user(msg_t *msg, int sockfd)
{
memset(msg, 0, sizeof(msg_t));
msg->type = R;
printf("[注册]请输入用户名:");
scanf("%s", msg->name);
printf("[注册]请输入密码:");
scanf("%s", msg->data);
// printf("[regist_user]msg.type = %d\n", msg->type);
// printf("[regist_user]msg.name = %s\n", msg->name);
// printf("[regist_user]msg.data = %s\n", msg->data);
if (-1 == send(sockfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
// printf("[regist_send]msg.type = %d\n", msg->type);
// printf("[regist_send]msg.name = %s\n", msg->name);
// printf("[regist_send]msg.data = %s\n", msg->data);
memset(msg, 0, sizeof(msg));
if (-1 == recv(sockfd, msg, sizeof(msg_t), 0))
{
perror("recv error");
exit(-1);
}
// printf("[regist_recv]msg.type = %d\n", msg->type);
// printf("[regist_recv]msg.name = %s\n", msg->name);
// printf("[regist_recv]msg.data = %s\n", msg->data);
if (strcmp(msg->data, "OK") == 0)
{
printf("注册成功...\n");
return 1;
}
printf("用户名已存在,注册失败...\n");
return 0;
}
int login_user(msg_t *msg, int sockfd, sqlite3 *my_db)
{
memset(msg, 0, sizeof(msg_t));
msg->type = L;
printf("[登录]请输入用户名:");
scanf("%s", msg->name);
printf("[登录]请输入密码:");
scanf("%s", msg->data);
// printf("[login_user]msg.type = %d\n", msg->type);
// printf("[login_user]msg.name = %s\n", msg->name);
// printf("[login_user]msg.data = %s\n", msg->data);
if (-1 == send(sockfd, msg, sizeof(msg_t), 0))
{
perror("send error");
return -1;
}
// printf("[login_user_send]msg.type = %d\n", msg->type);
// printf("[login_user_send]msg.name = %s\n", msg->name);
// printf("[login_user_send]msg.data = %s\n", msg->data);
// memset(msg, 0, sizeof(msg_t));
if (-1 == recv(sockfd, msg, sizeof(msg_t), 0))
{
perror("recv error");
return -1;
}
// printf("[login_user_recv]msg.type = %d\n", msg->type);
// printf("[login_user_recv]msg.name = %s\n", msg->name);
// printf("[login_user_recv]msg.data = %s\n", msg->data);
if (strcmp(msg->data, "OK") == 0)
{
printf("登录成功...\n");
return 1;
}
printf("用户名或密码错误...\n");
return 0;
}
// int exit_search(msg_t *msg, int sockfd)
// {
// return 0;
// }
int search_world(msg_t *msg, int sockfd, sqlite3 *my_db)
{
// msg->type = S;
while (1)
{
// memset(msg, 0, sizeof(msg_t));
msg->type = S;
printf("[查询]请输入要查询的单词(按#退出):");
scanf("%s", msg->data);
if (msg->data[strlen(msg->data) - 1] == '\n')
{
msg->data[strlen(msg->data) - 1] = '\0';
}
// printf("[scanf]msg->name = %s\n", msg->name);
if (strcmp(msg->data, "#") == 0)
{
printf("你主动退出了查单词功能...\n");
break;
}
// printf("[do_search_send]msg.type = %d\n", msg->type);
// printf("[do_search_send]msg.name = %s\n", msg->name);
// printf("[do_search_send]msg.data = %s\n", msg->data);
if (-1 == send(sockfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
memset(msg, 0, sizeof(msg));
if (-1 == recv(sockfd, msg, sizeof(msg_t), 0))
{
perror("recv error");
exit(-1);
}
// printf("[do_search_recv]msg.type = %d\n", msg->type);
// printf("[do_search_recv]msg.name = %s\n", msg->name);
// printf("[do_search_recv]msg.data = %s\n", msg->data);
if (strncmp(msg->data, "NO_WORLD", 8) == 0)
{
printf("[*****没有此单词*****]...\n");
// return 0;
}
else
{
printf("[*****解释*****]:%s", msg->data);
}
}
return 0;
}
int check_user(msg_t msg, int sockfd)
{
// printf("[check_user_send]msg.type = %d\n", msg.type);
// printf("[check_user_send]msg.name = %s\n", msg.name);
// printf("[check_user_send]msg.data = %s\n", msg.data);
if (-1 == send(sockfd, &msg, sizeof(msg), 0))
{
perror("send error");
exit(-1);
}
memset(&msg, 0, sizeof(msg));
if (-1 == recv(sockfd, &msg, sizeof(msg), 0))
{
perror("recv error");
exit(-1);
}
// printf("[check_user_recv]msg.type = %d\n", msg.type);
// printf("[check_user_recv]msg.name = %s\n", msg.name);
// printf("[check_user_recv]msg.data = %s\n", msg.data);
if (strcmp(msg.data, "user already exit,regist fail") == 0)
{
return -1;
}
else
{
return 0;
}
}
运行结果图
3.4 完成查询单词功能(search)
单词文件
链接:https://pan.baidu.com/s/17qgZZpO7YyyQ0pCLYclg8A
提取码:关注收藏后,私信获取
service.c
#include <head.h>
#include <sqlite3.h> //sqlite3
#define R 1
#define L 2
#define Q 3
#define S 4
#define H 5
#define E 6
#define DATABASE "test.db"
#define FILEPATH "./dict.txt"
typedef struct _MSG
{
int type;
char name[256];
char data[128];
} msg_t;
void do_regist(msg_t *msg, int acceptfd, sqlite3 *my_db);
void do_quit(msg_t *msg, int acceptfd, sqlite3 *my_db);
int do_login(msg_t *msg, int acceptfd, sqlite3 *my_db);
int do_search(msg_t *msg, int acceptfd, sqlite3 *my_db, FILE *fp);
int flafs = 0; // 全局变量 使得系统只有在第一次查询时 打开一次文件,第二次不用再打开
int main(int argc, const char *argv[])
{
// 入参合理性检查
if (3 != argc)
{
printf("usage error:%s <port> <ip>\n", argv[0]);
exit(-1);
}
// 创建数据库
sqlite3 *my_db;
if (SQLITE_OK != sqlite3_open(DATABASE, &my_db))
{
printf("sqlite3 open error:%s\n", sqlite3_errmsg(my_db));
exit(-1);
}
FILE *fp; // 传入do_search();
// 创建套接字
int sockfd;
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("sockfd error");
exit(-1);
}
// 填充服务器网络信息结构体
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);
struct sockaddr_in clientaddr;
socklen_t clientaddr_len = sizeof(clientaddr);
memset(&clientaddr, 0, sizeof(clientaddr));
// 绑定
if (-1 == bind(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len))
{
perror("bind error");
exit(-1);
}
// 监听
printf("%d\n", sockfd);
if (listen(sockfd, 5) < 0)
{
perror("fail to listen");
exit(-1);
}
int acceptfd;
msg_t msg;
while (1)
{
acceptfd = 0;
printf("阻塞等待客户端连接...\n");
if (-1 == (acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &clientaddr_len)))
{
perror("accept errror");
exit(-1);
}
printf("111111111\n");
printf("客户端[%s][%d]连接成功...\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
pid_t pid;
pid = fork();
if (pid == -1)
{
perror("fork error");
exit(-1);
}
else if (pid == 0)
{
while (1)
{
if (-1 == recv(acceptfd, &msg, sizeof(msg), 0))
{
perror("recv error");
exit(-1);
}
printf("[main]msg_type = %d\n", msg.type);
printf("[main]msg.name = %s\n", msg.name);
printf("[main]msg.data = %s\n", msg.data);
switch (msg.type)
{
case R:
do_regist(&msg, acceptfd, my_db);
break;
case L:
do_login(&msg, acceptfd, my_db);
break;
case Q:
printf("客户端[%s][%d]退出了...\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
do_quit(&msg, acceptfd, my_db);
break;
case S:
do_search(&msg, acceptfd, my_db, fp);
break;
case H:
break;
case E:
break;
}
}
// 子进程
}
else
{
// 父进程
close(acceptfd);
wait(NULL);
}
}
return 0;
}
int do_search(msg_t *msg, int acceptfd, sqlite3 *my_db, FILE *fp)
{
char buff[256] = {0};
printf("要查询的单词为:%s\n", msg->name);
int word_len = 0;
char *p = NULL;
word_len = strlen(msg->name);
printf("要查询的单词%s的长度%d\n", msg->name, word_len);
// if (flafs == 0)
// {
// printf("第一次查询,单词文件打开中...\n");
if (NULL == (fp = fopen(FILEPATH, "r")))
{
printf("[第一次]打开单词文件失败...\n");
strcpy(msg->name, "open error or EOF");
strcpy(msg->data, "search error");
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
}
p = NULL;
fgets(buff, sizeof(buff), fp);
while (strncmp(msg->name, buff, word_len) != 0)
{
if (fgets(buff, sizeof(buff), fp) == NULL)
{
strcpy(msg->name, "NO_WORLD");
// printf("11111111111111111\n");
printf("[do_search]msg.type = %d\n", msg->type);
printf("[do_search]msg.name = %s\n", msg->name);
printf("[do_search]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
return 0;
}
}
printf("[strncmp]msg.name = %s\n", msg->name);
printf("[strncmp]buff=%s\n", buff);
p = buff;
p += word_len;
if (*p != ' ')
{
// printf("22222222222\n");
strcpy(msg->name, "NO_WORLD");
printf("[do_search]msg.type = %d\n", msg->type);
printf("[do_search]msg.name = %s\n", msg->name);
printf("[do_search]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
return 0;
}
while (*p == ' ')
{
p++;
}
strcpy(msg->name, "OK");
strcpy(msg->data, p);
printf("[do_search]msg.type = %d\n", msg->type);
printf("[do_search]msg.name = %s\n", msg->name);
printf("[do_search]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
fclose(fp);
return 1;
}
void do_regist(msg_t *msg, int acceptfd, sqlite3 *my_db)
{
char sqlstr[512] = {0};
char *errmsg;
printf("[msg]msg.type = %d\n", msg->type);
printf("[msg]msg.name = %s\n", msg->name);
printf("[msg]msg.data = %s\n", msg->data);
sprintf(sqlstr, "insert into user values('%s','%s')", msg->name, msg->data);
printf("[do_regist]sqlstr = %s\n", sqlstr);
if (sqlite3_exec(my_db, sqlstr, NULL, NULL, &errmsg) != SQLITE_OK)
{
printf("errmsg = %s\n", errmsg);
strcpy(msg->data, "user already exit,regist fail");
}
else
{
strcpy(msg->data, "OK");
}
printf("[do_regist_send]msg.type = %d\n", msg->type);
printf("[do_regist_send]msg.name = %s\n", msg->name);
printf("[do_regist_send]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
return;
}
// int callback(void *arg, int ncolumn, char **f_value, char **f_name)
// {
// int i = 0;
// int flag = 0;
// msg_t *msg_callback = (msg_t *)arg;
// printf("[msg_callbackmsg_callback.type = %d\n", msg_callback->type);
// printf("[msg_callback]msg_callback.name = %s\n", msg_callback->name);
// printf("[msg_callback]msg_callback.data = %s\n", msg_callback->data);
// if (0 == flag)
// {
// // 先打印字段名
// for (i = 0; i < ncolumn; i++)
// {
// // strcpy(msg_callback->data, "user or passwd ereror");
// printf("\n");
// }
// flag = 1;
// }
// // 再打印字段的值
// for (i = 0; i < ncolumn; i++)
// {
// if ((f_value[i] != msg_callback->name) || (f_value[i] != msg_callback->data))
// {
// strcpy(msg_callback->data, "user or passwd ereror");
// }
// else
// {
// strcpy(msg_callback->data, "OK");
// }
// }
// return 0; // 这里的回调函数要求必须有返回值 如果没有会报错 query aborted
// }
int do_login(msg_t *msg, int acceptfd, sqlite3 *my_db)
{
char sqlstr[512] = {0};
printf("[msg]msg.type = %d\n", msg->type);
printf("[msg]msg.name = %s\n", msg->name);
printf("[msg]msg.data = %s\n", msg->data);
sprintf(sqlstr, "select * from user where name='%s' and passwd='%s'", msg->name, msg->data);
printf("[do_login]sqlstr = %s\n", sqlstr);
char *errmsg, **result;
int nrow, ncolumn;
// 通过sqlite3_get_table函数查询记录是否存在
sprintf(sqlstr, "select * from user where name = '%s' and passwd = '%s'", msg->name, msg->data);
if (sqlite3_get_table(my_db, sqlstr, &result, &nrow, &ncolumn, &errmsg) != SQLITE_OK)
{
printf("error : %s\n", errmsg);
}
// 通过nrow参数判断是否能够查询到疾记录,如果值为0,则查询不到,如果值为非0,则查询到
if (nrow == 0)
{
strcpy(msg->data, "user or passwd ereror");
printf("[do_login_send]msg.type = %d\n", msg->type);
printf("[do_login_send]msg.name = %s\n", msg->name);
printf("[do_login_send]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
return 0;
}
else
{
strncpy(msg->data, "OK", 256);
printf("[do_login_send]msg.type = %d\n", msg->type);
printf("[do_login_send]msg.name = %s\n", msg->name);
printf("[do_login_send]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
return 1;
}
}
void do_quit(msg_t *msg, int acceptfd, sqlite3 *my_db)
{
exit(-1);
}
client.c
#include <head.h>
#include <sqlite3.h> //sqlite3
#define R 1
#define L 2
#define Q 3
#define S 4
#define H 5
#define E 6
#define DATABASE "test.db"
#define FILEPATH "./dict.txt"
typedef struct _MSG
{
int type;
char name[256];
char data[128];
} msg_t;
void print_menu();
int login_user(msg_t *msg, int sockfd, sqlite3 *my_db);
void exit_system(msg_t *msg, int sockfd);
int regist_user(msg_t *msg, int sockfd);
int check_user(msg_t msg, int sockfd);
int search_world(msg_t *msg, int sockfd, sqlite3 *my_db);
int exit_search(msg_t *msg, int sockfd);
void query_menu();
int main(int argc, const char *argv[])
{
// 入参合理性检查
if (3 != argc)
{
printf("usage error:%s <ip> <port>\n", argv[1]);
exit(-1);
}
// 创建套接字
sqlite3 *my_db;
if (SQLITE_OK != sqlite3_open(DATABASE, &my_db))
{
printf("sqlite3 open error:%s\n", sqlite3_errmsg(my_db));
exit(-1);
}
int sockfd;
if (-1 == (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
{
perror("socket error");
exit(-1);
}
// 填充服务器网络信息结构体
struct sockaddr_in serveraddr;
socklen_t serveraddr_len = sizeof(serveraddr);
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
serveraddr.sin_port = htons(atoi(argv[2]));
int choose = 0;
msg_t msg;
if (-1 == connect(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len))
{
perror("connect error");
exit(-1);
}
printf("连接服务器成功...\n");
NEXT2:
while (1)
{
print_menu();
printf("请输入您的选择(1-3):");
scanf("%d", &choose);
switch (choose)
{
case R:
regist_user(&msg, sockfd);
break;
case L:
if (login_user(&msg, sockfd, my_db) == 1)
{
goto NEXT;
}
break;
case Q:
close(sockfd);
// system("clear");
printf("欢迎下次使用基于TCP的在线词典系统...\n");
exit(0);
}
}
NEXT:
while (1)
{
// system("clear");
query_menu();
printf("请输入您的选择(4-6):");
scanf("%d", &choose);
switch (choose)
{
case S:
search_world(&msg, sockfd, my_db);
break;
case H:
break;
case E:
goto NEXT2;
break;
}
}
return 0;
}
void print_menu()
{
printf("-------------------------------------\n");
printf("| 1.regist 2.login 3.quit |\n");
printf("-------------------------------------\n");
return;
}
void query_menu()
{
printf("-------------------------------------\n");
printf("| 4.search 5.history 6.quit |\n");
printf("-------------------------------------\n");
return;
}
void exit_system(msg_t *msg, int sockfd)
{
system("clear");
msg->type = Q;
if (-1 == send(sockfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
close(sockfd);
printf("欢迎下次使用基于TCP的在线词典系统...\n");
exit(-1);
}
int regist_user(msg_t *msg, int sockfd)
{
memset(msg, 0, sizeof(msg_t));
msg->type = R;
printf("[注册]请输入用户名:");
scanf("%s", msg->name);
printf("[注册]请输入密码:");
scanf("%s", msg->data);
// printf("[regist_user]msg.type = %d\n", msg->type);
// printf("[regist_user]msg.name = %s\n", msg->name);
// printf("[regist_user]msg.data = %s\n", msg->data);
if (-1 == send(sockfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
// printf("[regist_send]msg.type = %d\n", msg->type);
// printf("[regist_send]msg.name = %s\n", msg->name);
// printf("[regist_send]msg.data = %s\n", msg->data);
memset(msg, 0, sizeof(msg));
if (-1 == recv(sockfd, msg, sizeof(msg_t), 0))
{
perror("recv error");
exit(-1);
}
// printf("[regist_recv]msg.type = %d\n", msg->type);
// printf("[regist_recv]msg.name = %s\n", msg->name);
// printf("[regist_recv]msg.data = %s\n", msg->data);
if (strcmp(msg->data, "OK") == 0)
{
printf("注册成功...\n");
return 1;
}
printf("用户名已存在,注册失败...\n");
return 0;
}
int login_user(msg_t *msg, int sockfd, sqlite3 *my_db)
{
memset(msg, 0, sizeof(msg_t));
msg->type = L;
printf("[登录]请输入用户名:");
scanf("%s", msg->name);
printf("[登录]请输入密码:");
scanf("%s", msg->data);
// printf("[login_user]msg.type = %d\n", msg->type);
// printf("[login_user]msg.name = %s\n", msg->name);
// printf("[login_user]msg.data = %s\n", msg->data);
if (-1 == send(sockfd, msg, sizeof(msg_t), 0))
{
perror("send error");
return -1;
}
// printf("[login_user_send]msg.type = %d\n", msg->type);
// printf("[login_user_send]msg.name = %s\n", msg->name);
// printf("[login_user_send]msg.data = %s\n", msg->data);
// memset(msg, 0, sizeof(msg_t));
if (-1 == recv(sockfd, msg, sizeof(msg_t), 0))
{
perror("recv error");
return -1;
}
// printf("[login_user_recv]msg.type = %d\n", msg->type);
// printf("[login_user_recv]msg.name = %s\n", msg->name);
// printf("[login_user_recv]msg.data = %s\n", msg->data);
if (strcmp(msg->data, "OK") == 0)
{
printf("登录成功...\n");
return 1;
}
printf("用户名或密码错误...\n");
return 0;
}
// int exit_search(msg_t *msg, int sockfd)
// {
// return 0;
// }
int search_world(msg_t *msg, int sockfd, sqlite3 *my_db)
{
// msg->type = S;
while (1)
{
memset(msg, 0, sizeof(msg_t));
msg->type = S;
printf("[查询]请输入要查询的单词(按#退出):");
scanf("%s", msg->name);
if (msg->name[strlen(msg->name) - 1] == '\n')
{
msg->name[strlen(msg->name) - 1] = '\0';
}
// printf("[scanf]msg->name = %s\n", msg->name);
if (strcmp(msg->name, "#") == 0)
{
printf("你主动退出了查单词功能...\n");
break;
}
// printf("[do_search_send]msg.type = %d\n", msg->type);
// printf("[do_search_send]msg.name = %s\n", msg->name);
// printf("[do_search_send]msg.data = %s\n", msg->data);
if (-1 == send(sockfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
memset(msg, 0, sizeof(msg));
if (-1 == recv(sockfd, msg, sizeof(msg_t), 0))
{
perror("recv error");
exit(-1);
}
// printf("[do_search_recv]msg.type = %d\n", msg->type);
// printf("[do_search_recv]msg.name = %s\n", msg->name);
// printf("[do_search_recv]msg.data = %s\n", msg->data);
if (strncmp(msg->name, "OK", 3) == 0)
{
printf("[*****解释*****]:%s", msg->data);
}
else if (strncmp(msg->name, "NO_WORLD", 8) == 0)
{
printf("[*****没有此单词*****]...\n");
}
}
return 0;
}
int check_user(msg_t msg, int sockfd)
{
// printf("[check_user_send]msg.type = %d\n", msg.type);
// printf("[check_user_send]msg.name = %s\n", msg.name);
// printf("[check_user_send]msg.data = %s\n", msg.data);
if (-1 == send(sockfd, &msg, sizeof(msg), 0))
{
perror("send error");
exit(-1);
}
memset(&msg, 0, sizeof(msg));
if (-1 == recv(sockfd, &msg, sizeof(msg), 0))
{
perror("recv error");
exit(-1);
}
// printf("[check_user_recv]msg.type = %d\n", msg.type);
// printf("[check_user_recv]msg.name = %s\n", msg.name);
// printf("[check_user_recv]msg.data = %s\n", msg.data);
if (strcmp(msg.data, "user already exit,regist fail") == 0)
{
return -1;
}
else
{
return 0;
}
}
结果图
3.5 完成记录查询功能(history)
service.c
#include <head.h>
#include <sqlite3.h> //sqlite3
#define R 1
#define L 2
#define Q 3
#define S 4
#define H 5
#define E 6
#define DATABASE "test.db"
#define FILEPATH "./dict.txt"
typedef struct _MSG
{
int type;
char name[256];
char data[128];
} msg_t;
void do_regist(msg_t *msg, int acceptfd, sqlite3 *my_db);
void do_quit(msg_t *msg, int acceptfd, sqlite3 *my_db);
int do_login(msg_t *msg, int acceptfd, sqlite3 *my_db);
int do_search(msg_t *msg, int acceptfd, sqlite3 *my_db, FILE *fp);
void getdata(char *data);
int history_callback(void *arg, int f_num, char **f_value, char **f_name);
void do_history(msg_t *msg, int acceptfd, sqlite3 *my_db);
int flafs = 0; // 全局变量 使得系统只有在第一次查询时 打开一次文件,第二次不用再打开
int main(int argc, const char *argv[])
{
// 入参合理性检查
if (3 != argc)
{
printf("usage error:%s <port> <ip>\n", argv[0]);
exit(-1);
}
// 创建数据库
sqlite3 *my_db;
if (SQLITE_OK != sqlite3_open(DATABASE, &my_db))
{
printf("sqlite3 open error:%s\n", sqlite3_errmsg(my_db));
exit(-1);
}
FILE *fp; // 传入do_search();
// 创建套接字
int sockfd;
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("sockfd error");
exit(-1);
}
// 填充服务器网络信息结构体
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);
struct sockaddr_in clientaddr;
socklen_t clientaddr_len = sizeof(clientaddr);
memset(&clientaddr, 0, sizeof(clientaddr));
// 绑定
if (-1 == bind(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len))
{
perror("bind error");
exit(-1);
}
// 监听
printf("%d\n", sockfd);
if (listen(sockfd, 5) < 0)
{
perror("fail to listen");
exit(-1);
}
int acceptfd;
msg_t msg;
while (1)
{
acceptfd = 0;
printf("阻塞等待客户端连接...\n");
if (-1 == (acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &clientaddr_len)))
{
perror("accept errror");
exit(-1);
}
printf("111111111\n");
printf("客户端[%s][%d]连接成功...\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
pid_t pid;
pid = fork();
if (pid == -1)
{
perror("fork error");
exit(-1);
}
else if (pid == 0)
{
while (1)
{
if (-1 == recv(acceptfd, &msg, sizeof(msg), 0))
{
perror("recv error");
exit(-1);
}
printf("[main]msg_type = %d\n", msg.type);
printf("[main]msg.name = %s\n", msg.name);
printf("[main]msg.data = %s\n", msg.data);
switch (msg.type)
{
case R:
do_regist(&msg, acceptfd, my_db);
break;
case L:
do_login(&msg, acceptfd, my_db);
break;
case Q:
printf("客户端[%s][%d]退出了...\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
do_quit(&msg, acceptfd, my_db);
break;
case S:
do_search(&msg, acceptfd, my_db, fp);
break;
case H:
do_history(&msg, acceptfd, my_db);
break;
case E:
break;
}
}
// 子进程
}
else
{
// 父进程
close(acceptfd);
wait(NULL);
}
}
return 0;
}
void getdata(char *data)
{
time_t t;
struct tm *tp;
time(&t);
tp = localtime(&t);
sprintf(data, "%d-%d-%d %d:%d:%d", 1900 + tp->tm_year, 1 + tp->tm_mon, tp->tm_mday,
tp->tm_hour, tp->tm_min, tp->tm_sec);
}
void do_history(msg_t *msg, int acceptfd, sqlite3 *my_db)
{
char databuff[128] = {0};
getdata(databuff);
printf("[do_history]msg.type = %d\n", msg->type);
printf("[do_history]msg.name = %s\n", msg->name);
printf("[do_history]msg.data = %s\n", msg->data);
msg->type = H;
char sqlbuff[512] = {0};
sprintf(sqlbuff, "select * from record where name='%s'", msg->name);
if (sqlite3_exec(my_db, sqlbuff, history_callback, (void *)&acceptfd, NULL) != SQLITE_OK)
{
printf("do_history sqlite3_exec error");
}
strcpy(msg->data, "***OVER***");
send(acceptfd, msg, sizeof(msg_t), 0);
return;
}
int history_callback(void *arg, int f_num, char **f_value, char **f_name)
{
int acceptfd_1;
msg_t msg;
acceptfd_1 = *(int *)arg;
sprintf(msg.data, "%s:%s", f_value[0], f_value[2]);
send(acceptfd_1, &msg, sizeof(msg_t), 0);
return 0;
}
int do_search(msg_t *msg, int acceptfd, sqlite3 *my_db, FILE *fp)
{
char buff[256] = {0};
char databuff[128] = {0};
getdata(databuff);
printf("要查询的单词为:%s\n", msg->data);
char sqlbuff[512] = {0};
sprintf(sqlbuff, "insert into record values('%s','%s','%s')", databuff, msg->name, msg->data);
printf("sqlbuff = %s\n", sqlbuff);
if (sqlite3_exec(my_db, sqlbuff, NULL, NULL, NULL) != SQLITE_OK)
{
printf("do_search sqlite3_exec error\n");
return -1;
}
int word_len = 0;
char *p = NULL;
word_len = strlen(msg->data);
printf("要查询的单词%s的长度%d\n", msg->data, word_len);
// if (flafs == 0)
// {
// printf("第一次查询,单词文件打开中...\n");
if (NULL == (fp = fopen(FILEPATH, "r")))
{
printf("[第一次]打开单词文件失败...\n");
strcpy(msg->data, "open error or EOF");
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
}
p = NULL;
fgets(buff, sizeof(buff), fp);
while (strncmp(msg->data, buff, word_len) != 0)
{
if (fgets(buff, sizeof(buff), fp) == NULL)
{
strcpy(msg->data, "NO_WORLD");
// printf("11111111111111111\n");
printf("[do_search]msg.type = %d\n", msg->type);
printf("[do_search]msg.name = %s\n", msg->name);
printf("[do_search]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
return 0;
}
}
printf("[strncmp]msg.data = %s\n", msg->data);
printf("[strncmp]buff=%s\n", buff);
p = buff;
p += word_len;
if (*p != ' ')
{
// printf("22222222222\n");
strcpy(msg->data, "NO_WORLD");
printf("[do_search]msg.type = %d\n", msg->type);
printf("[do_search]msg.name = %s\n", msg->name);
printf("[do_search]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
return 0;
}
while (*p == ' ')
{
p++;
}
strcpy(msg->data, p);
printf("[do_search]msg.type = %d\n", msg->type);
printf("[do_search]msg.name = %s\n", msg->name);
printf("[do_search]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
fclose(fp);
return 1;
}
void do_regist(msg_t *msg, int acceptfd, sqlite3 *my_db)
{
char sqlstr[512] = {0};
char *errmsg;
printf("[msg]msg.type = %d\n", msg->type);
printf("[msg]msg.name = %s\n", msg->name);
printf("[msg]msg.data = %s\n", msg->data);
sprintf(sqlstr, "insert into user values('%s','%s')", msg->name, msg->data);
printf("[do_regist]sqlstr = %s\n", sqlstr);
if (sqlite3_exec(my_db, sqlstr, NULL, NULL, &errmsg) != SQLITE_OK)
{
printf("errmsg = %s\n", errmsg);
strcpy(msg->data, "user already exit,regist fail");
}
else
{
strcpy(msg->data, "OK");
}
printf("[do_regist_send]msg.type = %d\n", msg->type);
printf("[do_regist_send]msg.name = %s\n", msg->name);
printf("[do_regist_send]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
return;
}
// int callback(void *arg, int ncolumn, char **f_value, char **f_name)
// {
// int i = 0;
// int flag = 0;
// msg_t *msg_callback = (msg_t *)arg;
// printf("[msg_callbackmsg_callback.type = %d\n", msg_callback->type);
// printf("[msg_callback]msg_callback.name = %s\n", msg_callback->name);
// printf("[msg_callback]msg_callback.data = %s\n", msg_callback->data);
// if (0 == flag)
// {
// // 先打印字段名
// for (i = 0; i < ncolumn; i++)
// {
// // strcpy(msg_callback->data, "user or passwd ereror");
// printf("\n");
// }
// flag = 1;
// }
// // 再打印字段的值
// for (i = 0; i < ncolumn; i++)
// {
// if ((f_value[i] != msg_callback->name) || (f_value[i] != msg_callback->data))
// {
// strcpy(msg_callback->data, "user or passwd ereror");
// }
// else
// {
// strcpy(msg_callback->data, "OK");
// }
// }
// return 0; // 这里的回调函数要求必须有返回值 如果没有会报错 query aborted
// }
int do_login(msg_t *msg, int acceptfd, sqlite3 *my_db)
{
char sqlstr[512] = {0};
printf("[msg]msg.type = %d\n", msg->type);
printf("[msg]msg.name = %s\n", msg->name);
printf("[msg]msg.data = %s\n", msg->data);
sprintf(sqlstr, "select * from user where name='%s' and passwd='%s'", msg->name, msg->data);
printf("[do_login]sqlstr = %s\n", sqlstr);
char *errmsg, **result;
int nrow, ncolumn;
// 通过sqlite3_get_table函数查询记录是否存在
sprintf(sqlstr, "select * from user where name = '%s' and passwd = '%s'", msg->name, msg->data);
if (sqlite3_get_table(my_db, sqlstr, &result, &nrow, &ncolumn, &errmsg) != SQLITE_OK)
{
printf("error : %s\n", errmsg);
}
// 通过nrow参数判断是否能够查询到疾记录,如果值为0,则查询不到,如果值为非0,则查询到
if (nrow == 0)
{
strcpy(msg->data, "user or passwd ereror");
printf("[do_login_send]msg.type = %d\n", msg->type);
printf("[do_login_send]msg.name = %s\n", msg->name);
printf("[do_login_send]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
return 0;
}
else
{
strncpy(msg->data, "OK", 256);
printf("[do_login_send]msg.type = %d\n", msg->type);
printf("[do_login_send]msg.name = %s\n", msg->name);
printf("[do_login_send]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
return 1;
}
}
void do_quit(msg_t *msg, int acceptfd, sqlite3 *my_db)
{
exit(-1);
}
client.c
#include <head.h>
#include <sqlite3.h> //sqlite3
#define R 1
#define L 2
#define Q 3
#define S 4
#define H 5
#define E 6
#define DATABASE "test.db"
#define FILEPATH "./dict.txt"
typedef struct _MSG
{
int type;
char name[256];
char data[128];
} msg_t;
void print_menu();
int login_user(msg_t *msg, int sockfd, sqlite3 *my_db);
void exit_system(msg_t *msg, int sockfd);
int regist_user(msg_t *msg, int sockfd);
int check_user(msg_t msg, int sockfd);
int search_world(msg_t *msg, int sockfd, sqlite3 *my_db);
int exit_search(msg_t *msg, int sockfd);
void query_menu();
int printf_history(msg_t *msg, int sockfd, sqlite3 *my_db);
int main(int argc, const char *argv[])
{
// 入参合理性检查
if (3 != argc)
{
printf("usage error:%s <ip> <port>\n", argv[1]);
exit(-1);
}
// 创建套接字
sqlite3 *my_db;
if (SQLITE_OK != sqlite3_open(DATABASE, &my_db))
{
printf("sqlite3 open error:%s\n", sqlite3_errmsg(my_db));
exit(-1);
}
int sockfd;
if (-1 == (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
{
perror("socket error");
exit(-1);
}
// 填充服务器网络信息结构体
struct sockaddr_in serveraddr;
socklen_t serveraddr_len = sizeof(serveraddr);
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
serveraddr.sin_port = htons(atoi(argv[2]));
int choose = 0;
msg_t msg;
if (-1 == connect(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len))
{
perror("connect error");
exit(-1);
}
printf("连接服务器成功...\n");
NEXT2:
while (1)
{
print_menu();
printf("请输入您的选择(1-3):");
scanf("%d", &choose);
switch (choose)
{
case R:
regist_user(&msg, sockfd);
break;
case L:
if (login_user(&msg, sockfd, my_db) == 1)
{
goto NEXT;
}
break;
case Q:
close(sockfd);
// system("clear");
printf("欢迎下次使用基于TCP的在线词典系统...\n");
exit(0);
}
}
NEXT:
while (1)
{
// system("clear");
query_menu();
printf("请输入您的选择(4-6):");
scanf("%d", &choose);
switch (choose)
{
case S:
search_world(&msg, sockfd, my_db);
break;
case H:
printf_history(&msg, sockfd, my_db);
break;
case E:
goto NEXT2;
break;
}
}
return 0;
}
void print_menu()
{
printf("-------------------------------------\n");
printf("| 1.regist 2.login 3.quit |\n");
printf("-------------------------------------\n");
return;
}
void query_menu()
{
printf("-------------------------------------\n");
printf("| 4.search 5.history 6.quit |\n");
printf("-------------------------------------\n");
return;
}
void exit_system(msg_t *msg, int sockfd)
{
system("clear");
msg->type = Q;
if (-1 == send(sockfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
close(sockfd);
printf("欢迎下次使用基于TCP的在线词典系统...\n");
exit(-1);
}
int printf_history(msg_t *msg, int sockfd, sqlite3 *my_db)
{
// memset(msg, 0, sizeof(msg_t));
// printf("[printf_history]msg.type = %d\n", msg->type);
// printf("[printf_history]msg.name = %s\n", msg->name); // 单词
// printf("[printf_history]msg.data = %s\n", msg->data); // OK
msg->type = H;
if (-1 == send(sockfd, msg, sizeof(msg_t), 0))
{
perror("send error");
return -1;
}
printf("<---历史记录如下--->\n");
while (1)
{
if (-1 == recv(sockfd, msg, sizeof(msg_t), 0))
{
perror("recv error");
exit(-1);
}
if (strcmp(msg->data, "***OVER***") == 0)
{
break;
}
printf("%s\n", msg->data);
}
return 0;
}
int regist_user(msg_t *msg, int sockfd)
{
memset(msg, 0, sizeof(msg_t));
msg->type = R;
printf("[注册]请输入用户名:");
scanf("%s", msg->name);
printf("[注册]请输入密码:");
scanf("%s", msg->data);
// printf("[regist_user]msg.type = %d\n", msg->type);
// printf("[regist_user]msg.name = %s\n", msg->name);
// printf("[regist_user]msg.data = %s\n", msg->data);
if (-1 == send(sockfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
// printf("[regist_send]msg.type = %d\n", msg->type);
// printf("[regist_send]msg.name = %s\n", msg->name);
// printf("[regist_send]msg.data = %s\n", msg->data);
memset(msg, 0, sizeof(msg));
if (-1 == recv(sockfd, msg, sizeof(msg_t), 0))
{
perror("recv error");
exit(-1);
}
// printf("[regist_recv]msg.type = %d\n", msg->type);
// printf("[regist_recv]msg.name = %s\n", msg->name);
// printf("[regist_recv]msg.data = %s\n", msg->data);
if (strcmp(msg->data, "OK") == 0)
{
printf("注册成功...\n");
return 1;
}
printf("用户名已存在,注册失败...\n");
return 0;
}
int login_user(msg_t *msg, int sockfd, sqlite3 *my_db)
{
memset(msg, 0, sizeof(msg_t));
msg->type = L;
printf("[登录]请输入用户名:");
scanf("%s", msg->name);
printf("[登录]请输入密码:");
scanf("%s", msg->data);
// printf("[login_user]msg.type = %d\n", msg->type);
// printf("[login_user]msg.name = %s\n", msg->name);
// printf("[login_user]msg.data = %s\n", msg->data);
if (-1 == send(sockfd, msg, sizeof(msg_t), 0))
{
perror("send error");
return -1;
}
// printf("[login_user_send]msg.type = %d\n", msg->type);
// printf("[login_user_send]msg.name = %s\n", msg->name);
// printf("[login_user_send]msg.data = %s\n", msg->data);
// memset(msg, 0, sizeof(msg_t));
if (-1 == recv(sockfd, msg, sizeof(msg_t), 0))
{
perror("recv error");
return -1;
}
// printf("[login_user_recv]msg.type = %d\n", msg->type);
// printf("[login_user_recv]msg.name = %s\n", msg->name);
// printf("[login_user_recv]msg.data = %s\n", msg->data);
if (strcmp(msg->data, "OK") == 0)
{
printf("登录成功...\n");
return 1;
}
printf("用户名或密码错误...\n");
return 0;
}
// int exit_search(msg_t *msg, int sockfd)
// {
// return 0;
// }
int search_world(msg_t *msg, int sockfd, sqlite3 *my_db)
{
// msg->type = S;
while (1)
{
// memset(msg, 0, sizeof(msg_t));
msg->type = S;
printf("[查询]请输入要查询的单词(按#退出):");
scanf("%s", msg->data);
if (msg->data[strlen(msg->data) - 1] == '\n')
{
msg->data[strlen(msg->data) - 1] = '\0';
}
// printf("[scanf]msg->name = %s\n", msg->name);
if (strcmp(msg->data, "#") == 0)
{
printf("你主动退出了查单词功能...\n");
break;
}
// printf("[do_search_send]msg.type = %d\n", msg->type);
// printf("[do_search_send]msg.name = %s\n", msg->name);
// printf("[do_search_send]msg.data = %s\n", msg->data);
if (-1 == send(sockfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
memset(msg, 0, sizeof(msg));
if (-1 == recv(sockfd, msg, sizeof(msg_t), 0))
{
perror("recv error");
exit(-1);
}
// printf("[do_search_recv]msg.type = %d\n", msg->type);
// printf("[do_search_recv]msg.name = %s\n", msg->name);
// printf("[do_search_recv]msg.data = %s\n", msg->data);
if (strncmp(msg->data, "NO_WORLD", 8) == 0)
{
printf("[*****没有此单词*****]...\n");
// return 0;
}
else
{
printf("[*****解释*****]:%s", msg->data);
}
}
return 0;
}
int check_user(msg_t msg, int sockfd)
{
// printf("[check_user_send]msg.type = %d\n", msg.type);
// printf("[check_user_send]msg.name = %s\n", msg.name);
// printf("[check_user_send]msg.data = %s\n", msg.data);
if (-1 == send(sockfd, &msg, sizeof(msg), 0))
{
perror("send error");
exit(-1);
}
memset(&msg, 0, sizeof(msg));
if (-1 == recv(sockfd, &msg, sizeof(msg), 0))
{
perror("recv error");
exit(-1);
}
// printf("[check_user_recv]msg.type = %d\n", msg.type);
// printf("[check_user_recv]msg.name = %s\n", msg.name);
// printf("[check_user_recv]msg.data = %s\n", msg.data);
if (strcmp(msg.data, "user already exit,regist fail") == 0)
{
return -1;
}
else
{
return 0;
}
}
3.5通过select实现以上全部功能(多路IO复用)
service.c
#include <head.h>
#include <sqlite3.h> //sqlite3
#define R 1
#define L 2
#define Q 3
#define S 4
#define H 5
#define E 6
#define DATABASE "test.db"
#define FILEPATH "./dict.txt"
typedef struct _MSG
{
int type;
char name[256];
char data[128];
} msg_t;
void do_regist(msg_t *msg, int acceptfd, sqlite3 *my_db); // 处理注册信息的函数
int do_login(msg_t *msg, int acceptfd, sqlite3 *my_db); // 处理登录请求的函数
int do_search(msg_t *msg, int acceptfd, sqlite3 *my_db, FILE *fp); // 处理查单词的函数
void getdata(char *data);
int history_callback(void *arg, int f_num, char **f_value, char **f_name); // 历史记录查询回调函数 显示指定用户的所有历史查询记录
void do_history(msg_t *msg, int acceptfd, sqlite3 *my_db);
int main(int argc, const char *argv[])
{
// 入参合理性检查
if (3 != argc)
{
printf("usage error:%s <port> <ip>\n", argv[0]);
exit(-1);
}
// 打开数据库 需要自己提前创建好.db结尾的数据库文件
// 终端执行sqlite3 test.db
// CREATE TABLE user(name TEXT PRIMARY KEY,passwd TEXT);
sqlite3 *my_db; // 数据库句柄
if (SQLITE_OK != sqlite3_open(DATABASE, &my_db)) // 打开数据库
{
printf("sqlite3 open error:%s\n", sqlite3_errmsg(my_db));
exit(-1);
}
FILE *fp; // FILE *指针 对应打开文件
// 创建套接字
int sockfd;
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("sockfd error");
exit(-1);
}
// 填充服务器网络信息结构体
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);
struct sockaddr_in clientaddr;
socklen_t clientaddr_len = sizeof(clientaddr);
memset(&clientaddr, 0, sizeof(clientaddr));
// 绑定
if (-1 == bind(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len))
{
perror("bind error");
exit(-1);
}
// 监听
printf("%d\n", sockfd);
if (listen(sockfd, 5) < 0)
{
perror("fail to listen");
exit(-1);
}
int max_fd = 0; // 用来记录最大的文件描述符
int acceptfd;
msg_t msg;
fd_set readfds; // 母本 监听集合
FD_ZERO(&readfds);
fd_set readfds_temp; // 副本 用于给select擦除用
FD_ZERO(&readfds_temp);
// 将sockfd放入监听集合
FD_SET(sockfd, &readfds);
max_fd = max_fd > sockfd ? max_fd : sockfd;
printf("max_fd = %d\n", max_fd);
int ret;
int i;
int recvbyte = 0;
while (1)
{
readfds_temp = readfds;
if (-1 == (ret = select(max_fd + 1, &readfds_temp, NULL, NULL, NULL))) // select阻塞
{
perror("select error");
return -1;
}
if (FD_ISSET(sockfd, &readfds_temp)) // 判断是否有客户端要连接
{
if (-1 == (acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &clientaddr_len)))
{
perror("accpet error");
return -1;
}
printf("*****客户端[%s][%d]连接成功...\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
FD_SET(acceptfd, &readfds);
printf("%d\n", acceptfd);
max_fd = max_fd > acceptfd ? max_fd : acceptfd;
}
for (i = 5; i < max_fd + 1 && ret != 0; i++) // 检测客户端,检查是哪一个客户端发送的消息
{
if (FD_ISSET(i, &readfds_temp))
{
if (-1 == (recvbyte = recv(i, &msg, sizeof(msg_t), 0)))
{
perror("recv error");
return -1;
}
else if (recvbyte == 0)
{
close(i);
FD_CLR(i, &readfds);
if (i == max_fd)
{
--max_fd;
}
printf("客户端退出了...\n");
}
else
{
switch (msg.type)
{
case R:
do_regist(&msg, i, my_db);
break;
case L:
do_login(&msg, i, my_db);
break;
case Q:
printf("客户端[%s][%d]退出了...\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
break;
case S:
do_search(&msg, i, my_db, fp);
break;
case H:
do_history(&msg, i, my_db);
break;
case E:
break;
}
}
}
}
}
return 0;
}
void getdata(char *data) // 获取系统时间函数
{
time_t t;
struct tm *tp;
time(&t);
tp = localtime(&t);
sprintf(data, "%d-%d-%d %d:%d:%d", 1900 + tp->tm_year, 1 + tp->tm_mon, tp->tm_mday,
tp->tm_hour, tp->tm_min, tp->tm_sec);
}
void do_history(msg_t *msg, int acceptfd, sqlite3 *my_db)
{
char databuff[128] = {0};
getdata(databuff);
printf("[do_history]msg.type = %d\n", msg->type);
printf("[do_history]msg.name = %s\n", msg->name);
printf("[do_history]msg.data = %s\n", msg->data);
msg->type = H;
char sqlbuff[512] = {0};
sprintf(sqlbuff, "select * from record where name='%s'", msg->name);
if (sqlite3_exec(my_db, sqlbuff, history_callback, (void *)&acceptfd, NULL) != SQLITE_OK)
{
printf("do_history sqlite3_exec error");
}
strcpy(msg->data, "***OVER***");
send(acceptfd, msg, sizeof(msg_t), 0);
return;
}
int history_callback(void *arg, int f_num, char **f_value, char **f_name)
{
int acceptfd_1;
msg_t msg;
acceptfd_1 = *(int *)arg;
sprintf(msg.data, "%s:%s", f_value[0], f_value[2]);
send(acceptfd_1, &msg, sizeof(msg_t), 0);
return 0;
}
int do_search(msg_t *msg, int acceptfd, sqlite3 *my_db, FILE *fp)
{
if (NULL == (fp = fopen(FILEPATH, "r")))
{
printf("[第一次]打开单词文件失败...\n");
strcpy(msg->data, "open error or EOF");
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
}
char buff[256] = {0};
char databuff[128] = {0};
getdata(databuff);
printf("要查询的单词为:%s\n", msg->data);
char sqlbuff[512] = {0};
sprintf(sqlbuff, "insert into record values('%s','%s','%s')", databuff, msg->name, msg->data);
printf("sqlbuff = %s\n", sqlbuff);
if (sqlite3_exec(my_db, sqlbuff, NULL, NULL, NULL) != SQLITE_OK)
{
printf("do_search sqlite3_exec error\n");
return -1;
}
int word_len = 0;
char *p;
p = NULL;
word_len = strlen(msg->data);
printf("要查询的单词%s的长度%d\n", msg->data, word_len);
while (fgets(buff, sizeof(buff), fp) != NULL)
{
p = buff;
p += word_len;
if (strncmp(msg->data, buff, word_len) == 0 && (*p == ' ')) // 相同字母
{
while (p++)
{
if (*p != ' ')
{
break;
}
} // 指向解释
strcpy(msg->data, p);
printf("[do_search2]msg.type = %d\n", msg->type);
printf("[do_search2]msg.name = %s\n", msg->name);
printf("[do_search2]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
return -1;
}
fclose(fp);
return 1;
}
else
{
continue;
}
}
strcpy(msg->data, "NO_WORLD");
printf("[do_search]msg.type = %d\n", msg->type);
printf("[do_search]msg.name = %s\n", msg->name);
printf("[do_search]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
fclose(fp);
return 0;
}
void do_regist(msg_t *msg, int acceptfd, sqlite3 *my_db)
{
char sqlstr[512] = {0};
char *errmsg;
printf("[msg]msg.type = %d\n", msg->type);
printf("[msg]msg.name = %s\n", msg->name);
printf("[msg]msg.data = %s\n", msg->data);
sprintf(sqlstr, "insert into user values('%s','%s')", msg->name, msg->data);
printf("[do_regist]sqlstr = %s\n", sqlstr);
if (sqlite3_exec(my_db, sqlstr, NULL, NULL, &errmsg) != SQLITE_OK)
{
printf("errmsg = %s\n", errmsg);
strcpy(msg->data, "user already exit,regist fail");
}
else
{
strcpy(msg->data, "OK");
}
printf("[do_regist_send]msg.type = %d\n", msg->type);
printf("[do_regist_send]msg.name = %s\n", msg->name);
printf("[do_regist_send]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
return;
}
int do_login(msg_t *msg, int acceptfd, sqlite3 *my_db)
{
char sqlstr[512] = {0};
printf("[msg]msg.type = %d\n", msg->type);
printf("[msg]msg.name = %s\n", msg->name);
printf("[msg]msg.data = %s\n", msg->data);
sprintf(sqlstr, "select * from user where name='%s' and passwd='%s'", msg->name, msg->data);
printf("[do_login]sqlstr = %s\n", sqlstr);
char *errmsg, **result;
int nrow, ncolumn;
// 通过sqlite3_get_table函数查询记录是否存在
sprintf(sqlstr, "select * from user where name = '%s' and passwd = '%s'", msg->name, msg->data);
if (sqlite3_get_table(my_db, sqlstr, &result, &nrow, &ncolumn, &errmsg) != SQLITE_OK)
{
printf("error : %s\n", errmsg);
}
// 通过nrow参数判断是否能够查询到疾记录,如果值为0,则查询不到,如果值为非0,则查询到
if (nrow == 0)
{
strcpy(msg->data, "user or passwd ereror");
printf("[do_login_send]msg.type = %d\n", msg->type);
printf("[do_login_send]msg.name = %s\n", msg->name);
printf("[do_login_send]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
return 0;
}
else
{
strncpy(msg->data, "OK", 256);
printf("[do_login_send]msg.type = %d\n", msg->type);
printf("[do_login_send]msg.name = %s\n", msg->name);
printf("[do_login_send]msg.data = %s\n", msg->data);
if (-1 == send(acceptfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
return 1;
}
}
client.c
#include <head.h>
#include <sqlite3.h> //sqlite3
#define R 1
#define L 2
#define Q 3
#define S 4
#define H 5
#define E 6
#define DATABASE "test.db"
#define FILEPATH "./dict.txt"
typedef struct _MSG
{
int type;
char name[256];
char data[128];
} msg_t;
void print_menu();
int login_user(msg_t *msg, int sockfd, sqlite3 *my_db);
void exit_system(msg_t *msg, int sockfd);
int regist_user(msg_t *msg, int sockfd);
int check_user(msg_t msg, int sockfd);
int search_world(msg_t *msg, int sockfd, sqlite3 *my_db);
int exit_search(msg_t *msg, int sockfd);
void query_menu();
int printf_history(msg_t *msg, int sockfd, sqlite3 *my_db);
int main(int argc, const char *argv[])
{
// 入参合理性检查
if (3 != argc)
{
printf("usage error:%s <ip> <port>\n", argv[1]);
exit(-1);
}
// 创建套接字
sqlite3 *my_db;
if (SQLITE_OK != sqlite3_open(DATABASE, &my_db))
{
printf("sqlite3 open error:%s\n", sqlite3_errmsg(my_db));
exit(-1);
}
int sockfd;
if (-1 == (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
{
perror("socket error");
exit(-1);
}
// 填充服务器网络信息结构体
struct sockaddr_in serveraddr;
socklen_t serveraddr_len = sizeof(serveraddr);
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
serveraddr.sin_port = htons(atoi(argv[2]));
int choose = 0;
msg_t msg;
if (-1 == connect(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len))
{
perror("connect error");
exit(-1);
}
printf("连接服务器成功...\n");
NEXT2:
while (1)
{
print_menu();
printf("请输入您的选择(1-3):");
scanf("%d", &choose);
switch (choose)
{
case R:
regist_user(&msg, sockfd);
break;
case L:
if (login_user(&msg, sockfd, my_db) == 1)
{
goto NEXT;
}
break;
case Q:
// exit_system(&msg, sockfd);
close(sockfd);
system("clear");
printf("欢迎下次使用基于TCP的在线词典系统...\n");
exit(0);
}
}
NEXT:
while (1)
{
// system("clear");
query_menu();
printf("请输入您的选择(4-6):");
scanf("%d", &choose);
switch (choose)
{
case S:
search_world(&msg, sockfd, my_db);
break;
case H:
printf_history(&msg, sockfd, my_db);
break;
case E:
goto NEXT2;
break;
}
}
return 0;
}
void print_menu()
{
printf("-------------------------------------\n");
printf("| 1.regist 2.login 3.quit |\n");
printf("-------------------------------------\n");
return;
}
void query_menu()
{
printf("-------------------------------------\n");
printf("| 4.search 5.history 6.quit |\n");
printf("-------------------------------------\n");
return;
}
void exit_system(msg_t *msg, int sockfd)
{
system("clear");
msg->type = Q;
if (-1 == send(sockfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
close(sockfd);
printf("欢迎下次使用基于TCP的在线词典系统...\n");
exit(-1);
}
int printf_history(msg_t *msg, int sockfd, sqlite3 *my_db)
{
// memset(msg, 0, sizeof(msg_t));
// printf("[printf_history]msg.type = %d\n", msg->type);
// printf("[printf_history]msg.name = %s\n", msg->name); // 单词
// printf("[printf_history]msg.data = %s\n", msg->data); // OK
msg->type = H;
if (-1 == send(sockfd, msg, sizeof(msg_t), 0))
{
perror("send error");
return -1;
}
printf("<---历史记录如下--->\n");
while (1)
{
if (-1 == recv(sockfd, msg, sizeof(msg_t), 0))
{
perror("recv error");
exit(-1);
}
if (strcmp(msg->data, "***OVER***") == 0)
{
break;
}
printf("%s\n", msg->data);
}
return 0;
}
int regist_user(msg_t *msg, int sockfd)
{
memset(msg, 0, sizeof(msg_t));
msg->type = R;
printf("[注册]请输入用户名:");
scanf("%s", msg->name);
printf("[注册]请输入密码:");
scanf("%s", msg->data);
// printf("[regist_user]msg.type = %d\n", msg->type);
// printf("[regist_user]msg.name = %s\n", msg->name);
// printf("[regist_user]msg.data = %s\n", msg->data);
if (-1 == send(sockfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
// printf("[regist_send]msg.type = %d\n", msg->type);
// printf("[regist_send]msg.name = %s\n", msg->name);
// printf("[regist_send]msg.data = %s\n", msg->data);
memset(msg, 0, sizeof(msg));
if (-1 == recv(sockfd, msg, sizeof(msg_t), 0))
{
perror("recv error");
exit(-1);
}
// printf("[regist_recv]msg.type = %d\n", msg->type);
// printf("[regist_recv]msg.name = %s\n", msg->name);
// printf("[regist_recv]msg.data = %s\n", msg->data);
if (strcmp(msg->data, "OK") == 0)
{
printf("注册成功...\n");
return 1;
}
printf("用户名已存在,注册失败...\n");
return 0;
}
int login_user(msg_t *msg, int sockfd, sqlite3 *my_db)
{
memset(msg, 0, sizeof(msg_t));
msg->type = L;
printf("[登录]请输入用户名:");
scanf("%s", msg->name);
printf("[登录]请输入密码:");
scanf("%s", msg->data);
// printf("[login_user]msg.type = %d\n", msg->type);
// printf("[login_user]msg.name = %s\n", msg->name);
// printf("[login_user]msg.data = %s\n", msg->data);
if (-1 == send(sockfd, msg, sizeof(msg_t), 0))
{
perror("send error");
return -1;
}
// printf("[login_user_send]msg.type = %d\n", msg->type);
// printf("[login_user_send]msg.name = %s\n", msg->name);
// printf("[login_user_send]msg.data = %s\n", msg->data);
// memset(msg, 0, sizeof(msg_t));
if (-1 == recv(sockfd, msg, sizeof(msg_t), 0))
{
perror("recv error");
return -1;
}
// printf("[login_user_recv]msg.type = %d\n", msg->type);
// printf("[login_user_recv]msg.name = %s\n", msg->name);
// printf("[login_user_recv]msg.data = %s\n", msg->data);
if (strcmp(msg->data, "OK") == 0)
{
printf("登录成功...\n");
return 1;
}
printf("用户名或密码错误...\n");
return 0;
}
// int exit_search(msg_t *msg, int sockfd)
// {
// return 0;
// }
int search_world(msg_t *msg, int sockfd, sqlite3 *my_db)
{
// msg->type = S;
while (1)
{
// memset(msg, 0, sizeof(msg_t));
msg->type = S;
printf("[查询]请输入要查询的单词(按#退出):");
scanf("%s", msg->data);
if (msg->data[strlen(msg->data) - 1] == '\n')
{
msg->data[strlen(msg->data) - 1] = '\0';
}
// printf("[scanf]msg->name = %s\n", msg->name);
if (strcmp(msg->data, "#") == 0)
{
printf("你主动退出了查单词功能...\n");
break;
}
// printf("[do_search_send]msg.type = %d\n", msg->type);
// printf("[do_search_send]msg.name = %s\n", msg->name);
// printf("[do_search_send]msg.data = %s\n", msg->data);
if (-1 == send(sockfd, msg, sizeof(msg_t), 0))
{
perror("send error");
exit(-1);
}
memset(msg, 0, sizeof(msg));
if (-1 == recv(sockfd, msg, sizeof(msg_t), 0))
{
perror("recv error");
exit(-1);
}
// printf("[do_search_recv]msg.type = %d\n", msg->type);
// printf("[do_search_recv]msg.name = %s\n", msg->name);
// printf("[do_search_recv]msg.data = %s\n", msg->data);
if (strncmp(msg->data, "NO_WORLD", 8) == 0)
{
printf("[*****没有此单词*****]...\n");
// return 0;
}
else
{
printf("[*****解释*****]:%s", msg->data);
}
}
return 0;
}
int check_user(msg_t msg, int sockfd)
{
// printf("[check_user_send]msg.type = %d\n", msg.type);
// printf("[check_user_send]msg.name = %s\n", msg.name);
// printf("[check_user_send]msg.data = %s\n", msg.data);
if (-1 == send(sockfd, &msg, sizeof(msg), 0))
{
perror("send error");
exit(-1);
}
memset(&msg, 0, sizeof(msg));
if (-1 == recv(sockfd, &msg, sizeof(msg), 0))
{
perror("recv error");
exit(-1);
}
// printf("[check_user_recv]msg.type = %d\n", msg.type);
// printf("[check_user_recv]msg.name = %s\n", msg.name);
// printf("[check_user_recv]msg.data = %s\n", msg.data);
if (strcmp(msg.data, "user already exit,regist fail") == 0)
{
return -1;
}
else
{
return 0;
}
}
需提前安装sqlite3数据库,安装教程参考以下文章
gcc service.c -lsqlite3 -o service
gcc client.c -lsqlite3 -o client
./service 192.168.250.100 8888
./client 192.168.250.100 8888