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
相关产品推荐
相关产品推荐

