项目使用TCP搭建的高并发服务器,客户端根据服务器来写的相应功能来写,主要实现文件上传和文件下载。
开发环境:Linux系统中的Ubuntu
开发语言:C语言
编码软件:VScode
使用到的相关技术:使用sqlite3数据库保存用户信息和文件名,cJSON来进行数据的传递
项目中主要的文件夹,客户端和服务器都使用Makefile来管理,主要是好编译。
下面附上服务器和客户端的主要编码,服务器端为守护进程,项目一般,其中如有没见过的宏和结构体,那就是在头文件中,如果需要其他可找我要
服务器中main.c
/*************************************************************************
> File Name:server
> Author: Lxx
> Created Time: 2023-03-01 13:52:48
> description: 网盘系统基于TCP的服务器端
> versions: 1.0
************************************************************************/
#include "server.h"
int main(int argc, const char *argv[])
{
//创建守护进程
pid_t pid;
pid = fork();
if (-1 == pid) {
ERRLOG("fork error");
} else if (pid == 0) {
int fd;
// 1.产生了孤儿进程
// 2.设置sid
setsid();
// 3.修改路径(修改了线程的工作路径)
chdir("/home/student/network/server");
// 4.重新设置掩码
umask(0);
//关闭已打开的从3开始所有的文件描述符
int max = sysconf(_SC_OPEN_MAX);
for (int i = 3; i < max; i++)
close(i);
// 5.打开日志文件
if ((fd = open("syslog.log", O_RDWR | O_CREAT | O_APPEND, 0666)) == -1)
ERRLOG("open error");
// 6.重定向文件描述符
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
// 7.开启自己的服务
while (1) {
//创建流式套接字
int sockfd1 = 0, sockfd2 = 0;
if(-1 == (sockfd1 = socket(AF_INET, SOCK_STREAM, 0))){
ERRLOG("sockfd error");
}
if(-1 == (sockfd2 = socket(AF_INET, SOCK_STREAM, 0))){
ERRLOG("sockfd error");
}
//填充网络信息结构体
struct sockaddr_in serveraddr = {0};
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(PORT1);
serveraddr.sin_addr.s_addr = inet_addr(IP);
//绑定
if(-1 == bind(sockfd1, (struct sockaddr *)&serveraddr, sizeof(serveraddr))){
ERRLOG("bind error");
}
serveraddr.sin_port = htons(PORT2);
if(-1 == bind(sockfd2, (struct sockaddr *)&serveraddr, sizeof(serveraddr))){
ERRLOG("bind error");
}
//监听
if(-1 == listen(sockfd1, 10)){
ERRLOG("listen error");
}
if(-1 == listen(sockfd2, 10)){
ERRLOG("listen error");
}
//客户端结构体
struct sockaddr_in clientaddr = {0};
socklen_t clientaddr_len = sizeof(clientaddr);
int acceptfd = 0;
pthread_t pit1 = 0, pit2 = 0;
time_t t = 0;
struct tm *time_Temp = NULL;
struct addr msg = {0};
//创建两个进程
if(fork() == 0){//负责简单功能
//客户端连接
while(1){
if(-1 == (acceptfd = accept(sockfd1, (struct sockaddr *)&clientaddr, &clientaddr_len))){
ERRLOG("accept error");
}
msg.clientaddr = clientaddr;
msg.cid = acceptfd;
//客户端连接时间
time(&t);
time_Temp = localtime(&t);
printf("%d年%d月%d日 %d时%d分%d秒:",time_Temp->tm_year+1900,
time_Temp->tm_mon+1, time_Temp->tm_mday, time_Temp->tm_hour, time_Temp->tm_min,
time_Temp->tm_sec);
printf("有[%s:%d]连接进来了\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
fflush(stdout);
//一个客户端就创建一个线程
pthread_create(&pit1, NULL, pthread_func1, &msg);
//设置为分离态
pthread_detach(pit1);
}
}
if(fork() == 0){//负责文件功能
//客户端连接
while(1){
if(-1 == (acceptfd = accept(sockfd2, (struct sockaddr *)&clientaddr, &clientaddr_len))){
ERRLOG("accept error");
}
msg.clientaddr = clientaddr;
msg.cid = acceptfd;
time(&t);
time_Temp = localtime(&t);
printf("%d年%d月%d日 %d时%d分%d秒:",time_Temp->tm_year+1900,
time_Temp->tm_mon+1, time_Temp->tm_mday, time_Temp->tm_hour, time_Temp->tm_min,
time_Temp->tm_sec);
printf("有[%s:%d]连接进来了\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
fflush(stdout);
//一个客户端就创建一个线程
pthread_create(&pit2, NULL, pthread_func2, &msg);
//设置为分离态
pthread_detach(pit2);
}
}
//关闭流式套接字
close(sockfd1);
close(sockfd2);
//回收进程
wait(NULL);
wait(NULL);
}
} else {
exit(0);
}
return 0;
}
server.c
#include "server.h"
void JSON_Parese1(int cid, cJSON *object);//解析基本功能
void JSON_Parese2(int cid, cJSON *object);//解析文件功能
static int insert_filename(char *filename, int act);//向数据库添加文件名
static int find_filename_exsit(char *filename, int act);//查找文件是否存在
static int use_password_compare(char *old_password, int act);//密码比较
/*****************************************************************************
功 能:客户线程函数,处理客户端普通功能
参 数:
arg:客户套接字
****************************************************************************/
void *pthread_func1(void *arg){
//接收发过来的客户端acceptfd
struct addr msg = *(struct addr*)arg;
//循环处理客户端消息
char *buf = malloc(1024);
int len = 0;
time_t t = 0;
struct tm *time_Temp = NULL;
while (1)
{
memset(buf, 0, 1024);
len = read(msg.cid, buf, 1024);
if (len <= 0)
{
time(&t);
time_Temp = localtime(&t);
printf("%d年%d月%d日 %d时%d分%d秒:",time_Temp->tm_year+1900,
time_Temp->tm_mon+1, time_Temp->tm_mday, time_Temp->tm_hour, time_Temp->tm_min,
time_Temp->tm_sec);
printf("有[%s:%d]断开连接\n", inet_ntoa(msg.clientaddr.sin_addr), ntohs(msg.clientaddr.sin_port));
fflush(stdout);
break;
}
//printf("%s\n",buf);
//处理客户端消息
cJSON *object = cJSON_Parse(buf);
if (object != NULL)
{
//printf("%s\n", cJSON_Print(object));
//功能处理
JSON_Parese1(msg.cid, object);
//回收JSON
cJSON_Delete(object);
}
}
free(buf);
close(msg.cid);
pthread_exit(0);
}
/*****************************************************************************
功 能:处理文件文件功能
参 数:客户套接字
****************************************************************************/
void *pthread_func2(void *arg){
//接收发过来的客户端acceptfd
struct addr msg = *(struct addr*)arg;
//循环处理客户端消息
char *buf = malloc(1024);
int len = 0;
time_t t = 0;
struct tm *time_Temp = NULL;
while (1)
{
memset(buf, 0, 1024);
len = read(msg.cid, buf, 1024);
if (len <= 0)
{
time(&t);
time_Temp = localtime(&t);
printf("%d年%d月%d日 %d时%d分%d秒:",time_Temp->tm_year+1900,
time_Temp->tm_mon+1, time_Temp->tm_mday, time_Temp->tm_hour, time_Temp->tm_min,
time_Temp->tm_sec);
printf("有[%s:%d]断开连接\n", inet_ntoa(msg.clientaddr.sin_addr), ntohs(msg.clientaddr.sin_port));
fflush(stdout);
break;
}
//printf("%s\n",buf);
//处理客户端消息
cJSON *object = cJSON_Parse(buf);
if (object != NULL)
{
//printf("%s\n", cJSON_Print(object));
//功能处理
JSON_Parese2(msg.cid, object);
//回收JSON
cJSON_Delete(object);
}
}
free(buf);
close(msg.cid);
pthread_exit(0);
}
/*****************************************************************************
功 能:解析cJSON简单功能
参 数:
acceptfd:用户套接字
object:JSON
****************************************************************************/
void JSON_Parese1(int cid, cJSON *object){
cJSON *func = cJSON_GetObjectItem(object, "功能");
if(!strcmp(func->valuestring,"注册")){
pthread_Register(cid, object);
}else if(!strcmp(func->valuestring,"登录")){
pthread_Login(cid, object);
}else if(!strcmp(func->valuestring,"修改")){
pthread_Update(cid, object);
}else if(!strcmp(func->valuestring,"删除")){//删除文件
pthread_Delete(cid, object);
}else if(!strcmp(func->valuestring, "查询")){//即获取数据库中的相关文件名
pthread_Find(cid, object);
}
}
/*****************************************************************************
功 能:解析cJSON文件功能
参 数:
acceptfd:用户套接字
object:JSON
****************************************************************************/
void JSON_Parese2(int cid, cJSON *object){
cJSON *func = cJSON_GetObjectItem(object, "功能");
if(!strcmp(func->valuestring,"文件下载")){
pthread_GetFile(cid,object);
}else if(!strcmp(func->valuestring,"文件上传")){
pthread_PutFile(cid, object);
}else if(!strcmp(func->valuestring, "文件列表")){
pthread_GetList(cid, object);
}
}
/*****************************************************************************
功 能:注册用户,往数据库里存放
参 数:
cid:客户端套接字
object:cJSON对象
返回值:0表示成功,-1表示失败
****************************************************************************/
int pthread_Register(int cid,cJSON *object){
//解析cJSON
//密码
char *password = cJSON_GetObjectItem(object, "密码")->valuestring;
//昵称
char *name = cJSON_GetObjectItem(object,"昵称")->valuestring;
//手机号
char *num = cJSON_GetObjectItem(object, "手机号")->valuestring;
//打开数据库
sqlite3 *ppDb;
sqlite3_open(DATABASE, &ppDb);
//拼接sql语句
char sql_insert[200] = {0};
char *errmsg;
sprintf(sql_insert, "insert into 用户信息表(密码, 昵称, 手机号) values('%s', '%s', '%s');", password, name, num);
//执行数据sql语句
if(sqlite3_exec(ppDb, sql_insert, NULL, NULL, &errmsg) != 0){
//添加失败,给客户端返回消息
//创建json对象
cJSON *send_json = cJSON_CreateObject();
//填入相应的错误信息
cJSON_AddStringToObject(send_json, "功能", "注册");
cJSON_AddStringToObject(send_json, "状态", "失败");
cJSON_AddNumberToObject(send_json, CODE, FAILED);
cJSON_AddStringToObject(send_json, "原因", errmsg);
//发送个客户端
char *send_str = cJSON_Print(send_json);
write(cid, send_str, strlen(send_str));
//销毁
cJSON_Delete(send_json);
//关闭数据库
sqlite3_close(ppDb);
return -1;
}else{
char sql_select[200] = {0};
sprintf(sql_select, "select 账号 from 用户信息表 where 手机号 = '%s';", num);
//执行sql语句
char **pazResult;
char *errmsg;
int row, column;
//获取账号
sqlite3_get_table(ppDb, sql_select, &pazResult, &row, &column, &errmsg);
//账号
char *account = pazResult[1];
//创建对应的文件夹
char path[200] = {0};
char path_str[250] = {0};
getcwd(path,sizeof(path));
sprintf(path_str, "%s/用户资源文件夹/%s", path, account);
//转为整型
int act = atoi(account);
//printf("act = %d", act);
//printf("%s\n",path_str);
if(-1 == mkdir(path_str, 0777)){
return -1;
}
//创建json对象
cJSON *send_json = cJSON_CreateObject();
//填入相应的错误信息
cJSON_AddStringToObject(send_json, "功能", "注册");
cJSON_AddStringToObject(send_json, "状态", "成功");
cJSON_AddNumberToObject(send_json, "账号", act);
cJSON_AddStringToObject(send_json, "密码", password);
cJSON_AddStringToObject(send_json, "昵称", name);
cJSON_AddStringToObject(send_json, "手机号", num);
cJSON_AddNumberToObject(send_json, CODE, SUCCESS);
cJSON_AddStringToObject(send_json, "提醒", "请牢记你的账号密码");
//发送给客户端
char *send_str = cJSON_Print(send_json);
write(cid, send_str, strlen(send_str));
//销毁
cJSON_Delete(send_json);
//关闭数据库
sqlite3_close(ppDb);
}
return 0;
}
/*****************************************************************************
功 能:登录验证
参 数:
cid:客户端套接字
object:cJSON对象
返回值:0表示成功,-1表示失败
****************************************************************************/
int pthread_Login(int cid,cJSON *object){
//获取账号密码
int account = cJSON_GetObjectItem(object, "账号")->valueint;
char *password = cJSON_GetObjectItem(object, "密码")->valuestring;
//打开数据库
sqlite3 *ppDb;
sqlite3_open(DATABASE, &ppDb);
//拼接sql语句
char sql_select[200] = {0};
sprintf(sql_select, "select 账号, 密码 from 用户信息表 where 账号 = %d and 密码 = '%s';", account, password);
//执行sql语句
char **pazResult;
char *errmsg;
int row, column;
if((sqlite3_get_table(ppDb, sql_select, &pazResult, &row, &column, &errmsg)) != 0){
return -1;
}
if(row == 1){//查询成功
//给客户端返回成功的信息
cJSON *send_json = cJSON_CreateObject();
cJSON_AddNumberToObject(send_json, CODE, SUCCESS);
char *send_str = cJSON_Print(send_json);
write(cid, send_str, strlen(send_str));
//销毁json对象
cJSON_Delete(send_json);
//关闭数据库
sqlite3_close(ppDb);
}else{//查询失败,即没有该账号密码
//给客户端返回成功的信息
cJSON *send_json = cJSON_CreateObject();
cJSON_AddNumberToObject(send_json, CODE, FAILED);
char *send_str = cJSON_Print(send_json);
write(cid, send_str, strlen(send_str));
//销毁json对象
cJSON_Delete(send_json);
//关闭数据库
sqlite3_close(ppDb);
return -1;
}
return 0;
}
/*****************************************************************************
功 能:根据账号修改密码
参 数:
cid:客户端套接字
object:cJSON对象
返回值:0表示成功,-1表示失败
****************************************************************************/
int pthread_Update(int cid,cJSON *object){
//获取其中的账号
int account = cJSON_GetObjectItem(object, "账号")->valueint;
char *old_password = cJSON_GetObjectItem(object, "旧密码")->valuestring;
char *new_password = cJSON_GetObjectItem(object, "新密码")->valuestring;
//创建cJSON
cJSON *send_json = cJSON_CreateObject();
char *send_str = NULL;
//对比旧密码
if(-1 == use_password_compare(old_password, account)){
//封装
cJSON_AddNumberToObject(send_json, CODE, FAILED);
cJSON_AddStringToObject(send_json, "原因", "旧密码错误\n");
//转换为字符串
send_str = cJSON_Print(send_json);
//发送
write(cid, send_str, strlen(send_str));
//销毁
cJSON_Delete(send_json);
return -1;
}
//打开数据库
sqlite3 *ppDb;
sqlite3_open(DATABASE, &ppDb);
//拼接sql
char sql_update[200] = {0};
char *errmsg;
sprintf(sql_update,"update 用户信息表 set 密码 = '%s' where 账号 = %d", new_password, account);
//执行sql
if(sqlite3_exec(ppDb, sql_update, NULL, NULL, &errmsg) != 0 ){//执行失败
//封装
cJSON_AddNumberToObject(send_json, CODE, FAILED);
cJSON_AddStringToObject(send_json, "原因", errmsg);
//转换为字符串
send_str = cJSON_Print(send_json);
//发送
write(cid, send_str, strlen(send_str));
//销毁
cJSON_Delete(send_json);
//关闭数据库
sqlite3_close(ppDb);
return -1;
}else{
//封装
cJSON_AddNumberToObject(send_json, CODE, SUCCESS);
//转换为字符串
send_str = cJSON_Print(send_json);
//发送
write(cid, send_str, strlen(send_str));
//销毁
cJSON_Delete(send_json);
//关闭数据库
sqlite3_close(ppDb);
}
return 0;
}
/*****************************************************************************
功 能:根据用户账号删除相关文件和数据库对应的数据
参 数:
cid:客户端套接字
object:cJSON对象
返回值:0表示成功,-1表示失败
****************************************************************************/
int pthread_Delete(int cid,cJSON *object){
//获取其中的账号
int account = cJSON_GetObjectItem(object, "账号")->valueint;
//获取文件名
char *filename = cJSON_GetObjectItem(object, "文件名")->valuestring;
//创建cJSON
cJSON *send_json = cJSON_CreateObject();
char *send_str = NULL;
//判断文件是否存在
if(-1 == find_filename_exsit(filename, account)){
//文件不存在
//封装
cJSON_AddNumberToObject(send_json, CODE, FAILED);
cJSON_AddStringToObject(send_json, "原因", "文件不存在,无需删除\n");
//转换为字符串
send_str = cJSON_Print(send_json);
//发送
write(cid, send_str, strlen(send_str));
//销毁
cJSON_Delete(send_json);
return -1;
}
//文件存在,删除数据库中的数据和对应文件夹下的文件
//打开数据库
sqlite3 *ppDb;
sqlite3_open(DATABASE, &ppDb);
//拼接sql
char sql_delete[200] = {0};
char *errmsg;
sprintf(sql_delete,"delete from 用户文件表 where 账号 = %d and 文件名 = '%s';", account, filename);
//执行sql
if(sqlite3_exec(ppDb, sql_delete, NULL, NULL, &errmsg) != 0 ){//执行失败
//封装
cJSON_AddNumberToObject(send_json, CODE, FAILED);
cJSON_AddStringToObject(send_json, "原因", errmsg);
//转换为字符串
send_str = cJSON_Print(send_json);
//发送
write(cid, send_str, strlen(send_str));
//销毁
cJSON_Delete(send_json);
//关闭数据库
sqlite3_close(ppDb);
return -1;
}else{
//删除对应的账号下的文件
char path[100] = {0};
getcwd(path,sizeof(path));
char filepath_name[150] = {0};
sprintf(filepath_name, "%s/用户资源文件夹/%d/%s", path, account,filename);
remove(filepath_name);
//封装
cJSON_AddNumberToObject(send_json, CODE, SUCCESS);
//转换为字符串
send_str = cJSON_Print(send_json);
//发送
write(cid, send_str, strlen(send_str));
//销毁
cJSON_Delete(send_json);
//关闭数据库
sqlite3_close(ppDb);
}
return 0;
}
/*****************************************************************************
功 能:查询数据库中用户对应的文件
参 数:
cid:客户端套接字
object:cJSON对象
返回值:0表示成功,-1表示失败
****************************************************************************/
int pthread_Find(int cid,cJSON *object){
//获取客户的账号
int account = cJSON_GetObjectItem(object, "账号")->valueint;
//打开数据库
sqlite3 *ppDb;
sqlite3_open(DATABASE, &ppDb);
//拼接sql
char sql_select[200] = {0};
sprintf(sql_select,"select a.文件名 from 用户信息表 s left join 用户文件表 a on s.账号 = a.账号 where s.账号 = %d", account);
//执行sql语句
char **pazResult;
char *errmsg;
int row = 0, column = 0;
int i = 0, j = 0;
//创建json
cJSON *send_json = cJSON_CreateObject();
cJSON *json_array = cJSON_CreateArray();
cJSON *json_str = NULL;
if((sqlite3_get_table(ppDb, sql_select, &pazResult, &row, &column, &errmsg)) != 0){
return -1;
}else if(row < 1){//查询成功,但是没有相关文件
cJSON_AddStringToObject(send_json, "文件列表", NULL);
cJSON_AddNumberToObject(send_json, CODE, SUCCESS);
}else{
for(i = 1; i <= row; ++i){
for(j = 0; j < column; ++j){
json_str = cJSON_CreateString(pazResult[i*column + j]);
cJSON_AddItemToArray(json_array, json_str);
}
}
cJSON_AddItemToObject(send_json, "文件列表", json_array);
cJSON_AddNumberToObject(send_json, CODE, SUCCESS);
}
//发送
char *send_str = cJSON_Print(send_json);
write(cid, send_str, strlen(send_str));
//销毁
cJSON_Delete(send_json);
//关闭数据库
sqlite3_close(ppDb);
}
/*****************************************************************************
功 能:打开对应客户的目录,获取里面的文件列表
参 数:
cid:客户端套接字
object:cJSON对象
返回值:0表示成功,-1表示失败
****************************************************************************/
int pthread_GetList(int cid,cJSON *object){
//获取客户的账号
int account = cJSON_GetObjectItem(object, "账号")->valueint;
//获取当前文件工作路径
char path[200] = {0};
getcwd(path,sizeof(path));
char account_str[250] = {0};
sprintf(account_str, "%s/用户资源文件夹/%d", path, account);
//打开对应账户的目录
DIR *dir;
if(NULL == (dir = opendir(account_str))){
return -1;
}
//走到这里,说明目录已存在,获取目录中的文件名
//创建json数组
cJSON *cjson_array = cJSON_CreateArray();
cJSON *cjson_string = NULL;
struct dirent *dirp;
while(NULL != (dirp = readdir(dir))){
if(strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0){
continue;
}
cjson_string = cJSON_CreateString(dirp->d_name);
cJSON_AddItemToArray(cjson_array,cjson_string);
}
//创建cjson
cJSON *send_json = cJSON_CreateObject();
cJSON_AddItemToObject(send_json, "文件列表", cjson_array);
cJSON_AddNumberToObject(send_json, CODE, SUCCESS);
//转换为字符串
char *send_str;
send_str = cJSON_Print(send_json);
//发送
write(cid, send_str, strlen(send_str));
//销毁
cJSON_Delete(send_json);
return 0;
}
/*****************************************************************************
功 能:文件上传
参 数:
cid:客户端套接字
object:cJSON对象
返回值:0表示成功,-1表示失败
****************************************************************************/
int pthread_PutFile(int cid,cJSON *object){
//获取客户的账号
int account = cJSON_GetObjectItem(object, "账号")->valueint;
char *filename = cJSON_GetObjectItem(object, "文件名")->valuestring;
//获取当前文件工作路径
char path[100] = {0};
getcwd(path,sizeof(path));
char account_str[150] = {0};
sprintf(account_str, "%s/用户资源文件夹/%d", path, account);
//打开对应账户的目录
DIR *dir;
if(NULL == (dir = opendir(account_str))){
return -1;
}
//走到这里,说明目录已存在,获取目录中的文件名
cJSON *send_json = cJSON_CreateObject();
char *send_str = NULL;
struct dirent *dirp;
while(NULL != (dirp = readdir(dir))){
if(strcmp(dirp->d_name, filename) == 0 ){//文件存在,先返回相应的json,不需要上传了
cJSON_AddNumberToObject(send_json, CODE, FAILED);
cJSON_AddNumberToObject(send_json, "状态", FAILED);
//转换为字符串
send_str = cJSON_Print(send_json);
//发送
write(cid, send_str, strlen(send_str));
//销毁
cJSON_Delete(send_json);
return -1;
}
}
//这里说明没有重复文件
cJSON_AddNumberToObject(send_json, CODE, SUCCESS);
//已做好接收准备信号
cJSON_AddNumberToObject(send_json, "状态", SUCCESS);
//转换为字符串
send_str = cJSON_Print(send_json);
//发送
write(cid, send_str, strlen(send_str));
//销毁
cJSON_Delete(send_json);
struct common msg = {0};
int ret = 0;
int fd = 0;
strcat(account_str,"/");
strcat(account_str, filename);
if(-1 == (fd = open(account_str, O_WRONLY|O_CREAT, 0666))){
return -1;
}
//打开相应文件
while(1){
ret = read(cid, &msg, sizeof(msg));
if(ret <= 0 || msg.num == 0){
break;
}
write(fd, msg.data, msg.num);
}
//接收完毕后需要将文件名保存到对应的数据库中
if(insert_filename(filename, account) == -1){//表述插入数据库失败
return -1;
}
return 0;
}
/*****************************************************************************
功 能:文件下载
参 数:
cid:客户端套接字
object:cJSON对象
返回值:0表示成功,-1表示失败
****************************************************************************/
int pthread_GetFile(int cid,cJSON *object){
//获取客户的账号
int account = cJSON_GetObjectItem(object, "账号")->valueint;
char *filename = cJSON_GetObjectItem(object, "文件名")->valuestring;
//获取当前文件工作路径
char path[100] = {0};
getcwd(path,sizeof(path));
char account_str[150] = {0};
sprintf(account_str, "%s/用户资源文件夹/%d", path, account);
//打开对应账户的目录
DIR *dir;
if(NULL == (dir = opendir(account_str))){
return -1;
}
//走到这里,说明目录已存在,获取目录中的文件名
cJSON *send_json = cJSON_CreateObject();
char *send_str = NULL;
struct dirent *dirp;
while(NULL != (dirp = readdir(dir))){
if(strcmp(dirp->d_name, filename) == 0 ){//文件存在,先返回相应的json
cJSON_AddNumberToObject(send_json, CODE, SUCCESS);
//转换为字符串
send_str = cJSON_Print(send_json);
//发送
write(cid, send_str, strlen(send_str));
//销毁
cJSON_Delete(send_json);
//接收客户端已经做好就收的准备
char buf[1024];
if(read(cid, buf, sizeof(buf)) <= 0){
return -1;
}
//解析json
cJSON *recv_json = cJSON_Parse(buf);
if(recv_json == NULL){
return -1;
}
cJSON *temp = cJSON_GetObjectItem(recv_json, "状态");
if(temp->valueint == SUCCESS){
//发送文件
int fd = 0;
strcat(account_str, "/");
strcat(account_str,filename);
if(-1 == (fd = open(account_str, O_RDONLY))){
return -1;
}
struct common msg = {0};
int ret;
while(1){
ret = read(fd, msg.data, sizeof(msg.data));
msg.num = ret;
write(cid, &msg, sizeof(msg));
if(ret <= 0){
break;
}
}
cJSON_Delete(recv_json);
close(fd);
return 0;
}
}
}
//执行到此说明没有该文件
cJSON_AddNumberToObject(send_json, CODE, FAILED);
//转换为字符串
send_str = cJSON_Print(send_json);
//发送
write(cid, send_str, strlen(send_str));
//销毁
cJSON_Delete(send_json);
return -1;
}
/*****************************************************************************
功 能:保存用户相关文件到数据库
参 数:
filename:文件名
act:账号
返回值:0表示成功,-1表示失败
****************************************************************************/
static int insert_filename(char *filename, int act){
//打开数据库
sqlite3 *ppDb = NULL;
sqlite3_open(DATABASE, &ppDb);
//拼装sql语句
char sql_insert[100] = {0};
sprintf(sql_insert, "insert into 用户文件表(账号, 文件名) values(%d, '%s');", act, filename);
//执行sql语句
if(sqlite3_exec(ppDb, sql_insert, NULL, NULL, NULL) != 0){
//关闭数据库
sqlite3_close(ppDb);
return -1;
}
//关闭数据库
sqlite3_close(ppDb);
return 0;
}
/*****************************************************************************
功 能:查询某账户中某文件是否存在
参 数:
filename:文件名
act:账号
返回值:0表示存在,-1表示不存在
****************************************************************************/
static int find_filename_exsit(char *filename, int act){
//打开数据库
sqlite3 *ppDb = NULL;
sqlite3_open(DATABASE, &ppDb);
//拼装sql语句
char sql_insert[100] = {0};
sprintf(sql_insert, "select 文件名 from 用户文件表 where 账号 = %d and 文件名 = '%s';", act, filename);
//执行sql语句
char **pazResult = NULL;
char *errmsg = NULL;
int row = 0, column = 0;
if(sqlite3_get_table(ppDb, sql_insert, &pazResult, &row, &column, &errmsg) != 0){
return -1;
}
//查询出来row是真实的数据的行数
/* printf("row = %d\n",row);
for(int i = 0; i <= row; i++){
for(int j = 0; j < column; j++){
printf("%s\n", pazResult[i*column + j]);
}
} */
if(row < 1){
//说明没有数据,及文件不存在
//关闭数据库
sqlite3_close(ppDb);
return -1;
}
//关闭数据库
sqlite3_close(ppDb);
return 0;
}
/*****************************************************************************
功 能:修改密码的时候将旧密码和数据库密码进行比对
参 数:
old_password:旧密码
act:账号
返回值:0表示相同,-1表示不相同
****************************************************************************/
static int use_password_compare(char *old_password, int act){
//打开数据库
sqlite3 *ppDb = NULL;
sqlite3_open(DATABASE, &ppDb);
//拼装sql语句
char sql_insert[100] = {0};
sprintf(sql_insert, "select 密码 from 用户信息表 where 账号 = %d;", act);
//执行sql语句
char **pazResult = NULL;
char *errmsg = NULL;
int row = 0, column = 0;
if(sqlite3_get_table(ppDb, sql_insert, &pazResult, &row, &column, &errmsg) != 0){
return -1;
}
if(row < 1){//没查到
//说明没有数据,及文件不存在
//关闭数据库
sqlite3_close(ppDb);
return -1;
}
if(!strcmp(pazResult[1], old_password)){
//相同
//关闭数据库
sqlite3_close(ppDb);
return 0;
}
//不相同
//关闭数据库
sqlite3_close(ppDb);
return -1;
}
客户端中的main.c ,客户端中没有使用多线程和多进程,所以无法实现都文件下载
/*************************************************************************
> File Name:client
> Author: Lxx
> Created Time: 2023-03-01 13:54:21
> description: 网盘系统基于TCP的客户端
> version: 1.0
************************************************************************/
#include "client.h"
int main(int argc, const char *argv[])
{
//创建流式套接字
int sockfd1 = 0, sockfd2 = 0;
if(-1 == (sockfd1 = socket(AF_INET, SOCK_STREAM, 0))){
ERRLOG("sockfd error");
}
if(-1 == (sockfd2 = socket(AF_INET, SOCK_STREAM, 0))){
ERRLOG("sockfd error");
}
//填充网络信息结构体
struct sockaddr_in serveraddr = {0};
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(PORT1);
serveraddr.sin_addr.s_addr = inet_addr(IP);
socklen_t sockaddr_len = sizeof(serveraddr);
//连接服务器
if(-1 == (connect(sockfd1, (struct sockaddr *)&serveraddr, sockaddr_len))){
//连接失败
ERRLOG("accept error");
}
serveraddr.sin_port = htons(PORT2);
if(-1 == (connect(sockfd2, (struct sockaddr *)&serveraddr, sockaddr_len))){
//连接失败
ERRLOG("accept error");
}
welcome_UI(sockfd1,sockfd2);
//关闭套接字
close(sockfd1);
close(sockfd2);
return 0;
}
client.c
#include "client.h"
/*****************************************************************************
功 能:欢迎界面
参 数:
cid:对应服务器port1
cid2:对应服务器poet2
****************************************************************************/
void welcome_UI(int cid, int cid2){
system("clear");
printf("\t\t--------------------------------------------------\n");
printf("\t\t \n");
printf("\t\t \n");
printf("\t\t 欢迎使用网盘系统 \n");
printf("\t\t \n");
printf("\t\t \n");
printf("\t\t \n");
printf("\t\t--------------------按回车键继续--------------------\n");
printf("\t\t");
getchar();
system("clear");
use_UI(cid, cid2);
}
/*****************************************************************************
功 能:用户界面
参 数:
cid:对应服务器port1
cid2:对应服务器poet2
****************************************************************************/
void use_UI(int cid, int cid2){
char c;
printf("\t\t--------------------------------------------------\n");
printf("\t\t 用户登录页面 \n");
printf("\t\t \n");
printf("\t\t 1.登陆用户 \n");
printf("\t\t 2.注册用户 \n");
printf("\t\t \n");
printf("\t\t \n");
printf("\t\t-------------------请输入对应数字-------------------\n");
printf("\t\t");
scanf("%c", &c);
//清空缓存区,把换行符(\n)拿走
getchar();
if(c == '1'){
//清空当前终端页面
system("clear");
login_UI(cid, cid2);
}else if(c == '2'){
system("clear");
register_UI(cid, cid2);
}else{
system("clear");
login_UI(cid, cid2);
}
}
/*****************************************************************************
功 能:登录用户页面
参 数:
cid:对应服务器port1
cid2:对应服务器poet2
****************************************************************************/
void login_UI(int cid, int cid2){
char c;
char name[20] = {0}, password[20] = {0};
printf("\t\t--------------------------------------------------\n");
printf("\t\t 请你登录 \n");
printf("\t\t \n");
printf("\t\t 账 号: \n");
printf("\t\t");
fgets(name,sizeof(name), stdin);
name[strlen(name)-1] = '\0';
printf("\t\t 密 码: \n");
printf("\t\t");
fgets(password,sizeof(password), stdin);
password[strlen(password)-1] = '\0';
printf("\t\t \n");
printf("\t\t \n");
printf("\t\t-------------------请按回车登录----------------------\n");
printf("\t\t");
getchar();
//将数据封装为json
cJSON *send_json = cJSON_CreateObject();
cJSON_AddStringToObject(send_json, "功能", "登录");
//将账号转为整型
cJSON_AddNumberToObject(send_json, "账号", atoi(name));
cJSON_AddStringToObject(send_json, "密码", password);
//转换为字符串
char *send_str = cJSON_Print(send_json);
//发送
write(cid, send_str, strlen(send_str));
//释放
cJSON_Delete(send_json);
//接收服务器返回的json数据
char buf[1024] = {0};
if(read(cid, buf, 1024) <= 0){
return;
}
//printf("%s\n", buf);
cJSON *object = cJSON_Parse(buf);
if(object == NULL){
return;
}
//解析
cJSON *func = cJSON_GetObjectItem(object, "标记");
if(func == NULL){
return;
}
if(func->valueint == SUCCESS){
//表示登录成功,跳转到功能页面
int act = atoi(name);//保留账号
cJSON_Delete(object);
system("clear");
function_UI(cid, cid2, act);
}else{
printf("\t\t登录失败,账号或密码错误,请按回车重新登录或者输入1回车,去注册\n");
char a;
scanf("%c", &a);
getchar();
if(a == '1'){
system("clear");
register_UI(cid, cid2);
}else{
system("clear");
login_UI(cid, cid2);
}
}
}
/*****************************************************************************
功 能:注册用户页面
参 数:
cid:对应服务器port1
cid2:对应服务器poet2
****************************************************************************/
void register_UI(int cid, int cid2){
char password[20] = {0}, name[20] = {0}, num[20] = {0};
printf("\t\t--------------------------------------------------\n");
printf("\t\t 请你注册 \n");
printf("\t\t \n");
printf("\t\t 昵 称: \n");
printf("\t\t");
fgets(name,sizeof(name), stdin);
name[strlen(name)-1] = '\0';
printf("\t\t 密 码: \n");
printf("\t\t");
fgets(password,sizeof(password), stdin);
password[strlen(password)-1] = '\0';
printf("\t\t 手机号: \n");
printf("\t\t");
fgets(num,sizeof(num), stdin);
num[strlen(num)-1] = '\0';
printf("\t\t \n");
printf("\t\t \n");
printf("\t\t------------------按回车注册------------------------\n");
//创建json
cJSON *send_json = cJSON_CreateObject();
cJSON_AddStringToObject(send_json, "功能", "注册");
cJSON_AddStringToObject(send_json, "昵称", name);
cJSON_AddStringToObject(send_json, "密码", password);
cJSON_AddStringToObject(send_json, "手机号", num);
//转化为字符串
char *send_str = cJSON_Print(send_json);
write(cid, send_str, strlen(send_str));
//销毁
cJSON_Delete(send_json);
//接收返回的json
char buf[1024] = {0};
if(read(cid, buf, 1024) <= 0){
return;
}
//将字符串转换为json
cJSON *recv_json = cJSON_Parse(buf);
if(recv_json == NULL){
return;
}
cJSON *str = cJSON_GetObjectItem(recv_json, "标记");
if(str == NULL){
return;
}
if(str->valueint == SUCCESS){
//注册成功
//打印注册账号
cJSON *account = cJSON_GetObjectItem(recv_json, "账号");
int act = account->valueint;
printf("\t\t恭喜你注册成功,你的账号为 %d ,请牢记\n", act);
printf("\t\t请按回车给自动登录...\n");
//销毁json
cJSON_Delete(recv_json);
getchar();
system("clear");
function_UI(cid, cid2, act);
}else{
//注册失败
//销毁json
cJSON *msg = cJSON_GetObjectItem(recv_json, "原因");
printf("错误原因:%s\n",msg->valuestring);
cJSON_Delete(recv_json);
printf("请按回车,重新注册\n");
getchar();
register_UI(cid, cid2);
}
}
/*****************************************************************************
功 能:功能页面
参 数:
cid:对应服务器port1
cid2:对应服务器poet2
act:为用户账号
****************************************************************************/
void function_UI(int cid, int cid2, int act){
char c;
printf("\t\t--------------------------------------------------\n");
printf("\t\t 用户功能页面 \n");
printf("\t\t \n");
printf("\t\t 1.文件列表 \n");
printf("\t\t 2.文件下载 \n");
printf("\t\t 3.文件上传 \n");
printf("\t\t 4.文件删除 \n");
printf("\t\t 5.修改密码 \n");
printf("\t\t 6.切换用户 \n");
printf("\t\t 7.退出系统 \n");
printf("\t\t \n");
printf("\t\t-------------------请输入对应数字-------------------\n");
printf("\t\t");
scanf("%c", &c);
//清空缓存区,把换行符(\n)拿走
getchar();
if(c == '1'){
//清空当前终端页面
system("clear");
file_list_UI(cid, cid2, act);
}else if(c == '2'){
system("clear");
file_download_UI(cid, cid2, act);
}else if(c == '3'){
system("clear");
file_Upload_UI(cid, cid2, act);
}else if(c == '4'){
system("clear");
file_delete_UI(cid, cid2, act);
}else if(c == '5'){
system("clear");
use_update_UI(cid, cid2, act);
}else if(c == '6'){
system("clear");
login_UI(cid, cid2);
}else if(c == '7'){
//system("clear");
close(cid);
close(cid2);
exit(0);
}else{
system("clear");
function_UI(cid, cid2, act);
}
}
/*****************************************************************************
功 能:显示当前用户的所有文件
参 数:
cid:对应服务器port1
cid2:对应服务器poet2
act:为用户账号
****************************************************************************/
void file_list_UI(int cid, int cid2, int act){
//向服务器发请求,得到所有文件列表
cJSON *send_json = cJSON_CreateObject();
cJSON_AddStringToObject(send_json, "功能","查询");
cJSON_AddNumberToObject(send_json, "账号", act);
char *send_str = cJSON_Print(send_json);
write(cid, send_str, strlen(send_str));
//销毁
cJSON_Delete(send_json);
//接收服务器返回的json
char buf[1024] = {0};
if(read(cid, buf, 1024) <= 0){
return;
}
//转换为cjson
cJSON *recv_json = cJSON_Parse(buf);
cJSON *msg = cJSON_GetObjectItem(recv_json,"文件列表");
cJSON *str = NULL;
int i = 0;
printf("\t\t--------------------------------------------------\n");
if(msg != NULL){
int array_size = cJSON_GetArraySize(msg);
for(i = 0; i < array_size; ++i){
str = cJSON_GetArrayItem(msg, i);
if(str != NULL){
printf("\t\t%s\n",str->valuestring);
}
}
}
printf("\t\t--------------------------------------------------\n");
//销毁json
cJSON_Delete(recv_json);
printf("\t\t已显示完毕,按下回车回到功能页面\n");
printf("\t\t");
getchar();
system("clear");
function_UI(cid, cid2, act);
}
/*****************************************************************************
功 能:文件下载
参 数:
cid:对应服务器port1
cid2:对应服务器poet2
act:为用户账号
****************************************************************************/
void file_download_UI(int cid, int cid2, int act){
//发送文件下载json
cJSON *send_json = cJSON_CreateObject();
cJSON_AddStringToObject(send_json, "功能", "文件下载");
cJSON_AddNumberToObject(send_json, "账号", act);
//文件名
char filename[20] = {0};
printf("\t\t你要下载的文件名:\n");
printf("\t\t");
fgets(filename,sizeof(filename), stdin);
filename[strlen(filename)-1] = '\0';
cJSON_AddStringToObject(send_json, "文件名", filename);
char *send_str = cJSON_Print(send_json);
write(cid2, send_str, strlen(send_str));
//销毁
cJSON_Delete(send_json);
//接收服务器返回的json
char buf[1024] = {0};
if(read(cid2, buf, 1024) <= 0){
return;
}
cJSON *recv_json = cJSON_Parse(buf);
cJSON *str = cJSON_GetObjectItem(recv_json, CODE);
struct common msg = {0};
if(str->valueint == SUCCESS){
//发送一个可以接收的json给服务器
cJSON *send_json = cJSON_CreateObject();
cJSON_AddNumberToObject(send_json, "状态", SUCCESS);
char *send_str = cJSON_Print(send_json);
write(cid2, send_str, strlen(send_str));
cJSON_Delete(send_json);
//接收文件
int fd;
if(-1 == (fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0666))){
return;
}
int ret = 0;
while(1){
//文件存在,接收服务器发送文件
ret = read(cid2, &msg, sizeof(msg));
if(ret <= 0 || msg.num == 0){
break;
}
//写服务器发来的文件
write(fd, msg.data, msg.num);
}
//关闭文件描述符
close(fd);
//销毁json
cJSON_Delete(recv_json);
printf("\t\t下载成功\n");
printf("\t\t按回车回到功能页面\n");
printf("\t\t");
getchar();
system("clear");
function_UI(cid, cid2, act);
}else{
//文件不存在
printf("\t\t文件不存在\n");
printf("\t\t按回车回到功能页面\n");
printf("\t\t");
getchar();
system("clear");
function_UI(cid, cid2, act);
}
}
/*****************************************************************************
功 能:文件上传
参 数:
cid:对应服务器port1
cid2:对应服务器poet2
act:为用户账号
****************************************************************************/
void file_Upload_UI(int cid, int cid2, int act){
//发送文件上传json信号
cJSON *send_json = cJSON_CreateObject();
cJSON_AddStringToObject(send_json, "功能", "文件上传");
cJSON_AddNumberToObject(send_json, "账号", act);
//文件路径
char filename_path[100];
A:
memset(filename_path, 0, sizeof(filename_path));
printf("\t\t你要上传的文件路径:\n");
printf("\t\t");
fgets(filename_path,sizeof(filename_path), stdin);
filename_path[strlen(filename_path)-1] = '\0';
//先判断文件是否存在,如果存在直接返回,不发数据给服务器
struct common msg = {0};
int fd;
if(-1 == (fd = open(filename_path, O_RDONLY))){
printf("\t\t文件不存在,请重新填写文件名\n");
goto A;
}
//剪切文件名
char *filename = strrchr(filename_path, '/');
if(filename != NULL){//找到了
//往前移一位
filename += 1;
}else{
filename = filename_path;
}
cJSON_AddStringToObject(send_json, "文件名", filename);
char *send_str = cJSON_Print(send_json);
write(cid2, send_str, strlen(send_str));
//销毁
cJSON_Delete(send_json);
//接收服务器返回的json
char buf[1024] = {0};
if(read(cid2, buf, 1024) <= 0){
return;
}
cJSON *recv_json = cJSON_Parse(buf);
cJSON *str = cJSON_GetObjectItem(recv_json, CODE);
if(str == NULL){
return;
}
if(str->valueint == SUCCESS){
cJSON *temp = cJSON_GetObjectItem(recv_json, "状态");
//接收服务器是否准备好接收
if(temp != NULL){
if(temp->valueint == SUCCESS){//表示已做好接收准备
//释放上一个json的空间
cJSON_Delete(recv_json);
//发送文件
int ret = 0;
while(1){
//文件存在,接收服务器发送文件
ret = read(fd, msg.data, sizeof(msg.data));
msg.num = ret;
//向服务器发文件
write(cid2, &msg, sizeof(msg));
if(ret <= 0){
break;
}
}
}
}
//关闭文件描述符
close(fd);
printf("\t\t上传成功\n");
printf("\t\t按回车回到功能页面\n");
printf("\t\t");
getchar();
system("clear");
function_UI(cid, cid2, act);
}else{
//文件存在
close(fd);
printf("\t\t文件存在,无需上传\n");
printf("\t\t按回车回到功能页面\n");
printf("\t\t");
getchar();
system("clear");
function_UI(cid, cid2, act);
}
}
/*****************************************************************************
功 能:文件删除
参 数:
cid:对应服务器port1
cid2:对应服务器poet2
act:为用户账号
****************************************************************************/
void file_delete_UI(int cid, int cid2, int act){
//发送文件上传json信号
cJSON *send_json = cJSON_CreateObject();
cJSON_AddStringToObject(send_json, "功能", "删除");
cJSON_AddNumberToObject(send_json, "账号", act);
//文件名
char filename[20];
memset(filename, 0, sizeof(filename));
printf("\t\t你要删除的文件:\n");
printf("\t\t");
fgets(filename,sizeof(filename), stdin);
filename[strlen(filename)-1] = '\0';
cJSON_AddStringToObject(send_json, "文件名", filename);
char *send_str = cJSON_Print(send_json);
write(cid, send_str, strlen(send_str));
//销毁
cJSON_Delete(send_json);
//接收服务器传来的json
char buf[1024];
if(-1 == read(cid, buf, sizeof(buf))){
return;
}
cJSON *recv_json = cJSON_Parse(buf);
cJSON *code = cJSON_GetObjectItem(recv_json, CODE);
if(code->valueint == SUCCESS){
cJSON_Delete(recv_json);
//删除成功
printf("\t\t删除成功\n");
printf("\t\t按回车回到功能页面\n");
printf("\t\t");
getchar();
system("clear");
function_UI(cid, cid2, act);
}else{
//删除失败
cJSON *because = cJSON_GetObjectItem(recv_json, "原因");
printf("\t\t删除失败\n");
printf("\t\t原因:%s\n",because->valuestring);
printf("\t\t按回车回到功能页面\n");
printf("\t\t");
cJSON_Delete(recv_json);
getchar();
system("clear");
function_UI(cid, cid2, act);
}
}
/*****************************************************************************
功 能:用户修改密码
参 数:
cid:对应服务器port1
cid2:对应服务器poet2
act:为用户账号
****************************************************************************/
void use_update_UI(int cid, int cid2, int act){
//发送文件上传json信号
cJSON *send_json = cJSON_CreateObject();
cJSON_AddStringToObject(send_json, "功能", "修改");
cJSON_AddNumberToObject(send_json, "账号", act);
//输入新旧密码
char old_password[20] = {0}, new_password[20] = {0};
printf("\t\t输入你的旧密码:\n");
printf("\t\t");
fgets(old_password,sizeof(old_password), stdin);
old_password[strlen(old_password)-1] = '\0';
printf("\t\t输入你的新密码:\n");
printf("\t\t");
fgets(new_password,sizeof(new_password), stdin);
new_password[strlen(new_password)-1] = '\0';
cJSON_AddStringToObject(send_json, "旧密码", old_password);
cJSON_AddStringToObject(send_json, "新密码", new_password);
char *send_str = cJSON_Print(send_json);
write(cid, send_str, strlen(send_str));
//销毁
cJSON_Delete(send_json);
//接收服务器传来的json
char buf[1024];
if(-1 == read(cid, buf, sizeof(buf))){
return;
}
cJSON *recv_json = cJSON_Parse(buf);
cJSON *code = cJSON_GetObjectItem(recv_json, CODE);
if(code->valueint == SUCCESS){
cJSON_Delete(recv_json);
//修改成功
printf("\t\t修改成功\n");
printf("\t\t按回车回到功能页面\n");
printf("\t\t");
getchar();
system("clear");
function_UI(cid, cid2, act);
}else{
//修改失败
cJSON *because = cJSON_GetObjectItem(recv_json, "原因");
printf("\t\t修改失败\n");
printf("\t\t原因:%s\n",because->valuestring);
printf("\t\t按回车回到功能页面\n");
printf("\t\t");
cJSON_Delete(recv_json);
getchar();
system("clear");
function_UI(cid, cid2, act);
}
}