Network Programming

Unit 1 · Chapter 1 · Book pp. 3–30

Introduction

This chapter answers the only question that matters before any of the API makes sense: what is the thing we are programming? By the end of it you will have read a complete TCP client and a complete TCP server, line by line, and you will know where in the stack your code actually sits.

What network programming actually is

Every program you have written so far talked to things inside one machine — a file, a keyboard, a screen, some memory. Network programming is writing programs that talk to a process on a different machine, across a network you do not control, using rules both sides agreed on in advance.

Network
An interconnection of devices, called nodes or hosts.
Protocol
The agreement — who speaks first, what the bytes mean, what counts as an error, how the conversation ends. HTTP, SMTP, FTP and Telnet are protocols.
Network programming
Writing the code that makes two nodes hold such a conversation. In this course, that code is written against the sockets API.
A protocol is just etiquette. When you phone a shop, there is an unwritten script: they say hello first, you state what you want, they answer, one of you says goodbye. Nobody enforces it, but if you both ignore it the call fails. A network protocol is the same script, written down precisely enough that two machines built by two different companies in two different decades still understand each other.

What people actually build with it

The client/server model

Almost every network application splits into two unequal halves. This asymmetry is the single most important idea in the chapter, and it is the reason the API has two different sets of functions.

The server

Starts first. Initialises itself, announces where it can be reached, then goes to sleep waiting to be contacted. It does not know who will call or when. When a client arrives, it performs some service and — in the general case — goes back to waiting.

The client

Starts whenever a person or program wants something. It must already know where the server is. It initiates contact, asks for a service, uses the answer, and typically goes away.

Typical services a client asks for:

From a client's point of view it talks to many servers over a browsing session. From a server's point of view, at any instant it is normally talking to several clients at once — Figure 1.2 in the book. How one server handles many clients simultaneously is the entire subject of Chapter 4 (fork) and Chapter 6 (select). §1.1 · p.3

Two processes, four layers, one wire

The client and the server appear to talk to each other directly. They do not. Data goes down the protocol stack on one machine, across the physical network, and back up the stack on the other. Only the top layer — your program — gets the illusion of a direct line.

Figure 1.3Client and server on the same Ethernet, using TCP
user process kernel web client web server TCP TCP IP IP Ethernetdriver Ethernetdriver application protocol TCP protocol IP protocol Ethernet protocol actual flow between client and server application transport network datalink
Dashed horizontal lines are the virtual conversation each layer believes it is having with its peer. The yellow line is what physically happens. Note also that the client and server are user processes, while TCP and IP live in the kernel — that boundary is exactly where the sockets API sits. §1.1 · p.4
Posting a letter. You write it (application layer) and hand it over. You never touch the sorting office, the van or the plane, and the person who reads it never touches them either. To the two of you it was a direct conversation; in reality it went down through four organisations and back up through four more. If the van changes to a train nothing about your letter changes — which is precisely the benefit layering buys.

A first program: the daytime client

This is a complete, working TCP client in 27 lines. It connects to a daytime server — a standard service on port 13 that replies with the current date and time as human-readable text and then closes the connection. Step through it; the diagram underneath shows the connection coming into existence as you go.

Question 4(a)(ii) of Internals-1 2025 asked for a Daytime server that prints the connecting client's IP and port — a small extension of the server below. It is worked in full in the solved paper.

Why this client is not good enough

The program above works only with IPv4, because AF_INET, struct sockaddr_in and inet_pton(AF_INET, …) are all hard-coded. Making it work with IPv6 means changing the structure, the family constant and the conversion call. The book calls the better answer protocol independence — writing clients and servers that do not name a protocol at all — and reaches it in Chapter 11 with getaddrinfo. §1.3 · p.10

Error handling: wrapper functions

Nearly every socket function can fail, and checking each one inflates every example by three lines. The book's solution is a wrapper function: the same call, capitalised, that checks the return value, prints a message and terminates on error.

int
Socket(int family, int type, int protocol)
{
    int   n;

    if ( (n = socket(family, type, protocol)) < 0)
        err_sys("socket error");
    return (n);
}

So Socket(AF_INET, SOCK_STREAM, 0) means exactly socket(AF_INET, SOCK_STREAM, 0) plus "and die if it fails". The convention throughout the book — and throughout this site — is:

