Memra

Resource arithmetic, and the tool that shows it

◈ 14 cards

Compute the sockets and processes a concurrent server uses for K clients under both server models, and read the ss output that shows them.

Counting a connection-oriented concurrent server

Picture a concurrent, connection-oriented server with K sessions open at once. How many sockets is it holding, and how many processes are alive? A written paper is fond of this one, and it is pure bookkeeping once you have the passive/active distinction from L14.1.

Work it with K = 3.

Sockets. S created one socket, bound it, and called listen() on it. That is the passive socket. It carries no data and it stays open for the entire life of the server, because the moment it closes, no new client can arrive. Each of the three current clients then arrived and was accept()ed, and each accept() returned a new active socket — one per client, because a full association needs a distinct remote IP and remote port pair, and three different clients have three different ones. So the count is 1 passive + 3 active = 4 sockets, which for K clients is K + 1.

Processes. The master forked once per client. Three slaves are alive, each holding one active socket and serving one session. The master is also alive, sitting in accept(). So there are 3 slave processes, or 4 processes in total counting the master. Say which you are counting — an answer of "K" and an answer of "K + 1" are both correct with the right sentence attached, and both are wrong without it. The safe form is: K slave processes, plus the master, so K + 1 processes in all.

One subtlety worth stating in an exam answer: the master closes its copy of each accepted socket immediately after forking, so it is not holding four sockets itself. The four exist across the process group. If the master failed to close its copies it would hold K + 1 descriptors and the connections would never tear down properly.

Counting a connectionless concurrent server

Now suppose S is connectionless instead. The naive answer is that the counts are the same, and that is the trap.

There is no accept(), because there is no connection to accept. There is no per-client socket, because there is no per-client connection state — a datagram carries the sender's address inside it, and recvfrom() hands that address to you along with the data. So the master holds one bound datagram socket, permanently, whatever K is. The socket count is 1, not K + 1.

The process count is different from the socket count here, and that asymmetry is the whole point of the question. The master still forks a slave per outstanding request, so there are still K slaves — each of them builds one response, sendto()s it, and exits. K slaves plus the master is K + 1 processes.

There is one legitimate variation to name if you want the extra mark: some connectionless designs have each slave open its own unbound socket to send the reply from, rather than sharing the master's. That model gives 1 + K sockets. State which model you are assuming and why, and either answer is defensible; state neither and the marker cannot give you the mark.

The reason the two models differ at all is one sentence: a connection is a full association, and a connectionless service has none to keep.

Seeing the count: ss

None of that arithmetic is hypothetical — you can look. The tool is ss, the socket statistics utility from iproute2. It reads the kernel's socket tables directly, and it supersedes the deprecated netstat, which the textbook still uses and which is no longer installed by default on many Linux systems.

Start the server, connect three clients, and look:

$ ss -ltn
State    Recv-Q   Send-Q   Local Address:Port   Peer Address:Port
LISTEN   0        16             0.0.0.0:9000        0.0.0.0:*

That is the passive socket — one line, LISTEN, bound to the wildcard address on port 9000 because we used INADDR_ANY. Send-Q on a listening socket is the backlog you passed to listen().

$ ss -tn
State    Recv-Q   Send-Q   Local Address:Port   Peer Address:Port
ESTAB    0        0          127.0.0.1:9000       127.0.0.1:41892
ESTAB    0        0          127.0.0.1:9000       127.0.0.1:41894
ESTAB    0        0          127.0.0.1:9000       127.0.0.1:41896

Three established sockets, all sharing local port 9000, each with a different peer port. There is the full association, printed: same local end, different remote ends, which is exactly why three distinct sockets are needed and why one could not have done the job. Four lines total, and 3 + 1 = 4 is the count we derived.

The five invocations the assignment asks about

ss -s prints a summary: total socket counts broken down by transport and by state. The -s is for summary, not for sockets and not for stream. It is the one invocation that prints no per-socket lines at all.

ss -l shows listening sockets only — the passive ones. -l is listening, not long. Its complement is -a (all), which shows listening and non-listening sockets together; plain ss with neither shows only the non-listening ones, which is why a bare ss never lists your server's listener.

ss -w -a shows all raw sockets. -w selects the raw socket family (as -t selects TCP and -u selects UDP), and -a is what widens the selection from non-listening to all — listening and non-listening alike. Without -a you would see only the non-listening raw sockets. Raw sockets need privilege to create, so this is usually a short list of things like ping.

ss -4 state syn-sent shows IPv4 sockets in the SYN-SENT state. -4 restricts the address family to IPv4 (-6 is the IPv6 counterpart). state syn-sent is a state filter, and SYN-SENT means an outbound connection whose SYN has been sent and not yet acknowledged — a connect() in flight. It is not "a socket that is sending SYNs": it is a socket waiting for the other half of the handshake, so a pile of them usually means a host you cannot reach.

time ss -atr times the execution of the listing. Take the flags apart: -a all sockets, -t TCP only, -r resolve numeric addresses and ports to names. The -r is the interesting one, and it is why time is worth putting in front. Resolution means a DNS lookup per address, and every lookup that has to leave the machine, or that times out, is added to the wall-clock the time prefix reports. The same listing with -n (numeric, the opposite of -r) returns essentially instantly. Running both and comparing the two real figures is the point of the exercise.

source S&K 3e ch20 §20.7, Q&P 41; ss(8) from iproute2 — the textbook never mentions ss

listen/acceptforkforkreadsreadsfd3passive, LISTENmasterin accept()slave1client Aslave2client Bfd4active, ESTABfd5active, ESTABfd3 carries no data. All traffic is on fd4 and fd5.
Count the socket nodes: one passive plus one per client. With K = 2 that is 3 sockets and 3 processes; in general K + 1 of each, where the processes are K slaves plus the master.

source S&K 3e ch20 §20.7, Q&P 41; ss(8) from iproute2 — the textbook never mentions ss

OptionSelectsThe misreading-sa summary of socket countsnot "sockets"-llistening sockets onlynot "long"-alistening AND non-listeningnot "address"-t / -u / -wTCP / UDP / raw socketsfamily selectors-4 / -6IPv4 / IPv6 onlyaddress family-r / -nresolve names / staynumeric-r is slowstate syn-sent filters by connection state: SYN sent, not yet acknowledged.
Selection options (-t, -u, -w, -4) narrow which sockets are considered; -a and -l narrow by listening state; -r and -n control name resolution; -s replaces the listing with a summary.

source S&K 3e ch20 §20.7, Q&P 41; ss(8) from iproute2 — the textbook never mentions ss

source S&K 3e ch20 §20.7, Q&P 41; ss(8) from iproute2 — the textbook never mentions ss

source S&K 3e ch20 §20.7, Q&P 41; ss(8) from iproute2 — the textbook never mentions ss

source S&K 3e ch20 §20.7, Q&P 41; ss(8) from iproute2 — the textbook never mentions ss

source S&K 3e ch20 §20.7, Q&P 41; ss(8) from iproute2 — the textbook never mentions ss

NORMAL ~/memra/learn/comp-325/counting-sockets-and-processes-and-reading-ss utf-8 LF