Linux Sockets Creating a Socket โ€“ the socket() System Call

 

Linux Sockets โ€“ Chapter 56
Part 2: Creating a Socket โ€“ the socket() System Call
๐Ÿ”ง socket()
๐Ÿšฉ SOCK_CLOEXEC
โšก SOCK_NONBLOCK

Section 56.2 โ€“ socket()

Before two processes can communicate using sockets, at least one of them must create a socket. This is done using the socket() system call. Think of it as manufacturing a telephone handset โ€” you need one before you can make or receive any calls.

Key Terms in This Part

socket() domain type protocol SOCK_CLOEXEC SOCK_NONBLOCK FD_CLOEXEC O_NONBLOCK file descriptor

Function Signature
#include <sys/socket.h>

int socket(int domain, int type, int protocol);

/* Returns: file descriptor (non-negative) on success, -1 on error */

Argument Type What it controls Common values
domain int Communication domain / address family AF_UNIX, AF_INET, AF_INET6
type int Socket type (can OR with flags) SOCK_STREAM, SOCK_DGRAM
protocol int Specific protocol (usually 0) 0, IPPROTO_RAW

Understanding Each Argument

1. domain

Specifies where the communication happens and what address format is used. The most common values are AF_UNIX (same machine, pathname address), AF_INET (IPv4 network), and AF_INET6 (IPv6 network).

2. type

Specifies how data flows. The two standard values are:

  • SOCK_STREAM โ€“ reliable, ordered, connection-based byte stream (TCP)
  • SOCK_DGRAM โ€“ unreliable, connectionless, preserves message boundaries (UDP)

Since Linux kernel 2.6.27, you can also bitwise-OR additional flags into type (explained below).

3. protocol

For the socket types covered in this chapter, always pass 0. The kernel picks the appropriate protocol automatically. You only set a non-zero value for special cases like raw sockets (IPPROTO_RAW).

Return Value

On success, socket() returns a new file descriptor โ€” a small non-negative integer just like the fd returned by open(). This fd is used in all subsequent socket system calls (bind(), connect(), send(), recv(), etc.).

On failure, socket() returns -1 and sets errno.

socket(AF_INET, SOCK_STREAM, 0)
โ†’
fd = 3 (new file descriptor)

The fd is now ready to be used with bind(), connect(), etc.

New Flags in type (Linux 2.6.27+)

Starting from kernel 2.6.27, Linux allows you to OR two extra flags into the type argument. This saves extra fcntl() calls that you would otherwise need after creating the socket.

SOCK_CLOEXEC

Sets the FD_CLOEXEC (close-on-exec) flag on the new socket fd.

Why useful: When a process calls exec() to run a new program, all file descriptors that do NOT have this flag will be inherited. If your server creates a socket and then spawns a child with exec(), you usually do NOT want the child to inherit the socket. SOCK_CLOEXEC prevents this automatically and race-free.

SOCK_NONBLOCK

Sets the O_NONBLOCK flag on the underlying open file description.

Why useful: Normally I/O calls on a socket block if data is not ready. With this flag, they return immediately with EAGAIN / EWOULDBLOCK if no data is available. Useful in event-driven (non-blocking) server designs like those using epoll.

Coding Examples

Example 1: Basic TCP socket creation

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

int main(void)
{
    int sockfd;

    /* Create an IPv4 TCP stream socket */
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    printf("Socket created successfully, fd = %d\n", sockfd);

    /* sockfd is now ready for bind() or connect() */
    close(sockfd);
    return 0;
}

Example 2: UDP socket (datagram)

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

int main(void)
{
    int sockfd;

    /* Create an IPv4 UDP datagram socket */
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    printf("UDP socket fd = %d\n", sockfd);
    close(sockfd);
    return 0;
}

Example 3: Using SOCK_CLOEXEC and SOCK_NONBLOCK flags

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

