I recommend the Stevens book, UNIX Network Programming, for starters.
The socket APIs in Un*x have to deal with different types of sockets. For example,
there are UDP sockets and UNIX sockets. When you bind an address to a socket,
you are essentially telling the operating system that your socket should receive
all data that is sent to the address that you are binding. For the networking world,
addresses are comprised of an IP address and a port number. But, for the UNIX
socket world, addresses are actually
strings instead.
The varied nature of the address format makes for some difficulty in the C world,
since I have to pass completely different types to bind() (and other system calls)
for the address.
C's solution to this is ersatz polymorphism. For internet-based sockets, there
is struct sockaddr_in, which contains members for the IP address and port.
For UNIX-based sockets, there is struct sockaddr_un, which contains a member
for the name (string). Since these are two completely different types, you have
to do two things: first, you have to typecast your struct instance to a common
type (struct sockaddr) to make the compiler happy. Second, you have to fill out
the "family" member of your address struct to tell the kernel what kind of address
you are passing -- a struct sockaddr_in, a struct sockaddr_un, or something else.
struct sockaddr is merely a facade; it contains, as its first member, the "family"
member (which you may know as sin_family), and the rest of the struct is
essentially padding, so that sizeof( sockaddr ) is at least the size of the largest
address struct you might pass in.
That said, your code in B is slightly flawed in that you should pass
as the third parameter.