为什么在套接字编程中需要 Value-Result 参数? (需要澄清)
Why do we need Value-Result arguments in socket programming? (Clarification required)
当被问到"What is a Value-Result argument and WHY is it needed in Socket Programming?"
这样的问题时,我有点困惑
尽管阅读了无数页面和此处的其他问题,但我仍在努力完全理解值结果参数到底是什么。
我的理解是,在值结果参数中,内核能够更改传递的参数(因为我们给它一个 reference/pointer,而不仅仅是它的值)和 return 它到调用它的 process/function。它既是调用函数时的 "value"(告诉内核结构的大小,例如它不会写太多),又是函数 returns 时的结果(我们实际写了多少在结构中)。
我很难回答的是,为什么这在套接字编程中如此重要?特别是,当我们处理 sockaddr
结构并将它们的引用和大小传递过来时,即 accept()
我知道这个问题听起来有点傻,但如果能对此做出任何澄清就更好了,在此先感谢您。
您对什么是值-结果参数的理解是正确的。您将输入值分配给变量并通过引用传递它,以便函数可以使用输出值修改变量。这样就不必为输出传递单独的参数,或更改函数的 return 值的语义。
原因需要sockaddr
参数是因为不同的传输实现不同的sockaddr_...
结构,这些结构具有不同的大小和布局(sockaddr_in
用于 IPv4,sockaddr_in6
用于 IPv6,sockaddr_un
用于 UNIX 域套接字等)。大多数平台还提供 sockaddr_storage
结构的实现,它足够容纳所有其他 sockaddr_...
结构,在编写多传输代码时很有用。
因此,在 accept()
的情况下,您必须传入将接收客户端 sockaddr_...
的缓冲区的完整大小。当新客户端到达时,accept()
将在填充缓冲区之前验证缓冲区是否足够大以接收客户端的实际 sockaddr_...
数据,并且 return 缓冲区实际有多少填写.
例如,如果您知道套接字仅支持 IPv4(创建为 AF_INET
系列套接字),则可以使用 sockaddr_in
作为缓冲区并使用 sizeof(sockaddr_in)
作为缓冲区大小,或者您可以使用 sockaddr_storage
作为缓冲区和 sizeof(sockaddr_storage)
作为缓冲区大小。在任何一种情况下,accept()
都会用 sockaddr_in
填充缓冲区,return 缓冲区大小为 sizeof(sockaddr_in)
。与仅 IPv6 套接字相同(创建为 AF_INET6
系列套接字),只是用 sockaddr_in6
代替。
现在,假设您有一个同时支持 IPv4 和 IPv6 的双栈套接字(AF_INET6
系列套接字,禁用了 IPV6_V6ONLY
选项)。您可以使用 sockaddr_storage
作为缓冲区并使用 sizeof(sockaddr_storage)
作为缓冲区大小,并且 accept()
将使用 sockaddr_in
或 sockaddr_in6
和 return 适当的缓冲区大小,具体取决于是否接受了 IPv4 或 IPv6 客户端。然后,您可以读取 sockaddr_storage
的 ss_family
字段并将数据类型转换为 sockaddr_in
for AF_INET
或 sockaddr_in6
for AF_INET6
。
当被问到"What is a Value-Result argument and WHY is it needed in Socket Programming?"
这样的问题时,我有点困惑尽管阅读了无数页面和此处的其他问题,但我仍在努力完全理解值结果参数到底是什么。
我的理解是,在值结果参数中,内核能够更改传递的参数(因为我们给它一个 reference/pointer,而不仅仅是它的值)和 return 它到调用它的 process/function。它既是调用函数时的 "value"(告诉内核结构的大小,例如它不会写太多),又是函数 returns 时的结果(我们实际写了多少在结构中)。
我很难回答的是,为什么这在套接字编程中如此重要?特别是,当我们处理 sockaddr
结构并将它们的引用和大小传递过来时,即 accept()
我知道这个问题听起来有点傻,但如果能对此做出任何澄清就更好了,在此先感谢您。
您对什么是值-结果参数的理解是正确的。您将输入值分配给变量并通过引用传递它,以便函数可以使用输出值修改变量。这样就不必为输出传递单独的参数,或更改函数的 return 值的语义。
原因需要sockaddr
参数是因为不同的传输实现不同的sockaddr_...
结构,这些结构具有不同的大小和布局(sockaddr_in
用于 IPv4,sockaddr_in6
用于 IPv6,sockaddr_un
用于 UNIX 域套接字等)。大多数平台还提供 sockaddr_storage
结构的实现,它足够容纳所有其他 sockaddr_...
结构,在编写多传输代码时很有用。
因此,在 accept()
的情况下,您必须传入将接收客户端 sockaddr_...
的缓冲区的完整大小。当新客户端到达时,accept()
将在填充缓冲区之前验证缓冲区是否足够大以接收客户端的实际 sockaddr_...
数据,并且 return 缓冲区实际有多少填写.
例如,如果您知道套接字仅支持 IPv4(创建为 AF_INET
系列套接字),则可以使用 sockaddr_in
作为缓冲区并使用 sizeof(sockaddr_in)
作为缓冲区大小,或者您可以使用 sockaddr_storage
作为缓冲区和 sizeof(sockaddr_storage)
作为缓冲区大小。在任何一种情况下,accept()
都会用 sockaddr_in
填充缓冲区,return 缓冲区大小为 sizeof(sockaddr_in)
。与仅 IPv6 套接字相同(创建为 AF_INET6
系列套接字),只是用 sockaddr_in6
代替。
现在,假设您有一个同时支持 IPv4 和 IPv6 的双栈套接字(AF_INET6
系列套接字,禁用了 IPV6_V6ONLY
选项)。您可以使用 sockaddr_storage
作为缓冲区并使用 sizeof(sockaddr_storage)
作为缓冲区大小,并且 accept()
将使用 sockaddr_in
或 sockaddr_in6
和 return 适当的缓冲区大小,具体取决于是否接受了 IPv4 或 IPv6 客户端。然后,您可以读取 sockaddr_storage
的 ss_family
字段并将数据类型转换为 sockaddr_in
for AF_INET
或 sockaddr_in6
for AF_INET6
。