lowercase — socket(), bind()
The real system call. You must check its return value yourself.
Capitalised — Socket(), Bind()
The book's wrapper. It has already checked, and exits on failure.
In an exam answer, either style is accepted, but be consistent. If you write Socket(...) you must not then also test its return value for < 0 — that combination tells the examiner you have not understood what the wrapper is for.
err_sys is used when the failure set errno — a system call went wrong, so the message can include strerror(errno). err_quit is used when nothing set errno, such as the user supplying the wrong number of arguments. §1.4 · p.11

A first server: the daytime server

Its counterpart, in 24 lines. Notice how different the shape is: the client is a straight line from top to bottom, while the server is a loop that never ends.

A server calls socket, bind, listen, accept. A client calls socket, connect. That is the entire shape of TCP network programming; everything in Chapters 4 and 5 is detail hung on those six calls.
CallTelephone equivalent
socket()Having a telephone to use.
bind()Telling people your number so they can call you.
listen()Switching the ringer on, so you hear an incoming call.
connect()Knowing someone's number and dialling it.
accept()Picking up when the phone rings.

The analogy has one honest limit worth knowing, because it is exactly the kind of nuance that earns a mark: caller ID shows you the number before you decide whether to answer, whereas accept hands you the client's identity only after the connection already exists. And if DNS is involved, it plays the part of the phone book — getaddrinfo looks up a name to get a number, getnameinfo does the reverse. §4.6

The server calls socket() and so does the client. So what actually makes one of them a server?

Nothing about the socket itself — the call is identical. What makes it a server is the sequence that follows: bind() gives it a well-known address so it can be found, and listen() marks it passive, meaning it will receive connections rather than initiate them. A client instead calls connect(), which is the active open.

Put the other way round: a socket is neither until you commit it. The kernel assumes active until listen says otherwise.

The OSI model, and where sockets sit

The OSI (Open Systems Interconnection) model was described by ISO to give a standard vocabulary for describing any network. It has seven layers. Its practical value here is that it tells you exactly which layer your program is, and which layers you are entitled to ignore.

"Open" originally meant that networks could be interconnected regardless of the underlying hardware, as long as the software followed the standard. It has since come to imply modularity as well — you can replace one layer without touching the ones above and below it.
Figure 1.14OSI model beside the Internet protocol suite
OSI model Internet protocol suite 7 application 6 presentation 5 session 4 transport 3 network 2 datalink 1 physical application your program TCP UDP IPv4, IPv6 device driver and hardware user process application details kernel communication details sockets, XTI raw socket
The upper three OSI layers collapse into one thing — "the application" — because with Internet protocols there is rarely any distinction between them. The sockets API is the interface from that application down into the transport layer, which is exactly the boundary this whole course lives on. §1.7 · p.19

What each layer actually does

LayerJobData unitExamples
7 Application Provides network services to user programs. Logical connection is process-to-process, end to end — two chat windows, for instance. Messages HTTP, SMTP, FTP, TELNET, SSH, SNMP, DNS
6 Presentation Data representation — encoding, compression, encryption. (folded into the application in TCP/IP)
5 Session Dialogue control, checkpointing, recovery. (folded into the application in TCP/IP)
4 Transport Sequence control, error detection, retransmission and flow control. Logical connection is end-to-end. Segments (TCP) · user datagrams (UDP) TCP, UDP, SCTP
3 Network Routing and addressing, host to host. IP itself is connectionless: no flow control, no error control, no congestion control. Datagrams IPv4, IPv6 · helped by ICMP, IGMP, DHCP, ARP
2 Datalink Moves a datagram across one link — a wired LAN with a switch, a wireless LAN, a WAN. Frames Ethernet, PPP, Wi-Fi
1 Physical Moves data as electromagnetic signals over a medium. Data must be converted into signals. Bits Copper, fibre, radio
IP does not work alone. ICMP reports problems encountered while routing a packet. IGMP helps IP with multicasting. DHCP gets a host its network-layer address. ARP finds the link-layer address that corresponds to a known network-layer address.

Why layer at all

Raw socket
A socket that lets the application bypass the transport layer and speak to IP — or even to the datalink layer — directly. This is the gap drawn between TCP and UDP in Figure 1.14.
TLI / XTI
The Transport Layer Interface, an alternative API introduced in AT&T System V Unix, sitting between the OSI transport and session layers. XTI (X/Open Transport Interface) is its evolution. Sockets won; XTI is now mainly of historical interest.

