Network: Part 1. Basics

Sockets and the Network Stack: an Engineering Understanding of How Applications Communicate over the Network.

Why understanding networks does not begin with TCP/IP

When engineers start learning networks, they usually encounter high-level abstractions: HTTP requests, REST APIs, WebSocket connections, or database clients. All of this creates the illusion that working over the network comes down to sending requests and receiving responses.
However, in reality there is an entire layer of operating system mechanisms between your application and the network. This is exactly where sockets reside — the fundamental interface through which programs gain access to the network stack.
Understanding sockets is important not only for systems programmers. It helps explain:

  • why timeouts occur;
  • where blocking in applications comes from;
  • how asynchronous servers work;
  • why high-load systems are designed in a certain way;
  • what actually happens when send() or recv() is called.

To think like an engineer, it is important to understand not only what happens, but also where exactly it happens.

A socket is not a connection

One of the most common mistakes is perceiving a socket as a network connection.
In fact, that is not quite true.
A socket is a software interface provided by the operating system for the interaction of an application with the network stack.
In other words, a socket represents the application's entry point into the network subsystem of the operating system.
It is important to distinguish two concepts:

Concept What it means
Socket The interface for accessing the network stack
TCP Connection A logical connection between two nodes

A TCP connection exists inside the operating system's TCP stack and represents a set of states and protocol parameters.
The socket, on the other hand, is the means of controlling that connection.
It can be put this way:

A socket is a door handle, and a TCP connection is the room behind that door.

The socket in Unix: the network as ordinary input/output

One of the most elegant ideas of Unix is the concept:

"Everything is a file."

This does not mean that a socket is stored on disk as a file. We are talking about a unified input/output model.
In Unix-like systems, a socket is represented by a file descriptor (File Descriptor, FD).
A file descriptor is an integer identifier of an open resource managed by the operating system kernel.
For example:

import socket

s = socket.socket()
print(s.fileno())

Output:

3

The number 3 is the file descriptor.
This means that many input/output operations use a single interface:

read()
write()
close()

or their network counterparts:

recv()
send()
close()

The main conclusion here is as follows:

For Unix, the network is just another kind of input/output.

That is exactly why the mechanisms for working with files and with the network are in many ways similar.

What happens when a socket is created

Creating a socket looks very simple:

socket()

But behind this call hides a considerable amount of work by the operating system.
When socket() is called, the following happens:

  1. A system call is performed.
  2. The process hands control over to the kernel.
  3. The kernel creates an internal socket object.
  4. The necessary data structures are allocated.
  5. The send and receive buffers are initialized.
  6. An entry is created in the process's file descriptor table.
  7. A file descriptor is returned to the application.

Schematically, the process looks like this:

Application
      ↓
socket()
      ↓
System call
      ↓
OS kernel
      ↓
Creation of the socket object
      ↓
Allocation of buffers
      ↓
Return of the File Descriptor

After this, the application gains the ability to interact with the network stack through the created descriptor.
It is important to understand:

The socket itself exists inside the kernel. The application receives only a reference to this object.

User Space and Kernel Space

Modern operating systems divide the execution space into two areas.

User Space

This is where user applications run:

  • browsers;
  • databases;
  • Python applications;
  • web servers.

Programs in user space have limited privileges and cannot access hardware directly.

Kernel Space

In kernel space reside:

  • device drivers;
  • the process scheduler;
  • file systems;
  • the network stack;
  • memory management.

The kernel has full access to the computer's hardware resources.

System calls: the bridge between the application and the OS

When an application calls:

sock.send(data)

this is not an ordinary function call.
A system call is performed.
The process looks as follows:

Python application
        ↓
send()
        ↓
Transition User Mode → Kernel Mode
        ↓
Processing by the OS network stack
        ↓
Return of control to the application

This transition requires:

  • switching the processor mode;
  • saving the execution context;
  • checking access rights;
  • handing control over to the kernel.

Therefore, system calls are relatively expensive operations.
It is precisely for this reason that high-performance applications strive to:

  • reduce the number of system calls;
  • send data in batches;
  • use asynchronous input/output mechanisms.

The TCP connection as a state machine

TCP is a stateful protocol.
This means that each connection has its own set of states.
The main TCP states:

CLOSED
LISTEN
SYN_SENT
SYN_RECEIVED
ESTABLISHED
FIN_WAIT_1
FIN_WAIT_2
TIME_WAIT
CLOSE_WAIT
LAST_ACK

The transition between states is determined by events inside the protocol.
For example:

