Sockets: Communication Domains

 

Chapter 56 – Sockets: Communication Domains
Part 2 of 4  |  AF_UNIX · AF_INET · AF_INET6 · Address Structures
Series
Linux IPC
Chapter
56 – TLPI
Level
Beginner–Intermediate

What You Will Learn

When you create a socket you must choose a communication domain. The domain decides two things: (1) the format of the address used to identify a socket, and (2) whether communication stays on the local machine or travels over a network. This part explains every domain supported on Linux and shows the exact address structures you need in code.

Key Terms

AF_UNIX AF_INET AF_INET6 sockaddr_un sockaddr_in sockaddr_in6 IPv4 IPv6 Port Number PF_ vs AF_

1. What is a Communication Domain?

Every socket lives inside a communication domain. You pick the domain as the first argument to socket(). The domain controls:

  • The address format — how you write down “where” a socket is.
  • The reach — same machine only, or across a network.

Think of it like a postal system: a local delivery note (just a room number) works inside one building, while a full postal address is needed for international mail. Domains work the same way.

2. The Three Main Domains — At a Glance
Domain Flag Transport Scope Address Format C Structure
AF_UNIX Kernel (no network stack) Same host only Filesystem pathname sockaddr_un
AF_INET IPv4 network stack Hosts on an IPv4 network 32-bit IP + 16-bit port sockaddr_in
AF_INET6 IPv6 network stack Hosts on an IPv6 network 128-bit IP + 16-bit port sockaddr_in6

3. AF_UNIX — Unix Domain Sockets

AF_UNIX (also written AF_LOCAL in POSIX.1g, though SUSv3 uses AF_UNIX) keeps all data inside the kernel. No network packets are created. The address is simply a filesystem path like /tmp/myapp.sock.

When to use AF_UNIX:

  • Communicating between a client and server daemon on the same machine.
  • You need higher throughput than pipes but no network overhead.
  • Example: nginx talking to a PHP-FPM worker process.

Address structure — sockaddr_un:

#include <sys/un.h>

struct sockaddr_un {
    sa_family_t sun_family;          /* Always AF_UNIX */
    char        sun_path[108];       /* Filesystem path (null-terminated) */
};

Client
AF_UNIX socket
Linux Kernel
Data copies via kernel memory
Path: /tmp/myapp.sock
Server
AF_UNIX socket

Code — create a Unix domain socket:

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

#define SOCK_PATH "/tmp/demo.sock"

int main(void)
{
    int                sockfd;
    struct sockaddr_un addr;

    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sockfd == -1) { perror("socket"); exit(1); }

    /* Zero out then fill the address structure */
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, SOCK_PATH, sizeof(addr.sun_path) - 1);

    printf("AF_UNIX socket created, path will be: %s\n", addr.sun_path);

    close(sockfd);
    return 0;
}

4. AF_INET — IPv4 Internet Domain

AF_INET is used for communication between processes on machines connected via an IPv4 network. An IPv4 address is 32 bits long (the familiar 192.168.1.100 format). Together with a 16-bit port number, it uniquely identifies a socket on the entire internet.

Address structure — sockaddr_in:

#include <netinet/in.h>

struct sockaddr_in {
    sa_family_t    sin_family;   /* AF_INET */
    in_port_t      sin_port;     /* 16-bit port number (network byte order) */
    struct in_addr sin_addr;     /* 32-bit IPv4 address */
};

struct in_addr {
    uint32_t s_addr;             /* IPv4 address in network byte order */
};

Important: Port numbers and IP addresses must be stored in network byte order (big-endian). Use htons() for port and inet_pton() for IP conversion:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(void)
{
    int                sockfd;
    struct sockaddr_in addr;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) { perror("socket"); exit(1); }

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port   = htons(8080);               /* host-to-network-short */

    /* Convert "192.168.1.1" string into binary network-order form */
    if (inet_pton(AF_INET, "192.168.1.1", &addr.sin_addr) <= 0) {
        perror("inet_pton");
        exit(1);
    }

    printf("AF_INET socket ready for 192.168.1.1:8080\n");
    close(sockfd);
    return 0;
}

IPv4 Socket Address = IP (32-bit) + Port (16-bit)
32-bit IPv4 Address (4 octets) 16-bit Port
192 168 1 1 8080

