Add LZ4 compression support for TCP transfers
- Add bundled LZ4 library (lz4.h, lz4.c) - Add conn_options_t for connection-level mode + compression negotiation - Add COMPRESS_NONE and COMPRESS_LZ4 constants - Extend file_meta_t with compress and compressed_size fields - Client: compress files before sending when compression enabled - Server: decompress received files when compression flag is set - Add -c flag to DISABLE compression (default: ON) - Update Makefile to compile lz4.o - Compression falls back to uncompressed if LZ4 doesn't reduce size Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
+97
-37
@@ -1,4 +1,5 @@
|
||||
#include "common.h"
|
||||
#include "lz4.h"
|
||||
#include <getopt.h>
|
||||
#include <poll.h>
|
||||
#include <time.h>
|
||||
@@ -335,15 +336,17 @@ static void *client_thread(void *arg) {
|
||||
void handle_client(int sock_fd, const char *dest_dir) {
|
||||
printf("Client connected. Starting transfer session...\n");
|
||||
|
||||
// Read mode (first 4 bytes from client)
|
||||
uint32_t mode = MODE_SYNC; // Default to sync mode
|
||||
ssize_t n = readn(sock_fd, &mode, sizeof(mode));
|
||||
if (n != sizeof(mode)) {
|
||||
fprintf(stderr, "Failed to read mode from client\n");
|
||||
// Read connection options (mode + compression)
|
||||
conn_options_t opts;
|
||||
ssize_t n = readn(sock_fd, &opts, sizeof(opts));
|
||||
if (n != sizeof(opts)) {
|
||||
fprintf(stderr, "Failed to read connection options from client\n");
|
||||
return;
|
||||
}
|
||||
int use_raw = (mode == MODE_RAW);
|
||||
printf("Mode: %s\n", use_raw ? "RAW" : "SYNC");
|
||||
int use_raw = (opts.mode == MODE_RAW);
|
||||
int use_compress = opts.compress; // COMPRESS_LZ4 or COMPRESS_NONE
|
||||
printf("Mode: %s, Compression: %s\n", use_raw ? "RAW" : "SYNC",
|
||||
use_compress ? "LZ4" : "none");
|
||||
|
||||
int use_udp = 0;
|
||||
int udp_fd = -1;
|
||||
@@ -464,33 +467,91 @@ void handle_client(int sock_fd, const char *dest_dir) {
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("Receiving (pipelined): %s (%lu bytes)\n", filename, (unsigned long)meta.file_size);
|
||||
printf("Receiving (pipelined%s): %s (%lu bytes)\n",
|
||||
meta.compress ? "+compress" : "", filename, (unsigned long)meta.file_size);
|
||||
|
||||
// Read file data
|
||||
char buffer[65536];
|
||||
uint64_t bytes_left = meta.file_size;
|
||||
int write_error = 0;
|
||||
while (bytes_left > 0) {
|
||||
size_t to_read = (bytes_left > sizeof(buffer)) ? sizeof(buffer) : bytes_left;
|
||||
ssize_t nread = read(sock_fd, buffer, to_read);
|
||||
if (nread < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
perror("Socket read error");
|
||||
|
||||
if (meta.compress == COMPRESS_LZ4 && meta.compressed_size > 0) {
|
||||
// Receive compressed data
|
||||
char *comp_buf = malloc(meta.compressed_size);
|
||||
if (!comp_buf) {
|
||||
perror("malloc comp_buf");
|
||||
write_error = 1;
|
||||
break;
|
||||
}
|
||||
if (nread == 0) {
|
||||
fprintf(stderr, "Unexpected socket EOF\n");
|
||||
write_error = 1;
|
||||
break;
|
||||
}
|
||||
if (!write_error) {
|
||||
if (writen(out_fd, buffer, nread) != nread) {
|
||||
perror("File write error");
|
||||
write_error = 1;
|
||||
} else {
|
||||
// Read all compressed data
|
||||
size_t total_read = 0;
|
||||
while (total_read < meta.compressed_size && !write_error) {
|
||||
size_t to_read = (meta.compressed_size - total_read > sizeof(buffer))
|
||||
? sizeof(buffer) : meta.compressed_size - total_read;
|
||||
ssize_t nread = read(sock_fd, buffer, to_read);
|
||||
if (nread < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
perror("Socket read error (compressed)");
|
||||
write_error = 1;
|
||||
break;
|
||||
}
|
||||
if (nread == 0) {
|
||||
fprintf(stderr, "Unexpected socket EOF (compressed)\n");
|
||||
write_error = 1;
|
||||
break;
|
||||
}
|
||||
memcpy(comp_buf + total_read, buffer, nread);
|
||||
total_read += nread;
|
||||
}
|
||||
|
||||
if (!write_error && total_read == meta.compressed_size) {
|
||||
// Decompress
|
||||
char *decomp_buf = malloc(meta.file_size);
|
||||
if (!decomp_buf) {
|
||||
perror("malloc decomp_buf");
|
||||
write_error = 1;
|
||||
} else {
|
||||
int decompressed = LZ4_decompress_safe(comp_buf, decomp_buf,
|
||||
meta.compressed_size, meta.file_size);
|
||||
if (decompressed != (int)meta.file_size) {
|
||||
fprintf(stderr, "Decompression failed: expected %lu, got %d\n",
|
||||
(unsigned long)meta.file_size, decompressed);
|
||||
write_error = 1;
|
||||
} else {
|
||||
// Write decompressed data
|
||||
if (writen(out_fd, decomp_buf, meta.file_size) != (ssize_t)meta.file_size) {
|
||||
perror("File write error (decompressed)");
|
||||
write_error = 1;
|
||||
}
|
||||
}
|
||||
free(decomp_buf);
|
||||
}
|
||||
}
|
||||
free(comp_buf);
|
||||
}
|
||||
} else {
|
||||
// No compression: read and write directly
|
||||
uint64_t bytes_left = meta.file_size;
|
||||
while (bytes_left > 0 && !write_error) {
|
||||
size_t to_read = (bytes_left > sizeof(buffer)) ? sizeof(buffer) : bytes_left;
|
||||
ssize_t nread = read(sock_fd, buffer, to_read);
|
||||
if (nread < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
perror("Socket read error");
|
||||
write_error = 1;
|
||||
break;
|
||||
}
|
||||
if (nread == 0) {
|
||||
fprintf(stderr, "Unexpected socket EOF\n");
|
||||
write_error = 1;
|
||||
break;
|
||||
}
|
||||
if (!write_error) {
|
||||
if (writen(out_fd, buffer, nread) != nread) {
|
||||
perror("File write error");
|
||||
write_error = 1;
|
||||
}
|
||||
}
|
||||
bytes_left -= nread;
|
||||
}
|
||||
bytes_left -= nread;
|
||||
}
|
||||
if (!write_error) {
|
||||
fsync(out_fd); // Ensure data is flushed to disk
|
||||
@@ -514,17 +575,16 @@ void handle_client(int sock_fd, const char *dest_dir) {
|
||||
|
||||
|
||||
// We received a file metadata packet (read rest of it)
|
||||
uint32_t name_len = 0;
|
||||
uint64_t file_size = 0;
|
||||
uint32_t mode = 0;
|
||||
|
||||
if (readn(sock_fd, &name_len, sizeof(name_len)) != sizeof(name_len) ||
|
||||
readn(sock_fd, &file_size, sizeof(file_size)) != sizeof(file_size) ||
|
||||
readn(sock_fd, &mode, sizeof(mode)) != sizeof(mode)) {
|
||||
fprintf(stderr, "Failed to read file metadata fields\n");
|
||||
file_meta_t meta;
|
||||
if (readn(sock_fd, &meta, sizeof(meta)) != sizeof(meta)) {
|
||||
fprintf(stderr, "Failed to read file metadata\n");
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t name_len = meta.name_len;
|
||||
uint64_t file_size = meta.file_size;
|
||||
int file_mode = meta.mode;
|
||||
|
||||
// Read filename
|
||||
char filename[1024];
|
||||
if (name_len >= sizeof(filename)) {
|
||||
@@ -571,7 +631,7 @@ void handle_client(int sock_fd, const char *dest_dir) {
|
||||
active.received_blocks = calloc(active.total_blocks, 1);
|
||||
snprintf(active.target_path, sizeof(active.target_path), "%s", target_path);
|
||||
|
||||
active.out_fd = open(target_path, O_WRONLY | O_CREAT | O_TRUNC, mode);
|
||||
active.out_fd = open(target_path, O_WRONLY | O_CREAT | O_TRUNC, file_mode);
|
||||
if (active.out_fd < 0) {
|
||||
perror("Failed to open target file for UDP sync");
|
||||
free(active.received_blocks);
|
||||
@@ -605,7 +665,7 @@ void handle_client(int sock_fd, const char *dest_dir) {
|
||||
break;
|
||||
}
|
||||
|
||||
int out_fd = open(target_path, O_WRONLY | O_CREAT | O_TRUNC, mode);
|
||||
int out_fd = open(target_path, O_WRONLY | O_CREAT | O_TRUNC, file_mode);
|
||||
if (out_fd < 0) {
|
||||
perror("Failed to open target file for TCP sync");
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user