Client                     Server
   |                           |
   | ------ SYN ----------->   |
   |                           |
   | <---- SYN + ACK -------   |
   |                           |
   | ------ ACK ----------->   |
   |                           |
 ESTABLISHED              ESTABLISHED

After the three-way handshake completes, the connection is considered established.
It is important to understand:

A TCP connection is not just a data transmission channel, but a finite state machine with strictly defined rules for transitioning between states.

The lifecycle of a TCP connection

The operation of TCP can be conditionally divided into three stages.

1. Establishing the connection

The Three-Way Handshake mechanism is used.

Client → SYN → Server
Server → SYN-ACK → Client
Client → ACK → Server

At this stage the parties agree on:

  • initial sequence numbers;
  • the possibility of establishing communication;
  • connection parameters.

2. Data transfer

After the connection is established, TCP provides:

  • reliable delivery;
  • control of packet ordering;
  • retransmission of lost segments;
  • delivery confirmation (ACK);
  • transfer flow control.

3. Closing the connection

Closing a TCP connection also requires an exchange of service packets.
The following scheme is usually used:

FIN
ACK
FIN
ACK

This guarantees the correct completion of data transfer and the release of resources.

Why this matters

Even if you work exclusively with high-level libraries, understanding these mechanisms helps explain many real-world problems:

  • hanging network operations;
  • a large number of connections in the TIME_WAIT state;
  • timeouts;
  • scaling problems;
  • unexpected behavior of asynchronous applications.

High-level tools hide the implementation details, but they do not cancel the existence of those details.
The better an engineer understands the fundamental principles of how the network works, the more effectively they are able to diagnose and solve problems in real systems.

Socket buffers: why send() does not mean the data has been sent

Many engineers intuitively perceive the call:

sock.send(data)

as the action:

"the data was immediately sent over the network."

In reality this is not so.
Between the application and the physical network there are several intermediate layers, and one of the most important elements of this chain is socket buffers.

Buffering as the foundation of network interaction

Every TCP socket has two main buffers:

  • Send Buffer — the buffer for sending;
  • Receive Buffer — the buffer for receiving.

Schematically it looks like this:

Application
     ↓
Send Buffer
     ↓
TCP/IP Stack
     ↓
Network
     ↓
TCP/IP Stack
     ↓
Receive Buffer
     ↓
Application

The send buffer (Send Buffer)

When the application calls:

sock.send(data)

the following usually happens:

  1. The data is copied from user space into the kernel buffer.
  2. The function returns control to the application.
  3. TCP independently determines:
    -- when to send the segments;
    -- what size they will be;
    -- whether retransmission is required.

This means:

A successful send() call means only that the data was accepted by the OS kernel.

But it does not guarantee that:

  • the data has already left the computer;
  • the remote side has received it;
  • the remote side has processed it.

The receive buffer (Receive Buffer)

When a network packet arrives at the machine:

  1. The network card receives the data.
  2. The kernel processes the packet.
  3. The data is placed into the Receive Buffer.
  4. The application calls recv() and reads it.

It is important to understand:

TCP may receive data earlier than the application is ready to process it.

Buffering allows temporarily compensating for the difference in speeds between the network and the application.

Why buffers are necessary

Without buffers, serious problems would arise.
For example:

Network → very fast
Application → temporarily busy

If no intermediate storage existed, packets would have to be dropped immediately.
Buffers allow:

  • smoothing out short-term load spikes;
  • compensating for the difference in operating speeds of components;
  • reducing the number of system calls;
  • increasing the overall efficiency of data transfer.

Flow Control: protecting the receiver from overload

Imagine the situation:

Sender → 1 Gbit/s
Receiver → processes 100 Mbit/s

Obviously, sooner or later the receiver's buffer will overflow.
TCP solves this problem with the Flow Control mechanism.

How flow control works

The receiver tells the sender:

"Right now I can accept N more bytes of data."

This information is transmitted through the Receive Window (rwnd) parameter.
If the buffer starts filling up:

Receive Window ↓

the sender automatically reduces the transmission rate.
If the buffer is completely full:

Window = 0

the sender temporarily stops sending.
In this way TCP prevents data loss due to overload of the receiving side.

Backpressure: reverse pressure in systems

Flow Control is a special case of a more general concept — Backpressure.
The idea is simple:

Slow components of a system must have the ability to slow down fast ones.

This is encountered not only in networks:

  • TCP connections;
  • message queues;
  • reactive systems;
  • streaming platforms;
  • asynchronous data processing pipelines.

