多线程并发实现TCP服务器上传、下载、查询图片操作V1.1

目录

一、较与上版本的优化

二、代码编写思路

服务器

客户端

三、代码优缺点

服务器

优点:

缺点:

客户端

优点

缺点

四、源代码

服务器

客户端


更多文章和源码在我的个人博客:首页 (niuniu65.top)

如需咨询请添加个人微信:a15135158368

欢迎叨扰,多多交流

一、较与上版本的优化

(如需查看,请移步到本人上篇文章)

1、服务器和客户端加入了多线程,实现多并发处理数据传输;

2、更加健壮的代码程序

3、标准输出加入了高亮文本,更方便调试


二、代码编写思路

服务器

主函数

  1. 信号处理:使 'sigaction'注册了'SIGINT'

  2. 服务器初始化:创建服务器套接字,并设置 SO_REUSEADDR 选项,允许服务器在关闭后立即重新绑定到相同的地址和端口。

  3. 绑定与监听:将服务器套接字绑定到指定的 IP 地址和端口,并开始监听客户端的连接请求。

  4. 客户端连接处理:进入一个无限循环,持续等待客户端的连接请求。每次接收到新的客户端连接时,创建一个新线程处理客户端的请求。线程由 do_client_request 函数实现,负责处理具体的客户端请求。

2.客户端请求处理 (do_client_request)

  1. 从参数中获取客户端套接字,并通过 handle_client 函数处理客户端的文件上传、下载和查询操作。

  2. 在处理完请求后,关闭客户端套接字并释放资源。

3.处理与客户端的通信 (handle_client)

接收操作码:从客户端接收操作码,表示客户端希望执行的操作类型。支持的操作码有:

  • 0x01:文件上传。

  • 0x02:文件下载。

  • 0x03:文件查询。

接收文件名:从客户端接收文件名,并根据操作码执行相应操作。

  • 上传操作 (0x01):

    • 接收文件大小,并将客户端发送的文件数据保存到服务器端的文件中。

  • 下载操作 (0x02):

    • 检查文件是否存在,并将文件内容发送给客户端。

  • 查询操作 (0x03):

    • 检查文件是否存在,并将查询结果发送给客户端。

4.信号处理函数 (handle_sigint)

在接收到 SIGINT 信号时,关闭服务器套接字并退出程序。

客户端

  1. 主函数:

    1. 信号处理:使用 sigaction 注册信号处理函数 handle_sigint

    2. 服务器连接:在主循环中,客户端通过套接字与服务器进行连接。

    3. 用户输入

      • 用户输入文件名和操作码(1: 上传, 2: 下载, 3: 查询, 0: 退出)。

      • 对输入进行基本验证。

    4. 线程创建:根据用户选择的操作码,动态分配线程数据结构,将套接字和操作参数传递给新创建的线程。每个线程独立处理一个用户操作。

  2. 线程处理逻辑 (handle_operation 函数):

    1. 上传文件 (upload_image):将本地文件发送到服务器。

    2. 下载文件 (download_image):从服务器下载文件到本地。

    3. 查询文件 (query_image):检查服务器上是否存在指定文件。

    4. 线程执行完相应的操作后关闭套接字,并释放动态分配的内存。

  3. 文件操作

    1. 上传文件

      • 打开本地文件并读取其内容,将文件名、文件大小和内容发送到服务器。

    2. 下载文件

      • 从服务器接收文件内容并将其写入本地文件。先接收文件大小,然后循环接收文件数据,直到完整下载文件。

    3. 查询文件

      • 发送文件名到服务器,接收服务器返回的文件存在状态(存在或不存在)。

三、代码优缺点

服务器

优点:
  1. 多线程并发

  2. 信号处理

  3. 基本的文件操作支持

缺点:
  1. 缺乏心跳机制:无法实时监测客户端的状态

  2. 缺乏文件操作的并发控制:加入文件锁等同步机制

  3. 硬编码配置:ip地址和端口号等配置被硬编码在代码中,缺乏灵活性。

客户端

优点
  1. 多线程并发处理

    • 并发性:采用多线程处理用户操作,使得客户端可以同时执行多个文件操作(上传、下载、查询),提高了程序的并发性能和响应速度。

    • 资源管理:每个操作都在独立线程中执行,使用 pthread_detach 实现了线程分离,避免了线程阻塞主线程,也免去了主线程需要手动管理线程生命周期的负担。

  2. 代码结构清晰

    • 模块化设计:代码通过函数划分不同的功能模块(如上传、下载、查询、信号处理等),使得代码逻辑清晰易懂,便于维护和扩展。

    • 动态内存管理:使用动态内存分配 (mallocfree) 来管理线程数据,避免了不必要的全局变量,提高了程序的可扩展性和灵活性。

