如何使用 connect() 获取内核分配给 UDP 套接字的本地地址?

How to get local address the kernel assign to a UDP socket with connect()?

正在阅读 https://www.amazon.com/Unix-Network-Programming-Sockets-Networking/dp/0131411551

8.14 使用 UDP 确定传出接口

A connected UDP socket can also be used to determine the outgoing interface that will be used to a particular destination. This is because of a side effect of the connect function when applied to a UDP socket: The kernel chooses the local IP address (assuming the process has not already called bind to explicitly assign this). This local IP address is chosen by searching the routing table for the destination IP address, and then using the primary IP address for the resulting interface.

如果我尝试 运行 示例 (udpcli01.c):

#define BSIZE 256
#define SERV_PORT 9877

typedef struct sockaddr SA;

//argv[1] = ip address
int main(int argc, char **argv)
{
    int sockfd;
    socklen_t servlen, clilen;
    struct sockaddr_in cliaddr, servaddr;
    char ip[BSIZE];

    if (argc < 2)
    {
        die("usage: %s <ip>\n", argv[0]);
    }

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    servlen = sizeof(servaddr);
    memset(&servaddr, 0, servlen);
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

    connect(sockfd, (SA *)&servaddr, servlen);

    clilen = sizeof(clilen);
    if (getsockname(sockfd, (SA *)&cliaddr, &clilen) < 0)
    {
        perror("getsockname");
    }

    inet_ntop(AF_INET, &cliaddr, ip, BSIZE);
    printf("address %s:%hd\n", ip, cliaddr.sin_port);
}

现在如果我运行一个终端中的服务器(地址INADDR_ANY和端口9877),然后运行上面的客户端:

terminal 1:
$ ./udpserv01

terminal 2:
$ #I will try localhost first
$ ./udpcli01 127.0.0.1
address 2.0.178.211:-11342
$ #did not work, now my host ip 10.0.0.11
$ ./udpcli01 10.0.0.11
address 2.0.193.86:22209

即使客户端在打印其地址和端口之前连接到正在侦听的服务器,我也总是会收到一些垃圾信息。如果我试图打印服务器地址和端口,那么它会工作(也就是说,它会分别打印 127.0.0.1:987710.0.0.11:9877 -> 我已经尝试过了)。所以我知道 inet_ntop 工作正常并且还获取了端口号。那么客户端的问题在哪里?内核真的按照书上说的在connect()上分配地址和端口吗?如果是这样,那为什么我的示例打印随机垃圾?

uname -a:
Linux Shepherd 5.8.0-36-generic #40-Ubuntu SMP Tue Jan 5 21:54:35 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

您正在将 struct sockaddr_in 的地址传递给 inet_ntop 函数。对于 AF_INET 套接字,它需要一个指向 struct in_addr.

的指针

所以不是这个:

inet_ntop(AF_INET, &cliaddr, ip, BSIZE);

你想要这个:

inet_ntop(AF_INET, &cliaddr.sin_addr, ip, BSIZE);

此外,请务必在打印 sin_port 成员之前调用 ntohs 以获得正确的值,并使用 %d 而不是 %hd

printf("address %s:%d\n", ip, ntohs(cliaddr.sin_port));

并且clilen未正确初始化:

clilen = sizeof(clilen);

应该是:

clilen = sizeof(cliaddr);