基于TCP实现服务器连接客户端后主动发送文件的技术求助
嘿,你已经搞定了TCP连接和字符串发送,这可是迈出了一大步!接下来要实现服务器一连接就主动发文件其实没那么复杂,咱们一步步来把这个功能加上。
实现TCP服务器主动向客户端发送文件
核心思路
服务器在和客户端建立连接后,需要完成这几个关键步骤:
- 打开要发送的目标文件,检查是否能正常读取
- 循环读取文件内容,通过socket发送给客户端(注意:
send可能不会一次性发送所有数据,要根据返回值判断是否需要继续发送剩余内容) - 文件发送完成后,关闭文件和socket
- 客户端这边则需要循环接收数据,写入本地文件,直到服务器发送完毕
修改后的服务器代码片段(server.c)
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #define PORT 8080 #define BUFFER_SIZE 1024 // 定义每次发送的缓冲区大小 int main() { int server_fd, new_socket; struct sockaddr_in address; int opt = 1; int addrlen = sizeof(address); char buffer[BUFFER_SIZE] = {0}; FILE *file; ssize_t bytes_read, bytes_sent; // 创建socket文件描述符 if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } // 设置socket选项,允许重用端口和地址 if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) { perror("setsockopt"); exit(EXIT_FAILURE); } address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); // 绑定socket到指定端口 if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } // 监听连接请求 if (listen(server_fd, 3) < 0) { perror("listen"); exit(EXIT_FAILURE); } printf("服务器已启动,等待客户端连接...\n"); // 接受客户端连接 if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) { perror("accept"); exit(EXIT_FAILURE); } printf("客户端已连接,开始发送文件...\n"); // 打开要发送的文件(这里替换成你要发送的文件路径,比如"test.txt") file = fopen("test.txt", "rb"); if (!file) { perror("无法打开文件"); close(new_socket); close(server_fd); exit(EXIT_FAILURE); } // 循环读取文件并发送 while ((bytes_read = fread(buffer, 1, BUFFER_SIZE, file)) > 0) { char *ptr = buffer; // 确保所有数据都发送出去 while (bytes_read > 0) { bytes_sent = send(new_socket, ptr, bytes_read, 0); if (bytes_sent < 0) { perror("发送数据失败"); fclose(file); close(new_socket); close(server_fd); exit(EXIT_FAILURE); } bytes_read -= bytes_sent; ptr += bytes_sent; } } if (ferror(file)) { perror("读取文件出错"); } else { printf("文件发送完成!\n"); } // 清理资源 fclose(file); close(new_socket); close(server_fd); return 0; }
修改后的客户端代码片段(client.c)
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define PORT 8080 #define BUFFER_SIZE 1024 int main() { int sock = 0; struct sockaddr_in serv_addr; char buffer[BUFFER_SIZE] = {0}; FILE *file; ssize_t bytes_received; // 创建socket if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket creation failed"); exit(EXIT_FAILURE); } serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(PORT); // 将IP地址转换为二进制格式(这里替换成你的服务器IP,比如本地测试用127.0.0.1) if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) { perror("无效的IP地址"); exit(EXIT_FAILURE); } // 连接服务器 if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { perror("连接失败"); exit(EXIT_FAILURE); } printf("已连接到服务器,开始接收文件...\n"); // 创建本地文件用于保存接收的数据 file = fopen("received_file.txt", "wb"); if (!file) { perror("无法创建文件"); close(sock); exit(EXIT_FAILURE); } // 循环接收数据并写入文件 while ((bytes_received = recv(sock, buffer, BUFFER_SIZE, 0)) > 0) { if (fwrite(buffer, 1, bytes_received, file) != bytes_received) { perror("写入文件出错"); fclose(file); close(sock); exit(EXIT_FAILURE); } } if (bytes_received < 0) { perror("接收数据失败"); } else { printf("文件接收完成!已保存为received_file.txt\n"); } // 清理资源 fclose(file); close(sock); return 0; }
关键注意点
- 缓冲区大小:
BUFFER_SIZE可以根据你的需求调整,1024字节是比较常用的大小,平衡了效率和内存占用 - 发送/接收完整性:一定要检查
send和recv的返回值,因为网络环境可能导致一次无法发送/接收全部数据,必须循环处理剩余内容 - 文件打开模式:服务器用
rb(二进制读),客户端用wb(二进制写),这样可以处理所有类型的文件(比如图片、视频,不只是文本) - 错误处理:每个系统调用(socket、bind、listen、accept、fopen、send、recv等)都要做错误检查,避免程序崩溃或者出现未知问题
- 资源清理:用完文件和socket后一定要关闭,避免资源泄漏
内容的提问来源于stack exchange,提问作者Deepesh Meena
相关产品推荐
相关产品推荐

