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()orrecv()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:
- A system call is performed.
- The process hands control over to the kernel.
- The kernel creates an internal socket object.
- The necessary data structures are allocated.
- The send and receive buffers are initialized.
- An entry is created in the process's file descriptor table.
- 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_WAITstate; - 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:
- The data is copied from user space into the kernel buffer.
- The function returns control to the application.
- 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:
- The network card receives the data.
- The kernel processes the packet.
- The data is placed into the Receive Buffer.
- 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:
- Find the data on disk.
- Load the page into RAM.
- Update the page tables.
- 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.
The data link level
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, andepoll;- 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.