TCP File Transfer Server Implementation
Build a TCP-based file transfer server that allows clients to upload and download files. This project explores binary data transmission, file I/O operations, and protocol design for non-text data.
Project Structure
The file transfer server is organized as follows:
phase3/tcp-file-transfer/
├── CMakeLists.txt
├── include/
│ └── file_server.h # File server interface and classes
└── src/
└── file_server.cpp # Implementation of file transfer functionality
Learning Objectives
During this project, you will:
- Implement binary data transmission over TCP
- Design custom protocols for file operations
- Handle large file transfers with proper buffering
- Manage file I/O operations safely across network connections
- Implement file transfer progress tracking
- Apply security measures for file system access
- Design efficient packet-based communication
Core Concepts
Protocol Design
Design a structured protocol for file operations:
- Command Packets: Define message types for upload, download, list files, etc.
- Header Format: Include length, command type, and optional flags
- Data Format: Define how file data is segmented and transmitted
- Error Handling: Protocol-level error responses
Binary Data Transmission
Unlike text protocols, binary protocols must:
- Specify exact message lengths in headers
- Use binary-safe encoding for metadata
- Handle partial messages and reassembly
- Implement proper endianness handling for multi-byte values
Implementation Architecture
FileServer Interface
class FileServer {
public:
explicit FileServer(int port, const std::string& storage_path);
void start();
void stop();
private:
int server_fd_;
int port_;
std::string storage_path_;
std::atomic<bool> running_;
std::vector<std::thread> client_threads_;
void accept_connections();
void handle_client(int client_fd);
void process_command(int client_fd);
// File operation handlers
bool handle_upload(int client_fd);
bool handle_download(int client_fd);
bool handle_list_files(int client_fd);
};
Protocol Message Format
enum class CommandType : uint8_t {
UPLOAD = 1,
DOWNLOAD = 2,
LIST_FILES = 3,
DELETE_FILE = 4,
RESPONSE = 5
};
struct FileCommand {
CommandType type;
uint32_t length; // Length of data that follows
std::string filename; // Optional, depending on command
std::vector<char> data; // Command-specific data
};
struct FileResponse {
uint8_t status; // 0 = success, non-zero = error code
uint32_t length; // Length of optional message
std::string message; // Optional error message or file list
};
Implementation Requirements
Core Features
- File Upload: Clients can upload files to the server
- File Download: Clients can download files from the server
- File Listing: Clients can request a list of available files
- Protocol Implementation: Structured binary protocol with headers
- Security: Prevent access to files outside the storage directory
- Concurrent Access: Handle multiple simultaneous file transfers
Transfer Protocol
Design a reliable transfer protocol:
- Upload: Client sends file in chunks with size indicators
- Download: Server sends file in chunks with size indicators
- Checksum Verification: Optional integrity checking for large files (advanced)
- Resume Support: Ability to resume interrupted transfers (advanced)
Sample Implementation Approach
class FileServer {
private:
struct TransferSession {
std::string filename;
size_t file_size;
size_t bytes_transferred;
std::fstream file_stream;
};
std::string storage_path_;
std::unordered_map<int, TransferSession> active_transfers_;
std::mutex transfers_mutex_;
bool read_command_header(int client_fd, FileCommand& cmd);
bool send_response(int client_fd, const FileResponse& resp);
bool transfer_file_data(int client_fd, std::fstream& file, size_t size);
public:
void handle_client(int client_fd);
};
Binary Protocol Implementation
Message Framing
Implement proper message framing since TCP is a stream protocol:
// Example: Read a complete message
bool read_message(int fd, std::vector<char>& buffer) {
uint32_t length;
if (::recv(fd, &length, sizeof(length), 0) != sizeof(length)) {
return false;
}
length = ntohl(length); // Convert from network byte order
buffer.resize(length);
size_t received = 0;
while (received < length) {
ssize_t result = ::recv(fd, buffer.data() + received,
length - received, 0);
if (result <= 0) return false;
received += result;
}
return true;
}
Security Considerations
File System Access
- Validate filenames to prevent directory traversal attacks
- Use absolute paths when checking access permissions
- Limit file operations to within the designated storage directory
- Implement proper access controls (optional advanced feature)
Resource Management
- Limit the number of concurrent transfers
- Implement timeouts for inactive connections
- Validate file sizes to prevent resource exhaustion
- Close files properly even in error conditions
Performance Considerations
- Use appropriate buffer sizes for file I/O (typically 64KB-1MB)
- Consider memory mapping for very large files
- Optimize network buffer usage to minimize system calls
- Implement proper flow control to avoid overwhelming the receiver
- Consider compression for text-based or compressible files (advanced)
Testing Strategy
Test your file transfer server with:
- File uploads of various sizes
- File downloads with verification (compare checksums)
- Concurrent uploads and downloads
- Error conditions (network interruption, invalid paths)
- Large file transfers
- Different file types (text, binary, executables)
Usage Example
#include "file_server.h"
#include <iostream>
int main() {
try {
// Store files in the ./storage directory, listen on port 9090
FileServer server(9090, "./storage");
std::cout << "File server starting on port 9090..." << std::endl;
server.start();
// Wait for user to stop the server
std::cout << "Press Enter to stop the server..." << std::endl;
std::cin.get();
server.stop();
std::cout << "Server stopped." << std::endl;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
Client Implementation Considerations
Your server should work with a custom client that implements the same protocol, but for testing you might also implement:
- Command-line client for uploads/downloads
- Protocol testing tools
- Performance benchmarking clients
Advanced Features (Optional)
Consider implementing additional capabilities:
- File Permissions: Implement user authentication and file permissions
- Transfer Encryption: Use TLS/SSL for secure transfers
- Resume Interrupted Transfers: Allow clients to resume from partial transfers
- File Compression: Compress files during transfer to reduce bandwidth
- Transfer Speed Control: Implement bandwidth limiting
- File Deduplication: Prevent duplicate file storage
Building and Testing
# Build the project
cd build
cmake ..
make -j4
# Create storage directory
mkdir -p storage
# Start the file transfer server
./phase3/tcp-file-transfer/file_server
# Test with custom client implementation or scripts
Next Steps
After completing the file transfer server, move to Phase 4: Comprehensive Practice to build complete applications that combine all your skills.