TCP Protocol Mechanics and Socket Programming Essentials

OSI Model and Encapsulation

Data transmission across the ISO OSI seven-layer model involves encapsulation. Each layer prepends a header to the payload, with the data link layer appending an additional trailer. Decapsulation reverses this process, stripping headers and trailers as data moves up the stack.

Transmission Control Protocol (TCP) Mechanics

The TCP header contains 16-bit port numbers for process identification and sequence numbers to track data byte positioning. When transmission anomalies occur, timeout retransmission is triggered. Standard RTT calculations can suffer from retransmission ambiguity, necessitating refined algorithms to accurately estimate round-trip times.

TCP utilizes a sliding window mechanism to guarantee reliable delivery and manage flow control. By dynamically adjusting the window size, the receiver can regulate the data transmission rate, preventing buffer overflows.

Congestion control algorithms prevent network saturation. The primary approaches include:

  • Slow start and congestion avoidance
  • Fast retransmit and fast recovery

Socket Programming Interface

Within the TCP/IP stack, an IP address uniquely identifies a host on the network, while the transport layer protocol and port number pinpoint a specific application process. Sockets provide the programming interface leveraging these three elements for inter-process communication.

Implementing a TCP Client and Server

Client Implementation

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

constexpr unsigned short TARGET_PORT = 6666;
constexpr size_t BUFFER_CAPACITY = 4096;

int main(int argc, char* argv[]) {
    if (argc != 2) {
        std::cerr << "Usage: " << argv[0] << " <server_ip>" << std::endl;
        return EXIT_FAILURE;
    }

    int sock_desc = socket(AF_INET, SOCK_STREAM, 0);
    if (sock_desc < 0) {
        perror("Socket creation failed");
        return EXIT_FAILURE;
    }

    struct sockaddr_in remote_addr{};
    remote_addr.sin_family = AF_INET;
    remote_addr.sin_port = htons(TARGET_PORT);

    if (inet_pton(AF_INET, argv[1], &remote_addr.sin_addr) <= 0) {
        perror("Invalid address / Address not supported");
        return EXIT_FAILURE;
    }

    if (connect(sock_desc, reinterpret_cast<struct sockaddr*>(&remote_addr), sizeof(remote_addr)) < 0) {
        perror("Connection failed");
        return EXIT_FAILURE;
    }

    std::cout << "Enter message to send: ";
    char output_buf[BUFFER_CAPACITY];
    std::cin.getline(output_buf, BUFFER_CAPACITY);

    if (send(sock_desc, output_buf, strlen(output_buf), 0) < 0) {
        perror("Send failed");
        return EXIT_FAILURE;
    }

    close(sock_desc);
    return EXIT_SUCCESS;
}

Server Implementation

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

constexpr unsigned short LISTEN_PORT = 6666;
constexpr size_t BUFFER_CAPACITY = 4096;
constexpr int BACKLOG_QUEUE = 10;

int main() {
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0) {
        perror("Socket creation failed");
        return EXIT_FAILURE;
    }

    struct sockaddr_in host_addr{};
    host_addr.sin_family = AF_INET;
    // Binding to loopback (127.0.0.1) restricts connections to the local machine.
    // Use INADDR_ANY to allow connections on all network interfaces.
    inet_pton(AF_INET, "127.0.0.1", &host_addr.sin_addr);
    host_addr.sin_port = htons(LISTEN_PORT);

    if (bind(server_fd, reinterpret_cast<struct sockaddr*>(&host_addr), sizeof(host_addr)) < 0) {
        perror("Bind failed");
        return EXIT_FAILURE;
    }

    if (listen(server_fd, BACKLOG_QUEUE) < 0) {
        perror("Listen failed");
        return EXIT_FAILURE;
    }

    std::cout << "Server listening on port " << LISTEN_PORT << std::endl;

    while (true) {
        int client_fd = accept(server_fd, nullptr, nullptr);
        if (client_fd < 0) {
            perror("Accept failed");
            continue;
        }

        char input_buf[BUFFER_CAPACITY];
        ssize_t bytes_read = recv(client_fd, input_buf, BUFFER_CAPACITY - 1, 0);
        if (bytes_read > 0) {
            input_buf[bytes_read] = '\0';
            std::cout << "Received payload: " << input_buf << std::endl;
        }
        close(client_fd);
    }
}

Build Configuration

CC = g++
CFLAGS = -g -Wall

all: tcp_client tcp_server

tcp_server: server_main.o
	$(CC) $(CFLAGS) -o tcp_server server_main.o

tcp_client: client_main.o
	$(CC) $(CFLAGS) -o tcp_client client_main.o

%.o: %.cpp
	$(CC) $(CFLAGS) -c $<

clean:
	rm -f *.o tcp_server tcp_client

Binding the server to 127.0.0.1 (which corresponds to the integer 2130706433 in host byte order) restricts access to local connections only. Substituting this with htonl(INADDR_ANY) opens the service to external clients. Modern implementations should prefer inet_pton over deprecated functions like inet_addr for converting IP address strings to binary form.

Tags: tcp networking Socket Programming C++

Posted on Thu, 14 May 2026 14:57:19 +0000 by bladecatcher