🐍 Advanced Python Programming — NetworkingReal TCP Socket · Google Colab Notebook Pattern
SINGLE HTML · REAL SOCKET GUIDE
EXERCISE 1 · PATCH v2

Client–Server Chat for Google Colab

Run a real TCP server in a background thread, then use later Colab cells as clients. No fake network simulation.

Colab-native Socket Chat

Instead of keeping two notebook cells blocked by input(), the TCP server runs in a background thread. Other cells act as independent clients.

Server Thread← TCP →Client Function→send / recv
Cell 1 Start server in background
Cell 2 Define send_message()
Cell 3 Send messages as Alice/Bob
Cell 4 Inspect chat history
Cell 5 Stop server
The communication is still real TCP on 127.0.0.1:5000. Only the interaction style is adapted for a notebook.

Learning Target

Observe the complete socket lifecycle without simulating it:

socket() → bind() → listen()
accept() → recv()
connect() → sendall()
server reply → client recv()
close connection

Instruction

Copy this entire block into the first Colab cell and run it once.

Expected: [SERVER] listening on 127.0.0.1:5000. The cell finishes because the server itself continues inside a daemon thread.

Cell 1 · TCP Server

import socket
import threading

HOST = "127.0.0.1"
PORT = 5000

chat_history = []
server_running = True

def handle_client(conn, addr):
    try:
        data = conn.recv(1024).decode("utf-8")
        if not data:
            return

        chat_history.append(f"CLIENT {addr}: {data}")
        print(f"[SERVER] received: {data}")

        reply = f"ACK: {data}"
        conn.sendall(reply.encode("utf-8"))
    finally:
        conn.close()

def server_loop():
    global server_running

    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server.bind((HOST, PORT))
        server.listen()
        server.settimeout(1)

        print(f"[SERVER] listening on {HOST}:{PORT}")

        while server_running:
            try:
                conn, addr = server.accept()
                threading.Thread(
                    target=handle_client,
                    args=(conn, addr),
                    daemon=True
                ).start()
            except socket.timeout:
                pass

server_thread = threading.Thread(
    target=server_loop,
    daemon=True
)
server_thread.start()

Instruction

Run this in the second Colab cell. It defines a reusable client function but does not send anything yet.

socket()→connect()→sendall()→recv()

Cell 2 · Client Function

import socket

def send_message(username, message):
    payload = f"{username}: {message}"

    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client:
        client.connect((HOST, PORT))
        client.sendall(payload.encode("utf-8"))

        reply = client.recv(1024).decode("utf-8")

    print(f"[{username}] sent     : {message}")
    print(f"[{username}] received : {reply}")
    return reply

Cell 3 · Send Messages

send_message("Alice", "Hello Server")
send_message("Bob", "Hello Alice")
send_message("Alice", "How are you?")
Example output: [SERVER] received: Alice: Hello Server [Alice] received : ACK: Alice: Hello Server [SERVER] received: Bob: Hello Alice [Bob] received : ACK: Bob: Hello Alice

What is actually happening?

Each call creates a new TCP client socket.
Client connects to the background server.
Text becomes UTF-8 bytes.
Server receives bytes and returns ACK bytes.
The connection closes after one exchange.
Challenge: run the multi-message helper below.
def chat(username, messages):
    for message in messages:
        send_message(username, message)

chat("Alice", [
    "Hello!",
    "This is a real TCP socket.",
    "Nice to meet you."
])

chat("Bob", [
    "Hi Alice",
    "I am Bob."
])

Cell 4 · Server History

print("=== SERVER CHAT HISTORY ===")
for item in chat_history:
    print(item)
The shared chat_history proves that the background server processed the messages.

Cell 5 · Stop Server

server_running = False
server_thread.join(timeout=2)
print("[SERVER] stopped")
Advanced Mission: change the server so one client can keep its TCP connection open for multiple messages, then compare that design with the current one-message-per-connection model.