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"
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);
void do_login(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: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 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;
}
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;if (0 == flag){// 先打印字段名for (i = 0; i < ncolumn; i++){strcpy(msg_callback->data, "user or passwd ereror");}flag = 1;}// 再打印字段的值for (i = 0; i < ncolumn; i++){strcpy(msg_callback->data, "OK");}return 0; // 这里的回调函数要求必须有返回值 如果没有会报错 query aborted
}void do_login(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, "select * from user where name='%s' and passwd='%s'", msg->name, msg->data);printf("[do_regist]sqlstr = %s\n", sqlstr);if (sqlite3_exec(my_db, sqlstr, callback, (void *)msg, &errmsg) != SQLITE_OK){printf("errmsg = %s\n", errmsg);exit(-1);// sprintf(msg->data, "user or passwd ereror");}// 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 S 4
#define H 5
#define E 6
#define DATABASE "test.db"
typedef struct _MSG
{int type;char name[32];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;}else{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 (strncmp(msg->data, "OK", 3) == 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);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);}int choose = 0;if (strncmp(msg->data, "OK", 3) == 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)
{
}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
client.c