缺点
  1. 线程安全问题

    • 潜在的竞争条件:每个操作都在独立线程中执行,但如果多个线程试图同时访问或修改共享资源(如全局变量或文件),可能会引发竞争条件。

      虽然使用了独立的套接字连接,但如果程序扩展涉及共享数据(如计数器、日志文件等),则需注意线程同步问题。

  2. 客户端套接字管理

    • 重复连接:每次用户输入操作后,客户端都会重新创建一个新的套接字并连接到服务器。虽然简化了代码逻辑,但频繁的连接/断开操作可能会带来一定的性能开销。

  3. 用户交互阻塞

    • 主线程阻塞:主线程在每次并启动线程后,使用 sleep(0.5) 来等待一段时间,会使主线程在这段时间内阻塞,影响程序的实时性。

四、源代码

服务器

/**
  ******************************************************************************
  * @file           : tcp_server.c
  * @author         : niuniu
  * @brief          : 该程序实现了一个简单的多线程并发TCP服务器,能够处理客户端的文件上传、下载和查询操作
  * @attention      :
  * @date           : 2024/8/13
  ******************************************************************************
  */
​
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <signal.h>
#include <pthread.h>
​
// 定义用于终端文本高亮显示的颜色代码
#define BRIGHT_RED_TEXT "\033[91m"       // 红色高亮文本
#define BRIGHT_GREEN_TEXT "\033[92m"     // 绿色高亮文本
#define BRIGHT_YELLOW_TEXT "\033[93m"    // 黄色高亮文本
#define BRIGHT_BLUE_TEXT "\033[94m"      // 蓝色高亮文本
#define RESET_COLOR "\033[0m"            // 重置颜色设置
​
#define PORT 8080                       // 服务器监听的端口号
#define BUFFER_SIZE 1024                // 缓冲区大小
#define SERVER_IP "192.168.216.149"     // 服务器IP地址
​
int server_socket;                     // 服务器套接字
struct sockaddr_in server_addr, client_addr;  // 存储服务器和客户端地址信息的结构体
​
//@brief 处理客户端请求的函数
int handle_client(int client_socket);
​
//@brief 处理SIGINT信号的函数(Ctrl+C)
void handle_sigint(int sig);
​
/**
 * @brief 客户端请求处理线程
 * @param argv 客户端套接字的指针
 * @return void* 线程返回值
 */
void *do_client_request(void* argv)
{
    int client_socket = *((int *)argv);  // 从参数中获取客户端套接字
    free(argv);  // 释放传递进来的指针内存
    printf(BRIGHT_GREEN_TEXT"等待指定操作中...\n"RESET_COLOR);
​
    // 处理客户端请求
    int handle_ret = handle_client(client_socket);
    if(handle_ret != EXIT_SUCCESS)
    {
        perror("handle_client");
        printf(BRIGHT_RED_TEXT"\n未能正确实现功能\n"RESET_COLOR);
    }
​
    // 关闭客户端套接字,释放资源
    printf(BRIGHT_GREEN_TEXT"释放client_socket:%d 资源\n\n"RESET_COLOR, client_socket);
    usleep(100000);  // 延迟100毫秒
    close(client_socket);
    return NULL;
}
​
/**
 * @brief 服务器主函数,初始化并启动服务器,持续监听客户端的连接请求
 * @return int 程序退出状态码
 */
int main(void)
{
    socklen_t client_addr_size;  // 客户端地址结构体的大小
​
    // 注册信号处理函数,用于处理 SIGINT (Ctrl+C)
    struct sigaction sa;
    sa.sa_handler = handle_sigint;  // 设置处理函数
    sa.sa_flags = 0;  // 默认标志
    sigemptyset(&sa.sa_mask);  // 清空信号屏蔽字
    if (sigaction(SIGINT, &sa, NULL) == -1)
    {
        perror("sigaction");
        exit(EXIT_FAILURE);
    }
​
    // 创建服务器套接字
    server_socket = socket(PF_INET, SOCK_STREAM, 0);
    if (server_socket == -1)
    {
        perror("socket");
        exit(EXIT_FAILURE);
    }
​
    // 允许在套接字关闭后立即重新绑定到相同的地址和端口
    int opt = 1;
    if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1)
    {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }
