基于TCP的FTP文件传输系统设计与实现(超详细)

基于TCP的FTP文件传输系统设计与实现

1. FTP协议概述与TCP基础

1.1 FTP协议简介

文件传输协议(FTP)是用于在网络上进行文件传输的一套标准协议,使用TCP/IP协议进行数据传输。FTP采用客户端-服务器(C/S)架构模型,通过建立两个连接来工作:控制连接和数据连接。

FTP协议的主要特点:

  • 使用两个独立连接:控制连接(端口21)和数据连接(端口20)
  • 支持多种文件类型(ASCII、二进制等)
  • 提供用户身份验证机制
  • 支持主动模式和被动模式

1.2 TCP协议基础

传输控制协议(TCP)是面向连接的、可靠的、基于字节流的传输层通信协议。TCP提供全双工通信,通过三次握手建立连接,四次挥手断开连接。

TCP的关键特性:

  • 可靠数据传输:通过序列号、确认应答、重传机制保证
  • 流量控制:通过滑动窗口机制实现
  • 拥塞控制:通过慢启动、拥塞避免等算法实现
  • 面向连接:建立连接后才能传输数据
c 复制代码
// TCP套接字创建的基本流程
// 服务器端:socket() -> bind() -> listen() -> accept()
// 客户端:socket() -> connect()

2. TCP Socket编程基础

2.1 Socket编程核心概念

Socket(套接字)是网络编程的抽象概念,是应用层与TCP/IP协议族通信的中间软件抽象层。在TCP/IP协议中,Socket组合了IP地址和端口号,用于标识网络中的唯一进程。

Socket类型:

  • 流式Socket(SOCK_STREAM):面向连接的TCP协议
  • 数据报Socket(SOCK_DGRAM):无连接的UDP协议
  • 原始Socket(SOCK_RAW):原始协议访问

2.2 TCP Socket API详解

c 复制代码
// 主要Socket API函数
#include <sys/types.h>
#include <sys/socket.h>

// 创建Socket
int socket(int domain, int type, int protocol);

// 绑定地址
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

// 监听连接
int listen(int sockfd, int backlog);

// 接受连接
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

// 建立连接
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

// 发送数据
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

// 接收数据
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

// 关闭连接
int close(int fd);

3. 系统设计与架构

3.1 系统整体架构设计

我们的FTP系统采用经典的客户端-服务器架构,设计上包含以下核心模块:

  1. 服务器端模块

    • 连接管理模块
    • 文件接收处理模块
    • 协议解析模块
    • 日志记录模块
  2. 客户端模块

    • 连接建立模块
    • 文件发送模块
    • 进度显示模块
    • 错误处理模块

3.2 通信协议设计

我们设计一个简化的FTP协议,包含以下命令格式:

复制代码
命令格式:COMMAND [参数]\n
响应格式:CODE 消息内容\n

支持的命令:
1. PUT filename size - 上传文件
2. GET filename - 下载文件
3. LIST - 列出文件
4. QUIT - 退出

4. 服务器端实现

4.1 服务器端主框架

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <dirent.h>
#include <pthread.h>

#define PORT 8888
#define BUFFER_SIZE 1024
#define MAX_CLIENTS 10
#define MAX_FILENAME 256

// 服务器配置结构体
typedef struct {
    int port;
    int max_clients;
    char root_dir[256];
    int verbose;
} ServerConfig;

// 客户端连接信息结构体
typedef struct {
    int client_socket;
    struct sockaddr_in client_addr;
    pthread_t thread_id;
    time_t connect_time;
} ClientInfo;

// 全局配置
ServerConfig server_config = {
    .port = PORT,
    .max_clients = MAX_CLIENTS,
    .root_dir = "./server_files",
    .verbose = 1
};

// 日志函数
void log_message(int level, const char *format, ...) {
    if (!server_config.verbose && level == 1) return;
    
    time_t now = time(NULL);
    char time_str[20];
    strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", localtime(&now));
    
    va_list args;
    va_start(args, format);
    printf("[%s] ", time_str);
    vprintf(format, args);
    printf("\n");
    va_end(args);
}

// 创建服务器Socket
int create_server_socket(int port) {
    int server_fd;
    struct sockaddr_in server_addr;
    
    // 创建Socket
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("Socket creation failed");
        return -1;
    }
    
    // 设置SO_REUSEADDR选项,避免地址占用
    int opt = 1;
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
        perror("Setsockopt failed");
        close(server_fd);
        return -1;
    }
    
    // 配置服务器地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(port);
    
    // 绑定地址
    if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("Bind failed");
        close(server_fd);
        return -1;
    }
    
    // 开始监听
    if (listen(server_fd, server_config.max_clients) < 0) {
        perror("Listen failed");
        close(server_fd);
        return -1;
    }
    
    return server_fd;
}

4.2 文件接收处理模块

c 复制代码
// 文件接收状态枚举
typedef enum {
    FILE_RECV_IDLE,
    FILE_RECV_HEADER,
    FILE_RECV_DATA,
    FILE_RECV_COMPLETE,
    FILE_RECV_ERROR
} FileRecvState;

// 文件传输上下文
typedef struct {
    char filename[MAX_FILENAME];
    long file_size;
    long received_bytes;
    int file_fd;
    FileRecvState state;
    time_t start_time;
} FileTransferContext;

// 初始化文件传输上下文
void init_file_transfer_context(FileTransferContext *ctx) {
    memset(ctx, 0, sizeof(FileTransferContext));
    ctx->state = FILE_RECV_IDLE;
    ctx->file_fd = -1;
}

// 解析文件传输头信息
int parse_file_header(const char *header, FileTransferContext *ctx) {
    // 格式:PUT filename filesize
    char command[10];
    char filename[MAX_FILENAME];
    long filesize;
    
    if (sscanf(header, "%s %s %ld", command, filename, &filesize) != 3) {
        return -1;
    }
    
    if (strcmp(command, "PUT") != 0) {
        return -1;
    }
    
    // 安全检查:防止路径遍历攻击
    if (strstr(filename, "..") != NULL || strchr(filename, '/') != NULL) {
        return -1;
    }
    
    strncpy(ctx->filename, filename, MAX_FILENAME - 1);
    ctx->file_size = filesize;
    ctx->received_bytes = 0;
    
    return 0;
}

// 处理文件接收
int receive_file_data(int client_fd, FileTransferContext *ctx) {
    char buffer[BUFFER_SIZE];
    ssize_t bytes_read;
    ssize_t bytes_written;
    ssize_t total_written = 0;
    
    while (ctx->received_bytes < ctx->file_size) {
        // 计算剩余字节数
        long remaining = ctx->file_size - ctx->received_bytes;
        ssize_t to_read = (remaining < BUFFER_SIZE) ? remaining : BUFFER_SIZE;
        
        // 从Socket读取数据
        bytes_read = recv(client_fd, buffer, to_read, 0);
        
        if (bytes_read <= 0) {
            if (bytes_read == 0) {
                log_message(1, "Client disconnected during file transfer");
            } else {
                perror("Error reading file data");
            }
            return -1;
        }
        
        // 写入文件
        bytes_written = write(ctx->file_fd, buffer, bytes_read);
        if (bytes_written != bytes_read) {
            log_message(0, "Error writing to file: wrote %ld of %ld bytes", 
                       bytes_written, bytes_read);
            return -1;
        }
        
        ctx->received_bytes += bytes_written;
        total_written += bytes_written;
        
        // 每接收1MB显示一次进度
        if (ctx->file_size > 0 && (ctx->received_bytes % (1024*1024)) < BUFFER_SIZE) {
            int percent = (int)((ctx->received_bytes * 100) / ctx->file_size);
            log_message(1, "Receiving file: %s [%d%%] %ld/%ld bytes", 
                       ctx->filename, percent, ctx->received_bytes, ctx->file_size);
        }
    }
    
    log_message(0, "File received successfully: %s (%ld bytes)", 
               ctx->filename, total_written);
    return 0;
}

// 处理客户端上传文件请求
int handle_file_upload(int client_fd, const char *request) {
    FileTransferContext ctx;
    init_file_transfer_context(&ctx);
    
    // 解析请求头
    if (parse_file_header(request, &ctx) != 0) {
        send(client_fd, "ERROR Invalid file header format\n", 33, 0);
        return -1;
    }
    
    // 检查文件大小限制(例如100MB)
    if (ctx.file_size > 100 * 1024 * 1024) {
        char error_msg[100];
        snprintf(error_msg, sizeof(error_msg), 
                "ERROR File too large: %ld bytes (max 100MB)\n", ctx.file_size);
        send(client_fd, error_msg, strlen(error_msg), 0);
        return -1;
    }
    
    // 准备文件路径
    char filepath[MAX_FILENAME + 100];
    snprintf(filepath, sizeof(filepath), "%s/%s", 
             server_config.root_dir, ctx.filename);
    
    // 打开文件(如果存在则清空)
    int flags = O_WRONLY | O_CREAT;
    if (access(filepath, F_OK) == 0) {
        log_message(1, "File exists, truncating: %s", filepath);
        flags |= O_TRUNC;
    } else {
        log_message(1, "Creating new file: %s", filepath);
    }
    
    ctx.file_fd = open(filepath, flags, 0644);
    if (ctx.file_fd < 0) {
        perror("Failed to open file");
        send(client_fd, "ERROR Cannot create file\n", 25, 0);
        return -1;
    }
    
    // 发送准备接收的响应
    send(client_fd, "READY Start sending file data\n", 30, 0);
    
    // 开始接收文件数据
    ctx.start_time = time(NULL);
    int result = receive_file_data(client_fd, &ctx);
    
    // 计算传输统计
    time_t end_time = time(NULL);
    double elapsed = difftime(end_time, ctx.start_time);
    double speed = (elapsed > 0) ? (ctx.received_bytes / elapsed) : 0;
    
    // 关闭文件
    close(ctx.file_fd);
    
    if (result == 0) {
        // 发送成功响应
        char success_msg[200];
        snprintf(success_msg, sizeof(success_msg),
                "SUCCESS File uploaded: %s (%ld bytes, %.1f seconds, %.2f KB/s)\n",
                ctx.filename, ctx.received_bytes, elapsed, speed / 1024);
        send(client_fd, success_msg, strlen(success_msg), 0);
        return 0;
    } else {
        // 删除不完整的文件
        unlink(filepath);
        send(client_fd, "ERROR File transfer failed\n", 27, 0);
        return -1;
    }
}

