目录
一、前言
今天是培训的第62天,这两个月的时间里,先后学习了C语言、shell编程、makefile编程、数据结构、IO进程和网络编程,时间紧任务重,但只学习知识不去运用肯定是不行的,所以通过今天这个项目,对所学知识进行回顾和融会贯通,同样也是一个短期的成果展示。
二、项目介绍
主要利用数据库和TCP网络编程,制作一个服务器和客户端程序,实现:一用户注册,二用户登录,三用户在线查询单词,四查询用户历史记录功能。
服务器程序等待客户端连接,接收客户端发来的数据,根据不同的数据包类型,决定是执行注册操作、还是登录操作,如果是注册程序还需遍历数据库用户表,判断用户名是否重复注册,如果是登录操作还需与数据库用户表对比,判断用户名和密码是否匹配;接着如果用户登录成功,再根据数据包类型,决定执行单词查询操作,或者查询历史操作,并将期间该用户的查询到的单词数据库入表,以提供后续的查询历史操作。
客户端程序提供可视化界面,提示用户按需求操作,根据用户选择组装相应的数据包发送给服务器,根据数据的应答数据包,显示对应的提示,用户登录成功后,跳转到单词查询的二级界面,以提供接下来的服务。
三、功能实现
3.1. 用户注册
3.1.1 功能演示
3.1.2 功能函数实现
server.c
//用户注册函数
void do_register(int connectfd, msg_t *msg, sqlite3 *db)
{
char sqlstr[STR_NUM] = {0};
char *errmsg;
//使用sqlite3_exec函数调用插入函数判断是否能够插入成功
//由于用户名设置为主键,所以如果用户名已经存在就会报错
sprintf(sqlstr, "INSERT INTO usr VALUES('%s', '%s')", msg->name, msg->data);
if (sqlite3_exec(db, sqlstr, NULL, NULL, &errmsg) != SQLITE_OK)
{
sprintf(msg->data, "User %s already exist!!!", msg->name);
}
else
{
strcpy(msg->data, "Register was successful!!!");
}
if (-1 == send(connectfd, msg, sizeof(msg_t), 0))
{
perror("fail to send");
exit(-1);
}
return;
}
client.c
//用户注册函数
void do_register(int socketfd, msg_t *msg)
{
//指定操作码
msg->type = REGISTER;
//输入用户名
printf("input your name:");
scanf("%s", msg->name);
//输入密码
printf("input your password:");
scanf("%s", msg->data);
//发送数据
if (-1 == send(socketfd, msg, sizeof(msg_t), 0))
{
perror("fail to send");
exit(-1);
}
//接收数据并输出
if (-1 == recv(socketfd, msg, sizeof(msg_t), 0))
{
perror("fail to recv");
exit(-1);
}
printf("register : %s\n", msg->data);
return;
}
3.2. 用户登录
3.2.1 功能演示
3.2.2 功能函数实现
server.c
//用户登录函数
void do_login(int connectfd, msg_t *msg, sqlite3 *db)
{
char sqlstr[STR_NUM] = {0};
char *errmsg, **result;
int nrow, ncolumn;
//通过sqlite3_get_table函数查询记录是否存在
sprintf(sqlstr, "select * from usr where name = '%s' and pass = '%s'", msg->name, msg->data);
if (sqlite3_get_table(db, sqlstr, &result, &nrow, &ncolumn, &errmsg) != SQLITE_OK)
{
printf("error : %s\n", errmsg);
}
//通过nrow参数判断是否能够查询到疾记录,如果值为0,则查询不到,如果值为非0,则查询到
if (nrow == 0)
{
strcpy(msg->data, "name or password is wrong!!!");
}
else
{
strncpy(msg->data, "Login was successful", sizeof(msg->data));
}
if (-1 == send(connectfd, msg, sizeof(msg_t), 0))
{
perror("fail to send");
exit(-1);
}
return;
}
client.c
//用户登录函数
int do_login(int socketfd, msg_t *msg)
{
//设置操作码
msg->type = LOGIN;
//输入用户名
printf("input your name:");
scanf("%s", msg->name);
//输入密码
printf("input your password:");
scanf("%s", msg->data);
//发送数据给服务器
if (-1 == send(socketfd, msg, sizeof(msg_t), 0))
{
perror("fail to send");
exit(-1);
}
//接收服务器发送的数据
if (-1 == recv(socketfd, msg, sizeof(msg_t), 0))
{
perror("fail to recv");
exit(-1);
}
//判断是否登录成功
//用21 可以防止“xx”后面有其他垃圾字符
//登录成功返回1
if (0 == strncmp(msg->data, "Login was successful", 21))
{
printf("Login was successful!!!\n");
return 1;
}
//登录失败返回0
printf("login : %s\n", msg->data);
return 0;
}
3.3. 查询单词
3.3.1 功能演示
3.3.2 功能函数实现
将存放单词释义的文件存入数据库的dict表中
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sqlite3.h>
#include <unistd.h>
#define DATABASE "my.db"
int main(int argc, char const *argv[])
{
//把文件导入数据库中
sqlite3 *db;
FILE *fp;
if(SQLITE_OK !=sqlite3_open(DATABASE,&db)){
perror("sqlite err");
exit(1);
}
fp =