​
    // 初始化服务器地址结构体
    memset(&server_addr, 0, sizeof(server_addr));  // 清空结构体
    server_addr.sin_family = AF_INET;  // 使用IPv4协议
    server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);  // 绑定服务器IP地址
    server_addr.sin_port = htons(PORT);  // 绑定端口号
​
    // 绑定服务器套接字到指定IP和端口
    if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1)
    {
        perror("bind");
        exit(EXIT_FAILURE);
    }
​
    // 监听端口,设置最大等待队列长度为5
    if (listen(server_socket, 5) == -1)
    {
        perror("listen");
        exit(EXIT_FAILURE);
    }
​
    printf(BRIGHT_YELLOW_TEXT"服务器正在监听【%d】号端口号\n\n"RESET_COLOR, PORT);
​
    while (1)  // 无限循环,持续接收客户端请求
    {
        // 初始化客户端结构体大小
        client_addr_size = sizeof(client_addr);
        int *client_socket = malloc(sizeof(int));  // 动态分配内存存储客户端套接字
        if (client_socket == NULL)
        {
            perror("malloc");
            continue;
        }
​
        // 接受客户端连接
        *client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_addr_size);
        if(*client_socket == -1)
        {
            perror("accept");
            free(client_socket);  // 释放内存,避免内存泄漏
            continue;
        }
​
        printf(BRIGHT_GREEN_TEXT"成功接收了客户端的连接\n"RESET_COLOR);
        printf(BRIGHT_GREEN_TEXT"新连接到的客户端为{%s : %d}\n\n"RESET_COLOR, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
​
        // 创建线程处理客户端请求
        pthread_t pid;
        int pid_ret = pthread_create(&pid, NULL, do_client_request, (void*)client_socket);
        if (pid_ret != 0)  // 线程创建失败
        {
            perror("pthread_create");
            close(*client_socket);  // 关闭连接
            free(client_socket);
            continue;
        }
​
        pthread_detach(pid);  // 设置线程为 detached 状态,自动回收资源
        printf(BRIGHT_GREEN_TEXT"创建子线程并处理为detached状态\n\n"RESET_COLOR);
    }
​
    close(server_socket);  // 关闭服务器套接字
    return 0;
}
​
/**
 * @brief 处理SIGINT信号的函数(Ctrl+C)
 * @param sig 信号值
 */
void handle_sigint(int sig)
{
    printf(BRIGHT_RED_TEXT"\n接收到信号 %d,正在关闭服务器...\n"RESET_COLOR, sig);
    printf(BRIGHT_RED_TEXT"释放资源\n"RESET_COLOR);
    close(server_socket);
    exit(0);
}
​
/**
 * @brief 处理与客户端的通信,执行上传、下载、查询操作
 *
 * @param client_socket 与客户端通信的套接字
 */
