You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 03:47:41