Where the API came from

The sockets API originated in 4.2BSD, released in 1983, and spread from there into essentially every operating system. That history is why the API has some awkward corners — the sin_zero padding, the bzero/bcopy family, the word "socket" itself — that make no sense from first principles but plenty of sense as accumulated compatibility. §1.8 · p.20

Unix standards

Exam questions on this section are recall questions. Learn the names, the years and what each one added.

Unix was not designed once and then shipped. Dozens of vendors each grew their own version, and by the late 1980s a program written for one would not compile on another. Standardisation is the long, boring effort to make them agree again — and the reason your socket code compiles on Linux, macOS and Solaris alike.
UNICS → UNIX
The name began as UNiplexed Information Computing System, single-user and single-process. It later became UNIX — a multi-user, multiprocessing operating system.
POSIX
Portable Operating System Interface, an IEEE family of standards. Its primary purpose is to maintain compatibility between operating systems, particularly Unix-derived ones.
Austin Common Standards Revision Group (CSRG)
The body that now runs the standardisation work. Its output carries both the IEEE POSIX designation and The Open Group's Technical Standard designation.
StandardWhat it defined
IEEE Std 1003.1–1988 Process primitives (fork, exec, signals, timers), the process environment (user IDs, process groups), files and directories (all the I/O functions), terminal I/O, system databases (password and group files), and the tar and cpio archive formats.
IEEE Std 1003.1–1990 "Part 1: System Application Program Interface (API) [C Language]" — this is POSIX.1.
IEEE Std 1003.2–1992 "Part 2: Shell and Utilities" — this is POSIX.2.
IEEE Std 1003.1b–1993 Realtime extensions: file synchronisation, asynchronous I/O, semaphores, memory management (mmap and shared memory), execution scheduling, clocks and timers, message queues.
IEEE Std 1003.1, 1996 edition Adds threads: thread synchronisation (mutexes and condition variables), thread scheduling, synchronisation scheduling.
IEEE Std 1003.1g The networking API standard, referred to as POSIX.1g. It defines two APIs, which it calls Detailed Network Interfaces (DNIs):
1. DNI/Socket, based on the 4.4BSD sockets API.
2. DNI/XTI, based on the X/Open XPG4 specification.
The Open Group
An international consortium of vendors and end-user customers from industry, government and academia. Its combination with IEEE's work is what produced The Single Unix Specification; most Unix systems today conform to some version of POSIX.1 and POSIX.2, and many comply with Single Unix Specification Version 3.
IETF — Internet Engineering Task Force
A large, open, international community of network designers, operators, vendors and researchers concerned with the evolution of the Internet architecture and the smooth operation of the Internet. It publishes RFCs (Request For Comments) — UDP is RFC 768, for example.
Two bodies, two jobs, and it is easy to swap them in an answer. POSIX / The Open Group standardise the API — what socket() is called and what it returns. IETF standardises the protocols — what a TCP segment looks like on the wire.

64-bit architectures

Through the 1990s Unix systems used the ILP32 model: integers, longs and pointers all 32 bits. The move to 64 bits chose LP64 — long and pointer become 64 bits, but int stays 32.

TypeILP32LP64
char88
short1616
int3232
long3264
pointer3264
This is why htonl is a problem waiting to happen if you read its name literally. The "l" means 32-bit value, not long — it is a historical artefact from when long was 32 bits. Under LP64 a long is 64 bits, but htonl still converts 32. The same goes for "s": think 16-bit value, not short. §1.11 · p.28
Why must a socket address structure be zeroed before you fill it in, when you are about to assign every field you care about anyway?

Because you are not assigning every field. struct sockaddr_in contains sin_zero, an 8-byte padding member that exists only to bring the structure up to 16 bytes, and on BSD-derived systems there is also sin_len. You never assign those, so without a bzero they hold whatever was on the stack.

The structure is handed to the kernel wholesale, so those bytes go too. bzero is a one-line guarantee that nothing undefined crosses the boundary.

The client's read() returns 0. What happened, and is it an error?

It is not an error. A return of 0 from read on a socket means end-of-file: the peer has closed its end of the connection and sent a FIN. For the daytime protocol this is meaningful — the close is the end-of-message marker.

Errors are signalled by a return of -1, with the reason in errno. That is why the client tests n < 0 separately after the loop.