int handle_client(int client_socket)
{
    char buffer[BUFFER_SIZE];  // 用于存储接收和发送的数据的缓冲区
    int name_len;              // 文件名长度
    char file_name[256] = {0}; // 用于存储文件名的字符数组
    unsigned int file_size;    // 文件大小(字节数)
​
    // 接收操作码,表示客户端希望执行的操作类型
    if (recv(client_socket, buffer, 1, 0) <= 0)  // 接收操作码失败则退出循环
    {
        perror("recv_opcode");
        return EXIT_FAILURE;
    }
​
    char opcode = buffer[0];  // 操作码,0x01上传,0x02下载,0x03查询
​
    // 接收文件名长度
    if (recv(client_socket, buffer, 1, 0) <= 0)  // 接收文件名长度失败则退出循环
    {
        perror("recv_len");
        return EXIT_FAILURE;
    }
​
    // 获取文件名长度
    name_len = buffer[0];
​
    // 接收文件名
    if (recv(client_socket, file_name, name_len, 0) <= 0)  // 接收文件名失败则退出循环
    {
        perror("recv_name");
        return EXIT_FAILURE;
    }
    file_name[name_len] = '\0';  // 文件名字符串的末尾添加结束符
​
    switch (opcode)
    {
        case 0x01:
        {
            int file_fd;
            // 接收文件大小
            if (recv(client_socket, buffer, 4, 0) <= 0)  // 接收文件大小失败则退出循环
            {
                perror("recv4");
                return EXIT_FAILURE;
            }
​
            file_size = *((unsigned int *)buffer);  // 将接收的文件大小转换为整数
​
            // 打开或者创建一个新文件,用于保存上传的图片
            file_fd = open(file_name, O_WRONLY | O_CREAT | O_TRUNC, 0666);
            if (file_fd < 0)  // 文件打开失败则退出循环
            {
                perror("open");
                return EXIT_FAILURE;
            }
​
            int received = 0;  // 已接收的数据大小
            // 循环接收文件内容,直到接收完整个文件
            while (received < file_size)
            {
                int len = recv(client_socket, buffer, BUFFER_SIZE, 0);
                if (len <= 0)  // 接收数据失败则退出循环
                {
                    perror("recv_content");
                    close(file_fd);
                    return EXIT_FAILURE;
                }
                received += len;  // 更新已接收的字节数
                write(file_fd, buffer, len);  // 将接收的数据写入文件
            }
​
            close(file_fd);  // 关闭文件描述符
            printf(BRIGHT_YELLOW_TEXT"已将文件【%s】上传\n"RESET_COLOR, file_name);  // 打印成功接收的文件名
            return EXIT_SUCCESS;
        }
        case 0x02:
        {
            int file_fd;
            file_fd = open(file_name, O_RDONLY);  // 打开要下载的文件
            if (file_fd < 0)  // 文件不存在,发送文件大小为0
            {
                file_size = 0;
                send(client_socket, &file_size, sizeof(file_size), 0);
                perror("open failed");
                close(file_fd);
                return EXIT_FAILURE;
            }
            else
            {
                // 计算文件大小 (通过 lseek 的偏移量返回值)
                file_size = lseek(file_fd, 0, SEEK_END);
                lseek(file_fd, 0, SEEK_SET);
                // 发送文件大小给客户端
                send(client_socket, &file_size, sizeof(file_size), 0);
​
                // 循环发送文件内容
                int read_len = 0;
                while ((read_len = read(file_fd, buffer, BUFFER_SIZE)) > 0)
                {
                    send(client_socket, buffer, read_len, 0);
                }
                printf(BRIGHT_YELLOW_TEXT"已将文件【%s】发送下载成功\n"RESET_COLOR, file_name);
            }
            close(file_fd);  // 关闭文件描述符
            return EXIT_SUCCESS;
        }
        case 0x03:
        {
            // 使用 access 函数判断文件是否存在,存在返回1,不存在返回0
            int exists = access(file_name, F_OK) != -1;
            send(client_socket, &exists, sizeof(exists), 0);
            printf(BRIGHT_YELLOW_TEXT"已查询文件【%s】是否存在\n"RESET_COLOR, file_name);
            return EXIT_SUCCESS;
        }
    }
    return EXIT_SUCCESS;
}

客户端

/**
  ******************************************************************************
  * @file           : tcp_client.c
  * @author         : niuniu
  * @brief          : 采用多线程并发实现客户端程序,用于连接服务器并执行上传、下载、查询操作
  * @attention      : None
  * @date           : 2024/8/13
  ******************************************************************************
  */
