Python · Course navigation

TCP sockets: a loopback echo server

Open a listening socket, connect a client, and exchange bytes over a reliable stream.

On this page
Before you start

Threads and bytes versus strings.

  • Open a listening socket, connect a client, and exchange bytes over a reliable stream.
  • Predict the sample output and explain the state changes.
  • Complete the practice task and check the boundary cases.

The concept

TCP provides a reliable, ordered, byte-stream connection between two processes. A server creates a socket, binds it to an address and port, and listens. A client connects, and the server accepts the connection and gets a new socket for that client. Both sides then send and receive bytes until one of them closes.

The program runs both ends in one process using the loopback address 127.0.0.1, so no traffic leaves the machine. Asking the operating system to bind port 0 picks a free port automatically; getsockname() reports which one. The server thread uppercases what it receives and sends it back, and the client prints the reply.

TCP is a stream, not a sequence of messages. One send can arrive split across several recv calls, and two sends can arrive combined. Real protocols therefore add framing, such as a length prefix or a delimiter, and loop until a full message has been read. The single small message here arrives in one piece, but code should not depend on that.

Code example

Python
import socket
import threading

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 0))
server.listen(1)
port = server.getsockname()[1]


def serve():
    connection, _ = server.accept()
    with connection:
        data = connection.recv(1024)
        connection.sendall(data.upper())


thread = threading.Thread(target=serve)
thread.start()
with socket.create_connection(("127.0.0.1", port)) as client:
    client.sendall(b"hello tcp")
    reply = client.recv(1024)
thread.join()
server.close()
print(port > 0)
print(reply.decode())
Output
True
HELLO TCP

Follow the code

Open the walkthrough to inspect the example one step at a time.

Show guided walkthrough
Guided walkthroughReady
1import socket
2import threading
3
4server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
5server.bind(("127.0.0.1", 0))
6server.listen(1)
7port = server.getsockname()[1]
8
9
10def serve():
11 connection, _ = server.accept()
12 with connection:
13 data = connection.recv(1024)
14 connection.sendall(data.upper())
15
16
17thread = threading.Thread(target=serve)
18thread.start()
19with socket.create_connection(("127.0.0.1", port)) as client:
20 client.sendall(b"hello tcp")
21 reply = client.recv(1024)
22thread.join()
23server.close()
24print(port > 0)
25print(reply.decode())

Follow the example

Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.

View the complete dry-run table
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput so far
Bind to the loopback address; port 0 asks the OS for any free port.OS chooses a free port
—
Start listening, with room for one pending connection.OS chooses a free port
—
The server thread waits until a client connects, then gets a new socket just for that client.accept() returns the connection
—
The client sends the bytes b'hello tcp' over the established connection.accept() returns the connection
—
The server sent back the uppercase bytes, decoded here into text.accept() returns the connection
True
HELLO TCP

Behind the scenes

How Python processes this code

Python runs this source through its interpreter. It creates objects for values and resolves names as each statement runs.

Cost and common mistakes

Setup is O(1). Transferring n bytes takes O(n) time; TCP handles retransmission and ordering for you.

  • recv(n) returns up to n bytes, not necessarily a whole message. Loop until you have read a complete, framed message.
  • Sockets carry bytes. Encode text with .encode() and decode received data with .decode().
  • Close sockets, or use with blocks, to release the port and file descriptor. A server that never closes leaks resources.

Try it yourself

Why does the program bind to port 0 instead of a fixed port such as 8080?

Reveal answer and reasoning

A fixed port could already be in use on the machine. Port 0 lets the operating system pick a free port, which getsockname() then reports.

Further reading