4.3 多线程客户端处理

c 复制代码
// 客户端处理线程函数
void *handle_client_thread(void *arg) {
    ClientInfo *client_info = (ClientInfo *)arg;
    int client_fd = client_info->client_socket;
    char client_ip[INET_ADDRSTRLEN];
    
    // 获取客户端IP地址
    inet_ntop(AF_INET, &(client_info->client_addr.sin_addr),
              client_ip, INET_ADDRSTRLEN);
    
    log_message(0, "New client connected: %s:%d", 
               client_ip, ntohs(client_info->client_addr.sin_port));
    
    char buffer[BUFFER_SIZE];
    char response[BUFFER_SIZE];
    
    // 发送欢迎消息
    const char *welcome_msg = "Welcome to Simple FTP Server\n"
                             "Commands: PUT <filename> <size>, GET <filename>, LIST, QUIT\n";
    send(client_fd, welcome_msg, strlen(welcome_msg), 0);
    
    // 处理客户端请求
    while (1) {
        memset(buffer, 0, BUFFER_SIZE);
        
        // 接收客户端命令
        ssize_t bytes_received = recv(client_fd, buffer, BUFFER_SIZE - 1, 0);
        
        if (bytes_received <= 0) {
            if (bytes_received == 0) {
                log_message(1, "Client disconnected: %s:%d", 
                           client_ip, ntohs(client_info->client_addr.sin_port));
            } else {
                perror("Error receiving from client");
            }
            break;
        }
        
        // 移除换行符
        buffer[strcspn(buffer, "\n")] = 0;
        
        log_message(1, "Received command from %s: %s", client_ip, buffer);
        
        // 解析命令
        if (strncmp(buffer, "PUT ", 4) == 0) {
            // 处理文件上传
            handle_file_upload(client_fd, buffer);
        } 
        else if (strncmp(buffer, "GET ", 4) == 0) {
            // 处理文件下载
            handle_file_download(client_fd, buffer + 4);
        }
        else if (strcmp(buffer, "LIST") == 0) {
            // 处理文件列表请求
            handle_list_files(client_fd);
        }
        else if (strcmp(buffer, "QUIT") == 0) {
            send(client_fd, "Goodbye!\n", 9, 0);
            break;
        }
        else {
            // 未知命令
            snprintf(response, sizeof(response), 
                    "ERROR Unknown command: %s\n", buffer);
            send(client_fd, response, strlen(response), 0);
        }
    }
    
    // 清理资源
    close(client_fd);
    free(client_info);
    
    log_message(1, "Client thread exiting: %s", client_ip);
    pthread_exit(NULL);
}

// 文件下载处理函数
int handle_file_download(int client_fd, const char *filename) {
    char filepath[MAX_FILENAME + 100];
    struct stat file_stat;
    
    // 安全检查
    if (strstr(filename, "..") != NULL || strchr(filename, '/') != NULL) {
        send(client_fd, "ERROR Invalid filename\n", 23, 0);
        return -1;
    }
    
    snprintf(filepath, sizeof(filepath), "%s/%s", 
             server_config.root_dir, filename);
    
    // 检查文件是否存在
    if (stat(filepath, &file_stat) < 0) {
        send(client_fd, "ERROR File not found\n", 21, 0);
        return -1;
    }
    
    // 检查是否为常规文件
    if (!S_ISREG(file_stat.st_mode)) {
        send(client_fd, "ERROR Not a regular file\n", 25, 0);
        return -1;
    }
    
    // 发送文件信息
    char header[100];
    snprintf(header, sizeof(header), "FILE %s %ld\n", 
             filename, file_stat.st_size);
    send(client_fd, header, strlen(header), 0);
    
    // 打开文件
    int file_fd = open(filepath, O_RDONLY);
    if (file_fd < 0) {
        perror("Failed to open file for reading");
        send(client_fd, "ERROR Cannot open file\n", 23, 0);
        return -1;
    }
    
    // 发送文件内容
    char buffer[BUFFER_SIZE];
    ssize_t bytes_read;
    long total_sent = 0;
    
    while ((bytes_read = read(file_fd, buffer, BUFFER_SIZE)) > 0) {
        ssize_t bytes_sent = send(client_fd, buffer, bytes_read, 0);
        if (bytes_sent < 0) {
            perror("Error sending file data");
            break;
        }
        total_sent += bytes_sent;
        
        // 显示进度
        int percent = (file_stat.st_size > 0) ? 
                     (int)((total_sent * 100) / file_stat.st_size) : 0;
        if (percent % 10 == 0) {
            log_message(1, "Sending file: %s [%d%%]", filename, percent);
        }
    }
    
    close(file_fd);
    log_message(0, "File sent: %s (%ld bytes)", filename, total_sent);
    
    return 0;
}

// 文件列表处理函数
int handle_list_files(int client_fd) {
    DIR *dir;
    struct dirent *entry;
    struct stat file_stat;
    char response[BUFFER_SIZE * 4];
    char line[256];
    time_t current_time = time(NULL);
    
    snprintf(response, sizeof(response), 
             "Files in server directory:\n");
    snprintf(response + strlen(response), sizeof(response) - strlen(response),
             "%-40s %10s %20s\n", "Filename", "Size", "Modified");
    snprintf(response + strlen(response), sizeof(response) - strlen(response),
             "%-40s %10s %20s\n", "--------", "----", "--------");
    
    dir = opendir(server_config.root_dir);
    if (dir == NULL) {
        perror("Failed to open directory");
        send(client_fd, "ERROR Cannot list directory\n", 27, 0);
        return -1;
    }
    
    while ((entry = readdir(dir)) != NULL) {
        // 跳过隐藏文件和目录
        if (entry->d_name[0] == '.') continue;
        
        char filepath[512];
        snprintf(filepath, sizeof(filepath), "%s/%s", 
                 server_config.root_dir, entry->d_name);
        
        if (stat(filepath, &file_stat) == 0 && S_ISREG(file_stat.st_mode)) {
            // 格式化文件大小
            char size_str[20];
            if (file_stat.st_size < 1024) {
                snprintf(size_str, sizeof(size_str), "%ldB", file_stat.st_size);
            } else if (file_stat.st_size < 1024*1024) {
                snprintf(size_str, sizeof(size_str), "%.1fKB", 
                         file_stat.st_size / 1024.0);
            } else {
                snprintf(size_str, sizeof(size_str), "%.1fMB", 
                         file_stat.st_size / (1024.0*1024.0));
            }
            
            // 格式化修改时间
            char time_str[20];
            struct tm *timeinfo = localtime(&file_stat.st_mtime);
            strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", timeinfo);
            
            snprintf(line, sizeof(line), "%-40s %10s %20s\n",
                    entry->d_name, size_str, time_str);
            
            // 检查缓冲区是否足够
            if (strlen(response) + strlen(line) < sizeof(response) - 100) {
                strcat(response, line);
            } else {
                strcat(response, "... (truncated)\n");
                break;
            }
        }
    }
    
    closedir(dir);
    
    // 发送文件列表
    send(client_fd, response, strlen(response), 0);
    return 0;
}

4.4 服务器主函数

c 复制代码
// 服务器主函数
int main(int argc, char *argv[]) {
    int server_fd;
    struct sockaddr_in client_addr;
    socklen_t client_len = sizeof(client_addr);
    
    // 解析命令行参数
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) {
            server_config.port = atoi(argv[++i]);
        } else if (strcmp(argv[i], "-d") == 0 && i + 1 < argc) {
            strncpy(server_config.root_dir, argv[++i], 
                    sizeof(server_config.root_dir) - 1);
        } else if (strcmp(argv[i], "-v") == 0) {
            server_config.verbose = 1;
        } else if (strcmp(argv[i], "-q") == 0) {
            server_config.verbose = 0;
        }
    }
    
    // 创建服务器目录(如果不存在)
    struct stat st = {0};
    if (stat(server_config.root_dir, &st) == -1) {
        mkdir(server_config.root_dir, 0755);
        log_message(0, "Created server directory: %s", server_config.root_dir);
    }
    
    // 创建服务器Socket
    server_fd = create_server_socket(server_config.port);
    if (server_fd < 0) {
        log_message(0, "Failed to create server socket");
        return 1;
    }
    
    log_message(0, "FTP Server started on port %d", server_config.port);
    log_message(0, "Server directory: %s", server_config.root_dir);
    log_message(0, "Maximum clients: %d", server_config.max_clients);
    log_message(0, "Waiting for connections...\n");
    
    // 主循环,接受客户端连接
    while (1) {
        // 分配客户端信息结构
        ClientInfo *client_info = malloc(sizeof(ClientInfo));
        if (!client_info) {
            perror("Failed to allocate client info");
            continue;
        }
        
        // 接受客户端连接
        client_info->client_socket = accept(server_fd, 
                                          (struct sockaddr*)&client_addr, 
                                          &client_len);
        
        if (client_info->client_socket < 0) {
            perror("Accept failed");
            free(client_info);
            continue;
        }
        
        // 保存客户端信息
        memcpy(&client_info->client_addr, &client_addr, sizeof(client_addr));
        client_info->connect_time = time(NULL);
        
        // 创建线程处理客户端
        pthread_t thread_id;
        if (pthread_create(&thread_id, NULL, handle_client_thread, client_info) != 0) {
            perror("Failed to create thread");
            close(client_info->client_socket);
            free(client_info);
            continue;
        }
        
        client_info->thread_id = thread_id;
        
        // 分离线程,让系统自动回收资源
        pthread_detach(thread_id);
        
        log_message(1, "Created thread for new client");
    }
    
    // 关闭服务器Socket(通常不会执行到这里)
    close(server_fd);
    return 0;
}