​
​
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>
#include <signal.h>
​
#define BRIGHT_RED_TEXT "\033[91m"       //红色高亮文本
#define BRIGHT_GREEN_TEXT "\033[92m"     //绿色高亮文本
#define BRIGHT_YELLOW_TEXT "\033[93m"    //黄色高亮文本
#define BRIGHT_BLUE_TEXT "\033[94m"      //蓝色高亮文本
#define RESET_COLOR "\033[0m"            //重置颜色设置
​
#define SERVER_IP "192.168.216.149"   // 服务器的IP地址
#define PORT 8080                     // 服务器的端口号
#define BUFFER_SIZE 1024              // 缓冲区大小
#define FILE_SIZE 256                 // 文件名大小
​
void upload_image(int client_socket, const char *file_path);        //上传图片
void download_image(int client_socket, const char *file_name);      //下载图片
void query_image(int client_socket, const char *file_name);         //查找图片是否存在
void handle_sigint(int sig);        //信号处理函数
​
int client_socket = -1;
​
// 定义一个结构体来存储线程所需的数据
typedef struct {
    int client_socket;
    int opcode;
    char file_name[FILE_SIZE];
} thread_data_t;
​
// 线程处理函数
void *handle_operation(void *arg) {
    // 将传入的 void* 类型参数转换为 thread_data_t* 类型
    // 这样我们就可以访问传递给线程的数据,包括操作码、文件名和客户端套接字
    thread_data_t *data = (thread_data_t *)arg;
​
    // 根据操作码执行相应的文件操作
    switch (data->opcode) {
        case 1:
            // 如果操作码为 1,则执行上传操作
            printf(BRIGHT_GREEN_TEXT"上传文件: [%s]\n\n"RESET_COLOR, data->file_name);
            // 调用上传函数,将文件上传到服务器
            upload_image(data->client_socket, data->file_name);
            break;
        case 2:
            // 如果操作码为 2,则执行下载操作
            printf(BRIGHT_GREEN_TEXT"下载文件: [%s]\n\n"RESET_COLOR, data->file_name);
            // 调用下载函数,从服务器下载文件
            download_image(data->client_socket, data->file_name);
            break;
        case 3:
            // 如果操作码为 3,则执行查询操作
            printf(BRIGHT_GREEN_TEXT"查询文件: [%s]\n\n"RESET_COLOR, data->file_name);
            // 调用查询函数,查询服务器上是否存在指定文件
            query_image(data->client_socket, data->file_name);
            break;
        default:
            // 如果操作码不在预期范围内,打印错误信息
            fprintf(stderr, BRIGHT_RED_TEXT"无效的操作码\n"RESET_COLOR);
            break;
    }
​
    // 关闭客户端套接字,以释放与服务器的连接资源
    close(data->client_socket);
​
    // 释放线程数据结构所占用的内存
    // 该内存是在主线程中为每个线程动态分配的
    free(data);
​
    // 线程执行完毕后返回 NULL
    return NULL;
}
​
int main(void) {
​
    // 注册信号处理函数,用于处理 SIGINT (Ctrl+C)
    struct sigaction sa;
    sa.sa_handler = handle_sigint;
    sa.sa_flags = 0;
    sigemptyset(&sa.sa_mask);
    if (sigaction(SIGINT, &sa, NULL) == -1)
    {
        perror("sigaction");
        exit(EXIT_FAILURE);
    }
​
    struct sockaddr_in server_addr;
    // 清零 server_addr 结构体
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;  // 设置地址族为 IPv4
    server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);  // 设置服务器 IP 地址
    server_addr.sin_port = htons(PORT);  // 设置端口号,使用网络字节序
