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

avatar
作者
猴君
阅读量:0

目录

一、较与上版本的优化

二、代码编写思路

服务器

客户端

三、代码优缺点

服务器

优点:

缺点:

客户端

优点

缺点

四、源代码

服务器

客户端


更多文章和源码在我的个人博客:首页 (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);  // 退出程序 }

    广告一刻

    为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!