功能包括
使用HASH加密防止sql注入
获取文件列表,选择文件上传or下载
登录注册、传输异常处理,客户端掉线记录断点,实现断点重传的功能。
多线程支持。
上下载进度条等。
客户端:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <openssl/sha.h>
#include <pthread.h>
#include <poll.h>
#define SIZE 64
#define connect_init_error_code -1
#define quit_code 1
#define up_end_code 2
#define password_error_code 3;
#define name_exit_code 4
#define register_successful_code 5
#define register_failue_code 6
#define login_successfully_code 7
#define login_failue_code 8
#define unrefister_code 9
#define upload_statu_code 10
#define download_statu_code 11
#define exit_upload_statu_code 12
#define exit_download_statu_code 13
#define end_get_file_code 14
#define download_file_not_exist_code 15
#define begin_download_code 16
#define HEARTBEAT_CODE 20
#define have_up_exception_code 22
#define have_down_exception_code 21
int is_data_transferring = 0; // 全局变量,表示是否正在传输数据
// 定义一个状态机的菜单结构体,用于描述用户当前所处于的状态
// 枚举类型
typedef enum
{
STATE_START_MENU, // 开始界面
STATE_LOGIN, // 登录
STATE_REGISTER, // 注册
STATE_FUCTION_MENU, // 功能菜单
STATE_UPLOAD, // 上传
STATE_DOWNLOAD, // 下载
STATE_UPLOAD_EXCEPTION, // 上传异常
STATE_DOWNLOAD_EXCEPTION, // 下载异常
STATE_QUIT // 退出
} State;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int check_args(int argc);
int create_socket_and_connect(char *argv[], int *cfd);
int open_send_file(int *cfd, int *fd);
void send_file_content(int *cfd, int *fd);
int upload_file(int *cfd, int *fd);
// 定义一个菜单函数,用于实现界面跳转
int client_menu(int *cfd, int *fd);
// 定义一个函数,用于登录 ,发送用户名和密码,根据服务器决定返回值,成功返回1,未注册返回一个值,密码错误一个值
int login(int *cfd);
// 定义一个函数,用于注册,,有返回值,根据服务器那边数据库比对,如果没有该用户,返回成功,已经有该用户,返回失败,注册函数执行完毕,去登录界面
int Register_login(int *cfd, int ROL);
int send_up_down_statue(int *cfd, int status);
void sha256_hash_string(unsigned char hash[SHA256_DIGEST_LENGTH], char outputBuffer[65]);
void sha256_string(char *string, char outputBuffer[65]);
int send_file_content_exception(int *cfd, char *filename, off_t start_pos);
int main(int argc, char *argv[])
{
int cfd, fd;
int choice;
if (check_args(argc) == connect_init_error_code)
return connect_init_error_code;
if (create_socket_and_connect(argv, &cfd) == connect_init_error_code)
return connect_init_error_code;
// 选择执行上传还是下载,上传就进入上传函数,下载先获取服务器列表
client_menu(&cfd, &fd);
return 0;
}
// 将SHA-256哈希值转换为十六进制字符串
void sha256_hash_string(unsigned char hash[SHA256_DIGEST_LENGTH], char outputBuffer[65])
{
int i = 0;
// 遍历每个字节的哈希值
for (i = 0; i < SHA256_DIGEST_LENGTH; i++)
{ //%02x表示将一个整数转换为两位的十六进制数,如果不足两位则在前面补0
sprintf(outputBuffer + (i * 2), "%02x", hash[i]);
}
outputBuffer[64] = 0;
}
void sha256_string(char *string, char outputBuffer[65])
{
// 定义一个数组,用于存储SHA-256哈希值
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256_CTX sha256;
SHA256_Init(&sha256);
SHA256_Update(&sha256, string, strlen(string));
SHA256_Final(hash, &sha256);
// 将计算出的哈希值转换为一个十六进制字符串,并将这个字符串存储在outputBuffer中
sha256_hash_string(hash, outputBuffer);
}
int check_args(int argc)
{
if (3 > argc)
{
printf("USLIKE:IP+PORT\n");
return connect_init_error_code;
}
return 0;
}
int create_socket_and_connect(char *argv[], int *cfd)
{
// 创建socket
*cfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == *cfd)
{
perror("socket");
return connect_init_error_code;
}
// 开启keepalive选项
int enable = 1;
int idle = 10; // 空闲时间10秒
int interval = 15; // 每15秒发送一次
int count = 3; // 发送3次后仍无响应则断开连接
setsockopt(*cfd, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable));
setsockopt(*cfd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle));
setsockopt(*cfd, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(interval));
setsockopt(*cfd, IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(count));
struct sockaddr_in sddr;
sddr.sin_family = AF_INET; // IPV4
sddr.sin_port = htons(atoi(argv[2]));
sddr.sin_addr.s_addr = inet_addr(argv[1]);
// 连接到服务器
if (-1 == connect(*cfd, (void *)&sddr, sizeof(sddr)))
{
perror("connect");
return connect_init_error_code;
}
return 0;
}
void send_file_content(int *cfd, int *fd)
{
char buf[SIZE];
int count;
// 获取文件大小
struct stat st;
fstat(*fd, &st);
int file_size = st.st_size;
int sent_size = 0; // 已发送的数据大小
write(*cfd, &file_size, sizeof(int));
while ((count = read(*fd, buf, SIZE)) > 0)
{
write(*cfd, buf, count);
sent_size += count;
// 计算并打印上传进度
float progress = (float)sent_size / file_size * 100;
printf("Upload progress: %.2f%%\r", progress);
fflush(stdout); // 刷新输出,使进度条能够实时更新
}
puts("\nDone");
close(*fd);
}
int send_file_content_exception(int *cfd, char *filename, off_t start_pos)
{
int fd;
// 打开文件
fd = open(filename, O_RDONLY);
if (-1 == fd)
{
perror("open");
return 0;
}
char buf[SIZE];
int count;
// 获取文件大小
struct stat st;
fstat(fd, &st);
int file_size = st.st_size;
int sent_size = start_pos;
write(*cfd, &file_size, sizeof(int));
lseek(fd, start_pos, SEEK_SET); // 设置文件偏移量
while ((count = read(fd, buf, SIZE)) > 0)
{
write(*cfd, buf, count);
sent_size += count;
// 计算并打印上传进度
float progress = (float)sent_size / file_size * 100;
printf("Upload progress: %.2f%%\r", progress);
fflush(stdout); // 刷新输出,使进度条能够实时更新
}
puts("Done");
close(fd);
return 1;
}
int open_send_file(int *cfd, int *fd)
{
// 从键盘获取文件名:
char filename[30];
printf("please enter the filename\n");
fgets(filename, 30, stdin);
filename[strlen(filename) - 1] = '\0';
int file_len = strlen(filename) + 1;
// 如果文件名为quit,则退出
if (0 == strncmp("quit", filename, 4))
{
write(*cfd, &file_len, sizeof(int));
write(*cfd, filename, file_len);
return quit_code;
}
*fd = open(filename, O_RDONLY);
if (-1 == *fd)
{
perror("open");
return -1;
}
write(*cfd, &file_len, sizeof(int));
printf("%d", file_len);
write(*cfd, filename, file_len);
send_file_content(cfd, fd);
return 0;
}
// 封装一个函数,将上传的代码封装为一个函数
int upload_file(int *cfd, int *fd)
{
while (1)
{
int ret = open_send_file(cfd, fd);
if (ret == -1)
return -1;
if (ret == quit_code)
break;
}
// close(*cfd);
return exit_upload_statu_code;
}
// 封装一个函数用于接收来自服务器的文件
void *recv_file(void *pnfd, char *filename)
{
int nfd = *(int *)pnfd;
printf("downloading file %s\n", filename);
// 创建文件
int fd = open(filename, O_WRONLY | O_CREAT, 0664);
if (-1 == fd)
{
perror("open");
exit(-1);
}
int file_size;
read(nfd, &file_size, sizeof(int));
// 接收文件内容并写入到文件中
char buf[SIZE];
int count;
int size_judge = 0;
while ((count = read(nfd, buf, SIZE)) > 0)
{
write(fd, buf, count);
size_judge = size_judge + count;
float progress = (float)size_judge / file_size * 100;
printf("Upload progress: %.2f%%\r", progress);
if (size_judge == file_size)
{
close(fd);
puts("Done");
break;
}
}
return NULL;
}
int client_menu(int *cfd, int *fd)
{
int choice;
int log_status;
int fun_status;
int up_status;
pthread_t tid_heatbeat_send;
State current_state = STATE_START_MENU;
// 状态机思想
while (current_state != STATE_QUIT)
{
switch (current_state)
{
case STATE_START_MENU:
printf("请输入你的选择:1.登录 2.注册 3.退出\n");
scanf("%d", &choice);
getchar(); // scanf不会处理换行符/n吃掉换行符避免后面的fets获取到\n
if (choice == 1)
{
// 处理登录
current_state = STATE_LOGIN;
}
else if (choice == 2)
{
// 处理注册
current_state = STATE_REGISTER;
}
else if (choice == 3)
{
// 退出
current_state = STATE_QUIT;
}
break;
case STATE_REGISTER:
// 注册
log_status = Register_login(cfd, 0);
if (log_status == register_successful_code)
{
current_state = STATE_START_MENU;
}
else if (log_status == login_failue_code)
{
current_state = STATE_REGISTER;
}
break;
case STATE_LOGIN:
// 登录
log_status = Register_login(cfd, 1);
switch (log_status)
{
case login_successfully_code:
current_state = STATE_FUCTION_MENU;
break;
case login_failue_code:
current_state = STATE_START_MENU;
break;
case have_down_exception_code:
current_state = STATE_DOWNLOAD_EXCEPTION;
break;
case have_up_exception_code:
current_state = STATE_UPLOAD_EXCEPTION;
break;
default:
break;
}
break;
case STATE_UPLOAD_EXCEPTION:
printf("有数据未上传完毕,开始断点重传\n");
// int send_type = 22;
// write(*cfd, &send_type, sizeof(int));
int message_len;
char message_expection[128];
char filename[256];
char type[256];
int transferred;
int read_ret = read(*cfd, &message_len, sizeof(int));
if (read_ret <= 0)
{
printf("read eception error\n");
}
read(*cfd, message_expection, message_len);
sscanf(message_expection, "Filename: %s Type: %s Transferred: %d", filename, type, &transferred);
printf("%s\n", message_expection);
printf("%s\n", filename);
int exception_judge = send_file_content_exception(cfd, filename, transferred);
if (exception_judge == 1)
{
printf("断点重传成功\n");
}
current_state = STATE_FUCTION_MENU;
break;
case STATE_FUCTION_MENU:
printf("请输入你的选择:1.上传 2.下载 3.退出\n");
scanf("%d", &choice);
getchar(); // scanf不会处理换行符/n吃掉换行符避免后面的fets获取到\n
if (choice == 1)
{
// 处理上传
current_state = STATE_UPLOAD;
}
else if (choice == 2)
{
// 处理下载
current_state = STATE_DOWNLOAD;
}
else if (choice == 3)
{
// 处理下载
current_state = STATE_QUIT;
}
break;
case STATE_UPLOAD:
fun_status = upload_statu_code;
printf("enter quit to exit upload state\n");
write(*cfd, &fun_status, sizeof(int));
up_status = upload_file(cfd, fd);
if (exit_upload_statu_code == up_status)
{
write(*cfd, &up_status, sizeof(int));
current_state = STATE_FUCTION_MENU;
}
break;
case STATE_DOWNLOAD:
// 处理下载
fun_status = download_statu_code;
int while_status;
int server_response;
int filename_buffer_size = 128;
char buffer[128];
char download_filenaeme_buffer[128];
int ret;
puts("**************************");
printf("enter quit to exit down state\n");
puts("**************************");
// 发送下载状态指令
write(*cfd, &fun_status, sizeof(int));
while ((ret = read(*cfd, buffer, sizeof(buffer))) > 0)
{
if (0 == strncmp(buffer, "END_SEND_FILENAME", 17))
{
puts("**************************");
break;
}
printf("%s\n", buffer);
}
puts("please enter filename you want to download");
while (1)
{
fgets(download_filenaeme_buffer, filename_buffer_size, stdin);
// 发送文件名
download_filenaeme_buffer[strlen(download_filenaeme_buffer) - 1] = '\0';
int file_len = strlen(download_filenaeme_buffer) + 1;
// 发送文件名长度
write(*cfd, &file_len, sizeof(int));
write(*cfd, download_filenaeme_buffer, filename_buffer_size);
if (0 == strncmp(download_filenaeme_buffer, "quit", 4))
{
current_state = STATE_FUCTION_MENU;
break;
}
// 获取文件名的服务器结果
read(*cfd, &server_response, sizeof(int));
switch (server_response)
{
case download_file_not_exist_code:
puts("file not exist");
current_state = STATE_FUCTION_MENU;
break;
case begin_download_code:
recv_file(cfd, download_filenaeme_buffer);
current_state = STATE_DOWNLOAD;
break;
default:
break;
}
break;
}
break;
}
}
}
// 注册登录函数
int Register_login(int *cfd, int ROL)
{
char username_buffer[15];
char password_buffer[11];
char password_hash_buffer[65];
int server_response;
puts("**************************");
printf("pelease enter the name: \n");
puts("**************************");
fgets(username_buffer, 15, stdin);
puts("**************************");
printf("pelease enter the Password: \n");
puts("**************************");
fgets(password_buffer, 11, stdin);
// 将数据组合为一个注册包发送给服务器,注册包的开头为0,登录包的开头为1
sha256_string(password_buffer, password_hash_buffer);
char register_pack[100];
if (0 == ROL)
{
sprintf(register_pack, "0%s%s", username_buffer, password_hash_buffer);
}
else if (1 == ROL)
{
sprintf(register_pack, "1%s%s", username_buffer, password_hash_buffer);
}
// 发送到服务器
write(*cfd, register_pack, strlen(register_pack));
read(*cfd, &server_response, sizeof(int));
switch (server_response)
{
case name_exit_code:
puts("**************************");
printf("Username already exists.\n");
puts("**************************");
return login_failue_code;
break;
case register_successful_code:
puts("**************************");
printf("Register success.\n");
puts("**************************");
return register_successful_code;
break;
case register_failue_code:
puts("**************************");
printf("Register fail\n ");
puts("**************************");
return login_failue_code;
break;
case login_successfully_code:
puts("**************************");
printf("login successful.\n");
puts("**************************");
return login_successfully_code;
break;
case login_failue_code:
puts("**************************");
printf("login failue.\n");
puts("**************************");
return login_failue_code;
case have_down_exception_code:
puts("**************************");
printf("down_exception.\n");
puts("**************************");
return have_down_exception_code;
case have_up_exception_code:
puts("**************************");
printf("up_exception.\n");
puts("**************************");
return have_up_exception_code;
break;
default:
break;
}
}
服务器:
// 空了实现一下防止sql注入,保证数据完整传输的两种方法、解决一下套接字复用的问题(使用poll模型),让多文件多线程接收实现真并发
// 完善一下文件存在的问题设定一个数据库,写入文件名,设定返回值用于返回文件上传的结果
// 接收完毕,向客户端发送发送完毕的码,表示服务器以及正确接收文件
/*
* **************************************************************************
* ******************** ********************
* ******************** COPYRIGHT INFORMATION ********************
* ******************** ********************
* **************************************************************************
* *
* _oo8oo_ *
* o8888888o *
* 88" . "88 *
* (| -_- |) *
* 0\ = /0 *
* ___/'==='\___ *
* .' \\| |// '. *
* / \\||| : |||// \ *
* / _||||| -:- |||||_ \ *
* | | \\\ - /// | | *
* | \_| ''\---/'' |_/ | *
* \ .-\__ '-' __/-. / *
* ___'. .' /--.--\ '. .'___ *
* ."" '< '.___\_<|>_/___.' >' "". *
* | | : `- \`.:`\ _ /`:.`/ -` : | | *
* \ \ `-. \_ __\ /__ _/ .-` / / *
* =====`-.____`.___ \_____/ ___.`____.-`===== *
* `=---=` *
* **************************************************************************
* ******************** ********************
* ******************** ********************
* ******************** 佛祖保佑 永无BUG 永不宕机 ********************
* ******************** ********************
* **************************************************************************
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <sqlite3.h>
#include <dirent.h>
#include <netinet/tcp.h>
#include <errno.h>
#define SIZE 64
#define PORT 8888
#define quit_code 1
#define error_code 2
#define password_error_code 3
#define name_exit_code 4
#define register_successful_code 5
#define register_fali_code 6
#define login_successfully_code 7
#define login_failue_code 8
#define unrefister_code 9
#define upload_statu_code 10
#define download_statu_code 11
#define exit_upload_or_download_statu_code 12
#define end_download_statu_code 13
#define end_send_file_code 14
#define download_file_not_exist_code 15
#define begin_download_code 16
#define end 17
#define have_up_exception_code 21
#define have_down_exception_code 22
#define HEARTBEAT_CODE 20
typedef struct sockaddr_in sock;
typedef struct user
{
char passwords[11];
char name[15];
} usr;
typedef struct client
{
char IP[15];
char username[15];
int nfd;
} client;
// 创建套接字,返回创建成功的套接字
int socket_creat()
{
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sfd)
{
perror("socket");
return -1;
}
// 开启keepalive选项
int enable = 1;
setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable));
// 设置keepalive参数
int idle = 15; // 空闲时间15秒
int interval = 15; // 每5秒发送一次
int count = 3; // 发送3次后仍无响应则断开连接
setsockopt(sfd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle));
setsockopt(sfd, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(interval));
setsockopt(sfd, IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(count));
return sfd;
}
// 设置套接字选项,保证端口在断开后能立即使用
int set_socker_options(int sfd)
{
int ok = 1;
if (-1 == setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &ok, sizeof(ok)))
{
return -1;
}
return 0;
}
// 绑定套接字
int bind_socket(int sfd, sock *sddr)
{
if (-1 == bind(sfd, (void *)sddr, sizeof(*sddr)))
{
perror("bind");
return -1;
}
return 0;
}
// 监听套接字
int listen_socket(int sfd)
{
if (-1 == listen(sfd, 20))
{
perror("listen");
}
puts("listen...");
}
// 接受连接
// 返回结构体
client accept_connection(int sfd, sock *cddr)
{
int len1 = sizeof(*cddr);
int nfd = accept(sfd, (void *)cddr, &len1);
printf("IP:%s PORT:%d connected!\n", inet_ntoa(cddr->sin_addr), ntohs(cddr->sin_port));
client client_message;
strcpy(client_message.IP, inet_ntoa(cddr->sin_addr));
client_message.nfd = nfd;
return client_message;
}
// 接受文件
int recv_file(void *pnfd)
{
int type; //
client client_message = *(client *)pnfd;
int nfd = client_message.nfd;
printf("%s\n", client_message.username);
while (1)
{
int file_name_len;
int ret = read(nfd, &file_name_len, sizeof(int));
if (ret <= 0) // 客户端已经断开连接或读取完一个文件
{
close(nfd);
pthread_exit(NULL);
}
// 接收文件名
char filename[file_name_len];
read(nfd, filename, file_name_len);
// 如果文件名为quit,则退出
if (0 == strncmp("quit", filename, 4))
{
printf("客户端退出下载\n");
return 0;
}
printf("uploading file %s\n", filename);
// 创建文件
int fd = open(filename, O_WRONLY | O_CREAT, 0664);
if (-1 == fd)
{
perror("open");
exit(error_code);
}
int file_size;
read(nfd, &file_size, sizeof(int));
// 接收文件内容并写入到文件中
char buf[SIZE];
int count;
int size_judge = 0;
while ((count = read(nfd, buf, SIZE)) > 0)
{
write(fd, buf, count);
size_judge = size_judge + count;
float progress = (float)size_judge / file_size * 100;
printf("Upload progress: %.2f%%\r", progress);
fflush(stdout);
if (size_judge == file_size)
{
close(fd);
puts("Done");
break;
}
}
if (size_judge < file_size)
{
printf("文件未传输完毕,记录信息至数据库\n");
sqlite3 *db;
char *errmsg;
type = have_down_exception_code;
if (sqlite3_open("Exception.db", &db) != SQLITE_OK)
{
printf("Can't open database: %s\n", sqlite3_errmsg(db));
}
char sql_command_insert[256];
sprintf(sql_command_insert, "INSERT INTO exception (ip_username, filename, type, transferred) VALUES('%s_%s', '%s', '%d', %d);", client_message.IP, client_message.username, filename, type, size_judge);
if (sqlite3_exec(db, sql_command_insert, NULL, NULL, &errmsg) != SQLITE_OK)
{
printf("SQL error: %s\n", errmsg);
sqlite3_free(errmsg);
}
sqlite3_close(db);
close(nfd);
printf("客户端断开连接,线程退出\n");
pthread_exit(NULL);
return 1;
}
}
}
int exception_recv_file(void *pnfd, char *filename, int offset)
{
int type = have_down_exception_code;
client client_message = *(client *)pnfd;
int nfd = client_message.nfd;
while (1)
{
printf("开始接收未传输完毕的文件: %s\n", filename);
// 创建文件
int fd = open(filename, O_WRONLY | O_CREAT, 0664);
if (-1 == fd)
{
perror("open");
exit(error_code);
}
// 移动文件指针到指定的偏移量
lseek(fd, offset, SEEK_SET);
int file_size;
read(nfd, &file_size, sizeof(int));
// 接收文件内容并写入到文件中
char buf[SIZE];
int count;
int size_judge = offset;
printf("从%d字节开始传输,文件总大小:%d\n", size_judge, file_size);
while ((count = read(nfd, buf, SIZE)) > 0)
{
write(fd, buf, count);
size_judge += count;
float progress = (float)size_judge / file_size * 100;
printf("Upload progress: %.2f%%\r", progress);
fflush(stdout);
if (size_judge == file_size)
{
close(fd);
puts("Done");
break;
}
}
if (size_judge < file_size)
{
printf("文件未传输完毕,记录信息至数据库\n");
sqlite3 *db;
char *errmsg;
type = have_down_exception_code;
if (sqlite3_open("Exception.db", &db) != SQLITE_OK)
{
printf("Can't open database: %s\n", sqlite3_errmsg(db));
}
char sql_command_insert[256];
sprintf(sql_command_insert, "INSERT INTO exception (ip_username, filename, type, transferred) VALUES('%s_%s', '%s', '%d', %d);", client_message.IP, client_message.username, filename, type, size_judge);
if (sqlite3_exec(db, sql_command_insert, NULL, NULL, &errmsg) != SQLITE_OK)
{
printf("SQL error: %s\n", errmsg);
sqlite3_free(errmsg);
}
printf("客户端已断开连接\n");
close(fd);
sqlite3_close(db);
pthread_exit(NULL);
return 1;
}
else if (size_judge == file_size)
{
printf("传输完毕!\n");
// 打开数据库
sqlite3 *db;
char *errmsg;
if (sqlite3_open("Exception.db", &db) != SQLITE_OK)
{
printf("Can't open database: %s\n", sqlite3_errmsg(db));
return -1;
}
// 创建SQL DELETE语句
char sql_command_delete[256];
sprintf(sql_command_delete, "DELETE FROM exception WHERE ip_username = '%s_%s' AND filename = '%s';", client_message.IP, client_message.username, filename);
// 执行SQL DELETE语句
if (sqlite3_exec(db, sql_command_delete, NULL, NULL, &errmsg) != SQLITE_OK)
{
printf("SQL error: %s\n", errmsg);
sqlite3_free(errmsg);
}
// 关闭数据库
sqlite3_close(db);
return 1;
}
}
}
// 总初始化函数
int srver_fd_init(sock *sddr, int *sfd)
{
sddr->sin_family = AF_INET;
sddr->sin_port = htons(PORT);
sddr->sin_addr.s_addr = INADDR_ANY;
*sfd = socket_creat();
set_socker_options(*sfd);
bind_socket(*sfd, sddr);
listen_socket(*sfd);
sqlite3 *db; // database
char *errmsg;
if (SQLITE_OK != sqlite3_open("Exception.db", &db))
{
fprintf(stderr, "%s", sqlite3_errmsg(db));
return -1;
}
char *sql_command_create = {"create table if not exists exception(ip_username text PRIMARY KEY, filename text, type text, transferred integer);"};
if (SQLITE_OK != sqlite3_exec(db, sql_command_create, NULL, NULL, &errmsg))
{
printf(" errmsg:%s", errmsg);
return -1;
}
sqlite3_close(db);
}
// 定义一个函数,用于获取从服务器端来的注册信息
int get_Register(char *usr, char *psd, int *response_code)
{
sqlite3 *db; // database
char *errmsg;
if (SQLITE_OK != sqlite3_open("User.db", &db))
{
fprintf(stderr, "%s", sqlite3_errmsg(db));
return -1;
}
// AUTOINCREMENT表示自动生成id,INCREMENT表示自动增加
char *sql_command_create = {"create table if not exists logfile(id INTEGER PRIMARY KEY AUTOINCREMENT, name text, password text);"};
// 创建表:表不存在则创建
if (SQLITE_OK != sqlite3_exec(db, sql_command_create, NULL, NULL, &errmsg))
{
printf(" errmsg:%s", errmsg);
return -1;
}
const char *sql_id_get = "SELECT id FROM logfile ORDER BY id DESC LIMIT 1;";
char sql_command_insert[356];
sprintf(sql_command_insert, "INSERT INTO logfile (name, password) VALUES('%s', '%s');", usr, psd);
// 查询用户是否存在
char sql_command_check[256];
sprintf(sql_command_check, "SELECT * FROM logfile WHERE name = '%s';", usr);
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db, sql_command_check, -1, &stmt, NULL) == SQLITE_OK)
{
if (sqlite3_step(stmt) == SQLITE_ROW)
{
*response_code = name_exit_code;
sqlite3_finalize(stmt);
sqlite3_close(db);
return name_exit_code; // 返回一个错误值
}
else
{
if (SQLITE_OK != sqlite3_exec(db, sql_command_insert, NULL, NULL, &errmsg))
{
printf(" Insert:%s", errmsg);
return register_fali_code;
}
*response_code = register_successful_code;
return register_successful_code;
}
}
sqlite3_finalize(stmt);
sqlite3_close(db);
}
// 定义一个函数用于处理登录验证
int login_check(char *usr, char *psd, int *response_code)
{
sqlite3 *db; // database
int Back_val;
if (SQLITE_OK != sqlite3_open("User.db", &db))
{
fprintf(stderr, "%s", sqlite3_errmsg(db));
return -1;
}
char sql_command_check[256];
sprintf(sql_command_check, "SELECT * FROM logfile WHERE name = '%s' AND password = '%s';", usr, psd);
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db, sql_command_check, -1, &stmt, NULL) == SQLITE_OK)
{
int ret = sqlite3_step(stmt);
sqlite3_reset(stmt);
if (ret == SQLITE_ROW)
{
printf("login successfully.\n");
*response_code = login_successfully_code;
Back_val = login_successfully_code; // 返回
}
else
{
printf("login fail.\n");
*response_code = login_failue_code;
Back_val = login_failue_code;
}
}
sqlite3_finalize(stmt);
sqlite3_close(db);
return Back_val;
}
// 定义一个函数,用于回复客户端的下载指令
// 文件发送函数:
int open_send_file(int *nfd, char *filename)
{
int file_download_status;
int fd;
// 如果文件名为quit,则退出
if (0 == strncmp("quit", filename, 4))
{
return end_download_statu_code;
}
fd = open(filename, O_RDONLY);
if (-1 == fd)
{
perror("open");
return download_file_not_exist_code;
}
else
{
file_download_status = begin_download_code;
write(*nfd, &file_download_status, sizeof(int));
}
char buf[SIZE];
int count;
// 获取文件大小
struct stat st;
fstat(fd, &st);
int file_size = st.st_size;
printf("%d\n", file_size);
write(*nfd, &file_size, sizeof(int));
while ((count = read(fd, buf, SIZE)) > 0)
{
write(*nfd, buf, count);
}
puts("Done");
close(fd);
return end;
}
int send_file(int *pnfd)
{
int nfd = *pnfd;
DIR *d;
struct dirent *dir;
char send_status_buffer[128] = {"END_SEND_FILENAME"}; // 17
char download_filenaeme_buffer[128] = {0};
int file_download_status;
int filename_buffer_size = 128;
// char filename_buffer[128];
d = opendir(".");
if (d)
{
while ((dir = readdir(d)) != NULL)
{
if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0)
{
continue;
}
write(nfd, dir->d_name, filename_buffer_size);
// write(nfd, "\n", 1); // 换行,以便客户端可以区分不同的文件名
}
// 发送完毕发送结束字符
write(nfd, send_status_buffer, filename_buffer_size);
closedir(d);
}
else
{
perror("opendir");
}
// 开始接收函数
while (1)
{
int file_name_len;
int ret = read(nfd, &file_name_len, sizeof(int));
if (ret <= 0) // 客户端已经断开连接或读取完一个文件
{
return 0;
}
read(nfd, download_filenaeme_buffer, file_name_len);
if (0 != strncmp("quit", download_filenaeme_buffer, 4))
printf("doloding_file:%s", download_filenaeme_buffer);
file_download_status = open_send_file(&nfd, download_filenaeme_buffer);
if (end_download_statu_code == file_download_status)
{
puts("Download status quit");
// write(nfd, &file_download_status, sizeof(int));
return end_download_statu_code;
}
else if (download_file_not_exist_code == file_download_status)
{
write(nfd, &file_download_status, sizeof(int));
return download_file_not_exist_code;
}
else if (end == file_download_status)
{
break;
}
}
}
// 定义一个函数用于解析登录注册包
int analysis(char *client_comond_buf, void *nfds, int *is_logged_in_addr)
{
int response_code;
int is_logged_in = *is_logged_in_addr;
char *p = client_comond_buf;
char username_buffer[15] = {0};
char password_buffer[65] = {0};
char *usr = username_buffer;
char *psd = password_buffer;
client client_message = *(client *)nfds;
int *nfd = &client_message.nfd;
char *k = p + 1;
// 解析用户名和密码
while (*k != '\n')
{
*usr = *k;
usr++;
k++;
}
k = k + 1;
while (*k)
{
*psd = *k;
psd++;
k++;
}
printf("analysis success\n");
strcpy((*(client *)nfds).username, username_buffer);
// 如果是注册包:
if ('0' == *p)
{
printf("Register ....\n");
int register_result = get_Register(username_buffer, password_buffer, &response_code);
// 如果用户名以及存在,则返回客户端,用户已存在
if (name_exit_code == register_result)
{
printf("Username already exists.\n");
write(*nfd, &response_code, sizeof(int));
}
else if (register_successful_code == register_result)
{
write(*nfd, &response_code, sizeof(int));
printf("Register success.\n");
}
else
{
response_code = register_fali_code;
printf("Register fail.\n");
write(*nfd, &response_code, sizeof(int));
}
}
if ('1' == *p)
{
// 登录验证代码
int login_result = login_check(username_buffer, password_buffer, &response_code);
if (login_successfully_code == login_result)
{
// 查询异常数据库
sqlite3 *db;
char *errmsg;
printf("开始查询异常数据库\n");
if (sqlite3_open("Exception.db", &db) != SQLITE_OK)
{
printf("Can't open database: %s\n", sqlite3_errmsg(db));
return -1;
}
char sql_command_check[256];
sprintf(sql_command_check, "SELECT * FROM exception WHERE ip_username = '%s_%s';", client_message.IP, (*(client *)nfds).username);
printf("%s\n", sql_command_check);
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db, sql_command_check, -1, &stmt, NULL) == SQLITE_OK)
{
if (sqlite3_step(stmt) == SQLITE_ROW)
{
// 有异常信息,返回给客户端
printf("有未接收完毕的文件\n");
char *filename = (char *)sqlite3_column_text(stmt, 1); // 此指针由sqlite3管理。关闭即释放
char *type = (char *)sqlite3_column_text(stmt, 2);
int send_type = 22;
write(*nfd, &send_type, sizeof(int));
// sleep(5);
int transferred = sqlite3_column_int(stmt, 3);
char message[256];
sprintf(message, "Filename: %s Type: %s Transferred: %d", filename, type, transferred);
printf("%s\n", message);
int message_len = strlen(message);
write(*nfd, &message_len, sizeof(int));
write(*nfd, message, message_len);
char my_filename[128];
strcpy(my_filename, filename);
printf("copy filename %s\n", my_filename);
sqlite3_finalize(stmt);
sqlite3_close(db);
exception_recv_file(&client_message, my_filename, transferred);
}
else
{
// 无异常
printf("无异常\n");
write(*nfd, &response_code, sizeof(int));
}
}
sqlite3_close(db);
is_logged_in = 1; // 设置登录状态为已登录
return login_successfully_code; // 返回一个成功值
}
else if (login_failue_code == login_result)
{
write(*nfd, &response_code, sizeof(int));
printf("login failue.\n");
return login_failue_code; // 返回一个失败值
}
}
}
// 定义一个回调函数,用于新线程解析并处理来自客户端的命令
void *analysis_connection(void *pnfd)
{
int ret;
int log_status;
client client_message = *(client *)pnfd;
int nfd = client_message.nfd; // 将nfd的值复制到新的int中
char client_comond_buf[100];
int is_logged_in = 0; // 登录状态局部变量
while (1)
{
if (!is_logged_in) // 如果客户端还没有登录
{
// 从客户端获取信息,看是登录包还是注册包,登陆包完毕后,再执行接下来的操作
ret = read(nfd, client_comond_buf, sizeof(client_comond_buf));
if (ret <= 0) // 客户端已经断开连接或读取完一个文件
{
close(nfd);
pthread_exit(NULL);
}
// 解析包
log_status = analysis(client_comond_buf, &client_message, &is_logged_in);
if (log_status == login_successfully_code)
{
is_logged_in = 1; // 设置登录状态为已登录
}
}
if (is_logged_in) // 如果客户端已经登录
{
int should_continue = 1;
while (should_continue) // 等待新指令的循环
{
// 接收指令
struct timeval timeout;
timeout.tv_sec = 3600; // 设置超时时间为3600秒
timeout.tv_usec = 0;
setsockopt(nfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
int up_down_exit_heart_status;
ret = recv(nfd, &up_down_exit_heart_status, sizeof(int), 0);
if (ret <= 0) // 客户端已经断开连接或读取完一个文件
{
if (errno == EAGAIN || errno == EWOULDBLOCK)
{
printf("客户端超时!t\n");
}
else
{
printf("客户端退出!\n");
}
is_logged_in = 0;
close(nfd);
pthread_exit(NULL);
}
switch (up_down_exit_heart_status)
{
case upload_statu_code:
puts("uploading.....");
recv_file(&client_message);
printf("接收状态退出\n");
break;
case download_statu_code:
puts("downloading.....");
send_file(&nfd);
break;
case exit_upload_or_download_statu_code:
// is_logged_in = 0;
should_continue = 0;
break;
default:
// 处理未知的状态
break;
}
}
}
}
return NULL;
}
int main(int argc, char *argv[])
{
char client_comond_buf[64];
sock sddr, cddr;
int sfd;
// 初始化
srver_fd_init(&sddr, &sfd);
while (1)
{
// 此函数的accept函数会阻塞
client client_message = accept_connection(sfd, &cddr);
// 开线程,用于解析来自客户端的连接指令
pthread_t tid_client_anlysis;
if (0 != pthread_create(&tid_client_anlysis, NULL, analysis_connection, &client_message))
{
perror("pthread_creat");
pthread_exit(NULL);
}
pthread_detach(tid_client_anlysis); // 分离线程,使其在结束时自动回收资源
}
close(sfd);
return 0;
}