Back to all articlesNetworking Programming

Understanding TCP/IP Socket Programming

18 min read

TCP/IP Socket Programming Guide

TCP/IP socket programming is the foundation of networked applications—web servers, databases, chat apps, games, and more. This blog walks you through what sockets are, how TCP/IP fits in, and how to write a simple client–server program in C/C++, explaining the key structures and system calls along the way.

1. What Is a Socket?

A socket is an endpoint for communication between two machines (or processes) over a network.

Think of it like:

  • IP address → identifies the machine
  • Port number → identifies the application
  • Socket → (IP address + port + protocol)

Sockets allow programs to:

  • Send data
  • Receive data
  • Manage connections

2. TCP/IP in a Nutshell

TCP/IP is a protocol suite with layers:

LayerResponsibility
ApplicationHTTP, FTP, SMTP
TransportTCP, UDP
InternetIP
LinkEthernet, Wi-Fi

In socket programming:

  • TCP sockets → reliable, connection-oriented
  • UDP sockets → fast, connectionless

This blog focuses on TCP sockets.

3. Client–Server Model

TCP Server Flow

  1. Create a socket
  1. Bind it to an IP + port
  1. Listen for connections
  1. Accept a client
  1. Send/receive data
  1. Close connection

TCP Client Flow

  1. Create a socket
  1. Connect to server
  1. Send/receive data
  1. Close socket

4. Key Data Structures

struct sockaddr

Generic socket address structure.

struct sockaddr {
    sa_family_t sa_family;
    char sa_data[14];
};

Usually not used directly.

struct sockaddr_in (IPv4)

struct sockaddr_in {
    sa_family_t    sin_family; // AF_INET
    in_port_t      sin_port;   // Port number
    struct in_addr sin_addr;   // IP address
};

Example:

struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080);
server_addr.sin_addr.s_addr = INADDR_ANY;
  • htons() → host to network byte order (short)
  • INADDR_ANY → bind to all local interfaces

struct addrinfo (Modern & Flexible)

Used with getaddrinfo() for protocol-independent code.

struct addrinfo hints, *res;
memset(&hints, 0, sizeof hints);

hints.ai_family = AF_INET;      // IPv4
hints.ai_socktype = SOCK_STREAM; // TCP

5. Important Socket Functions

1. socket()

Creates a socket.

int sockfd = socket(AF_INET, SOCK_STREAM, 0);
  • AF_INET → IPv4
  • SOCK_STREAM → TCP

2. bind()

Associates socket with IP + port (server-side).

bind(sockfd,
     (struct sockaddr *)&server_addr,
     sizeof(server_addr));

3. listen()

Marks socket as passive (waiting for connections).

listen(sockfd, 5);
  • 5 → backlog (pending connections)

4. accept()

Accepts an incoming connection.

int client_fd;
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);

client_fd = accept(sockfd,
                   (struct sockaddr *)&client_addr,
                   &addr_len);

Returns new socket descriptor for the client.

5. connect()

Client connects to server.

connect(sockfd,
        (struct sockaddr *)&server_addr,
        sizeof(server_addr));

6. send() / recv()

send(client_fd, buffer, strlen(buffer), 0);
recv(client_fd, buffer, sizeof(buffer), 0);

6. Simple TCP Server (C)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

int main() {
    int server_fd, client_fd;
    struct sockaddr_in server_addr, client_addr;
    socklen_t addr_len = sizeof(client_addr);
    char buffer[1024] = {0};

    server_fd = socket(AF_INET, SOCK_STREAM, 0);

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080);
    server_addr.sin_addr.s_addr = INADDR_ANY;

    bind(server_fd,
         (struct sockaddr *)&server_addr,
         sizeof(server_addr));

    listen(server_fd, 5);
    printf("Server listening on port 8080...\n");

    client_fd = accept(server_fd,
                       (struct sockaddr *)&client_addr,
                       &addr_len);

    recv(client_fd, buffer, sizeof(buffer), 0);
    printf("Client says: %s\n", buffer);

    send(client_fd, "Hello from server", 17, 0);

    close(client_fd);
    close(server_fd);
    return 0;
}

7. Simple TCP Client (C++)

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>

int main() {
    int sockfd;
    struct sockaddr_in server_addr;
    char buffer[1024] = {0};

    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080);
    inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);

    connect(sockfd,
            (struct sockaddr *)&server_addr,
            sizeof(server_addr));

    send(sockfd, "Hello from client", 17, 0);
    recv(sockfd, buffer, sizeof(buffer), 0);

    std::cout << "Server replied: " << buffer << std::endl;

    close(sockfd);
    return 0;
}

8. Common Pitfalls

  • Forgetting htons() / htonl()
  • Not checking return values
  • Using blocking sockets unintentionally
  • Not closing sockets (resource leaks)
  • Hardcoding IPv4 instead of getaddrinfo()

9. Why Use getaddrinfo()?

Modern, portable, and supports:

  • IPv4 & IPv6
  • Cleaner error handling
  • DNS resolution

Recommended for production code.

10. Final Thoughts

TCP/IP socket programming may feel low-level, but it gives you:

  • Full control over networking
  • Deeper understanding of how the internet works
  • Strong foundation for higher-level frameworks

Once comfortable with these basics, moving to libraries like Boost.Asio, libuv, or frameworks like gRPC becomes much easier.