5. 客户端实现

5.1 客户端主框架

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <ctype.h>

#define BUFFER_SIZE 1024
#define MAX_FILENAME 256

// 客户端配置结构体
typedef struct {
    char server_ip[16];
    int server_port;
    int verbose;
    int binary_mode;
} ClientConfig;

// 全局配置
ClientConfig client_config = {
    .server_ip = "127.0.0.1",
    .server_port = 8888,
    .verbose = 1,
    .binary_mode = 1
};

// 客户端状态枚举
typedef enum {
    CLIENT_DISCONNECTED,
    CLIENT_CONNECTING,
    CLIENT_CONNECTED,
    CLIENT_TRANSFERRING,
    CLIENT_ERROR
} ClientState;

// 客户端上下文
typedef struct {
    int sockfd;
    ClientState state;
    struct sockaddr_in server_addr;
    char current_dir[256];
} ClientContext;

// 初始化客户端上下文
void init_client_context(ClientContext *ctx) {
    memset(ctx, 0, sizeof(ClientContext));
    ctx->sockfd = -1;
    ctx->state = CLIENT_DISCONNECTED;
    getcwd(ctx->current_dir, sizeof(ctx->current_dir));
}

// 连接到服务器
int connect_to_server(ClientContext *ctx, const char *ip, int port) {
    // 创建Socket
    ctx->sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (ctx->sockfd < 0) {
        perror("Socket creation failed");
        return -1;
    }
    
    // 配置服务器地址
    memset(&ctx->server_addr, 0, sizeof(ctx->server_addr));
    ctx->server_addr.sin_family = AF_INET;
    ctx->server_addr.sin_port = htons(port);
    
    if (inet_pton(AF_INET, ip, &ctx->server_addr.sin_addr) <= 0) {
        perror("Invalid address");
        close(ctx->sockfd);
        ctx->sockfd = -1;
        return -1;
    }
    
    // 连接服务器
    printf("Connecting to %s:%d...\n", ip, port);
    
    if (connect(ctx->sockfd, (struct sockaddr*)&ctx->server_addr, 
                sizeof(ctx->server_addr)) < 0) {
        perror("Connection failed");
        close(ctx->sockfd);
        ctx->sockfd = -1;
        return -1;
    }
    
    ctx->state = CLIENT_CONNECTED;
    printf("Connected to server successfully!\n");
    
    // 接收服务器欢迎消息
    char buffer[BUFFER_SIZE];
    ssize_t bytes_received = recv(ctx->sockfd, buffer, BUFFER_SIZE - 1, 0);
    if (bytes_received > 0) {
        buffer[bytes_received] = '\0';
        printf("%s", buffer);
    }
    
    return 0;
}

// 发送命令并接收响应
int send_command(ClientContext *ctx, const char *command, char *response, size_t resp_size) {
    if (ctx->state != CLIENT_CONNECTED) {
        printf("Not connected to server\n");
        return -1;
    }
    
    // 发送命令(确保以换行符结尾)
    char cmd_with_newline[BUFFER_SIZE];
    snprintf(cmd_with_newline, sizeof(cmd_with_newline), "%s\n", command);
    
    if (send(ctx->sockfd, cmd_with_newline, strlen(cmd_with_newline), 0) < 0) {
        perror("Failed to send command");
        return -1;
    }
    
    if (client_config.verbose) {
        printf("Sent: %s", cmd_with_newline);
    }
    
    // 接收响应
    if (response && resp_size > 0) {
        memset(response, 0, resp_size);
        ssize_t bytes_received = recv(ctx->sockfd, response, resp_size - 1, 0);
        
        if (bytes_received > 0) {
            response[bytes_received] = '\0';
            if (client_config.verbose) {
                printf("Received: %s", response);
            }
            return bytes_received;
        } else if (bytes_received == 0) {
            printf("Server disconnected\n");
            ctx->state = CLIENT_DISCONNECTED;
            return -1;
        } else {
            perror("Failed to receive response");
            return -1;
        }
    }
    
    return 0;
}

5.2 文件上传功能

c 复制代码
// 计算文件大小
long get_file_size(const char *filename) {
    struct stat file_stat;
    if (stat(filename, &file_stat) == 0 && S_ISREG(file_stat.st_mode)) {
        return file_stat.st_size;
    }
    return -1;
}

// 上传文件到服务器
int upload_file(ClientContext *ctx, const char *filename) {
    // 检查文件是否存在
    long file_size = get_file_size(filename);
    if (file_size < 0) {
        printf("File not found or not a regular file: %s\n", filename);
        return -1;
    }
    
    printf("Uploading file: %s (size: %ld bytes)\n", filename, file_size);
    
    // 发送PUT命令
    char command[BUFFER_SIZE];
    char response[BUFFER_SIZE];
    
    snprintf(command, sizeof(command), "PUT %s %ld", filename, file_size);
    
    if (send_command(ctx, command, response, sizeof(response)) < 0) {
        return -1;
    }
    
    // 检查服务器响应
    if (strncmp(response, "READY", 5) != 0) {
        printf("Server not ready: %s", response);
        return -1;
    }
    
    // 打开本地文件
    int file_fd = open(filename, O_RDONLY);
    if (file_fd < 0) {
        perror("Failed to open file");
        return -1;
    }
    
    // 读取并发送文件内容
    char buffer[BUFFER_SIZE];
    ssize_t bytes_read;
    long total_sent = 0;
    time_t start_time = time(NULL);
    
    ctx->state = CLIENT_TRANSFERRING;
    
    while ((bytes_read = read(file_fd, buffer, BUFFER_SIZE)) > 0) {
        ssize_t bytes_sent = send(ctx->sockfd, buffer, bytes_read, 0);
        if (bytes_sent < 0) {
            perror("Error sending file data");
            close(file_fd);
            ctx->state = CLIENT_CONNECTED;
            return -1;
        }
        total_sent += bytes_sent;
        
        // 显示进度
        if (file_size > 0) {
            int percent = (int)((total_sent * 100) / file_size);
            if (percent % 5 == 0) {
                printf("\rProgress: [");
                int bars = percent / 2;
                for (int i = 0; i < 50; i++) {
                    if (i < bars) printf("=");
                    else printf(" ");
                }
                printf("] %d%% %ld/%ld bytes", percent, total_sent, file_size);
                fflush(stdout);
            }
        }
    }
    
    close(file_fd);
    
    // 显示传输完成
    printf("\n");
    
    // 接收服务器确认
    memset(response, 0, sizeof(response));
    ssize_t bytes_received = recv(ctx->sockfd, response, sizeof(response) - 1, 0);
    if (bytes_received > 0) {
        response[bytes_received] = '\0';
        printf("%s", response);
    }
    
    // 计算传输统计
    time_t end_time = time(NULL);
    double elapsed = difftime(end_time, start_time);
    double speed = (elapsed > 0) ? (total_sent / elapsed) : 0;
    
    printf("Upload complete: %.1f seconds, %.2f KB/s\n", 
           elapsed, speed / 1024);
    
    ctx->state = CLIENT_CONNECTED;
    return 0;
}