Without a backpressure mechanism, systems quickly become unstable.

Blocking sockets

By default, most sockets operate in blocking mode.
This means that:

data = sock.recv(1024)

will wait until:

  • data appears;
  • the connection is closed;
  • an error occurs.

What happens inside the OS

If there is no data:

recv()
    ↓
The kernel checks the Receive Buffer
    ↓
The buffer is empty
    ↓
The thread is put into a waiting state
    ↓
The CPU switches to executing other tasks

When data appears:

A packet arrived
    ↓
The thread wakes up
    ↓
recv() returns the data

The problem with the blocking approach

Imagine a server:

1 thread → 1 connection

If simultaneously connect:

10,000 clients

it will require:

10,000 threads

This leads to serious overhead:

  • increased memory consumption;
  • context switching costs;
  • reduced performance.

Such an approach scales poorly.

Non-blocking sockets

In non-blocking mode:

sock.setblocking(False)

the call:

sock.recv(1024)

will never wait.
Two scenarios are possible:

Data is available

recv()
    ↓
Data is returned immediately

There is no data

An error is returned:

BlockingIOError

or the corresponding operating system error code.

A new problem arises

If you constantly execute:

while True:
    sock.recv(...)

you get what is called busy waiting.
The processor will endlessly check for the presence of data.
This will lead to useless CPU load.
A different approach is required.

Multiplexing: serving many connections

The main idea is as follows:

A single thread must be able to wait for events on many sockets at once.

Multiplexing mechanisms exist precisely for this.

Select: the first generation

The mechanism:

select()

allows passing a list of sockets to the kernel:

"Tell me which of them are ready to work"

The kernel returns a list of:

  • those ready for reading;
  • those ready for writing;
  • those that ended with an error.

Limitations of select

The main drawbacks:

  • a limit on the number of descriptors;
  • the need to pass the entire list of sockets every time;
  • linear complexity of checking.

For small systems this is enough.
For highload applications — no.

Poll: an improved version

poll() eliminates some of the limitations of select().

Advantages:

  • no hard limit on the number of sockets;
  • a more convenient interface.

However, the problem remains:

O(n)

The operating system is still forced to check all descriptors.

Epoll: the foundation of modern high-load servers

Linux offers the mechanism:

epoll

The main idea:

Do not constantly ask which sockets are ready.

Instead:

The OS itself reports state changes.

How epoll works

Registering the events of interest

epoll_ctl()

The application reports:

"I am interested in these sockets"

Waiting for events

epoll_wait()

The thread goes to sleep.

An event occurs

For example:

Data arrived

The kernel places the event into an internal queue.

Waking up the application

epoll_wait() returns:

Only the active sockets

Why epoll scales better

If there are:

100,000 connections

and only these are active:

200

then:

select/poll

Check:

100,000 descriptors

epoll

Returns:

200 active descriptors

This substantially reduces the load on the system.

Event Loop: the foundation of asynchronous programming

Most modern servers use the event-driven model.
The principle of operation:

while True:
    events = epoll_wait()

    for event in events:
        handle()

It is precisely on this idea that these are built:

  • asyncio;
  • uvloop;
  • Node.js;
  • Nginx;
  • FastAPI;
  • Tornado.

Why asynchrony works

A common misconception:

Asynchrony makes code faster.

In reality:

Asynchrony reduces idle time.

Most network applications spend their time waiting:

  • for data from the client;
  • for a database response;
  • for an external API response.

The Event Loop allows using this waiting time efficiently.

The mental model of modern servers

The traditional approach:

Client
 ↓
Thread
 ↓
Blocking

The modern approach:

Clients
   ↓
epoll
   ↓
Event Loop
   ↓
A small number of threads

What happens after calling send(): the path of data inside the system

From an engineer's point of view, sending data looks extremely simple:

sock.send(data)

However, behind this call hides an entire chain of interactions between the application, the operating system, memory, and the network hardware.
To understand the performance of network applications, it is necessary to see the whole path of the data.
In simplified form it looks as follows:

Application
    ↓
User Space
    ↓
System call
    ↓
Kernel Space
    ↓
TCP/IP Stack
    ↓
Network card (NIC)
    ↓
Physical network

System calls and the transition into the kernel

When the application calls:

send()
recv()
connect()
accept()

this is not an ordinary function call.
A system call is performed.
This means:

User Mode
    ↓
Kernel Mode
    ↓
Processing of the request by the kernel
    ↓
Return to User Mode

Why system calls are expensive