int main(void)
{
    int sockfd;

    /*
     * Create TCP socket with two extra flags OR-ed in:
     * SOCK_CLOEXEC  โ€“ auto-close on exec(), avoids race with fork+exec
     * SOCK_NONBLOCK โ€“ I/O calls return EAGAIN instead of blocking
     *
     * Requires Linux kernel >= 2.6.27
     */
    sockfd = socket(AF_INET,
                    SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                    0);
    if (sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    printf("Non-blocking, close-on-exec TCP socket fd = %d\n", sockfd);
    close(sockfd);
    return 0;
}

Example 4: UNIX domain socket (same-machine IPC)

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

int main(void)
{
    int sockfd;

    /* AF_UNIX = local IPC via filesystem path, no network needed */
    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    printf("UNIX domain socket fd = %d\n", sockfd);
    close(sockfd);
    return 0;
}

Example 5: Equivalent using fcntl() (the old way)

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/socket.h>

int main(void)
{
    int sockfd, flags;

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

    /* Set O_NONBLOCK using fcntl (old method, two extra syscalls) */
    flags = fcntl(sockfd, F_GETFL);
    if (flags == -1) { perror("fcntl F_GETFL"); exit(1); }

    if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) == -1) {
        perror("fcntl F_SETFL");
        exit(1);
    }

    /*
     * The SOCK_NONBLOCK flag at socket() creation avoids these
     * two extra fcntl() calls shown above.
     */

    printf("Non-blocking socket fd = %d (done via fcntl)\n", sockfd);
    close(sockfd);
    return 0;
}

๐ŸŽฏ Interview Questions โ€“ socket() System Call
Q1. What does the socket() system call return?It returns a file descriptor (a non-negative integer) on success, or -1 on error. This fd is used in all subsequent socket operations just like a regular file descriptor.

Q2. What should you pass as the protocol argument in socket()?For the common socket types (SOCK_STREAM, SOCK_DGRAM), always pass 0. The kernel automatically selects the correct protocol (TCP for SOCK_STREAM over AF_INET, UDP for SOCK_DGRAM over AF_INET). Non-zero values like IPPROTO_RAW are only needed for raw sockets.

Q3. What is SOCK_CLOEXEC and why is it useful?SOCK_CLOEXEC sets the FD_CLOEXEC flag on the socket fd at creation time. This causes the fd to be automatically closed when the process calls exec(). Without it, a child process spawned via fork+exec would inherit the socket fd, which is usually undesirable in server programs. Using SOCK_CLOEXEC also avoids a race condition that would exist if you first create the socket and then set FD_CLOEXEC separately.

Q4. What is SOCK_NONBLOCK? How is it different from calling fcntl() after socket()?SOCK_NONBLOCK sets O_NONBLOCK on the underlying open file description at socket creation time. With O_NONBLOCK set, I/O calls return EAGAIN/EWOULDBLOCK immediately instead of blocking when data is not ready. Using the flag at socket() creation saves two extra fcntl() system calls (F_GETFL + F_SETFL) that you would otherwise need. It is equivalent in effect but more efficient and race-free.

Q5. From which kernel version are SOCK_CLOEXEC and SOCK_NONBLOCK available?Linux kernel 2.6.27 and later. On older kernels you must use fcntl() after socket() to set these properties.

Q6. What is the difference between socket() and open()?Both return a file descriptor. open() creates/opens a file in the filesystem. socket() creates a communication endpoint. A socket fd supports I/O operations like read/write but also socket-specific calls (bind, connect, listen, accept, send, recv). A socket has no name in the filesystem by default until bind() is called (for AF_UNIX sockets).

Q7. Can you use read() and write() on a socket fd?Yes. Because a socket is a file descriptor, standard read() and write() work on it. However, socket-specific calls like send() and recv() are preferred because they accept a flags argument that enables features like MSG_PEEK (look without consuming), MSG_DONTWAIT (non-blocking for one call), and MSG_WAITALL (wait for full buffer).

Next: Binding a Socket โ€“ bind() & struct sockaddr

Learn how to assign an address to a socket and understand the generic sockaddr structure.

Part 3 โ†’ โ† Part 1

Leave a Reply

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