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

C语言Socket编程:获取popen执行ls -l的输出字节数

问题解决:UDP FTP服务端ls功能的输出发送逻辑优化

核心问题

管道属于流式文件,不支持fseek和ftell这类定位操作(无法通过定位到末尾获取总输出长度),原代码中通过popen执行ls -l后尝试用这两个函数获取输出大小的逻辑必然失败。

可行修改方案

方案1:先将ls输出缓存到内存,再获取大小发送

先把ls -l的全部输出读取到内存缓冲区中,直接得到总字节数后,再按原逻辑分块或一次性发送。

修改后的核心代码段:

FILE *fp = popen("ls -l", "r");
if (!fp) {
    error("ERROR popen ls -l");
}

// 动态缓冲区存储ls输出,初始分配SO_SNDBUF大小,不足时自动扩容
char *ls_buf = malloc(SO_SNDBUF);
if (!ls_buf) {
    error("ERROR malloc ls_buf");
}
long total_size = 0;
size_t read_bytes;

// 循环读取所有输出到缓冲区
while ((read_bytes = fread(ls_buf + total_size, 1, SO_SNDBUF, fp)) > 0) {
    total_size += read_bytes;
    // 扩容缓冲区,每次追加SO_SNDBUF大小
    char *tmp = realloc(ls_buf, total_size + SO_SNDBUF);
    if (!tmp) {
        free(ls_buf);
        pclose(fp);
        error("ERROR realloc ls_buf");
    }
    ls_buf = tmp;
}

// 发送总大小
char tmp[SO_SNDBUF];
snprintf(tmp, sizeof(tmp), "%ld", total_size);
n = sendto(sockfd, tmp, strlen(tmp), 0, (struct sockaddr *) &clientaddr, clientlen);
if (n < 0) {
    free(ls_buf);
    pclose(fp);
    error("ERROR in sendto size");
}

// 发送ls内容,复用原逻辑
if (total_size < SO_SNDBUF) {
    n = sendto(sockfd, ls_buf, total_size, 0, (struct sockaddr *) &clientaddr, clientlen);
    if (n < 0) {
        free(ls_buf);
        pclose(fp);
        error("ERROR in sendto content");
    }
} else {
    long totalSent = 0;
    while (totalSent < total_size) {
        size_t send_len = (total_size - totalSent) < SO_SNDBUF ? (total_size - totalSent) : SO_SNDBUF;
        n = sendto(sockfd, ls_buf + totalSent, send_len, 0, (struct sockaddr *) &clientaddr, clientlen);
        if (n < 0) {
            free(ls_buf);
            pclose(fp);
            error("ERROR in sendto chunk");
        }
        totalSent += n;
    }
}

free(ls_buf);
pclose(fp);

方案2:不提前发送总大小,用结束标记标识传输完成

利用管道的流式特性,直接分块发送输出,最后发送一个客户端约定的结束标记(比如"__LS_END__"),客户端收到该标记后停止接收。此方案无需提前获取总长度,代码更简洁:

核心代码段修改:

FILE *fp = popen("ls -l", "r");
if (!fp) {
    error("ERROR popen ls -l");
}

char buf[SO_SNDBUF];
size_t read_bytes;

// 分块发送ls输出,预留结束标记的空间
while ((read_bytes = fread(buf, 1, SO_SNDBUF - strlen("__LS_END__"), fp)) > 0) {
    n = sendto(sockfd, buf, read_bytes, 0, (struct sockaddr *) &clientaddr, clientlen);
    if (n < 0) {
        pclose(fp);
        error("ERROR in sendto chunk");
    }
}

// 发送结束标记,告知客户端传输完成
strcpy(buf, "__LS_END__");
n = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *) &clientaddr, clientlen);
if (n < 0) {
    pclose(fp);
    error("ERROR in sendto end marker");
}

pclose(fp);

注意:客户端需同步修改接收逻辑,持续接收数据直到收到__LS_END__标记,再拼接所有内容。

方案选择建议

  • 若需严格保证传输字节数的准确性,优先选用方案1;
  • 若希望简化代码、避免内存分配的复杂度,优先选用方案2(需客户端配合修改接收逻辑)。

内容的提问来源于stack exchange,提问作者freyfrey01

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 06:57:36