【问题标题】:How to get local address the kernel assign to a UDP socket with connect()?如何使用connect()获取内核分配给UDP套接字的本地地址?
【发布时间】:2021-04-22 20:23:02
【问题描述】:

阅读https://www.amazon.com/Unix-Network-Programming-Sockets-Networking/dp/0131411551,章节

8.14 使用 UDP 确定传出接口

已连接的 UDP 套接字也可用于确定将要发送的传出接口 用于特定目的地。这是因为连接的副作用 应用于 UDP 套接字时的功能:内核选择本地 IP 地址 (假设该进程尚未调用 bind 来显式分配 this)。这个当地 通过在路由表中搜索目标 IP 地址来选择 IP 地址,并且 然后使用生成的接口的主 IP 地址。

如果我尝试运行示例 (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

【问题讨论】:

  • POSIX documentation for connect() 确实声明:“如果套接字尚未绑定到本地地址,connect() 应将其绑定到一个地址,除非套接字的地址族是 AF_UNIX,是未使用的本地地址。”所以你似乎发现了一些错误。您使用的是什么操作系统? 所有分配给您系统的 IP 地址是什么?并且端口值在网络字节顺序中为unsigned short
  • @AndrewHenle 查看编辑。 1)使用linux,2)我不知道如何找出分配给我的系统的地址。 3) 是的,我的错误,我忘记使用ntohs() 来转换端口。但是地址反正是垃圾

标签: c sockets udp udpclient


【解决方案1】:

您将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);

【讨论】:

  • 现在,在您建议的更改之后,./udpcli01 127.0.0.1./udpcli01 10.0.0.11 都会给出输出 address 0.0.0.0:-23170。地址始终是0.0.0.0,端口仍然是一些垃圾(即使是ntohs()),我认为这不是本书所显示的。那么真正的解决方案是什么?
  • @milanHrabos 对于端口,使用%d 而不是%hd,因为后者需要signed short。地址是正确的,因为您没有绑定到特定的 IP。
  • 我不装订,就像在书上一样。他们说 connect 函数的副作用是通过内核分配(而不是先前绑定)地址。所以我不应该有 0.0.0.0 因为我connect() (内核应该给我一个)。所以你的评论与本书相矛盾。
  • @milanHrabos 啊,好像还有一个问题。 clilen 设置不正确。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-20
  • 2015-05-23
  • 2015-11-26
相关资源
最近更新 更多