ProjectsEcho Server
A server that sends back every byte a client sends it, first over TCP and then over UDP, following the one-page Echo Protocol in RFC 862. Building it teaches what sits under every network service: how a connection is accepted and how its end shows up, why TCP delivers a stream of bytes rather than messages, how one program serves many clients at once, and how it stops without losing data.
From Coding Challenges #100 ↗ by John Crickett
No sign-up. You tell the mentor your language: Java, Python or Node.js.
What you'll learn · 6 concepts
In the order the steps use them. Each one links to the best places to learn it, and you explain it in your own words once its steps are built.
Step 1 binds, listens and accepts one client by hand, and step 3 has to notice the moment that client goes away.
Covers: bind, listen and accept · the listening socket versus connected sockets · the queue of connections waiting to be accepted · wildcard and loopback addresses · privileged ports · the client's temporary port · a read of zero bytes means the peer closed
Beej's Guide to Network Programming ↗ · listen(2), Linux manual page ↗ · recv(2), Linux manual page (see the return value) ↗
Step 3 reads from a connection and writes the same bytes back, and it only works if those bytes are treated as a stream rather than as lines or messages.
Covers: no message boundaries · one write can arrive in several reads, and several writes in one · echo only the bytes each read returned · a write can take fewer bytes than it was given · bytes, not text · flow control and full buffers
RFC 9293: Transmission Control Protocol (TCP) ↗ · Message Framing (Stephen Cleary) ↗
Step 2 has to keep accepting new clients while earlier ones stay connected, which a single loop that waits on one client can't do.
Covers: why one loop can serve only one client · a thread or task per connection · an event loop over many sockets · memory and switching costs · slow and idle clients · state shared between connections
Concurrent Servers, Part 1: Introduction (Eli Bendersky) ↗ · Concurrent Servers, Part 3: Event-driven (Eli Bendersky) ↗ · The C10K problem (Dan Kegel) ↗
Step 4 runs the same service over UDP, where the server never sees a connection, only datagrams that each carry a return address.
Covers: connections versus independent datagrams · one socket for every client · replying to each sender's address · boundaries kept, delivery not promised · datagrams cut short by a small buffer · forged senders
RFC 768: User Datagram Protocol ↗ · RFC 8085: UDP Usage Guidelines ↗ · udp(7), Linux manual page ↗
Step 5 asks the server to close every connection and send back any echoes still in flight before it exits.
Covers: stop signals, and the one that can't be caught · stop accepting first · draining data already received · closing the write side to signal the end · waking handlers that are blocked · a deadline for clients that never leave
shutdown(2), Linux manual page ↗ · The ultimate SO_LINGER page, or: why is my TCP not reliable (Bert Hubert) ↗ · Pod Lifecycle, see Termination of Pods (Kubernetes documentation) ↗
Step 3 is where the RFC's single rule for TCP, send back whatever arrives until the client ends the connection, becomes exact behaviour, with nothing added and nothing left out.
Covers: what kind of RFC it is, and whether it is current · requirement words, and RFCs older than them · one statement, one testable rule · what the spec leaves open
RFC 862: Echo Protocol ↗ · How to Read an RFC (Mark Nottingham) ↗ · RFC 2119: Key words for use in RFCs to Indicate Requirement Levels ↗
What you'll build · 6 steps
Steps of 30 to 60 minutes, in your own editor and language. Each one has a check, so you know when it works.
Create the project, read RFC 862, and make sure netcat or telnet is installed to act as a client. The server listens on the port named in the PORT environment variable rather than the privileged port 7, so it runs and can be tested without special rights.
Works whenYou can say in a sentence what the RFC asks of the TCP service and of the UDP service, and nc can open a connection to a port on your machine.
Bind to all local addresses on the port from PORT, listen, accept a single TCP connection, log the client's address and port, and then exit.
Works whenWith the server running, connecting with nc to that port on localhost makes the server log the connection with the client's address, such as 127.0.0.1, and a client port that changes on every run, and then exit.
Concepts: sockets and the life of a connection
Keep accepting after the first client. The main loop goes back to listening while each client is handled by its own concurrent unit of work, a thread or an async task.
Works whenSeveral nc clients connected at the same time are each logged with a different client port, and the server keeps running when any of them leaves.
Concepts: serving many clients
Read whatever each client sends and write the same bytes back, until the client disconnects. Then log that the client disconnected and carry on serving the others.
Works whenA line typed into nc comes back unchanged, two clients connected together each get back only their own data, and closing a client logs the disconnection while the server keeps running.
Concepts: TCP is a byte stream, not a series of messages · sockets and the life of a connection · reading an RFC and turning it into behaviour
Add a -udp flag that runs the service over UDP on the same port. Each datagram received is sent back unchanged, in a datagram addressed to its sender. Log that the server is listening for UDP.
Works whenUsing nc in UDP mode with -u, a line typed and sent to the port comes back unchanged.
Concepts: streams versus datagrams
When asked to stop, stop accepting new clients, finish sending back everything already received, close every connection, and exit.
Works whenThe page gives no test. One way: with clients connected and data on its way, stop the server. Every byte it had already received still comes back, the clients see the connection close cleanly rather than reset, and the process exits promptly with a success code.
Concepts: graceful shutdown and draining