对于 UDP 套接字,在 close() 返回的那一刻,IP 地址是无限的吗?

Posted

技术标签:

【中文标题】对于 UDP 套接字,在 close() 返回的那一刻,IP 地址是无限的吗?【英文标题】:For UDP sockets, is the ip address unbounded the moment close() returns? 【发布时间】:2015-03-23 17:10:06 【问题描述】:

在阅读 this great answer 时,我了解到 TCP 套接字可以有一个名为 TIME_WAIT 的状态。由于该状态,即使close(int fd) 函数返回了0,TCP 套接字也可能没有释放它所绑定的地址。

鉴于 UDP 是无连接的,并且它没有像 TCP 那样传递数据的可靠性要求,是否可以安全地假设一旦 close(int fd) 返回 0,地址是未绑定的?

【问题讨论】:

【参考方案1】:

是的,根据源代码https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/udp.c?id=refs/tags/v3.19-rc6,udp_destroy_sock()(~2028 行)会刷新所有待处理的帧,并释放释放地址的套接字。

你可以用一个简单的例子来证明这一点。你需要netcat,一个客户端和一个服务器。一台服务器,运行以下代码:

#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
int main() 
    struct sockaddr_in me;
    int sock;

    if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) 
        perror("socket error:");
        return -1;
        
    memset(&me, 0, sizeof(me));
    me.sin_family = AF_INET;
    me.sin_port = htons(60000);
    me.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(sock, (struct sockaddr*)&me, sizeof(me)) == -1) 
        perror("bind error: ");
        return -1;
        

    printf("On client execute:\n");
    printf("      nc -u servers ip address 60000\n\n");
    printf("type: hello world<enter>\n");
    printf("Hit enter when you've done this...");
    getchar();

    printf("\nNow check the input queue on this server\n");
    printf("    netstat -an|grep 60000\n");
    printf("Notice that we have buffered data we have not read\n");
    printf("(probably about 360 bytes)\n");
    printf("Hit enter to continue...");
    getchar();

    printf("\nI'm going to end. After I do, run netstat -an again\n");
    printf("and you'll notice port 60000 is gone.\n\n");
    printf("Re-run this program on server again and see you\n");
    printf("have no problem re-acquiring the UDP port.\n");
    return 0;
    

【讨论】:

您能否添加一个代码示例(可以运行)如何显示 TCP 不会在套接字关闭时释放地址而 UDP 会释放地址的场景? 我认为没有必要展示 TCP 套接字的 TIME_WAIT 状态,因为它是规范要求的状态,几乎到处都有它的文档,并且 Linux 内核包含影响其持续时间的可调参数。一个socket在正常终止时总是进入TIME_WAIT状态。 我认为 OP 试图确认收单方进程结束时 UDP 端口已完全释放。我同意 linux 内核参考是权威的答案,但他正在寻找更多的信心——这是一种测试方法。【参考方案2】:

TL;DR: UDP 套接字将立即关闭和解除绑定(除非它是与其他侦听器的广播/多播地址)。

TIME WAIT 由原始RFC 793 指定仅适用于 TCP。它需要 2 * Maximum Segment Lifetime 才能在 TCP 中关闭套接字之前过期。

Stevens 著名的Unix Network Programming 也为好奇的人更详细地解释了 TCP 的 TIME WAIT。

UDP 没有连接。TIME WAIT 不是该协议的一部分。

Linux 源代码虽然与基于 Linux 的系统上的潜在专有行为相关,但并不是此类问题的权威。

最初的 1981 DARPA TCP RFC 793 是权威的,Berkeley Sockets api 所基于的 POSIX sockets 定义了套接字 API 的预期行为。

同样相关的是BSD network stack,它为早期互联网中的 TCP/IP 提供支持,直至今天的 Windows、ios 和 OSX,并提供 TCP/IP RFC 的权威参考实现。 BSD 堆栈仍被视为the benchmark for a future Linux stack several years from now.(Facebook 在 2014 年年中发布了一个专家职位,以帮助使 Linux 达到或超过 FreeBSD 堆栈的可靠性和性能)。

【讨论】:

以上是关于对于 UDP 套接字,在 close() 返回的那一刻,IP 地址是无限的吗?的主要内容,如果未能解决你的问题,请参考以下文章

Socket 编程中 close() 和 shutdown() 有啥区别?

udp/tcp流程

对于 UDP 套接字,发送函数不会失败,但写入的数据仍然比请求的少吗?

UDP的connect

close函数

UDP套接字