// 下载文件从服务器
int download_file(ClientContext *ctx, const char *filename) {
    char command[BUFFER_SIZE];
    char response[BUFFER_SIZE];
    
    // 发送GET命令
    snprintf(command, sizeof(command), "GET %s", filename);
    
    if (send_command(ctx, command, response, sizeof(response)) < 0) {
        return -1;
    }
    
    // 检查响应
    if (strncmp(response, "FILE", 4) != 0) {
        printf("Server error: %s", response);
        return -1;
    }
    
    // 解析文件信息
    char response_filename[MAX_FILENAME];
    long file_size;
    
    if (sscanf(response, "FILE %s %ld", response_filename, &file_size) != 2) {
        printf("Invalid server response format\n");
        return -1;
    }
    
    printf("Downloading file: %s (size: %ld bytes)\n", response_filename, file_size);
    
    // 创建本地文件(如果存在则清空)
    int file_fd = open(response_filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (file_fd < 0) {
        perror("Failed to create local file");
        return -1;
    }
    
    // 接收文件数据
    char buffer[BUFFER_SIZE];
    ssize_t bytes_received;
    long total_received = 0;
    time_t start_time = time(NULL);
    
    ctx->state = CLIENT_TRANSFERRING;
    
    while (total_received < file_size) {
        long remaining = file_size - total_received;
        ssize_t to_receive = (remaining < BUFFER_SIZE) ? remaining : BUFFER_SIZE;
        
        bytes_received = recv(ctx->sockfd, buffer, to_receive, 0);
        if (bytes_received <= 0) {
            if (bytes_received == 0) {
                printf("\nServer disconnected during transfer\n");
            } else {
                perror("Error receiving file data");
            }
            close(file_fd);
            unlink(response_filename); // 删除不完整的文件
            ctx->state = CLIENT_CONNECTED;
            return -1;
        }
        
        // 写入本地文件
        ssize_t bytes_written = write(file_fd, buffer, bytes_received);
        if (bytes_written != bytes_received) {
            printf("\nError writing to local file\n");
            close(file_fd);
            unlink(response_filename);
            ctx->state = CLIENT_CONNECTED;
            return -1;
        }
        
        total_received += bytes_received;
        
        // 显示进度
        if (file_size > 0) {
            int percent = (int)((total_received * 100) / file_size);
            if (percent % 5 == 0) {
                printf("\rProgress: [");
                int bars = percent / 2;
                for (int i = 0; i < 50; i++) {
                    if (i < bars) printf("=");
                    else printf(" ");
                }
                printf("] %d%% %ld/%ld bytes", percent, total_received, file_size);
                fflush(stdout);
            }
        }
    }
    
    close(file_fd);
    
    // 显示传输完成
    printf("\n");
    
    // 计算传输统计
    time_t end_time = time(NULL);
    double elapsed = difftime(end_time, start_time);
    double speed = (elapsed > 0) ? (total_received / elapsed) : 0;
    
    printf("Download complete: %s (%.1f seconds, %.2f KB/s)\n", 
           response_filename, elapsed, speed / 1024);
    
    ctx->state = CLIENT_CONNECTED;
    return 0;
}

// 列出服务器文件
int list_server_files(ClientContext *ctx) {
    char response[BUFFER_SIZE * 4];
    
    if (send_command(ctx, "LIST", response, sizeof(response)) < 0) {
        return -1;
    }
    
    printf("%s", response);
    return 0;
}

5.3 客户端交互界面

c 复制代码
// 显示帮助信息
void show_help() {
    printf("\nAvailable commands:\n");
    printf("  connect <ip> <port>  - Connect to FTP server\n");
    printf("  put <filename>       - Upload file to server\n");
    printf("  get <filename>       - Download file from server\n");
    printf("  list                 - List files on server\n");
    printf("  pwd                  - Show current directory\n");
    printf("  cd <directory>       - Change local directory\n");
    printf("  ls                   - List local files\n");
    printf("  mode binary/ascii    - Set transfer mode\n");
    printf("  verbose on/off       - Toggle verbose mode\n");
    printf("  help                 - Show this help\n");
    printf("  quit                 - Exit client\n");
    printf("\n");
}

// 列出本地文件
void list_local_files() {
    DIR *dir;
    struct dirent *entry;
    struct stat file_stat;
    
    printf("Local files in current directory:\n");
    printf("%-40s %10s %20s\n", "Filename", "Size", "Modified");
    printf("%-40s %10s %20s\n", "--------", "----", "--------");
    
    dir = opendir(".");
    if (dir == NULL) {
        perror("Failed to open directory");
        return;
    }
    
    while ((entry = readdir(dir)) != NULL) {
        // 跳过隐藏文件
        if (entry->d_name[0] == '.') continue;
        
        if (stat(entry->d_name, &file_stat) == 0) {
            // 格式化文件大小
            char size_str[20];
            if (S_ISDIR(file_stat.st_mode)) {
                strcpy(size_str, "<DIR>");
            } else if (file_stat.st_size < 1024) {
                snprintf(size_str, sizeof(size_str), "%ldB", file_stat.st_size);
            } else if (file_stat.st_size < 1024*1024) {
                snprintf(size_str, sizeof(size_str), "%.1fKB", 
                         file_stat.st_size / 1024.0);
            } else {
                snprintf(size_str, sizeof(size_str), "%.1fMB", 
                         file_stat.st_size / (1024.0*1024.0));
            }
            
            // 格式化修改时间
            char time_str[20];
            struct tm *timeinfo = localtime(&file_stat.st_mtime);
            strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", timeinfo);
            
            printf("%-40s %10s %20s\n", entry->d_name, size_str, time_str);
        }
    }
    
    closedir(dir);
}

// 改变本地目录
int change_local_directory(const char *path) {
    if (chdir(path) == 0) {
        char cwd[256];
        getcwd(cwd, sizeof(cwd));
        printf("Current directory: %s\n", cwd);
        return 0;
    } else {
        perror("Failed to change directory");
        return -1;
    }
}

// 客户端主循环
void client_main_loop(ClientContext *ctx) {
    char input[BUFFER_SIZE];
    char command[50];
    char arg1[100];
    char arg2[100];
    
    show_help();
    
    while (1) {
        printf("\nftp> ");
        fflush(stdout);
        
        // 读取用户输入
        if (fgets(input, sizeof(input), stdin) == NULL) {
            break;
        }
        
        // 移除换行符
        input[strcspn(input, "\n")] = 0;
        
        // 解析命令
        int argc = sscanf(input, "%s %s %s", command, arg1, arg2);
        
        if (argc == 0) {
            continue;
        }
        
        // 转换为小写以便比较
        for (int i = 0; command[i]; i++) {
            command[i] = tolower(command[i]);
        }
        
        // 处理命令
        if (strcmp(command, "connect") == 0) {
            if (argc >= 3) {
                int port = atoi(arg2);
                connect_to_server(ctx, arg1, port);
            } else {
                printf("Usage: connect <ip> <port>\n");
            }
        }
        else if (strcmp(command, "put") == 0) {
            if (argc >= 2) {
                upload_file(ctx, arg1);
            } else {
                printf("Usage: put <filename>\n");
            }
        }
        else if (strcmp(command, "get") == 0) {
            if (argc >= 2) {
                download_file(ctx, arg1);
            } else {
                printf("Usage: get <filename>\n");
            }
        }
        else if (strcmp(command, "list") == 0) {
            list_server_files(ctx);
        }
        else if (strcmp(command, "ls") == 0) {
            list_local_files();
        }
        else if (strcmp(command, "pwd") == 0) {
            char cwd[256];
            getcwd(cwd, sizeof(cwd));
            printf("Current directory: %s\n", cwd);
        }
        else if (strcmp(command, "cd") == 0) {
            if (argc >= 2) {
                change_local_directory(arg1);
            } else {
                printf("Usage: cd <directory>\n");
            }
        }
        else if (strcmp(command, "mode") == 0) {
            if (argc >= 2) {
                if (strcasecmp(arg1, "binary") == 0) {
                    client_config.binary_mode = 1;
                    printf("Transfer mode set to BINARY\n");
                } else if (strcasecmp(arg1, "ascii") == 0) {
                    client_config.binary_mode = 0;
                    printf("Transfer mode set to ASCII\n");
                } else {
                    printf("Invalid mode. Use 'binary' or 'ascii'\n");
                }
            } else {
                printf("Current mode: %s\n", 
                       client_config.binary_mode ? "BINARY" : "ASCII");
            }
        }
        else if (strcmp(command, "verbose") == 0) {
            if (argc >= 2) {
                if (strcasecmp(arg1, "on") == 0) {
                    client_config.verbose = 1;
                    printf("Verbose mode ON\n");
                } else if (strcasecmp(arg1, "off") == 0) {
                    client_config.verbose = 0;
                    printf("Verbose mode OFF\n");
                } else {
                    printf("Usage: verbose on|off\n");
                }
            } else {
                printf("Verbose mode is %s\n", 
                       client_config.verbose ? "ON" : "OFF");
            }
        }
        else if (strcmp(command, "help") == 0) {
            show_help();
        }
        else if (strcmp(command, "quit") == 0 || strcmp(command, "exit") == 0) {
            if (ctx->state == CLIENT_CONNECTED) {
                send_command(ctx, "QUIT", NULL, 0);
                close(ctx->sockfd);
            }
            printf("Goodbye!\n");
            break;
        }
        else {
            printf("Unknown command: %s\n", command);
            printf("Type 'help' for available commands\n");
        }
    }
}

5.4 客户端主函数

c 复制代码
// 客户端主函数
int main(int argc, char *argv[]) {
    ClientContext ctx;
    
    // 初始化客户端
    init_client_context(&ctx);
    
    // 解析命令行参数
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-h") == 0 && i + 1 < argc) {
            strncpy(client_config.server_ip, argv[++i], 
                    sizeof(client_config.server_ip) - 1);
        } else if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) {
            client_config.server_port = atoi(argv[++i]);
        } else if (strcmp(argv[i], "-v") == 0) {
            client_config.verbose = 1;
        } else if (strcmp(argv[i], "-q") == 0) {
            client_config.verbose = 0;
        } else if (strcmp(argv[i], "-connect") == 0 && i + 2 < argc) {
            strncpy(client_config.server_ip, argv[++i], 
                    sizeof(client_config.server_ip) - 1);
            client_config.server_port = atoi(argv[++i]);
        }
    }
    
    printf("Simple FTP Client\n");
    printf("================\n");
    
    // 如果有指定连接参数,则自动连接
    if (argc > 1) {
        for (int i = 1; i < argc; i++) {
            if (strcmp(argv[i], "-connect") == 0 && i + 2 < argc) {
                if (connect_to_server(&ctx, client_config.server_ip, 
                                     client_config.server_port) == 0) {
                    break;
                }
            }
        }
    }
    
    // 启动客户端交互
    client_main_loop(&ctx);
    
    // 清理资源
    if (ctx.sockfd >= 0) {
        close(ctx.sockfd);
    }
    
    return 0;
}

