• The three data structures presented below are in place to model the IP address/port pairs:
struct sockaddr { // generic socket
    unsigned short sa_family; // protocol family for socket
    char sa_data[14];
    // address data (and defines full size to be 16 bytes)
};
The sockaddr_in is used to model IPv4 address/port pairs.
  • The sin_family field should always be initialized to be AF_INET, which is a constant used to be clear that IPv4 addresses are being used. If it feels redundant that a record dedicated to IPv4 needs to store a constant saying everything is IPv4, then stay tuned.
  • The sin_port field stores a port number in network byte (i.e. big endian) order.
  • The sockaddr_in field stores an IPv4 address as a packed, big endian int, as you saw with gethostbynameand the struct hostent.
  • The sin_zero field is generally ignored (though it's often set to store all zeroes). It exists primarily to pad the record up to 16 bytes.
struct sockaddr_in { // IPv4 socket address record
    unsigned short sin_family;
    unsigned short sin_port;
    struct in_addr sin_addr;
    unsigned char sin_zero[8];
};
struct sockaddr_in6 { // IPv6 socket address record
    unsigned short sin6_family;
    unsigned short sin6_port;
    unsigned int sin6_flowinfo;;
    struct in6_addr sin6_addr;
    unsigned int sin6_scope_id;
};

The sockaddr family

  • The three data structures presented below are in place to model the IP address/port pairs:
The sockaddr_in6 is used to model IPv6 address/port pairs.
  • The sin6_family field should always be set to AF_INET6. As with the sin_family field, sin6_family field occupies the first two bytes of surrounding record.
  • The sin6_port field holds a two-byte, network-byte-ordered port number, just like sin_port does.
  • A struct in6_addr is also wedged in there to manage a 128-bit IPv6 address.
  • sin6_flowinfo and sin6_scope_id are beyond the scope of what we need, so we'll ignore them.
struct sockaddr { // generic socket
    unsigned short sa_family; // protocol family for socket
    char sa_data[14];
    // address data (and defines full size to be 16 bytes)
};
struct sockaddr_in { // IPv4 socket address record
    unsigned short sin_family;
    unsigned short sin_port;
    struct in_addr sin_addr;
    unsigned char sin_zero[8];
};
struct sockaddr_in6 { // IPv6 socket address record
    unsigned short sin6_family;
    unsigned short sin6_port;
    unsigned int sin6_flowinfo;;
    struct in6_addr sin6_addr;
    unsigned int sin6_scope_id;
};

The sockaddr family

  • The three data structures presented below are in place to model the IP address/port pairs:
The struct sockaddr is the best C can do to emulate an abstract base class.
  • You rarely if ever declare variables of type struct sockaddr, but many system calls will accept parameters of type struct sockaddr *.
  • Rather than define a set of network system calls for IPv4 addresses and a second set of system calls for IPv6 addresses, Linux defines one set for both.
  • If a system call accepts a parameter of type struct sockaddr *, it really accepts the address of either a struct sockaddr_in or a struct sockaddr_in6. The system call relies on the value within the first two bytes—the sa_family field—to determine what the true record type is.
struct sockaddr { // generic socket
    unsigned short sa_family; // protocol family for socket
    char sa_data[14];
    // address data (and defines full size to be 16 bytes)
};
struct sockaddr_in { // IPv4 socket address record
    unsigned short sin_family;
    unsigned short sin_port;
    struct in_addr sin_addr;
    unsigned char sin_zero[8];
};
struct sockaddr_in6 { // IPv6 socket address record
    unsigned short sin6_family;
    unsigned short sin6_port;
    unsigned int sin6_flowinfo;;
    struct in6_addr sin6_addr;
    unsigned int sin6_scope_id;
};

The sockaddr family