I'm learning HTTP protocol following a tutorial which gives an understandable piece of code and here's part of it.
struct sockaddr_in address;
...
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( PORT );
memset(address.sin_zero, '\0', sizeof address.sin_zero);
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0)
{
perror("In bind");
exit(EXIT_FAILURE);
}
The example code works well, although I don't understand the some kind of transfer between two structs.
the definition of struct sockaddr_in in <netinet/in.h> is
struct sockaddr_in {
__uint8_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
the definition of struct sockaddr in <sys/socket.h> is
struct sockaddr {
__uint8_t sa_len; /* total length */
sa_family_t sa_family; /* [XSI] address family */
char sa_data[14]; /* [XSI] addr value (actually larger) */
};
They have different structures, how the "transfer/casting" works there?
I don't understand the some kind of transfer between two structs.
There is no data transfer between different structs, nor any conversion of structure objects. In bind(server_fd, (struct sockaddr *)&address, sizeof(address)), a pointer to a struct is converted to a different object pointer type. This is explicitly allowed by C.
The C language specification does not define any behavior for accessing the struct via the converted pointer. Any attempt to do so would violate the strict aliasing rule, but that's not your problem. The example you presented demonstrates an utterly standard usage idiom for the bind() function, for which it was designed. Therefore, you can rely on the bind() implementation to do the right thing with it, by whatever magic is required.
Conceptually, though, you can observe that the first two members of struct sockaddr and struct sockaddr_in have the same data types. You could imagine, then, that bind is able to access those two members via the converted pointer, despite it constituting a strict-aliasing violation. Although C does not define behavior for that, POSIX implicitly requires that it work in at least this case. Having then done that, the second of those members indicates the address family, by which bind() can invoke the appropriate behavior for the address's actual type.
That is a variation on C-style polymorphism. It is helped out by the third bind argument, the size of the address object, which enables bind() to copy the address object without knowing its true effective data type.
These structure types and the bind() API could have been defined a bit differently to avoid the implied strict-aliasing violation, but that wasn't necessary in early C, where member names corresponded directly to offsets from the beginning of the structure. And where those names were global, which is why you see the sin_ and sa_ prefixes in those member names, and similar in many other structure types provided by the system. Nowadays, it's best to just accept that that's how bind() is used, and it's up to the system to provide a bind() implementation that accommodates it.
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