POSIX Socket Initialization and Connection in C

Network socket programming in C relies on standard POSIX system calls to enable inter-process communication across a network. This article covers the step-by-step process of creating and connecting a client-side TCP stream socket using standard POSIX APIs, detailing address resolution with getaddrinfo(), descriptor initialization with socket(), connection establishment via connect(), and proper resource deallocation.

1. Address Resolution with getaddrinfo

Modern POSIX networking uses getaddrinfo() instead of deprecated functions like gethostbyname(). It handles both IPv4 and IPv6 transparently and prepares the necessary sockaddr structures.

To configure hints for address resolution:

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>

struct addrinfo hints;
struct addrinfo *res;

memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;     // Allow IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // TCP stream socket

int status = getaddrinfo("example.com", "80", &hints, &res);
if (status != 0) {
    // Handle error using gai_strerror(status)
}

2. Socket Initialization with socket

The socket() system call allocates a file descriptor for network communication. It requires three parameters: the communication domain (protocol family), the communication type, and the specific protocol.

Using the results from getaddrinfo() ensures that the parameters passed to socket() match the resolved host address:

int sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sockfd == -1) {
    // Handle error (check errno)
    freeaddrinfo(res);
}
  • ai_family: Typically AF_INET (IPv4), AF_INET6 (IPv6), or AF_UNSPEC (protocol-independent).
  • ai_socktype: SOCK_STREAM for sequenced, reliable, two-way connection-based byte streams (TCP), or SOCK_DGRAM for connectionless datagrams (UDP).
  • ai_protocol: Typically 0 to automatically select the default protocol for the specified type, or a specific value such as IPPROTO_TCP.

3. Establishing the Connection with connect

For connection-oriented protocols like TCP, the client initiates the three-way handshake using the connect() system call. It links the local socket file descriptor to the remote destination specified in the address structure.

if (connect(sockfd, res->ai_addr, res->ai_addrlen) == -1) {
    // Handle connection failure
    close(sockfd);
    freeaddrinfo(res);
}

Once connect() returns 0, the socket is connected and ready for data transfer via send(), recv(), read(), or write(). The allocated address list must be freed with freeaddrinfo(res) once the connection is established.

4. Complete Implementation Example

The following program combines these steps to connect to a web server over HTTP (port 80):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int main(void) {
    struct addrinfo hints, *servinfo, *p;
    int sockfd = -1;

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    int status = getaddrinfo("example.com", "80", &hints, &servinfo);
    if (status != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
        return 1;
    }

    // Loop through results and connect to the first available address
    for (p = servinfo; p != NULL; p = p->ai_next) {
        sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
        if (sockfd == -1) {
            continue;
        }

        if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);
            sockfd = -1;
            continue;
        }

        break; // Successfully connected
    }

    freeaddrinfo(servinfo);

    if (sockfd == -1) {
        fprintf(stderr, "Failed to connect to host\n");
        return 2;
    }

    printf("Successfully connected to host.\n");

    // Close the socket when done
    close(sockfd);
    return 0;
}

5. Teardown

When communication is finished, close the socket descriptor with close(sockfd). To terminate only transmission or reception before closing, use shutdown(sockfd, how) with SHUT_RD, SHUT_WR, or SHUT_RDWR.