【问题标题】:Whats the addrlen field in recvfrom() used for?recvfrom() 中的 addrlen 字段是做什么用的?
【发布时间】:2013-03-01 22:43:23
【问题描述】:

我在我的程序中使用 recvfrom 从我在 src_addr 中指定的服务器获取 DGRAM 数据。但是,我不确定为什么需要初始化并传入 addrlen。

我阅读了手册页,但我并没有真正理解它的含义。

如果 src_addr 不为 NULL,且底层协议提供源地址,则填写此源地址。当 src_addr 为NULL,什么都不填;在这种情况下, addrlen 不是 已使用,也应为 NULL。参数 addrlen 是一个值结果参数,调用者应该在 调用与 src_addr 关联的缓冲区的大小,以及 返回时修改以指示源地址的实际大小。如果提供的缓冲区太小,返回的地址会被截断; 在这种情况下,addrlen 将返回一个大于提供给调用的值。

我猜这与 src_addr 是 ipv4 或 ipv6 有关。这是正确的吗?

谢谢!

【问题讨论】:

  • 是的,这确实与 IPv4 与 IPv6 以及每种所需的不同存储量有关。
  • 当接收到您的 DGRAM 时,如果数据包中的协议提供了源地址,则将复制到您使用 src_addr 参数提供的缓冲区中。您将长度值设置为作为 src_addr 传递的缓冲区可以接受的最大大小。如果一切顺利,结果将是填充到缓冲区中的源地址,以及通过地址传递的 addr_len 变量中更新的“已使用”字节数。实际上,您是在说“这是一个缓冲区及其大小。如果可以,请用源地址填充它。如果可以,它会这样做,并更新大小以反映实际计数。
  • 在 IP 接管世界之前,存在许多不同类型的网络协议。诸如 DECnet、IPX、AppleTalk、NetBIOS、Banyan 之类的东西。每个人对地址的外观都有自己的想法。是的,今天 ip4 与 ip6。

标签: c sockets udp recv


【解决方案1】:

也许您的理解有误。谈:

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, 
                 struct sockaddr *src_addr, socklen_t *addrlen);

src_addr不是用来交in你想收听的地址,而是你提供的一个存储位置,用来获取实际的源地址分发

因此,如果您将src_addr 设置为NULL,因为您对地址根本不感兴趣,您不必关心addrlen,因为它无论如何都不会被使用。

另一方面,如果您想知道源地址,您不仅要提供存储位置,还要说明您提供的存储位置有多大。 这就是为什么您应该将*addr_len 初始化为您分配的缓冲区大小。

在您调用后,addrlen 指向的值将告知您分配用于存储源地址的空间中有多少(如果有)实际填充了数据。

关于尺寸

struct sockaddr 和来回传递大小的整个麻烦与这样一个事实有关,即使它们在网络套接字中使用最频繁,但其意图是更笼统的概念。

以 unix 域套接字为例,因为它们是通过文件系统实现的,它们需要一个与基于 IP 的网络已知的完全不同的寻址方案。这里使用的 sockaddr 的类型是:

struct sockaddr_un {
  sa_family_t sun_family;               /* AF_UNIX */
  sun_path[UNIX_PATH_MAX];  /* pathname */
};

将此与基于 IP 的网络中使用的结构进行比较:

struct sockaddr_in {
  sa_family_t    sin_family; /* address family: AF_INET */
  in_port_t      sin_port;   /* port in network byte order */
  struct in_addr sin_addr;   /* internet address */
};

应该清楚两者没有太多共同点。

插座的设计能够适应这两种情况。

【讨论】:

  • 就是这样。出于某种原因,我认为您应该在传入 src_addr 之前对其进行填充。谢谢!
  • 如果我们在sendto 中使用NULL 作为destaddr 会发生什么
【解决方案2】:
ssize_t recvfrom(int socket, void *buffer, size_t length, int flags,
         struct sockaddr *address, socklen_t *address_len);`

address_len 参数指定了address 结构的长度,即从address 指示的起始地址开始使用的字节数(内存位置的起始地址+ 从起始地址开始保存的字节数)值)

结构在/usr/include/bits/socket.h中定义

/* Structure describing a generic socket address.  */
struct sockaddr
{
    __SOCKADDR_COMMON (sa_);    /* Common data: address family and length.      */  
    char sa_data[14];       /* Address data.  */
};

因此sa_data 字段保存地址数据(数据的起始地址),其长度由address_len 参数指示。

...每当一个函数说它需要一个 struct sockaddr* 你就可以转换你的 struct sockaddr_in*、struct sockaddr_in6* 或 struct sockadd_storage* 轻松安全地转换为那种类型。

因此,如手册页和问题评论中的@WhozCraig 所示,此字段会在方法返回时更新为实际大小。

更多信息

【讨论】:

  • 这只是部分正确,因为 struct sockaddr 只是某种主模板 - 必须传递大小没有任何意义,因为希望该函数可以足够巧妙地实现以获得sizeof (struct sockaddr) 自己。事实上,有一大堆不同的 struct sockaddr 结构。哪一个被有效使用取决于所使用的底层协议。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-29
  • 1970-01-01
  • 2016-10-31
  • 2018-01-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多