During the transition into kernel mode the processor must:

  • save the current execution state;
  • switch the privilege level;
  • hand control over to the kernel;
  • perform the necessary processing;
  • restore the user context.

Even if the operation itself is performed quickly, the cost of switching modes remains.
Therefore, high-performance systems strive to:

  • reduce the number of system calls;
  • perform operations in batches;
  • avoid unnecessary switches between processor operating modes.

Copying data: the hidden cost of network operations

Many believe that the main cost of network interaction is associated exclusively with transmitting data over the network.
In practice, a significant part of the time can be taken up by memory copying operations.
Consider a typical data sending scenario.

Stage 1. The data is in user space

For example:

response = b"Hello World"
sock.send(response)

The variable response is located in the process's memory.

User Space RAM

Stage 2. Copying into the kernel buffer

When send() is called, a copy takes place:

User Space RAM
        ↓
Kernel Send Buffer

After this, the function can return control to the application.

Stage 3. The work of the TCP stack

The kernel performs:

  • segmentation of the data;
  • formation of TCP headers;
  • formation of IP packets;
  • management of transmission windows;
  • control of retransmissions.

Stage 4. Handing over to the network card

Next, the data is passed to the network device driver.

TCP/IP Stack
      ↓
NIC Driver

Stage 5. Sending into the network

The network card sends the data through the physical communication channel.

NIC
 ↓
Ethernet/Wi-Fi
 ↓
Network

Why copying becomes a problem

For small volumes of data this is practically unnoticeable.
However, in high-load systems the situation changes.
For example:

  • a web server transfers large files;
  • a CDN serves millions of requests;
  • a proxy forwards gigabytes of data.

In such cases it turns out that:

Memory copying can cost more than the data transmission itself.

That is exactly why engineers strive to reduce the number of copies.

Zero-Copy: reducing the number of copies

Zero-Copy is a group of techniques that allow minimizing the movement of data between different memory areas.
It is important to understand:

Zero-Copy does not always mean a complete absence of copying.

More often we are talking about reducing the number of intermediate copies.

The ordinary file transfer scheme

Disk
 ↓
Kernel Buffer
 ↓
User Space
 ↓
Kernel Buffer
 ↓
NIC

Several copies take place.

Using sendfile()

The system call:

sendfile()

allows transferring data directly:

Disk
 ↓
Kernel Buffer
 ↓
NIC

The application does not participate in moving the data.

Why this is important

Advantages:

  • lower CPU load;
  • fewer copying operations;
  • lower latency;
  • higher throughput of the system.

That is exactly why modern web servers actively use Zero-Copy techniques.

RAM: the true center of network interaction

When people talk about networks, they usually imagine:

  • cables;
  • routers;
  • packets;
  • protocols.

However, most operations do not happen in the network at all.
They happen in random access memory (RAM).

What RAM is

RAM (Random Access Memory) is the computer's main memory, in which reside:

  • program code;
  • application data;
  • kernel structures;
  • network buffers;
  • packet queues.

Put simply:

RAM is the working area of the entire system.

The CPU works precisely with RAM

The processor practically never interacts directly:

  • with disks;
  • with network devices.

Its work looks as follows:

RAM
 ↓
CPU
 ↓
RAM

This means:

Any processing of network data inevitably passes through random access memory.

The memory hierarchy

The speed of the different levels of memory differs greatly.

CPU registers
     ↓
L1 Cache
     ↓
L2 Cache
     ↓
L3 Cache
     ↓
RAM
     ↓
SSD/HDD

The higher the level:

  • the faster the access;
  • the smaller the volume.

Why the cache is so important

Modern processors are significantly faster than random access memory.
Without caches, the CPU would spend most of its time idle waiting for data.
Therefore the following are used:

  • L1 Cache — the fastest;
  • L2 Cache;
  • L3 Cache — shared among several cores.

The principle of locality

The operation of caches is based on two ideas.

Temporal Locality

If data was used recently:

there is a high probability that it will be needed again.

Spatial Locality

If data at a certain address is being used:

it is likely that neighboring memory areas will soon be needed.

Virtual memory: the illusion of contiguous space

Processes do not work directly with physical memory.
Each process receives its own virtual address space.
For example:

Process A → 0x1000
Process B → 0x1000

Both processes see the same addresses.
But physically they may be located in completely different areas of RAM.

MMU: address translation

Address translation is handled by a special hardware component:

MMU
(Memory Management Unit)

It performs the mapping:

Virtual address
       ↓
Physical address

