TCP foundations

Build a TCP server

Create a loopback TCP server, accept a connection, send one message, and close it cleanly.

10 minute lesson

~~~

A socket is the endpoint your program uses to talk over a network. For TCP, one side listens and the other connects. In this lesson you build the listening side.

Node’s node:net module exposes TCP as asynchronous sockets. A server accepts connections; each accepted socket represents one independent byte stream between your process and one client.

Create server.mjs:

import net from 'node:net'

const server = net.createServer(socket => {
  socket.end('hello from TCP\n')
})

server.listen(4000, '127.0.0.1', () => {
  console.log('listening on 127.0.0.1:4000')
})

net.createServer() takes a callback that runs once per accepted connection. The socket argument is a duplex stream: you read client bytes from it and write replies to it. Here we write one line and close with socket.end().

server.listen(4000, '127.0.0.1') asks the operating system to reserve TCP port 4000 on the loopback interface. From that point the kernel completes handshakes for you and hands each new connection to the callback.

Verify the listener

Run the server, then check the port from another terminal:

ss -lnt | grep 4000
# LISTEN 0  511  127.0.0.1:4000  0.0.0.0:*

On macOS use lsof -nP -iTCP:4000 -sTCP:LISTEN instead. Either way you should see exactly one listener, and the local address column should show it bound only to loopback.

Now connect as a client with netcat:

nc 127.0.0.1 4000
# hello from TCP

nc prints the greeting and exits, because the server closed the connection right after writing.

When the port is taken

Start a second copy of the server while the first is still running:

Error: listen EADDRINUSE: address already in use 127.0.0.1:4000

EADDRINUSE means another process already owns that address and port pair. Find it with ss -lntp on Linux or the lsof command above, stop it, then retry. You’ll hit this constantly in development, usually because an old copy of your own server is still running in a forgotten terminal.

Keep the first lab on 127.0.0.1. Binding to all interfaces exposes the service to other reachable devices, and this server has no authentication yet.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →