我在我的程序中使用recvfrom从我在src_addr中指定的服务器获取DGRAM数据。但是,我不确定为什么我需要初始化并传入addrlen。
我阅读了手册页,但我并不真正了解它的内容。
如果src_addr不为NULL,并且底层协议提供源地址,则填写此源地址。 src_addr为NULL,没有填写任何内容;在这种情况下,addrlen不是 使用过,也应该是NULL。参数addrlen是一个value-result参数,调用者应该在之前初始化 调用与src_addr关联的缓冲区的大小,和 返回时修改以指示源地址的实际大小。如果提供的缓冲区太小,则截断返回的地址; 在这种情况下,addrlen将返回一个大于提供给调用的值。
我猜这与src_addr是ipv4或ipv6有关。这是对的吗?
谢谢!
答案 0 :(得分:6)
也许你身边有一个错误的解释。谈论:
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
src_addr
用于将用于您希望收听的地址,而是用于提供实际存储位置的源地址 out 。
因此,如果您将src_addr
设置为NULL,因为您根本不对该地址感兴趣,那么您无需关心addrlen
,因为它无论如何都不会被使用。
另一方面,如果您希望了解源地址,则不仅需要提供存储位置,还要告知您提供的存储位置有多大。
这就是为什么你应该将*addr_len
初始化为你分配的缓冲区大小。
在您调用之后,addrlen
指向的值将通知您分配的用于存储源地址的空间(如果有的话)实际填充了数据。
结构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 */
};
应该清楚两者没有太多共同之处。
插座设计为能够适应两种情况。
答案 1 :(得分:3)
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所示,该字段将在方法返回时使用实际大小进行更新。
更多信息