6. 编译与测试

6.1 编译脚本

makefile 复制代码
# Makefile for Simple FTP System

CC = gcc
CFLAGS = -Wall -Wextra -O2 -pthread
SERVER_SRC = ftp_server.c
CLIENT_SRC = ftp_client.c
SERVER_BIN = ftp_server
CLIENT_BIN = ftp_client

all: $(SERVER_BIN) $(CLIENT_BIN)

$(SERVER_BIN): $(SERVER_SRC)
	$(CC) $(CFLAGS) -o $(SERVER_BIN) $(SERVER_SRC)

$(CLIENT_BIN): $(CLIENT_SRC)
	$(CC) $(CFLAGS) -o $(CLIENT_BIN) $(CLIENT_SRC)

clean:
	rm -f $(SERVER_BIN) $(CLIENT_BIN) *.o

run_server: $(SERVER_BIN)
	./$(SERVER_BIN) -v

run_client: $(CLIENT_BIN)
	./$(CLIENT_BIN)

test: all
	@echo "Starting server in background..."
	@./$(SERVER_BIN) -q &
	@sleep 2
	@echo "Testing client connection..."
	@./$(CLIENT_BIN) -connect 127.0.0.1 8888 -q
	@pkill ftp_server

.PHONY: all clean run_server run_client test

6.2 测试脚本

bash 复制代码
#!/bin/bash
# test_ftp.sh - FTP系统测试脚本

echo "=== FTP系统测试脚本 ==="
echo "1. 编译项目..."
make clean
make

echo -e "\n2. 启动服务器..."
./ftp_server -p 8888 -d ./test_server_files -v &
SERVER_PID=$!
sleep 2  # 等待服务器启动

echo -e "\n3. 测试客户端基本功能..."
echo "3.1 连接服务器"
./ftp_client -connect 127.0.0.1 8888 -q << EOF
ls
put test_file.txt
list
get test_file.txt
quit
EOF

echo -e "\n3.2 测试大文件传输..."
# 创建测试大文件
dd if=/dev/urandom of=large_file.bin bs=1M count=10 2>/dev/null
./ftp_client -connect 127.0.0.1 8888 -q << EOF
put large_file.bin
list
get large_file.bin
quit
EOF

echo -e "\n3.3 测试错误处理..."
./ftp_client -connect 127.0.0.1 8888 -q << EOF
get non_existent_file.txt
put /etc/passwd  # 应该被拒绝
quit
EOF

echo -e "\n4. 清理测试文件..."
rm -f test_file.txt large_file.bin downloaded_*.bin
rm -rf ./test_server_files

echo -e "\n5. 停止服务器..."
kill $SERVER_PID
wait $SERVER_PID 2>/dev/null

echo -e "\n=== 测试完成 ==="

6.3 性能测试

c 复制代码
// performance_test.c - 性能测试程序
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>

#define TEST_FILE_SIZE (10 * 1024 * 1024) // 10MB

// 生成测试文件
void generate_test_file(const char *filename, size_t size) {
    FILE *file = fopen(filename, "wb");
    if (!file) {
        perror("Failed to create test file");
        exit(1);
    }
    
    unsigned char *buffer = malloc(1024 * 1024); // 1MB缓冲区
    if (!buffer) {
        perror("Memory allocation failed");
        fclose(file);
        exit(1);
    }
    
    // 用伪随机数据填充缓冲区
    srand(time(NULL));
    for (size_t i = 0; i < 1024 * 1024; i++) {
        buffer[i] = rand() % 256;
    }
    
    // 写入文件
    size_t remaining = size;
    while (remaining > 0) {
        size_t to_write = (remaining > 1024*1024) ? 1024*1024 : remaining;
        fwrite(buffer, 1, to_write, file);
        remaining -= to_write;
    }
    
    free(buffer);
    fclose(file);
    printf("Generated test file: %s (%ld bytes)\n", filename, size);
}

// 运行性能测试
void run_performance_test() {
    struct timeval start, end;
    double upload_time, download_time;
    const char *test_file = "performance_test.bin";
    
    // 生成测试文件
    generate_test_file(test_file, TEST_FILE_SIZE);
    
    printf("\n=== 性能测试开始 ===\n");
    
    // 上传测试
    printf("\n1. 上传测试...\n");
    gettimeofday(&start, NULL);
    
    // 这里应该调用实际上传函数
    system("./ftp_client -connect 127.0.0.1 8888 -q << 'EOF'\n"
           "put performance_test.bin\n"
           "quit\n"
           "EOF");
    
    gettimeofday(&end, NULL);
    upload_time = (end.tv_sec - start.tv_sec) + 
                  (end.tv_usec - start.tv_usec) / 1000000.0;
    
    // 下载测试
    printf("\n2. 下载测试...\n");
    gettimeofday(&start, NULL);
    
    // 这里应该调用实际下载函数
    system("./ftp_client -connect 127.0.0.1 8888 -q << 'EOF'\n"
           "get performance_test.bin\n"
           "quit\n"
           "EOF");
    
    gettimeofday(&end, NULL);
    download_time = (end.tv_sec - start.tv_sec) + 
                    (end.tv_usec - start.tv_usec) / 1000000.0;
    
    // 输出结果
    printf("\n=== 性能测试结果 ===\n");
    printf("文件大小: %.2f MB\n", TEST_FILE_SIZE / (1024.0 * 1024.0));
    printf("上传时间: %.2f 秒\n", upload_time);
    printf("上传速度: %.2f MB/s\n", 
           (TEST_FILE_SIZE / (1024.0 * 1024.0)) / upload_time);
    printf("下载时间: %.2f 秒\n", download_time);
    printf("下载速度: %.2f MB/s\n", 
           (TEST_FILE_SIZE / (1024.0 * 1024.0)) / download_time);
    
    // 清理
    remove(test_file);
    remove("downloaded_performance_test.bin");
}

int main() {
    run_performance_test();
    return 0;
}

7. 高级功能扩展

7.1 断点续传功能

c 复制代码
// 断点续传实现
typedef struct {
    char filename[MAX_FILENAME];
    long file_size;
    long transferred_bytes;
    int is_resume;
    char temp_filename[MAX_FILENAME + 10];
} ResumeContext;

// 检查文件传输状态
int check_file_transfer_status(const char *filename, ResumeContext *ctx) {
    char temp_file[256];
    snprintf(temp_file, sizeof(temp_file), "%s.part", filename);
    
    struct stat st;
    if (stat(temp_file, &st) == 0) {
        // 存在部分传输的文件
        ctx->is_resume = 1;
        ctx->transferred_bytes = st.st_size;
        strcpy(ctx->temp_filename, temp_file);
        
        // 获取原始文件大小(需要从服务器获取)
        // 这里简化为从文件名中提取
        return 1;
    }
    
    return 0;
}

// 支持断点续传的文件上传
int upload_file_with_resume(ClientContext *ctx, const char *filename) {
    ResumeContext resume_ctx;
    memset(&resume_ctx, 0, sizeof(resume_ctx));
    strcpy(resume_ctx.filename, filename);
    
    // 检查是否有未完成的传输
    if (check_file_transfer_status(filename, &resume_ctx)) {
        printf("发现未完成的传输,已传输 %ld 字节\n", resume_ctx.transferred_bytes);
        printf("是否继续? (y/n): ");
        
        char response[10];
        fgets(response, sizeof(response), stdin);
        if (response[0] != 'y' && response[0] != 'Y') {
            // 重新开始
            unlink(resume_ctx.temp_filename);
            resume_ctx.is_resume = 0;
            resume_ctx.transferred_bytes = 0;
        }
    }
    
    // 获取文件总大小
    long file_size = get_file_size(filename);
    if (file_size <= 0) {
        printf("无法获取文件大小\n");
        return -1;
    }
    
    // 发送断点续传命令
    char command[BUFFER_SIZE];
    if (resume_ctx.is_resume) {
        snprintf(command, sizeof(command), 
                "RESUME %s %ld %ld", 
                filename, file_size, resume_ctx.transferred_bytes);
    } else {
        snprintf(command, sizeof(command), 
                "PUT %s %ld", filename, file_size);
    }
    
    // 继续传输...
    return 0;
}

7.2 加密传输功能

c 复制代码
// 简单异或加密(仅示例,实际应使用更安全的算法)
void simple_encrypt(char *data, size_t len, const char *key) {
    size_t key_len = strlen(key);
    for (size_t i = 0; i < len; i++) {
        data[i] ^= key[i % key_len];
    }
}

// 加密发送
ssize_t send_encrypted(int sockfd, const void *buf, size_t len, 
                       int flags, const char *key) {
    char *encrypted_buf = malloc(len);
    if (!encrypted_buf) return -1;
    
    memcpy(encrypted_buf, buf, len);
    simple_encrypt(encrypted_buf, len, key);
    
    ssize_t result = send(sockfd, encrypted_buf, len, flags);
    free(encrypted_buf);
    return result;
}

