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

UDP/UDS数据报服务器while(1)循环无法退出导致传输耗时无法统计

UDP/UDS数据报服务器循环无法退出的解决方法

要测试TCP、管道、mmap等各类套接字传输100MB文件的耗时,客户端在发送数据前后计时,服务器在接收数据前和写入文件完成后计时,但UDP/UDS数据报服务器的while(1)循环始终无法退出,导致无法统计传输耗时。以下是UDP IPv6服务器的循环部分代码:

clock_t start = 0, end = 0;
FILE *fp;
char *filename = "new_data.txt";
fp = fopen(filename, "w");
/* now wait until we get a datagram */
printf("waiting for a datagram...\n");
clilen = sizeof(client_addr);
start = clock();

while (1) {
    ssize_t bytes = recvfrom(sock, buffer, 1024, 0,
                             (struct sockaddr *) &client_addr,
                             &clilen);
    if (bytes < 0) {
        perror("recvfrom failed");
        exit(4);
    }
    fprintf(fp, "%s", buffer);
    bzero(buffer, 1024);

}
close(sock);
//fclose(fp);
end = clock();
double cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
printf("Receive in %f seconds\n", cpu_time_used);

核心问题

UDP是无连接的报文协议,服务器无法像TCP那样通过连接关闭判断传输结束,因此需要额外的机制来触发循环退出。

可行解决方案

1. 约定结束标记

客户端在发送完所有文件数据后,发送一个预定义的结束标记(比如字符串"EOF"、空数据包),服务器收到该标记后退出循环。

修改后的服务器循环示例:

while (1) {
    ssize_t bytes = recvfrom(sock, buffer, 1024, 0,
                             (struct sockaddr *) &client_addr,
                             &clilen);
    if (bytes < 0) {
        perror("recvfrom failed");
        exit(4);
    }
    // 判断是否收到结束标记
    if (bytes == 3 && strncmp(buffer, "EOF", 3) == 0) {
        break;
    }
    // 用接收的字节数指定输出长度,避免buffer无'\0'导致的乱码/越界
    fprintf(fp, "%.*s", (int)bytes, buffer);
    bzero(buffer, 1024);
}
// 必须关闭文件,确保数据写入磁盘
fclose(fp);

2. 累计接收字节数

由于已知传输文件大小为100MB,服务器可以累计接收的总字节数,当达到100MB时退出循环。

修改后的服务器循环示例:

#define TOTAL_SIZE 100 * 1024 * 1024 // 100MB
size_t total_received = 0;

while (1) {
    ssize_t bytes = recvfrom(sock, buffer, 1024, 0,
                             (struct sockaddr *) &client_addr,
                             &clilen);
    if (bytes < 0) {
        perror("recvfrom failed");
        exit(4);
    }
    fprintf(fp, "%.*s", (int)bytes, buffer);
    total_received += bytes;
    // 达到总大小则退出
    if (total_received >= TOTAL_SIZE) {
        break;
    }
    bzero(buffer, 1024);
}
fclose(fp);

3. 设置套接字超时

通过setsockopt给套接字设置接收超时,当超过一定时间没有收到数据时,判定传输结束并退出循环。这种方法适合作为辅助机制,避免因异常情况导致服务器无限阻塞。

示例:

struct timeval timeout;
timeout.tv_sec = 5; // 超时5秒
timeout.tv_usec = 0;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));

while (1) {
    ssize_t bytes = recvfrom(sock, buffer, 1024, 0,
                             (struct sockaddr *) &client_addr,
                             &clilen);
    if (bytes < 0) {
        // 超时错误,判定传输结束
        if (errno == EAGAIN || errno == EWOULDBLOCK) {
            break;
        }
        perror("recvfrom failed");
        exit(4);
    }
    fprintf(fp, "%.*s", (int)bytes, buffer);
    bzero(buffer, 1024);
}
fclose(fp);

额外注意事项

  • 原代码中fprintf(fp, "%s", buffer)存在风险:如果buffer中没有字符串结束符'\0',会导致输出乱码或内存越界,建议改用%.*s格式,通过接收的字节数限制输出长度。
  • 必须取消注释fclose(fp),否则文件数据可能停留在缓冲区未写入磁盘,导致统计的写入完成时间不准确。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 02:01:16