​
    while (1) {
        // 创建套接字
        client_socket = socket(PF_INET, SOCK_STREAM, 0);
        if (client_socket == -1) {
            perror("socket");  // 打印错误信息
            exit(EXIT_FAILURE);  // 退出程序
        }
​
        // 尝试连接到服务器
        if (connect(client_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
            perror("connect");  // 打印错误信息
            close(client_socket);  // 关闭套接字
            exit(EXIT_FAILURE);  // 退出程序
        }
        printf(BRIGHT_BLUE_TEXT"已连接到服务器\n"RESET_COLOR);
​
        char file_name[FILE_SIZE];
        int opcode;
​
        // 提示用户输入文件名
        printf(BRIGHT_BLUE_TEXT"请输入文件名(最大为 %d 字符)\n"RESET_COLOR, FILE_SIZE);
        if (fgets(file_name, FILE_SIZE, stdin) == NULL) {
            fprintf(stderr, BRIGHT_RED_TEXT"读取文件名失败\n"RESET_COLOR);
            close(client_socket);
            continue;
        }
​
        // 去除文件名末尾的换行符
        size_t len = strlen(file_name);
        if (len > 0 && file_name[len - 1] == '\n') {
            file_name[len - 1] = '\0';
        }
​
        // 提示用户输入操作码
        printf(BRIGHT_BLUE_TEXT"请输入操作码 (1: 上传, 2: 下载, 3: 查询, 0: 退出): "RESET_COLOR);
        if (scanf("%d", &opcode) != 1) {
            fprintf(stderr, BRIGHT_RED_TEXT"无效的操作码\n"RESET_COLOR);
            while (getchar() != '\n');  // 清空输入缓冲区
            close(client_socket);
            continue;
        }
​
        while (getchar() != '\n');  // 清空输入缓冲区
​
        if (opcode == 0) {
            printf(BRIGHT_GREEN_TEXT"即将退出程序...\n"RESET_COLOR);
            close(client_socket);  // 关闭套接字
            break;
        }
​
        if (opcode < 1 || opcode > 3) {
            printf(BRIGHT_RED_TEXT"无效的操作码\n"RESET_COLOR);
            close(client_socket);  // 关闭套接字
            continue;
        }
​
        // 动态分配线程数据结构的内存,并初始化
        thread_data_t *data = (thread_data_t *)malloc(sizeof(thread_data_t));
        if (data == NULL) {
            perror("malloc");
            close(client_socket);
            continue;
        }
        data->client_socket = client_socket;
        data->opcode = opcode;
        strncpy(data->file_name, file_name, FILE_SIZE);
​
        // 创建线程并处理操作
        pthread_t thread_id;
        if (pthread_create(&thread_id, NULL, handle_operation, (void *)data) != 0) {
            perror("pthread_create");
            free(data);
            close(client_socket);
            continue;
        }
​
        // 分离线程,以便线程资源在完成时自动释放
        pthread_detach(thread_id);
        sleep(0.5);
    }
    return 0;
}
​
// 上传图片函数
void upload_image(int client_socket, const char *file_path)
{
    char buffer[BUFFER_SIZE];
    char file_name[FILE_SIZE];
    unsigned int file_size;
​
    int file_fd = open(file_path, O_RDONLY);
    if (file_fd < 0)
    {
        perror("open");
        return;
    }
​
    // 获取文件名
    snprintf(file_name, sizeof(file_name), "%s", strrchr(file_path, '/') ? strrchr(file_path, '/') + 1 : file_path);
​
    buffer[0] = 0x01;
    send(client_socket, buffer, 1, 0);
​
    buffer[0] = strlen(file_name);
    send(client_socket, buffer, 1, 0);
    send(client_socket, file_name, strlen(file_name), 0);
​
    file_size = lseek(file_fd, 0, SEEK_END);
    lseek(file_fd, 0, SEEK_SET);
    send(client_socket, &file_size, sizeof(file_size), 0);
​
    int read_len;
    while ((read_len = read(file_fd, buffer, BUFFER_SIZE)) > 0)
    {
        send(client_socket, buffer, read_len, 0);
    }
​
    close(file_fd);
    printf(BRIGHT_RED_TEXT"成功上传【%s】文件\n"RESET_COLOR, file_name);
}
​
// 下载图片函数
void download_image(int client_socket, const char *file_name)
{
    char buffer[BUFFER_SIZE];
    unsigned int file_size;
​
    buffer[0] = 0x02;
    send(client_socket, buffer, 1, 0);
​
    buffer[0] = strlen(file_name);
    send(client_socket, buffer, 1, 0);
    send(client_socket, file_name, strlen(file_name) + 1, 0);
​
    recv(client_socket, &file_size, sizeof(file_size), 0);
    if (file_size > 0)
    {
        int file_fd = open(file_name, O_WRONLY | O_CREAT | O_TRUNC, 0666);
        if (file_fd < 0)
        {
            perror("open");
            return;
        }
​
        int received = 0;
        while (received < file_size)
        {
            int len = recv(client_socket, buffer, BUFFER_SIZE, 0);
            if (len < 0)
            {
                perror("recv");
                close(file_fd);
                return;
            }
            else if (len == 0)
            {
                fprintf(stderr, "服务器意外关闭连接\n");
                close(file_fd);
                return;
            }
​
            if (write(file_fd, buffer, len) != len)
            {
                perror("write");
                close(file_fd);
                return;
            }
​
            received += len;
        }
        close(file_fd);
        printf(BRIGHT_RED_TEXT"下载文件【%s】成功\n"RESET_COLOR, file_name);
    }
    else
    {
        printf(BRIGHT_RED_TEXT"未找到【%s】文件\n"RESET_COLOR, file_name);
    }
}
​
// 查询图片是否存在函数
void query_image(int client_socket, const char *file_name)
{
    char buffer[BUFFER_SIZE];
    int exists;
​
    buffer[0] = 0x03;
    send(client_socket, buffer, 1, 0);
​
    buffer[0] = strlen(file_name);
    send(client_socket, buffer, 1, 0);
    send(client_socket, file_name, strlen(file_name), 0);
​
    recv(client_socket, &exists, sizeof(exists), 0);
​
    if (exists)
    {
        printf(BRIGHT_RED_TEXT"文件【%s】存在\n"RESET_COLOR, file_name);
    }
    else
    {
        printf(BRIGHT_RED_TEXT"文件【%s】不存在\n"RESET_COLOR, file_name);
    }
}
​
// 信号处理函数,处理 SIGINT 信号
void handle_sigint(int sig)
{
    printf("\n接收到中断信号(SIGINT),程序即将退出...\n");
    // 在这里可以添加程序退出前的清理代码,例如关闭打开的文件、套接字等
    if (client_socket != -1) {
        close(client_socket);
    }
    exit(EXIT_SUCCESS);  // 退出程序
}

  • 17
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值