// 解密接收
ssize_t recv_decrypted(int sockfd, void *buf, size_t len,
                       int flags, const char *key) {
    ssize_t result = recv(sockfd, buf, len, flags);
    if (result > 0) {
        simple_encrypt(buf, result, key); // 异或加密/解密是相同的操作
    }
    return result;
}

7.3 多线程并行传输

c 复制代码
// 并行传输上下文
typedef struct {
    int thread_id;
    int sockfd;
    char filename[MAX_FILENAME];
    long start_offset;
    long end_offset;
    long transferred;
    pthread_mutex_t *mutex;
} ParallelTransferContext;

// 并行传输线程函数
void *parallel_transfer_thread(void *arg) {
    ParallelTransferContext *ctx = (ParallelTransferContext *)arg;
    int file_fd = open(ctx->filename, O_RDONLY);
    
    if (file_fd < 0) {
        perror("Failed to open file");
        return NULL;
    }
    
    // 定位到指定偏移
    lseek(file_fd, ctx->start_offset, SEEK_SET);
    
    char buffer[BUFFER_SIZE];
    long remaining = ctx->end_offset - ctx->start_offset;
    long current_pos = ctx->start_offset;
    
    while (remaining > 0) {
        size_t to_read = (remaining < BUFFER_SIZE) ? remaining : BUFFER_SIZE;
        ssize_t bytes_read = read(file_fd, buffer, to_read);
        
        if (bytes_read <= 0) break;
        
        // 发送数据
        ssize_t bytes_sent = send(ctx->sockfd, buffer, bytes_read, 0);
        if (bytes_sent <= 0) break;
        
        // 更新统计
        pthread_mutex_lock(ctx->mutex);
        ctx->transferred += bytes_sent;
        pthread_mutex_unlock(ctx->mutex);
        
        remaining -= bytes_sent;
        current_pos += bytes_sent;
    }
    
    close(file_fd);
    return NULL;
}

// 启动并行传输
int start_parallel_transfer(const char *filename, int num_threads) {
    // 创建多个连接
    // 分割文件
    // 启动多个线程
    // 合并结果
    
    return 0;
}

8. 错误处理与优化

8.1 完善的错误处理机制

c 复制代码
// 错误码定义
typedef enum {
    FTP_SUCCESS = 0,
    FTP_ERROR_SOCKET,
    FTP_ERROR_CONNECT,
    FTP_ERROR_BIND,
    FTP_ERROR_LISTEN,
    FTP_ERROR_ACCEPT,
    FTP_ERROR_SEND,
    FTP_ERROR_RECV,
    FTP_ERROR_FILE_OPEN,
    FTP_ERROR_FILE_READ,
    FTP_ERROR_FILE_WRITE,
    FTP_ERROR_MEMORY,
    FTP_ERROR_TIMEOUT,
    FTP_ERROR_PROTOCOL,
    FTP_ERROR_AUTH,
    FTP_ERROR_PERMISSION
} FTPErrorCode;

// 错误处理函数
const char *ftp_strerror(FTPErrorCode error) {
    static const char *error_strings[] = {
        "Success",
        "Socket creation failed",
        "Connection failed",
        "Bind failed",
        "Listen failed",
        "Accept connection failed",
        "Send data failed",
        "Receive data failed",
        "Cannot open file",
        "Cannot read file",
        "Cannot write file",
        "Memory allocation failed",
        "Operation timeout",
        "Protocol error",
        "Authentication failed",
        "Permission denied"
    };
    
    if (error < 0 || error > FTP_ERROR_PERMISSION) {
        return "Unknown error";
    }
    
    return error_strings[error];
}

// 带错误处理的Socket操作
int safe_send(int sockfd, const void *buf, size_t len, int flags) {
    size_t total_sent = 0;
    const char *ptr = (const char *)buf;
    
    while (total_sent < len) {
        ssize_t sent = send(sockfd, ptr + total_sent, len - total_sent, flags);
        if (sent < 0) {
            if (errno == EINTR) continue; // 被信号中断,重试
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                // 非阻塞模式下,等待可写
                fd_set write_fds;
                struct timeval timeout = {5, 0}; // 5秒超时
                
                FD_ZERO(&write_fds);
                FD_SET(sockfd, &write_fds);
                
                if (select(sockfd + 1, NULL, &write_fds, NULL, &timeout) <= 0) {
                    return FTP_ERROR_TIMEOUT;
                }
                continue;
            }
            return FTP_ERROR_SEND;
        }
        total_sent += sent;
    }
    
    return FTP_SUCCESS;
}

8.2 连接池管理

c 复制代码
// 连接池结构
typedef struct ConnectionPool {
    int *connections;
    int pool_size;
    int used_count;
    pthread_mutex_t lock;
    pthread_cond_t available;
} ConnectionPool;

// 初始化连接池
ConnectionPool *init_connection_pool(const char *host, int port, int size) {
    ConnectionPool *pool = malloc(sizeof(ConnectionPool));
    if (!pool) return NULL;
    
    pool->connections = malloc(sizeof(int) * size);
    if (!pool->connections) {
        free(pool);
        return NULL;
    }
    
    // 创建连接
    for (int i = 0; i < size; i++) {
        pool->connections[i] = create_connection(host, port);
        if (pool->connections[i] < 0) {
            // 连接失败,清理已创建的连接
            for (int j = 0; j < i; j++) {
                close(pool->connections[j]);
            }
            free(pool->connections);
            free(pool);
            return NULL;
        }
    }
    
    pool->pool_size = size;
    pool->used_count = 0;
    pthread_mutex_init(&pool->lock, NULL);
    pthread_cond_init(&pool->available, NULL);
    
    return pool;
}

// 从连接池获取连接
int get_connection(ConnectionPool *pool) {
    pthread_mutex_lock(&pool->lock);
    
    while (pool->used_count >= pool->pool_size) {
        pthread_cond_wait(&pool->available, &pool->lock);
    }
    
    for (int i = 0; i < pool->pool_size; i++) {
        if (pool->connections[i] >= 0) {
            int conn = pool->connections[i];
            pool->connections[i] = -1; // 标记为使用中
            pool->used_count++;
            pthread_mutex_unlock(&pool->lock);
            return conn;
        }
    }
    
    pthread_mutex_unlock(&pool->lock);
    return -1;
}

// 归还连接到连接池
void release_connection(ConnectionPool *pool, int conn) {
    pthread_mutex_lock(&pool->lock);
    
    for (int i = 0; i < pool->pool_size; i++) {
        if (pool->connections[i] == -1) {
            pool->connections[i] = conn;
            pool->used_count--;
            pthread_cond_signal(&pool->available);
            break;
        }
    }
    
    pthread_mutex_unlock(&pool->lock);
}

9. 安全考虑

9.1 防止目录遍历攻击

c 复制代码
// 安全的路径检查
int is_safe_path(const char *path) {
    // 检查路径遍历
    if (strstr(path, "..") != NULL) {
        return 0;
    }
    
    // 检查绝对路径
    if (path[0] == '/') {
        return 0;
    }
    
    // 检查危险字符
    const char *dangerous_chars = "|;&$><`\\\"'";
    for (int i = 0; path[i]; i++) {
        if (strchr(dangerous_chars, path[i]) != NULL) {
            return 0;
        }
    }
    
    return 1;
}

// 安全的文件打开
int safe_open(const char *base_dir, const char *filename, int flags) {
    char safe_path[512];
    
    // 验证文件名
    if (!is_safe_path(filename)) {
        errno = EPERM;
        return -1;
    }
    
    // 构造完整路径
    if (snprintf(safe_path, sizeof(safe_path), "%s/%s", 
                 base_dir, filename) >= sizeof(safe_path)) {
        errno = ENAMETOOLONG;
        return -1;
    }
    
    // 规范化路径
    char resolved_path[512];
    if (realpath(safe_path, resolved_path) == NULL) {
        return -1;
    }
    
    // 确保路径在基础目录内
    size_t base_len = strlen(base_dir);
    if (strncmp(resolved_path, base_dir, base_len) != 0) {
        errno = EPERM;
        return -1;
    }
    
    return open(resolved_path, flags, 0644);
}

9.2 连接限制与超时控制

c 复制代码
// 连接限制管理器
typedef struct {
    int max_connections_per_ip;
    int connection_timeout; // 秒
    pthread_mutex_t lock;
} ConnectionManager;

// 检查连接频率
int check_connection_rate(struct sockaddr_in *client_addr) {
    static struct {
        struct sockaddr_in addr;
        time_t last_connect;
        int count;
    } connection_log[1000];
    
    static int log_count = 0;
    static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
    
    time_t now = time(NULL);
    pthread_mutex_lock(&log_mutex);
    
    // 查找现有记录
    for (int i = 0; i < log_count; i++) {
        if (connection_log[i].addr.sin_addr.s_addr == client_addr->sin_addr.s_addr) {
            // 检查连接频率
            if (now - connection_log[i].last_connect < 1) { // 1秒内
                connection_log[i].count++;
                if (connection_log[i].count > 5) { // 每秒最多5次连接
                    pthread_mutex_unlock(&log_mutex);
                    return 0; // 拒绝连接
                }
            } else {
                connection_log[i].count = 1;
            }
            connection_log[i].last_connect = now;
            pthread_mutex_unlock(&log_mutex);
            return 1; // 允许连接
        }
    }
    
    // 添加新记录
    if (log_count < 1000) {
        connection_log[log_count].addr = *client_addr;
        connection_log[log_count].last_connect = now;
        connection_log[log_count].count = 1;
        log_count++;
    }
    
    pthread_mutex_unlock(&log_mutex);
    return 1;
}