Paging organization of memory

Memory is divided into pages.
The typical page size:

4 KB

There exist:

  • virtual pages;
  • physical pages.

The operating system stores mapping tables between them.

Page Fault: when the page is not in memory

If a process accesses a missing page:

Page Fault

the kernel must:

  1. Find the data on disk.
  2. Load the page into RAM.
  3. Update the page tables.
  4. Resume program execution.

This is an extremely expensive operation.

How the network uses RAM

Consider the reception of a packet.

NIC
 ↓
DMA
 ↓
RAM
 ↓
Kernel Buffer
 ↓
Socket Buffer
 ↓
User Space
 ↓
Application

All processing happens through random access memory.
From this follows an important conclusion:

The network is a mechanism for moving data between the RAM of different computers.

DMA: memory access without CPU involvement

DMA stands for:

Direct Memory Access

This mechanism allows devices to work with memory directly.

Without DMA

NIC
 ↓
CPU
 ↓
RAM

The processor participates in every copy.

With DMA

NIC
 ↓
RAM

The network card writes data into memory on its own.
The CPU gets involved only at the processing stage.

Why DMA is so important

Advantages:

  • offloads the processor;
  • increases throughput;
  • reduces latency;
  • allows working efficiently with large volumes of data.

Modern network systems would be impossible without DMA.

Interrupts and Polling

When new data arrives, the processor must be notified.
There are two approaches.

Interrupt-Based

The network card generates an interrupt.

NIC
 ↓
Interrupt
 ↓
CPU

The processor immediately begins processing.

Polling

The processor checks for the presence of data on its own.

CPU
 ↓
NIC ?
 ↓
CPU
 ↓
NIC ?

The modern approach

Today a hybrid model is usually used.
Under low load:

Interrupts

Under high load:

Polling

This allows achieving a better balance between latency and performance.

An engineering understanding of network performance

It is important to realize that network performance is determined not only by the speed of the communication channel.
The limiting factors can be:

  • the number of system calls;
  • memory copying operations;
  • processor cache misses;
  • page faults;
  • the efficiency of DMA;
  • the implementation of the OS network stack.

That is exactly why optimizing high-load systems requires understanding not only protocols, but also the architecture of operating systems.

Why networks seem complicated

Many engineers perceive networks as a set of separate topics:

  • HTTP;
  • TCP;
  • sockets;
  • DNS;
  • routing;
  • asynchrony.

Because of this, the feeling forms that networks consist of many unrelated technologies.
In reality everything is significantly simpler.
Most concepts in networks are different levels of describing one and the same task:

How to reliably transfer data between two processes running on different computers.

Everything else is implementation details of this idea.

The main thought: data and its transmission are different things

This is one of the most important concepts that any engineer must understand.
It is necessary to distinguish:

  • what is transmitted;
  • how it is transmitted.

What is transmitted

This is application-level data:

  • HTML pages;
  • JSON documents;
  • images;
  • files;
  • API commands;
  • chat messages.

For the application these are meaningful objects.
For example:

{
  "name": "Markus",
  "role": "developer"
}

Or:

with open("report.pdf", "rb") as f:
    data = f.read()

For an engineer this is:

  • a file;
  • an object;
  • a document;
  • a data structure.

How it is transmitted

For the network everything looks different.
The network knows nothing about the existence of:

  • files;
  • JSON;
  • HTTP;
  • images.

It works only with sequences of bytes.
For example:

01001000
01100101
01101100
01101100
01101111

For the network stack there exists only a stream of data.
That is exactly why it is important to understand:

In the network there are no files. There are only bytes.

The Internet is a network of networks

The word Internet comes from:

Inter-network

or:

"a network of networks."

This means that the Internet is not a single centralized system.
It consists of many independent networks.
For example:

Home provider
        ↓
Backbone provider
        ↓
Data center
        ↓
Cloud infrastructure
        ↓
Application server

Each of these networks:

  • belongs to different organizations;
  • is managed independently;
  • uses its own equipment.

The Internet exists because these networks agreed to use common interaction protocols.

The network is inherently unreliable

This is one of the fundamental ideas of computer networks.
Intuitively it seems:

If data has been sent, it must arrive.

In reality everything is the opposite.
The network is initially assumed to be an unreliable medium.

What can happen in reality

Packets can:

Be lost

Reasons:

  • router overload;
  • transmission errors;
  • buffer overflow.

Arrive in the wrong order

For example:

Packet 1
Packet 2
Packet 3

may arrive as:

