Sockets Tutorial with Python 3 part 1 - sending and receiving data
Based on sentdex's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.
Use socket.AF_INET with IPv4 and socket.SOCK_STREAM for TCP byte-stream communication.
Briefing
A basic TCP socket setup in Python can reliably send and receive messages, but it also exposes a key reality of networking: TCP delivers a byte stream, not discrete “messages.” That difference drives the need for buffering logic and—eventually—protocol design (like sending a header that tells the receiver how much data to expect).
The walkthrough starts by creating two files, server.py and client.py, using Python’s built-in socket library. On the server side, it creates a TCP streaming socket with socket.AF_INET (IPv4) and socket.SOCK_STREAM (TCP). The server binds to a host/port tuple—using socket.gethostname() and port 1234—then calls listen(5) to queue up to five incoming connection attempts. In an infinite loop, it accepts a connection via accept(), which returns a new client socket object plus the client’s address. After printing a connection-established message, the server sends a UTF-8 encoded payload (“welcome to the server”) using client_socket.send(...).
On the client side, the same socket type is created, but instead of binding it calls connect((host, port)). With the connection established, the client receives bytes using recv(1024), then decodes them as UTF-8 and prints the resulting text. Running server.py and then client.py demonstrates the simplest case: the client receives the message and exits. The immediate disconnect behavior is treated as a limitation of the minimal example rather than a full “long-lived” chat-style design.
The tutorial then highlights the practical buffering problem. Because TCP is a stream, recv() reads up to a fixed buffer size; if the incoming data exceeds that size, the receiver may only get part of the payload. The example changes the buffer size down to 8 bytes, reruns the client, and shows that only a fragment (“welcome”) arrives unless the client keeps reading. Adding a while True loop to repeatedly call recv() allows the client to accumulate the full message into a full_message string by decoding each chunk and appending it.
However, the stream doesn’t end just because the message “feels complete.” Without a clear termination condition, the client can hang waiting for more bytes. The walkthrough points out that the connection remains open, so the loop never naturally stops. The fix demonstrated is to rely on connection closure as the end signal: when the server sends the message and the client reads until the stream ends, the client can break and print the full message. The takeaway is that real socket applications typically avoid guessing by defining a protocol—often sending a header that specifies payload length—so the receiver knows exactly how much data to read.
The next installment is teased as the place where buffering and keeping the stream open are handled more robustly, likely with a clearer message framing approach.
Cornell Notes
The tutorial builds a minimal TCP client/server in Python using socket.AF_INET and socket.SOCK_STREAM. The server binds to a host/port, listens with a queue size of 5, accepts a connection, and sends a UTF-8 message. The client connects, then receives bytes with recv(buffer_size) and decodes them to text. A central lesson follows: TCP is a byte stream, so recv() returns up to N bytes and may split a logical message across multiple reads. To reconstruct full data, the client must loop and buffer chunks until the stream ends or until a protocol tells it how much data to expect—an approach promised for the next video.
Why does the code use socket.AF_INET and socket.SOCK_STREAM, and what do they imply?
What role do bind(), listen(5), and accept() play on the server?
How does the client receive data, and why is recv(1024) not guaranteed to read the whole message?
What changes when the buffer size is reduced to 8 bytes?
Why can a buffering loop hang, and what termination condition is used in the example?
What protocol improvement is foreshadowed for real applications?
Review Questions
- In TCP, what does recv(buffer_size) return, and how does that affect reconstructing a logical message?
- What server-side calls are responsible for preparing for connections and creating a per-client socket?
- Why is relying on connection closure as an end-of-message signal fragile, and what alternative framing method is hinted at?
Key Points
- 1
Use socket.AF_INET with IPv4 and socket.SOCK_STREAM for TCP byte-stream communication.
- 2
Bind the server to a (host, port) tuple, then call listen(5) to queue incoming connection attempts.
- 3
Call accept() to create a dedicated client socket for each connected client.
- 4
Decode received bytes (e.g., UTF-8) after recv() returns raw byte chunks.
- 5
Treat TCP as a stream: recv(N) may return partial data, so buffer and loop to assemble full messages.
- 6
Avoid infinite recv loops by using a clear termination condition; connection closure works in the demo but is not ideal.
- 7
For robust messaging, send framing information (like a payload length header) so the receiver knows exactly how much to read.