// 设置Socket超时
int set_socket_timeout(int sockfd, int timeout_seconds) {
    struct timeval timeout;
    timeout.tv_sec = timeout_seconds;
    timeout.tv_usec = 0;
    
    if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, 
                   &timeout, sizeof(timeout)) < 0) {
        return -1;
    }
    
    if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, 
                   &timeout, sizeof(timeout)) < 0) {
        return -1;
    }
    
    return 0;
}

10. 性能优化

10.1 缓冲区优化

c 复制代码
// 动态缓冲区管理
typedef struct {
    char *data;
    size_t size;
    size_t capacity;
    size_t read_pos;
    size_t write_pos;
} DynamicBuffer;

// 初始化动态缓冲区
DynamicBuffer *buffer_create(size_t initial_capacity) {
    DynamicBuffer *buf = malloc(sizeof(DynamicBuffer));
    if (!buf) return NULL;
    
    buf->data = malloc(initial_capacity);
    if (!buf->data) {
        free(buf);
        return NULL;
    }
    
    buf->size = 0;
    buf->capacity = initial_capacity;
    buf->read_pos = 0;
    buf->write_pos = 0;
    
    return buf;
}

// 扩展缓冲区
int buffer_expand(DynamicBuffer *buf, size_t min_capacity) {
    size_t new_capacity = buf->capacity * 2;
    if (new_capacity < min_capacity) {
        new_capacity = min_capacity;
    }
    
    char *new_data = realloc(buf->data, new_capacity);
    if (!new_data) return -1;
    
    buf->data = new_data;
    buf->capacity = new_capacity;
    return 0;
}

// 写入缓冲区
ssize_t buffer_write(DynamicBuffer *buf, const void *data, size_t len) {
    if (buf->write_pos + len > buf->capacity) {
        if (buffer_expand(buf, buf->write_pos + len) < 0) {
            return -1;
        }
    }
    
    memcpy(buf->data + buf->write_pos, data, len);
    buf->write_pos += len;
    buf->size += len;
    
    return len;
}

// 从Socket读取到缓冲区
ssize_t buffer_read_from_socket(DynamicBuffer *buf, int sockfd) {
    char temp[4096];
    ssize_t bytes_read;
    
    // 确保有足够空间
    if (buf->capacity - buf->write_pos < 4096) {
        if (buffer_expand(buf, buf->capacity + 4096) < 0) {
            return -1;
        }
    }
    
    bytes_read = recv(sockfd, temp, sizeof(temp), 0);
    if (bytes_read > 0) {
        buffer_write(buf, temp, bytes_read);
    }
    
    return bytes_read;
}

10.2 零拷贝技术优化

c 复制代码
// 使用sendfile系统调用(Linux特有)
#ifdef __linux__
int send_file_zerocopy(int sockfd, int file_fd, off_t offset, size_t count) {
    off_t file_offset = offset;
    size_t remaining = count;
    
    while (remaining > 0) {
        ssize_t sent = sendfile(sockfd, file_fd, &file_offset, remaining);
        if (sent <= 0) {
            if (errno == EINTR) continue;
            if (errno == EAGAIN) {
                // 等待Socket可写
                fd_set write_fds;
                struct timeval timeout = {1, 0};
                
                FD_ZERO(&write_fds);
                FD_SET(sockfd, &write_fds);
                
                if (select(sockfd + 1, NULL, &write_fds, NULL, &timeout) <= 0) {
                    return -1;
                }
                continue;
            }
            return -1;
        }
        
        remaining -= sent;
    }
    
    return 0;
}
#endif

// 使用splice系统调用(Linux特有)
#ifdef __linux__
int transfer_file_pipe(int sockfd, int file_fd, size_t count) {
    int pipefd[2];
    if (pipe(pipefd) < 0) {
        return -1;
    }
    
    size_t remaining = count;
    while (remaining > 0) {
        // 从文件读取到管道
        ssize_t spliced = splice(file_fd, NULL, pipefd[1], NULL,
                                 (remaining > 65536) ? 65536 : remaining,
                                 SPLICE_F_MOVE);
        if (spliced <= 0) break;
        
        // 从管道写入到Socket
        ssize_t sent = splice(pipefd[0], NULL, sockfd, NULL,
                              spliced, SPLICE_F_MOVE);
        if (sent != spliced) break;
        
        remaining -= sent;
    }
    
    close(pipefd[0]);
    close(pipefd[1]);
    
    return (remaining == 0) ? 0 : -1;
}
#endif

11. 测试与验证

11.1 单元测试

c 复制代码
// test_ftp.c - 单元测试
#include <assert.h>
#include <stdio.h>
#include <string.h>

// 测试路径安全检查
void test_path_security() {
    printf("测试路径安全检查...\n");
    
    assert(is_safe_path("test.txt") == 1);
    assert(is_safe_path("../test.txt") == 0);
    assert(is_safe_path("/etc/passwd") == 0);
    assert(is_safe_path("test|bad.txt") == 0);
    assert(is_safe_path("normal_file.dat") == 1);
    
    printf("路径安全检查测试通过!\n");
}

// 测试协议解析
void test_protocol_parsing() {
    printf("测试协议解析...\n");
    
    char header[] = "PUT test.txt 1024";
    FileTransferContext ctx;
    
    assert(parse_file_header(header, &ctx) == 0);
    assert(strcmp(ctx.filename, "test.txt") == 0);
    assert(ctx.file_size == 1024);
    
    printf("协议解析测试通过!\n");
}

// 测试缓冲区管理
void test_buffer_management() {
    printf("测试缓冲区管理...\n");
    
    DynamicBuffer *buf = buffer_create(100);
    assert(buf != NULL);
    assert(buf->capacity >= 100);
    
    const char *test_data = "Hello, World!";
    ssize_t written = buffer_write(buf, test_data, strlen(test_data));
    assert(written == strlen(test_data));
    assert(buf->size == strlen(test_data));
    
    buffer_free(buf);
    printf("缓冲区管理测试通过!\n");
}

int main() {
    printf("开始FTP系统单元测试\n");
    printf("==================\n");
    
    test_path_security();
    test_protocol_parsing();
    test_buffer_management();
    
    printf("\n所有单元测试通过!\n");
    return 0;
}

11.2 集成测试

python 复制代码
#!/usr/bin/env python3
# integration_test.py - 集成测试脚本

import subprocess
import time
import os
import signal
import sys

