I am a little confused when asked a question such as "What is a Value-Result argument and WHY is it needed in Socket Programming?"
I am struggling to fully understand what exactly a value result argument is, despite reading countless pages and other questions on here.
My understanding is that in a value result argument the kernel is able to make changes to the argument passed(because we give a reference/pointer to it, not just its value) and return it to the the process/function that called it. It is both a "value" when the function is called(tells kernel the size of struct so it doesn't write too much for example) and a result when the function returns(how much we actually wrote in the struct).
What I am struggling to answer, is why this is so important in Socket Programming? In particular, when we are dealing with sockaddr
Structs and passing their reference AND size over, i.e. accept()
I realize this question may sound somewhat silly, but any clarification on this would be great, so thanks in advance.
Value-result argument (Figure 3.8): the size changes from an integer to be a pointer to an integer because the size is both a value when the function is called and a result when the function returns.
Various structures are used in Unix Socket Programming to hold information about the address and port, and other information. Most socket functions require a pointer to a socket address structure as an argument. Structures defined in this chapter are related to Internet Protocol Family.
A ParameterPassing mode, used in AdaLanguage to handle "IN OUT" parameters. In CallByValueResult, the actual parameter supplied by the caller is copied into the callee's formal parameter; the function is run; and the (possibly modified) formal parameter is then copied back to the caller.
The five socket functions that pass a socket address structure from the kernel to the process, accept, recvfrom, recvmsg, getpeername, and getsockname, all set the sin_len member before returning to the process.
You have the correct understanding of what a value-result parameter is. You assign an input value to a variable and pass it by-reference so the function can modify the variable with an output value. This saves having to pass a separate parameter for the output, or change the semantics of the function's return value.
The reason this is needed for sockaddr
parameters is because different transports implement different sockaddr_...
structures, which have different sizes and layouts (sockaddr_in
for IPv4, sockaddr_in6
for IPv6, sockaddr_un
for UNIX domain sockets, etc). Most platforms also provide an implementation of the sockaddr_storage
structure, which is large enough to hold all other sockaddr_...
structs, and is useful when writing multi-transport code.
So, in the case of accept()
, you have to pass in the FULL size of the buffer that will receive the client's sockaddr_...
. When a new client arrives, accept()
will validate that the buffer is large enough to receive the client's actual sockaddr_...
data before then populating the buffer, and will return how much of the buffer was actually filled in.
For example, if you know the socket only supports IPv4 (was created as an AF_INET
-family socket), then you can use a sockaddr_in
as the buffer and sizeof(sockaddr_in)
as the buffer size, or you can use a sockaddr_storage
as the buffer and sizeof(sockaddr_storage)
as the buffer size. In either case, accept()
will populate the buffer with a sockaddr_in
and return the buffer size as sizeof(sockaddr_in)
. Same with an IPv6-only socket (created as an AF_INET6
-family socket), just with sockaddr_in6
instead.
Now, lets say you have a dual-stack socket that supports both IPv4 and IPv6 (an AF_INET6
-family socket with the IPV6_V6ONLY
option disabled). You can use a sockaddr_storage
as the buffer and sizeof(sockaddr_storage)
as the buffer size, and accept()
will populate the buffer with either a sockaddr_in
or sockaddr_in6
and return an appropriate buffer size, depending on whether an IPv4 or IPv6 client was accepted. You can then read the ss_family
field of the sockaddr_storage
and type-cast the data to either sockaddr_in
for AF_INET
or sockaddr_in6
for AF_INET6
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With