Packet 2
Packet 1
Packet 3

Be duplicated

Due to retransmissions:

Packet 1
Packet 1
Packet 2

Be delayed

Some packets may arrive significantly later than the rest.

Reliability is not a property of the network

This is a very important idea.
Reliability is not provided by the network.
It is created by the protocols of the upper levels.
For example:

  • TCP;
  • TLS;
  • application-level mechanisms of repeated requests.

In other words:

The network represents chaos, and protocols try to bring order to it.

Why abstraction levels are needed

Modern networks are extremely complex.
If every application had to independently:

  • manage signals;
  • determine routes;
  • implement retransmission;
  • handle errors,

software development would be practically impossible.
Therefore, abstraction levels are used.

The idea of a layered architecture

Each level solves a strictly defined set of tasks.
At the same time it hides the implementation details of the underlying levels.
Schematically this looks as follows:

Application
     ↓
Sockets
     ↓
TCP
     ↓
IP
     ↓
Ethernet
     ↓
Physical medium

Each layer provides a service to the one above it.

Why this is important to an engineer

Imagine an ordinary HTTP request.

requests.get("https://example.com")

The engineer thinks:

"Send a GET request."

However, in reality a multitude of processes takes place.

The application level

An HTTP request is formed.

GET / HTTP/1.1
Host: example.com

The transport level

TCP provides:

  • connection establishment;
  • delivery control;
  • retransmission;
  • flow control.

The network level

IP determines:

  • by which route to transmit the data;
  • where to deliver the packets.

Ethernet is responsible for:

  • transmission within the local network;
  • interaction with the network equipment.

The physical level

Transmission of signals takes place:

  • electrical;
  • optical;
  • radio frequency.

Abstractions allow hiding complexity

It is precisely thanks to the layers that an engineer can use:

requests.get(...)

without thinking about:

  • TCP flags;
  • window sizes;
  • the operation of DMA;
  • the structure of Ethernet frames.

However, when problems arise, it is exactly the understanding of the underlying levels that allows effectively diagnosing the system.

The final engineering model of the network

Now everything can be brought together.
When an application sends data, the following happens:

Application
     ↓
Socket API
     ↓
System Call
     ↓
Kernel Space
     ↓
TCP/IP Stack
     ↓
Send Buffer
     ↓
NIC Driver
     ↓
DMA
     ↓
Network card
     ↓
Physical network
     ↓
Routers
     ↓
Remote machine
     ↓
NIC
     ↓
DMA
     ↓
Kernel Buffer
     ↓
TCP/IP Stack
     ↓
Receive Buffer
     ↓
recv()
     ↓
Application

This is the complete chain of data movement.

What an engineer must understand about networks

After studying the material written above, one must understand the following ideas.

1. The network is the transmission of bytes

Not files.
Not HTTP.
Not JSON.
But precisely bytes.

2. A socket is an OS interface

A socket is not a connection.
It provides the application with access to the network stack.

3. TCP is a state machine

Each connection has:

  • states;
  • buffers;
  • transmission control mechanisms.

4. Buffering is critically important

The send() and recv() operations work through buffers.
They do not interact with the network directly.

5. Asynchrony is connected with waiting

Most network applications are limited not by computation, but by waiting for events.

6. Performance depends not only on the network

A large influence is exerted by:

  • system calls;
  • memory copying;
  • the operation of processor caches;
  • DMA;
  • the implementation of the OS network stack.

7. Abstractions hide complexity

High-level libraries make development convenient.
But understanding what happens underneath them is what distinguishes an engineer from a user of technologies.

Conclusion

Understanding networks begins neither with studying protocols nor with writing servers.
It begins with understanding what happens between the application and the operating system.

We examined:

  • sockets as the interface for accessing the network;
  • system calls and interaction with the kernel;
  • TCP and its model of states;
  • buffers and flow control;
  • blocking and non-blocking input/output;
  • select, poll, and epoll;
  • the role of random access memory in network interactions;
  • Zero-Copy and DMA mechanisms;
  • the principles of the layered architecture of networks.

All these topics are united by one idea:

The network represents a mechanism for moving data between processes running on different computers, and the operating system acts as the intermediary that ensures this communication.

Understanding this model creates the foundation on which the further path of study is built:

  • the OSI and TCP/IP models;
  • routing;
  • DNS;
  • HTTP;
  • TLS;
  • high-load distributed systems.

It is from this moment that studying networks stops being a set of disconnected technologies and turns into a coherent engineering discipline.

🤖 Dubina