πŸ‡―πŸ‡΅ ζ—₯本θͺž | πŸ‡ΊπŸ‡Έ English

HTTP Client in C

Apply socket programming to talk to a real web server and fetch HTML.

Prerequisite: Simple TCP client & server for the socket/connect/send/recv basics.

What is Socket Communication?

A socket is the mechanism two programs use to exchange data across a network. Web browsers, email clients, chat apps β€” every network-aware program uses sockets under the hood.
Client
Your program
β†’
TCP/IP
Internet
β†’
Server
e.g. web server

TCP vs UDP

TCP (reliable)
Guarantees delivery and ordering.
Used by: web, email, file transfer.
We'll use this.
UDP (fast, lightweight)
No delivery or ordering guarantees.
Used by: video streaming, online games, DNS.

Required Headers

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>        // close()
#include <arpa/inet.h>     // inet_addr(), htons()
#include <netdb.h>         // gethostbyname()
#include <sys/socket.h>    // socket(), connect(), send(), recv()
Note: these headers are for Linux and macOS. On Windows, use winsock2.h.

Socket API Flow (Client Side)

A TCP client connects, exchanges data, and disconnects β€” all in five steps.
1. socket() β€” create a socket
2. connect() β€” connect to server
3. send() β€” send data
4. recv() β€” receive data
5. close() β€” close the socket
Telephone analogy: socket = pick up the phone, connect = dial, send/recv = speak and listen, close = hang up.

Build a TCP Client

Now let's actually connect to a web server, send an HTTP request, and read the response.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>

#define BUF_SIZE 4096

int main(void) {
    const char *host = "example.com";
    int port = 80;

    // ===== 1. Resolve hostname to IP address =====
    struct hostent *server = gethostbyname(host);
    if (server == NULL) {
        fprintf(stderr, "Host lookup failed\n");
        return 1;
    }

    // ===== 2. Create a socket =====
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("socket");
        return 1;
    }

    // ===== 3. Set up server address =====
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);  // port in network byte order
    memcpy(&addr.sin_addr.s_addr, server->h_addr, server->h_length);

    // ===== 4. Connect to server =====
    if (connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("connect");
        close(sockfd);
        return 1;
    }
    printf("Connected: %s:%d\n\n", host, port);

    // ===== 5. Send HTTP request =====
    char request[BUF_SIZE];
    snprintf(request, BUF_SIZE,
        "GET / HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", host);
    send(sockfd, request, strlen(request), 0);

    // ===== 6. Receive and print response =====
    char buf[BUF_SIZE];
    int n;
    while ((n = recv(sockfd, buf, BUF_SIZE - 1, 0)) > 0) {
        buf[n] = '\0';
        printf("%s", buf);
    }

    // ===== 7. Disconnect =====
    close(sockfd);
    printf("\n\nConnection closed\n");
    return 0;
}

Compile and Run

$ gcc http_client.c -o http_client $ ./http_client Connected: example.com:80 HTTP/1.1 200 OK Content-Type: text/html; charset=UTF-8 ... <html>...</html> Connection closed

Function Details

FunctionPurposeArgumentsReturns
socket()Create a socketAF_INET, SOCK_STREAM, 0socket fd (int)
gethostbyname()Hostname β†’ IP"example.com"pointer to hostent
connect()Connect to serversockfd, addr, addrlen0 = ok, -1 = fail
send()Send datasockfd, buf, len, flagsbytes sent
recv()Receive datasockfd, buf, len, flagsbytes received (0 = closed)
close()Close socketsockfd0 = ok
htons()Host-to-network short (ports)port numberconverted port

sockaddr_in Struct

struct sockaddr_in {
    short          sin_family;  // address family (AF_INET)
    unsigned short sin_port;    // port (use htons() to convert)
    struct in_addr sin_addr;    // IP address
    char           sin_zero[8]; // padding
};
htons = Host TO Network Short. CPU byte order (usually little-endian) differs from network byte order (big-endian), so conversion is required.

Challenges

Challenge 1: Extract a connect function
Refactor the connection setup above into int connect_to_server(const char *host, int port). Return the socket fd, or -1 on error.
Challenge 2: Generic send/recv helpers
Implement send_line and recv_line that send and receive one line at a time. send_line appends \r\n; recv_line reads up to \r\n.
Challenge 3: Pass host via command line
Accept host and port from argv, so you can run ./http_client example.com 80. See the argc/argv lesson.
Challenge 4: Save the response to a file
Add an option to save the HTTP response to a file. See the file I/O lesson.
Next: Build an SMTP email client β†’

Review Quiz

Check your understanding of this lesson!

Q1. Which header is mandatory in an HTTP/1.1 request?

Host
User-Agent
Connection

HTTP/1.1 requires the Host header so the server can distinguish multiple virtual hosts sharing one IP.

Q2. What byte sequence marks line endings in HTTP?

\r\n (CRLF)
\n only
\t

Text-based protocols like HTTP and SMTP traditionally separate lines with the two bytes CR + LF.

Q3. What does the 200 at the start of an HTTP response mean?

A status code indicating success
The content length
The HTTP version number

2xx means success, 3xx means redirect, 4xx is a client error, and 5xx is a server error.

Share this article
Share on X