linux/server.c
2024-12-18 00:21:22 +08:00

437 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <zlib.h>
#include <fcntl.h>
#include <time.h>
#include <sys/stat.h>
#include <gmssl/sm4.h>
#include <gmssl/rand.h>
#include <dispatch/dispatch.h>
#include <arpa/inet.h>
#define PORT 8001
#define MAX_CLIENTS 5
#define BUFFER_SIZE 1024
// 将信号量定义为全局变量
dispatch_semaphore_t semaphore;
int log_pipe[2]; // 日志记录的管道
// 生成时间戳目录
void create_timestamp_dir(char *dir_name)
{
time_t now = time(NULL);
struct tm *t = localtime(&now);
// t->tm_year从 1900 年开始的年数,因此需要加上 1900 得到完整的年份。
// t->tm_mon从 0 开始的月份0 表示 1 月),因此需要加上 1 得到实际的月份。
// t->tm_mday月份中的日期。
// t->tm_hour小时。
// t->tm_min分钟。
// t->tm_sec秒。
sprintf(dir_name, "./files/%04d%02d%02d%02d%02d%02d",
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec);
mkdir("./files", 0777);
mkdir(dir_name, 0777);
}
// 记录日志
void log_operation(const char *message)
{
write(log_pipe[1], message, strlen(message));
}
// 文件压缩
int compress_file(const char *src, const char *dest)
{
FILE *source = fopen(src, "rb");
gzFile dest_file = gzopen(dest, "wb");
if (!source || !dest_file)
return -1;
char buffer[BUFFER_SIZE];
int bytes_read;
while ((bytes_read = fread(buffer, 1, BUFFER_SIZE, source)) > 0)
{
gzwrite(dest_file, buffer, bytes_read);
}
fclose(source);
gzclose(dest_file);
return 0;
}
// 文件解压
int decompress_file(const char *src, const char *dest)
{
gzFile source = gzopen(src, "rb");
FILE *dest_file = fopen(dest, "wb");
if (!source || !dest_file)
return -1;
char buffer[BUFFER_SIZE];
int bytes_read;
while ((bytes_read = gzread(source, buffer, BUFFER_SIZE)) > 0)
{
fwrite(buffer, 1, bytes_read, dest_file);
}
gzclose(source);
fclose(dest_file);
return 0;
}
// 加密文件方法
int sm4_cbc_padding_encrypt_file(const char *input_file, const char *output_file, const uint8_t *key, const uint8_t *iv)
{
SM4_KEY enc_key;
sm4_set_encrypt_key(&enc_key, key); // 设置加密密钥
FILE *in_file = fopen(input_file, "rb");
FILE *out_file = fopen(output_file, "wb");
if (!in_file || !out_file)
{
perror("文件打开失败");
return 0;
}
uint8_t input_block[SM4_BLOCK_SIZE];
uint8_t output_block[SM4_BLOCK_SIZE + SM4_BLOCK_SIZE]; // 加密后可能需要填充
size_t bytes_read, outlen;
while ((bytes_read = fread(input_block, 1, SM4_BLOCK_SIZE, in_file)) > 0)
{
if (feof(in_file))
{
// 最后一块,带填充加密
if (sm4_cbc_padding_encrypt(&enc_key, iv, input_block, bytes_read, output_block, &outlen) != 1)
{
fprintf(stderr, "加密失败。\n");
fclose(in_file);
fclose(out_file);
return 0;
}
}
else
{
// 中间块,不需要填充
if (sm4_cbc_padding_encrypt(&enc_key, iv, input_block, SM4_BLOCK_SIZE, output_block, &outlen) != 1)
{
fprintf(stderr, "加密失败。\n");
fclose(in_file);
fclose(out_file);
return 0;
}
}
fwrite(output_block, 1, outlen, out_file);
}
fclose(in_file);
fclose(out_file);
return 1; // 成功
}
// 解密文件方法
int sm4_cbc_padding_decrypt_file(const char *input_file, const char *output_file, const uint8_t *key, const uint8_t *iv)
{
SM4_KEY dec_key;
sm4_set_decrypt_key(&dec_key, key); // 设置解密密钥
FILE *in_file = fopen(input_file, "rb");
FILE *out_file = fopen(output_file, "wb");
if (!in_file || !out_file)
{
perror("文件打开失败");
return 0;
}
uint8_t input_block[SM4_BLOCK_SIZE + SM4_BLOCK_SIZE]; // 假设有填充
uint8_t output_block[SM4_BLOCK_SIZE];
size_t bytes_read, outlen;
while ((bytes_read = fread(input_block, 1, sizeof(input_block), in_file)) > 0)
{
if (feof(in_file))
{
// 最后一块,带填充解密
if (sm4_cbc_padding_decrypt(&dec_key, iv, input_block, bytes_read, output_block, &outlen) != 1)
{
fprintf(stderr, "解密失败。\n");
fclose(in_file);
fclose(out_file);
return 0;
}
}
else
{
// 中间块,不需要处理填充
if (sm4_cbc_padding_decrypt(&dec_key, iv, input_block, bytes_read, output_block, &outlen) != 1)
{
fprintf(stderr, "解密失败。\n");
fclose(in_file);
fclose(out_file);
return 0;
}
}
fwrite(output_block, 1, outlen, out_file);
}
fclose(in_file);
fclose(out_file);
return 1; // 成功
}
// 客户端处理线程
void *client_handler(void *arg)
{
int client_socket = *(int *)arg;
free(arg);
char buffer[BUFFER_SIZE];
// 初始化缓冲区为 0
memset(buffer, 0, BUFFER_SIZE);
// 接收文件名
int bytes = read(client_socket, buffer, BUFFER_SIZE);
if (bytes <= 0)
{
perror("接收文件名失败");
return NULL;
}
buffer[bytes] = '\0'; // 确保以 NULL 结尾
char file_name[256];
snprintf(file_name, sizeof(file_name), "%s", buffer);
printf("接收文件名:%s\n", file_name);
// 发送 ACK 确认
send(client_socket, "ACK", strlen("ACK"), 0);
char origin_file_path[256], dir_name[128], file_path[256], compressed_file[256], decrypted_file[256];
// 先生成./files目录再生成时间戳目录调用 create_timestamp_dir 函数,生成一个基于时间戳的目录名称,并将其存储在 dir_name 中
create_timestamp_dir(dir_name);
// 根据传入的文件名以及时间戳拼接生成源文件路径、加密后的文件、压缩的文件、解密的文件路径
sprintf(origin_file_path, "%s/%s.txt", dir_name, buffer);
sprintf(file_path, "%s/%s.enc", dir_name, buffer);
sprintf(compressed_file, "%s/%s.gz", dir_name, buffer);
// sprintf(decrypted_file, sizeof(decrypted_file), "%s/%s_decrypted.txt", dir_name, buffer);
snprintf(decrypted_file, sizeof(decrypted_file), "%s/%s_decrypted.txt", dir_name, buffer);
printf("源文件路径%s\n加密后的文件%s\n压缩的文件%s\n解密的文件路径%s\n", origin_file_path, file_path, compressed_file, decrypted_file);
FILE *file = fopen(origin_file_path, "wb");
if (!file)
{
perror("文件打开失败");
return NULL;
}
// 按块读取源文件内容并保存到origin_file_path
while (1)
{
int bytes = read(client_socket, buffer, BUFFER_SIZE);
if (bytes < 0)
{
perror("读取数据失败");
fclose(file);
return NULL;
}
// 检测是否接收到结束标志
if (strncmp(buffer, "EOF", 3) == 0)
break;
// 将数据写入文件
if (fwrite(buffer, 1, bytes, file) != bytes)
{
perror("写入文件失败");
fclose(file);
return NULL;
}
}
fclose(file);
log_operation("文件已接收。\n");
// 随机生成加密密钥和 IV
uint8_t key[SM4_KEY_SIZE];
if (rand_bytes(key, SM4_KEY_SIZE) != 1)
{
fprintf(stderr, "随机密钥生成失败。\n");
return NULL;
}
uint8_t iv[SM4_BLOCK_SIZE];
if (rand_bytes(iv, SM4_BLOCK_SIZE) != 1)
{
fprintf(stderr, "随机IV生成失败。\n");
return NULL;
}
// 打印密钥和 IV
printf("随机生成的密钥: ");
for (size_t i = 0; i < SM4_KEY_SIZE; i++)
{
printf("%02X ", key[i]);
}
printf("\n");
printf("随机生成的IV: ");
for (size_t i = 0; i < SM4_BLOCK_SIZE; i++)
{
printf("%02X ", iv[i]);
}
printf("\n");
// 加密文件
printf("正在加密文件...\n");
sm4_cbc_padding_encrypt_file(origin_file_path, file_path, key, iv);
printf("文件加密完成。\n");
char log_message_file_path[BUFFER_SIZE];
sprintf(log_message_file_path, "文件已加密。加密文件保存路径为:%s\n", file_path);
log_operation(log_message_file_path);
// 压缩文件
printf("正在压缩被加密的文件...\n");
compress_file(file_path, compressed_file);
printf("文件压缩完成。\n");
// 删除加密文件
// remove(file_path);
char log_message_compressed_file[BUFFER_SIZE];
sprintf(log_message_compressed_file, "文件已加密。加密文件保存路径为:%s\n", compressed_file);
log_operation(log_message_compressed_file);
// 等待客户端请求
memset(buffer, 0, BUFFER_SIZE);
read(client_socket, buffer, BUFFER_SIZE);
if (strcmp(buffer, "DOWNLOAD") == 0)
{
printf("接收到下载请求...\n");
// 读取文件名
memset(buffer, 0, BUFFER_SIZE);
read(client_socket, buffer, BUFFER_SIZE);
char requested_file[256];
snprintf(requested_file, sizeof(requested_file), "%s", buffer);
printf("客户端请求的文件名: %s\n", requested_file);
printf("接收到下载请求,正在解压缩文件...\n");
decompress_file(compressed_file, decrypted_file);
printf("文件解压缩完成。\n");
printf("正在解密文件...\n");
sm4_cbc_padding_decrypt_file(decrypted_file, decrypted_file, key, iv);
printf("文件解密完成。\n");
log_operation("文件已解压并解密,准备发送。\n");
// 向客户端发送 "READY" 信号
send(client_socket, "READY", strlen("READY"), 0);
// 发送解密后的文件
FILE *file_to_send = fopen(decrypted_file, "rb");
while (!feof(file_to_send))
{
int bytes_read = fread(buffer, 1, BUFFER_SIZE, file_to_send);
send(client_socket, buffer, bytes_read, 0);
}
fclose(file_to_send);
printf("文件发送完成。\n");
// 删除解密后的文件
// remove(decrypted_file);
}
// 关闭连接
close(client_socket);
dispatch_semaphore_signal(semaphore);
return NULL;
}
int main()
{
int server_socket, client_socket;
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len = sizeof(client_addr);
// 创建服务器 socket
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == -1)
{
perror("服务器 socket 创建失败");
exit(EXIT_FAILURE);
}
// 设置服务器地址
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
// 绑定服务器 socket
if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)
{
perror("绑定失败");
close(server_socket);
exit(EXIT_FAILURE);
}
// 开始监听
if (listen(server_socket, MAX_CLIENTS) == -1)
{
perror("监听失败");
close(server_socket);
exit(EXIT_FAILURE);
}
// 初始化信号量
semaphore = dispatch_semaphore_create(MAX_CLIENTS);
if (semaphore == NULL)
{
fprintf(stderr, "信号量创建失败\n");
return -1;
}
// 示例:客户端处理逻辑
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // 等待信号量
// printf("客户端进入,当前资源被占用\n");
// 创建管道
pipe(log_pipe);
if (fork() == 0) // 日志进程
{
close(log_pipe[1]); // 关闭写端
char log_buffer[BUFFER_SIZE];
while (read(log_pipe[0], log_buffer, BUFFER_SIZE) > 0)
{
printf("日志: %s\n", log_buffer);
}
exit(0);
}
printf("服务器已启动,等待客户端连接...\n");
while (1)
{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// printf("客户端进入,当前资源被占用\n");
int client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &client_addr_len);
if (client_socket == -1)
{
perror("接受客户端连接失败");
continue;
}
printf("客户端连接成功,地址: %s\n", inet_ntoa(client_addr.sin_addr));
int *client_sock = malloc(sizeof(int));
*client_sock = client_socket;
pthread_t thread_id;
pthread_create(&thread_id, NULL, client_handler, client_sock);
pthread_detach(thread_id);
}
close(server_socket);
// 释放信号量
dispatch_release(semaphore);
return 0;
}