def start_server(port=8888):
    """启动FTP服务器"""
    server_cmd = ["./ftp_server", "-p", str(port), "-d", "./test_dir", "-q"]
    server_proc = subprocess.Popen(server_cmd, 
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
    time.sleep(2)  # 等待服务器启动
    return server_proc

def run_client_command(command):
    """运行客户端命令"""
    client_cmd = ["./ftp_client", "-connect", "127.0.0.1", "8888", "-q"]
    proc = subprocess.Popen(client_cmd,
                            stdin=subprocess.PIPE,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.PIPE,
                            text=True)
    stdout, stderr = proc.communicate(input=command)
    return proc.returncode, stdout, stderr

def create_test_file(filename, size_kb):
    """创建测试文件"""
    with open(filename, 'wb') as f:
        f.write(os.urandom(size_kb * 1024))

def main():
    print("FTP系统集成测试")
    print("==============")
    
    # 创建测试目录
    os.makedirs("./test_dir", exist_ok=True)
    
    # 启动服务器
    print("1. 启动服务器...")
    server = start_server()
    
    try:
        # 测试1: 基本连接
        print("\n2. 测试基本连接...")
        retcode, output, error = run_client_command("list\nquit\n")
        assert retcode == 0
        print("基本连接测试通过")
        
        # 测试2: 文件上传
        print("\n3. 测试文件上传...")
        create_test_file("upload_test.bin", 1024)  # 1MB文件
        retcode, output, error = run_client_command(
            "put upload_test.bin\nquit\n")
        assert retcode == 0
        print("文件上传测试通过")
        
        # 测试3: 文件下载
        print("\n4. 测试文件下载...")
        retcode, output, error = run_client_command(
            "get upload_test.bin\nquit\n")
        assert retcode == 0
        assert os.path.exists("downloaded_upload_test.bin")
        print("文件下载测试通过")
        
        # 测试4: 大文件传输
        print("\n5. 测试大文件传输...")
        create_test_file("large_file.bin", 10240)  # 10MB文件
        retcode, output, error = run_client_command(
            "put large_file.bin\nget large_file.bin\nquit\n")
        assert retcode == 0
        print("大文件传输测试通过")
        
        # 测试5: 错误处理
        print("\n6. 测试错误处理...")
        retcode, output, error = run_client_command(
            "get non_existent_file.txt\nquit\n")
        assert "ERROR" in output or "error" in output.lower()
        print("错误处理测试通过")
        
        print("\n所有集成测试通过!")
        
    finally:
        # 清理
        print("\n7. 清理测试文件...")
        server.send_signal(signal.SIGTERM)
        server.wait()
        
        files_to_remove = [
            "upload_test.bin",
            "downloaded_upload_test.bin",
            "large_file.bin",
            "downloaded_large_file.bin"
        ]
        
        for filename in files_to_remove:
            if os.path.exists(filename):
                os.remove(filename)
        
        if os.path.exists("./test_dir"):
            for filename in os.listdir("./test_dir"):
                os.remove(os.path.join("./test_dir", filename))
            os.rmdir("./test_dir")
        
        print("测试完成!")

if __name__ == "__main__":
    main()

12. 部署与监控

12.1 系统服务部署

bash 复制代码
#!/bin/bash
# deploy.sh - 部署脚本

# FTP服务器部署配置
FTP_USER="ftpuser"
FTP_GROUP="ftpgroup"
FTP_HOME="/opt/ftp_server"
FTP_PORT="8888"
LOG_DIR="/var/log/ftp_server"

echo "=== FTP服务器部署脚本 ==="

# 检查是否为root用户
if [ "$EUID" -ne 0 ]; then 
    echo "请使用root权限运行此脚本"
    exit 1
fi

# 创建用户和组
echo "1. 创建FTP用户和组..."
if ! getent group $FTP_GROUP > /dev/null; then
    groupadd $FTP_GROUP
fi

if ! id -u $FTP_USER > /dev/null 2>&1; then
    useradd -r -g $FTP_GROUP -s /bin/false -d $FTP_HOME $FTP_USER
fi

# 创建目录
echo "2. 创建目录结构..."
mkdir -p $FTP_HOME/{bin,conf,data,logs}
mkdir -p $LOG_DIR

# 复制文件
echo "3. 复制程序文件..."
cp ftp_server $FTP_HOME/bin/
cp ftp_client $FTP_HOME/bin/
chmod +x $FTP_HOME/bin/*

# 创建配置文件
echo "4. 创建配置文件..."
cat > $FTP_HOME/conf/server.conf << EOF
# FTP服务器配置
port = $FTP_PORT
root_dir = $FTP_HOME/data
max_clients = 100
log_file = $LOG_DIR/ftp_server.log
verbose = true
connection_timeout = 300
max_file_size = 1073741824  # 1GB
EOF

# 设置权限
echo "5. 设置权限..."
chown -R $FTP_USER:$FTP_GROUP $FTP_HOME
chown -R $FTP_USER:$FTP_GROUP $LOG_DIR

# 创建systemd服务
echo "6. 创建systemd服务..."
cat > /etc/systemd/system/ftp-server.service << EOF
[Unit]
Description=Simple FTP Server
After=network.target

[Service]
Type=simple
User=$FTP_USER
Group=$FTP_GROUP
WorkingDirectory=$FTP_HOME
ExecStart=$FTP_HOME/bin/ftp_server -c $FTP_HOME/conf/server.conf
Restart=on-failure
RestartSec=5
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=ftp-server

# 安全设置
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=$FTP_HOME/data $LOG_DIR

[Install]
WantedBy=multi-user.target
EOF

# 启用并启动服务
echo "7. 启动服务..."
systemctl daemon-reload
systemctl enable ftp-server
systemctl start ftp-server

echo "8. 检查服务状态..."
systemctl status ftp-server --no-pager

echo -e "\n=== 部署完成 ==="
echo "服务器目录: $FTP_HOME"
echo "数据目录: $FTP_HOME/data"
echo "日志目录: $LOG_DIR"
echo "服务名称: ftp-server"
echo ""
echo "管理命令:"
echo "  sudo systemctl start ftp-server    # 启动服务"
echo "  sudo systemctl stop ftp-server     # 停止服务"
echo "  sudo systemctl restart ftp-server  # 重启服务"
echo "  sudo systemctl status ftp-server   # 查看状态"
echo "  sudo journalctl -u ftp-server      # 查看日志"

12.2 监控脚本

bash 复制代码
#!/bin/bash
# monitor.sh - 监控脚本

LOG_FILE="/var/log/ftp_server/ftp_server.log"
ALERT_EMAIL="admin@example.com"
ALERT_THRESHOLD=90  # 磁盘使用率阈值

# 检查服务状态
check_service() {
    if ! systemctl is-active --quiet ftp-server; then
        echo "警告: FTP服务未运行!"
        echo "尝试重启服务..."
        systemctl restart ftp-server
        
        if [ $? -eq 0 ]; then
            echo "服务重启成功"
            # 发送警报邮件
            echo "FTP服务已重启" | mail -s "FTP服务监控警报" $ALERT_EMAIL
        else
            echo "服务重启失败,需要手动检查"
        fi
    fi
}

# 检查磁盘空间
check_disk_space() {
    local usage=$(df -h $FTP_HOME/data | awk 'NR==2 {print $5}' | sed 's/%//')
    
    if [ $usage -ge $ALERT_THRESHOLD ]; then
        echo "警告: 磁盘使用率超过 ${ALERT_THRESHOLD}% (当前: ${usage}%)"
        echo "磁盘空间不足警报" | mail -s "FTP磁盘空间警报" $ALERT_EMAIL
    fi
}

# 分析日志
analyze_logs() {
    echo "=== 日志分析报告 ==="
    echo "生成时间: $(date)"
    echo ""
    
    # 统计连接数
    echo "1. 连接统计:"
    grep "New client connected" $LOG_FILE | wc -l | xargs echo "  总连接数:"
    echo ""
    
    # 统计文件传输
    echo "2. 文件传输统计:"
    grep "File received successfully" $LOG_FILE | wc -l | xargs echo "  上传文件数:"
    grep "File sent:" $LOG_FILE | wc -l | xargs echo "  下载文件数:"
    echo ""
    
    # 错误统计
    echo "3. 错误统计:"
    grep -i "error\|failed\|disconnected" $LOG_FILE | wc -l | xargs echo "  错误数量:"
    echo ""
    
    # 最近活动
    echo "4. 最近活动:"
    tail -10 $LOG_FILE | while read line; do
        echo "  $line"
    done
}

# 主监控循环
main_monitor() {
    echo "开始FTP服务器监控..."
    echo "=================="
    
    while true; do
        echo "[$(date)] 执行监控检查..."
        
        check_service
        check_disk_space
        
        # 每小时生成一次详细报告
        if [ $(date +%M) -eq 0 ]; then
            analyze_logs > /tmp/ftp_monitor_report.txt
            # 可以添加发送报告的代码
        fi
        
        # 休眠5分钟
        sleep 300
    done
}

# 运行监控
main_monitor

13. 总结与展望

13.1 项目总结

通过本项目的开发,我们实现了一个完整的基于TCP的FTP文件传输系统,具有以下特点:

  1. 完整的FTP功能:实现了文件上传、下载、列表查看等核心功能
  2. 高性能设计:支持多线程、连接池、缓冲区优化等
  3. 安全可靠:包含路径安全检查、连接限制、错误处理等机制
  4. 易于部署:提供完整的部署脚本和监控方案
  5. 扩展性强:设计了模块化架构,便于添加新功能

13.2 性能指标

根据测试,本系统在以下方面表现良好:

  • 传输速度:在千兆网络环境下可达80-90MB/s
  • 并发连接:支持100+并发客户端连接
  • 内存使用:每个连接约1MB内存开销
  • CPU使用率:在高速传输时CPU使用率低于20%

13.3 未来改进方向

  1. 支持标准FTP协议:兼容RFC 959标准
  2. 添加TLS加密支持:实现FTPS协议
  3. 实现Web界面:提供浏览器访问方式
  4. 支持集群部署:实现负载均衡和高可用
  5. 添加用户管理:支持多用户和权限控制
  6. 实现增量同步:支持文件的增量备份和同步

13.4 学习收获

通过本项目的实践,我们可以深入理解:

  1. TCP协议的工作原理和Socket编程技术
  2. 多线程编程和并发控制
  3. 网络协议的设计和实现
  4. 系统性能优化方法
  5. 网络安全防护技术
  6. 软件工程实践和项目管理

本项目不仅是一个功能完整的FTP系统,更是一个优秀的学习案例,展示了从需求分析、系统设计、编码实现到测试部署的完整软件开发流程。


项目源码:完整的项目源码已包含在上述内容中,可以直接编译运行。

学习建议

  1. 先理解TCP Socket编程基础
  2. 从简单的客户端-服务器示例开始
  3. 逐步添加文件传输功能
  4. 实现错误处理和优化
  5. 最后添加高级功能和安全性

通过本项目的学习和实践,读者可以掌握网络编程的核心技能,为开发更复杂的网络应用打下坚实基础。

相关推荐
Sleepy MargulisItG3 小时前
【Linux网络编程】应用层自定义协议与序列化
linux·服务器·网络·网络协议·tcp/ip
技术净胜3 小时前
MATLAB 环境搭建与认知实战教程:从下载安装到入门全解析教程
开发语言·matlab
爱吃大芒果3 小时前
Flutter 自定义 Widget 开发:从基础绘制到复杂交互
开发语言·javascript·flutter·华为·ecmascript·交互
JoannaJuanCV3 小时前
自动驾驶—CARLA仿真(24)sensor_synchronization demo
网络·人工智能·自动驾驶·carla
..过云雨3 小时前
15-2.【Linux系统编程】进程信号 - 信号保存(信号处理流程的三种状态:未决、阻塞、递达,信号保存由未决表完成、sigset_t信号集类型及相关函数)
linux·c++·后端·信号处理
帅得不敢出门3 小时前
MTK Android11 APP调用OTA升级
android·java·开发语言·framework
林疏safe3 小时前
常见网络安全产品以及中国网络安全行业全景分析最新学习。
网络
Swift社区3 小时前
用 Task Local Values 构建 Swift 里的依赖容器:一种更轻量的依赖注入思路
开发语言·ios·swift
黑牛先生3 小时前
【GDB】调试Jsoncpp源码
开发语言·c++·算法