Exploring what sockaddr and sockaddr_in structures are, with "gcc -E sockaddr.c | less", where sockaddr.c is ---------[ sockaddr.c ]--------- #include #include // struct sockaddr #include // strict sockaddr_in int main() { printf("sizeof(sa_family_t): %d\n", sizeof(sa_family_t)); printf("sizeof(sockaddr): %d\n", sizeof(struct sockaddr)); } -------------------------------- Note that on Linux it gives 2 and 16, but on MacOS 1 and 16! Why? The answer is in the header files. Note: It's possible to grep the header files directly for these definitions, but their nesting and macros quite become tiring. Instead, we rely on "gcc -E" to resolve and pre-process them all, so that we see the definitions as the compiler itself gets them. So: -----------------[ Darwin ]----------------- # 93 "/usr/include/sys/socket.h" 3 4 # 1 "/usr/include/sys/_types/_sa_family_t.h" 1 3 4 # 30 "/usr/include/sys/_types/_sa_family_t.h" 3 4 typedef __uint8_t sa_family_t; ... # 283 "/usr/include/sys/socket.h" 3 4 struct sockaddr { __uint8_t sa_len; sa_family_t sa_family; char sa_data[14]; }; ... # 30 "/usr/include/sys/_types/_in_addr_t.h" 3 4 typedef __uint32_t in_addr_t; ... # 301 "/usr/include/netinet/in.h" 3 4 struct in_addr { in_addr_t s_addr; }; # 374 "/usr/include/netinet/in.h" 3 4 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]; }; -----------------[ Linux ]----------------- # 28 "/usr/include/bits/sockaddr.h" 3 4 typedef unsigned short int sa_family_t; # 168 "/usr/include/bits/socket.h" 2 3 4 struct sockaddr { sa_family_t sa_family; char sa_data[14]; }; ... # 23 "/usr/include/netinet/in.h" 2 3 4 typedef uint32_t in_addr_t; struct in_addr { in_addr_t s_addr; }; ... # 239 "/usr/include/netinet/in.h" 3 4 struct sockaddr_in { sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; unsigned char sin_zero[sizeof (struct sockaddr) - (sizeof (unsigned short int)) - sizeof (in_port_t) - sizeof (struct in_addr)]; }; ------------------------------------------------------- So we see that: sockaddr is just a generic "base" type (in C++/Java terms), and it doesn't even match between Linux and Darwin/MacOS. The sa_family_t, in particular, is 1 byte on Darwin, 2 bytes on Linux, and Darwin's sockaddr has a 1-byte sa_len field that Linux's doesn't! Sockaddr exists for the sake of passing (struct sockaddr *) pointers to many functions that take different socket structures, not just IPv4's sockaddr_in (e.g., there are also sockaddr_in6, which contains 16-byte IPv6 addresses---a single address in longer that the 14 bytes allowed for by sa_data!) So the actual struct passed around through a (struct sockaddr *) pointer may actually be longer than sizeof(struct sockaddr), and this size is useless for safety checks. But, when cast to sockaddr_in, the properly specialized address struct for Internet addresses that have fields for ports and IPs, these fields and their offsets match up across systems. That's a relief.