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: TypicallyAF_INET(IPv4),AF_INET6(IPv6), orAF_UNSPEC(protocol-independent).ai_socktype:SOCK_STREAMfor sequenced, reliable, two-way connection-based byte streams (TCP), orSOCK_DGRAMfor connectionless datagrams (UDP).ai_protocol: Typically0to automatically select the default protocol for the specified type, or a specific value such asIPPROTO_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.