5. AF_INET6 — IPv6 Internet Domain

IPv4 only has about 4 billion addresses — not enough for today’s internet. IPv6 uses 128-bit addresses (written as 2001:db8::1), providing a practically unlimited pool. AF_INET6 is the domain for IPv6 sockets.

Address structure — sockaddr_in6:

#include <netinet/in.h>

struct sockaddr_in6 {
    sa_family_t     sin6_family;    /* AF_INET6 */
    in_port_t       sin6_port;      /* 16-bit port (network byte order) */
    uint32_t        sin6_flowinfo;  /* IPv6 flow info (usually 0) */
    struct in6_addr sin6_addr;      /* 128-bit IPv6 address */
    uint32_t        sin6_scope_id;  /* Scope ID for link-local addresses */
};
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(void)
{
    int                 sockfd;
    struct sockaddr_in6 addr;

    sockfd = socket(AF_INET6, SOCK_STREAM, 0);
    if (sockfd == -1) { perror("socket"); exit(1); }

    memset(&addr, 0, sizeof(addr));
    addr.sin6_family = AF_INET6;
    addr.sin6_port   = htons(8080);

    /* ::1 is the IPv6 loopback address (equivalent of 127.0.0.1) */
    if (inet_pton(AF_INET6, "::1", &addr.sin6_addr) <= 0) {
        perror("inet_pton");
        exit(1);
    }

    printf("AF_INET6 socket ready for [::1]:8080\n");
    close(sockfd);
    return 0;
}

6. PF_ vs AF_ — The Historic Confusion

You may see both PF_INET and AF_INET in old code. Here is the story:

  • AF_ stands for Address Family — how addresses are formatted.
  • PF_ stands for Protocol Family — the protocol suite.

The original idea was that one protocol family might support multiple address families. That never happened. In every implementation, PF_INET == AF_INET, PF_UNIX == AF_UNIX, etc. — they are numeric aliases.

SUSv3 standardises only the AF_ constants. Always use AF_ in new code to keep things standard and clear.

/* These two lines do exactly the same thing — prefer AF_ */
int fd1 = socket(AF_INET,  SOCK_STREAM, 0);   /* correct — use this */
int fd2 = socket(PF_INET,  SOCK_STREAM, 0);   /* works but non-standard */

Interview Questions — Part 2

Q1. What are the three main socket domains in Linux and when do you choose each?

AF_UNIX — for local IPC on the same machine using a filesystem path as address, giving zero network overhead. AF_INET — for IPv4 network communication using a 32-bit IP address and 16-bit port. AF_INET6 — for IPv6 network communication using a 128-bit IP address and 16-bit port. Choose AF_UNIX for local daemons, AF_INET for legacy IPv4 services, and AF_INET6 for modern internet applications.

Q2. Why must you use htons() when setting a port number in sockaddr_in?

Network protocols require addresses and ports in big-endian (network byte order). x86/ARM machines are little-endian. htons() (host-to-network-short) converts a 16-bit value from host byte order to network byte order. Forgetting it means your port number will be interpreted wrongly on big-endian receivers.

Q3. What is the address of an AF_UNIX socket?

A filesystem pathname stored in sockaddr_un.sun_path, for example /var/run/nginx.sock. The kernel creates an entry in the filesystem when the server calls bind(), and removes it when the socket is unlinked with unlink().

Q4. What is the difference between AF_ and PF_ constants?

Historically AF_ was for address family and PF_ for protocol family. In practice they are numerically identical on every Linux implementation. SUSv3 only standardises AF_ constants, so those should always be used in portable code.

Q5. What function converts a dotted-decimal IPv4 string to binary form for sockaddr_in?

inet_pton(AF_INET, "192.168.1.1", &addr.sin_addr). The older inet_addr() is deprecated. inet_pton() also handles IPv6 addresses when called with AF_INET6.

Q6. What is the IPv6 loopback address equivalent of 127.0.0.1?

::1 — the 128-bit all-zeros address with the last bit set. In C code: inet_pton(AF_INET6, "::1", &addr.sin6_addr).

Next: Part 3 — Socket Types

Stream vs Datagram — reliability, boundaries, and when each is used.

Part 3 → ← Part 1

Leave a Reply

Your email address will not be published. Required fields are marked *