Socket Programming

Learning Objectives

By the end of this reading, you will be able to:

  • Understand the socket API and its role in network programming
  • Explain the client-server model
  • Create TCP socket applications in Python
  • Create UDP socket applications in Python
  • Handle common socket programming scenarios
  • Debug and troubleshoot socket applications
  • Build practical networked applications

Introduction

Socket programming is the foundation of network communication. A socket is an endpoint for sending or receiving data across a computer network. Whether you're building a web server, chat application, or multiplayer game, you'll use sockets.

In this reading, we'll explore socket programming using Python, building practical examples of both TCP and UDP applications.

What is a Socket?

A socket is a software endpoint that establishes bidirectional communication between a server program and one or more client programs.

Socket Analogy

Think of a socket like a telephone:

Phone Call:
  1. You have a phone (socket)
  2. You dial a number (IP address and port)
  3. Someone answers (connection established)
  4. You talk (send/receive data)
  5. You hang up (close connection)

Socket Address

A socket address uniquely identifies a network endpoint:

Socket = IP Address + Port Number + Protocol

Examples:
  192.168.1.100:8080 (TCP)
  10.0.0.5:53 (UDP)
  localhost:3000 (TCP)

Socket Types

  1. Stream Sockets (SOCK_STREAM):

    • Use TCP
    • Reliable, connection-oriented
    • Data arrives in order
    • Error checking and retransmission
  2. Datagram Sockets (SOCK_DGRAM):

    • Use UDP
    • Unreliable, connectionless
    • No order guarantee
    • Faster, less overhead
  3. Raw Sockets (SOCK_RAW):

    • Direct IP access
    • Custom protocols
    • Requires administrative privileges

The Client-Server Model

Most networked applications follow the client-server model:

┌──────────────────┐              ┌──────────────────┐
│     Client       │              │     Server       │
│                  │              │                  │
│ 1. Connect to    │              │ 1. Listen on     │
│    server        │              │    port          │
│                  │              │                  │
│ 2. Send request  │─────────────>│ 2. Accept        │
│                  │              │    connection    │
│                  │              │                  │
│ 3. Receive       │<─────────────│ 3. Process       │
│    response      │              │    request       │
│                  │              │                  │
│ 4. Close         │              │ 4. Send response │
│    connection    │              │                  │
└──────────────────┘              └──────────────────┘

Server Responsibilities

  1. Create socket
  2. Bind to address (IP + port)
  3. Listen for connections
  4. Accept incoming connections
  5. Receive/send data
  6. Close connections

Client Responsibilities

  1. Create socket
  2. Connect to server
  3. Send/receive data
  4. Close connection

TCP Socket Programming in Python

Python Socket Module

Python's socket module provides access to the BSD socket interface.

import socket

# Common constants
socket.AF_INET      # IPv4
socket.AF_INET6     # IPv6
socket.SOCK_STREAM  # TCP
socket.SOCK_DGRAM   # UDP

TCP Server: Step by Step

Complete TCP Echo Server

import socket

def tcp_echo_server(host='127.0.0.1', port=8080):
    """
    Simple TCP echo server that echoes back received messages.
    """
    # 1. Create socket
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 2. Set socket options (optional but recommended)
    # Allow reuse of address (avoid "Address already in use" error)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    # 3. Bind socket to address
    server_socket.bind((host, port))
    print(f"Server bound to {host}:{port}")

    # 4. Listen for connections (backlog queue size = 5)
    server_socket.listen(5)
    print("Server listening for connections...")

    try:
        while True:
            # 5. Accept incoming connection (blocks until client connects)
            client_socket, client_address = server_socket.accept()
            print(f"Connection from {client_address}")

            try:
                while True:
                    # 6. Receive data (up to 1024 bytes)
                    data = client_socket.recv(1024)

                    # Empty data means client closed connection
                    if not data:
                        print(f"Client {client_address} disconnected")
                        break

                    print(f"Received from {client_address}: {data.decode()}")

                    # 7. Send response (echo back)
                    client_socket.sendall(data)

            except Exception as e:
                print(f"Error handling client {client_address}: {e}")
            finally:
                # 8. Close client connection
                client_socket.close()

    except KeyboardInterrupt:
        print("\nServer shutting down...")
    finally:
        # 9. Close server socket
        server_socket.close()

if __name__ == "__main__":
    tcp_echo_server()

TCP Server Methods Explained

# Create socket
socket.socket(family, type, proto)
# family: AF_INET (IPv4), AF_INET6 (IPv6)
# type: SOCK_STREAM (TCP), SOCK_DGRAM (UDP)

# Bind to address
socket.bind((host, port))
# host: IP address or hostname
# port: Port number (1-65535)

# Listen for connections
socket.listen(backlog)
# backlog: Maximum queued connections

# Accept connection (blocking)
client_socket, address = socket.accept()
# Returns: new socket for client, client address tuple

# Receive data
data = socket.recv(buffer_size)
# buffer_size: Maximum bytes to receive
# Returns: bytes object

# Send data
socket.send(data)          # May not send all data
socket.sendall(data)       # Ensures all data sent

# Close socket
socket.close()

TCP Client: Step by Step

Complete TCP Echo Client

import socket

def tcp_echo_client(host='127.0.0.1', port=8080):
    """
    Simple TCP echo client that sends messages and receives echoes.
    """
    # 1. Create socket
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    try:
        # 2. Connect to server
        client_socket.connect((host, port))
        print(f"Connected to server {host}:{port}")

        # 3. Send and receive messages
        while True:
            # Get user input
            message = input("Enter message (or 'quit' to exit): ")

            if message.lower() == 'quit':
                break

            # 4. Send data
            client_socket.sendall(message.encode())

            # 5. Receive response
            response = client_socket.recv(1024)
            print(f"Server echoed: {response.decode()}")

    except ConnectionRefusedError:
        print(f"Could not connect to {host}:{port}")
    except Exception as e:
        print(f"Error: {e}")
    finally:
        # 6. Close connection
        client_socket.close()
        print("Connection closed")

if __name__ == "__main__":
    tcp_echo_client()

Multi-Client TCP Server (Threading)

Handle multiple clients simultaneously:

import socket
import threading

def handle_client(client_socket, client_address):
    """
    Handle individual client connection.
    """
    print(f"New thread for client {client_address}")

    try:
        while True:
            data = client_socket.recv(1024)
            if not data:
                break

            print(f"[{client_address}] {data.decode()}")
            client_socket.sendall(data)

    except Exception as e:
        print(f"Error with client {client_address}: {e}")
    finally:
        client_socket.close()
        print(f"Client {client_address} disconnected")

def tcp_multi_client_server(host='127.0.0.1', port=8080):
    """
    TCP server that handles multiple clients using threads.
    """
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind((host, port))
    server_socket.listen(5)

    print(f"Multi-client server listening on {host}:{port}")

    try:
        while True:
            client_socket, client_address = server_socket.accept()
            print(f"Connection from {client_address}")

            # Create new thread for each client
            client_thread = threading.Thread(
                target=handle_client,
                args=(client_socket, client_address)
            )
            client_thread.daemon = True  # Thread dies when main thread exits
            client_thread.start()

    except KeyboardInterrupt:
        print("\nServer shutting down...")
    finally:
        server_socket.close()

if __name__ == "__main__":
    tcp_multi_client_server()

HTTP Server Example

Simple HTTP server that serves a webpage:

import socket

def http_server(host='127.0.0.1', port=8080):
    """
    Simple HTTP server that serves a basic webpage.
    """
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind((host, port))
    server_socket.listen(5)

    print(f"HTTP Server running on http://{host}:{port}")

    # HTML content to serve
    html_content = """<!DOCTYPE html>
<html>
<head>
    <title>Simple HTTP Server</title>
</head>
<body>
    <h1>Hello from Python Socket Server!</h1>
    <p>This page is served by a simple TCP socket.</p>
</body>
</html>"""

    # HTTP response
    http_response = f"""HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: {len(html_content)}
Connection: close

{html_content}"""

    try:
        while True:
            client_socket, client_address = server_socket.accept()

            # Receive HTTP request
            request = client_socket.recv(1024).decode()
            print(f"Request from {client_address}:")
            print(request[:200])  # Print first 200 chars

            # Send HTTP response
            client_socket.sendall(http_response.encode())
            client_socket.close()

    except KeyboardInterrupt:
        print("\nServer stopped")
    finally:
        server_socket.close()

if __name__ == "__main__":
    http_server()

Visit http://127.0.0.1:8080 in your browser!

UDP Socket Programming in Python

UDP is connectionless: no handshake, just send and receive datagrams.

UDP Server

import socket

def udp_echo_server(host='127.0.0.1', port=8080):
    """
    Simple UDP echo server.
    """
    # 1. Create UDP socket
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    # 2. Bind to address
    server_socket.bind((host, port))
    print(f"UDP Server listening on {host}:{port}")

    try:
        while True:
            # 3. Receive data and client address (no accept() needed)
            data, client_address = server_socket.recvfrom(1024)

            print(f"Received from {client_address}: {data.decode()}")

            # 4. Send response (no connection, specify destination)
            server_socket.sendto(data, client_address)

    except KeyboardInterrupt:
        print("\nServer shutting down...")
    finally:
        server_socket.close()

if __name__ == "__main__":
    udp_echo_server()

UDP Client

import socket

def udp_echo_client(host='127.0.0.1', port=8080):
    """
    Simple UDP echo client.
    """
    # 1. Create UDP socket
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    # Optional: Set timeout
    client_socket.settimeout(5.0)

    try:
        while True:
            message = input("Enter message (or 'quit' to exit): ")

            if message.lower() == 'quit':
                break

            # 2. Send data (no connection needed, specify destination)
            client_socket.sendto(message.encode(), (host, port))

            try:
                # 3. Receive response
                data, server_address = client_socket.recvfrom(1024)
                print(f"Server echoed: {data.decode()}")
            except socket.timeout:
                print("Request timed out")

    except Exception as e:
        print(f"Error: {e}")
    finally:
        client_socket.close()

if __name__ == "__main__":
    udp_echo_client()

TCP vs UDP Socket Comparison

# TCP (Connection-oriented)
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((host, port))
server.listen(5)
client_sock, addr = server.accept()  # Accept connection
data = client_sock.recv(1024)        # Receive from connected socket
client_sock.send(data)               # Send to connected socket
client_sock.close()                  # Close client connection

# UDP (Connectionless)
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind((host, port))
# No listen() or accept()
data, addr = server.recvfrom(1024)   # Receive with sender address
server.sendto(data, addr)            # Send to specific address
# No close() per client (no connection)

Practical Examples

1. Chat Server (TCP)

import socket
import threading

clients = []  # List of connected client sockets
lock = threading.Lock()  # Thread synchronization

def broadcast(message, sender_socket):
    """Send message to all clients except sender."""
    with lock:
        for client in clients:
            if client != sender_socket:
                try:
                    client.sendall(message)
                except:
                    clients.remove(client)

def handle_client(client_socket, client_address):
    """Handle individual chat client."""
    print(f"{client_address} joined the chat")

    # Ask for username
    client_socket.sendall(b"Enter your name: ")
    username = client_socket.recv(1024).decode().strip()

    # Announce new user
    join_msg = f"{username} joined the chat\n".encode()
    broadcast(join_msg, client_socket)

    try:
        while True:
            data = client_socket.recv(1024)
            if not data:
                break

            # Broadcast message with username
            message = f"{username}: {data.decode()}".encode()
            print(message.decode().strip())
            broadcast(message, client_socket)

    except Exception as e:
        print(f"Error with {username}: {e}")
    finally:
        with lock:
            if client_socket in clients:
                clients.remove(client_socket)

        leave_msg = f"{username} left the chat\n".encode()
        broadcast(leave_msg, client_socket)
        client_socket.close()
        print(f"{username} disconnected")

def chat_server(host='127.0.0.1', port=9999):
    """Multi-client chat server."""
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind((host, port))
    server_socket.listen(5)

    print(f"Chat server running on {host}:{port}")

    try:
        while True:
            client_socket, client_address = server_socket.accept()

            with lock:
                clients.append(client_socket)

            thread = threading.Thread(
                target=handle_client,
                args=(client_socket, client_address)
            )
            thread.daemon = True
            thread.start()

    except KeyboardInterrupt:
        print("\nShutting down chat server...")
    finally:
        server_socket.close()

if __name__ == "__main__":
    chat_server()

2. File Transfer Server (TCP)

import socket
import os

def file_server(host='127.0.0.1', port=9000, directory='./files'):
    """
    Simple file transfer server.
    Client sends filename, server sends file content.
    """
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind((host, port))
    server_socket.listen(5)

    print(f"File server running on {host}:{port}")
    print(f"Serving files from: {os.path.abspath(directory)}")

    try:
        while True:
            client_socket, client_address = server_socket.accept()
            print(f"Connection from {client_address}")

            try:
                # Receive filename
                filename = client_socket.recv(1024).decode().strip()
                filepath = os.path.join(directory, filename)

                print(f"Client requested: {filename}")

                # Check if file exists
                if os.path.isfile(filepath):
                    # Send file size first
                    file_size = os.path.getsize(filepath)
                    client_socket.sendall(f"{file_size}\n".encode())

                    # Send file content
                    with open(filepath, 'rb') as f:
                        while chunk := f.read(4096):
                            client_socket.sendall(chunk)

                    print(f"Sent {filename} ({file_size} bytes)")
                else:
                    client_socket.sendall(b"ERROR: File not found\n")
                    print(f"File not found: {filename}")

            except Exception as e:
                print(f"Error: {e}")
            finally:
                client_socket.close()

    except KeyboardInterrupt:
        print("\nServer stopped")
    finally:
        server_socket.close()

def file_client(host='127.0.0.1', port=9000, filename='test.txt'):
    """
    File transfer client.
    """
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    try:
        client_socket.connect((host, port))
        print(f"Connected to {host}:{port}")

        # Send filename
        client_socket.sendall(filename.encode())

        # Receive file size or error
        response = client_socket.recv(1024).decode()

        if response.startswith("ERROR"):
            print(response)
            return

        file_size = int(response.strip())
        print(f"Receiving {filename} ({file_size} bytes)...")

        # Receive file content
        received = 0
        with open(f"received_{filename}", 'wb') as f:
            while received < file_size:
                chunk = client_socket.recv(min(4096, file_size - received))
                if not chunk:
                    break
                f.write(chunk)
                received += len(chunk)

        print(f"File saved as received_{filename}")

    except Exception as e:
        print(f"Error: {e}")
    finally:
        client_socket.close()

if __name__ == "__main__":
    import sys

    if len(sys.argv) > 1 and sys.argv[1] == 'client':
        filename = sys.argv[2] if len(sys.argv) > 2 else 'test.txt'
        file_client(filename=filename)
    else:
        file_server()

3. Port Scanner (TCP)

import socket
from concurrent.futures import ThreadPoolExecutor

def scan_port(host, port, timeout=1):
    """
    Check if a port is open on a host.
    Returns: (port, True/False)
    """
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(timeout)
        result = sock.connect_ex((host, port))
        sock.close()
        return (port, result == 0)  # 0 means success (open)
    except:
        return (port, False)

def port_scanner(host, start_port=1, end_port=1024, threads=100):
    """
    Scan range of ports on a host.
    """
    print(f"Scanning {host} for open ports ({start_port}-{end_port})...")
    print("-" * 50)

    open_ports = []

    # Use thread pool for concurrent scanning
    with ThreadPoolExecutor(max_workers=threads) as executor:
        futures = [
            executor.submit(scan_port, host, port)
            for port in range(start_port, end_port + 1)
        ]

        for future in futures:
            port, is_open = future.result()
            if is_open:
                # Try to get service name
                try:
                    service = socket.getservbyport(port)
                except:
                    service = "unknown"

                print(f"Port {port:5d} - OPEN  ({service})")
                open_ports.append(port)

    print("-" * 50)
    print(f"Found {len(open_ports)} open ports")
    return open_ports

if __name__ == "__main__":
    # Scan common ports on localhost
    port_scanner('127.0.0.1', 1, 1024)

4. DNS Lookup Utility (UDP)

import socket

def dns_lookup(domain):
    """
    Perform DNS lookup for a domain.
    """
    print(f"Looking up: {domain}")
    print("-" * 50)

    try:
        # Get IP address
        ip_address = socket.gethostbyname(domain)
        print(f"IPv4 Address: {ip_address}")

        # Get all addresses (including IPv6)
        addr_info = socket.getaddrinfo(domain, None)

        print("\nAll addresses:")
        for info in addr_info:
            family, socktype, proto, canonname, sockaddr = info
            ip = sockaddr[0]

            family_name = "IPv6" if family == socket.AF_INET6 else "IPv4"
            socktype_name = "TCP" if socktype == socket.SOCK_STREAM else "UDP"

            print(f"  {family_name} ({socktype_name}): {ip}")

        # Reverse lookup
        try:
            hostname = socket.gethostbyaddr(ip_address)
            print(f"\nReverse lookup: {hostname[0]}")
        except:
            print("\nReverse lookup: Not available")

    except socket.gaierror as e:
        print(f"Error: {e}")
    except Exception as e:
        print(f"Error: {e}")

if __name__ == "__main__":
    dns_lookup("www.google.com")
    print("\n" + "=" * 50 + "\n")
    dns_lookup("www.github.com")

Socket Options and Advanced Techniques

Setting Socket Options

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Reuse address (avoid "Address already in use")
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# Set timeout (seconds)
sock.settimeout(5.0)

# Set buffer sizes
sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 4096)  # Receive buffer
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 4096)  # Send buffer

# TCP keep-alive (detect dead connections)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)

# Disable Nagle's algorithm (send immediately, don't buffer)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)

Non-Blocking Sockets

import socket
import select

# Create non-blocking socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setblocking(False)

# Use select() to wait for socket to be ready
readable, writable, exceptional = select.select([sock], [], [], timeout)

if readable:
    data = sock.recv(1024)

Context Manager (with statement)

# Automatically closes socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
    sock.connect(('example.com', 80))
    sock.sendall(b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
    response = sock.recv(4096)
# Socket automatically closed here

Common Issues and Debugging

1. Address Already in Use

# Error: [Errno 48] Address already in use

# Solution: Use SO_REUSEADDR
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

2. Connection Refused

# Error: [Errno 61] Connection refused

# Causes:
# - Server not running
# - Wrong host/port
# - Firewall blocking connection

# Debugging:
# 1. Check if server is running
# 2. Verify host and port
# 3. Test with telnet: telnet localhost 8080

3. Broken Pipe

# Error: [Errno 32] Broken pipe

# Cause: Writing to socket after remote end closed

# Solution: Check if connection is alive before writing
try:
    sock.sendall(data)
except BrokenPipeError:
    print("Connection closed by remote host")

4. Incomplete Data Reception

# Problem: recv() may not receive all data at once

# Wrong:
data = sock.recv(1024)  # May only receive partial data

# Right:
def recv_all(sock, length):
    """Receive exactly 'length' bytes."""
    data = b''
    while len(data) < length:
        chunk = sock.recv(length - len(data))
        if not chunk:
            raise ConnectionError("Connection closed")
        data += chunk
    return data

# Or for unknown length, receive until delimiter:
def recv_until(sock, delimiter=b'\n'):
    """Receive until delimiter found."""
    data = b''
    while True:
        chunk = sock.recv(1)
        if not chunk:
            break
        data += chunk
        if chunk == delimiter:
            break
    return data

5. Socket Timeout

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5.0)  # 5 second timeout

try:
    sock.connect(('example.com', 80))
    data = sock.recv(1024)
except socket.timeout:
    print("Operation timed out")

Best Practices

  1. Always Close Sockets: Use try/finally or context managers
  2. Handle Exceptions: Network operations can fail
  3. Set Timeouts: Prevent indefinite blocking
  4. Use SO_REUSEADDR: For servers
  5. Validate Input: Check data before sending
  6. Buffer Management: Handle partial sends/receives
  7. Thread Safety: Use locks for shared resources
  8. Security: Validate client input, use encryption for sensitive data
  9. Logging: Log connections, errors, and important events
  10. Graceful Shutdown: Close connections properly

Exercises

Basic Exercises

  1. Echo Server: Modify the echo server to:

    • a) Convert received text to uppercase before echoing
    • b) Log all messages to a file
    • c) Send a welcome message when client connects
  2. Time Server: Create a TCP server that:

    • Returns current time when client connects
    • Format: "Current time: HH:MM:SS"
    • Closes connection after sending time
  3. UDP vs TCP: Create both UDP and TCP versions of a simple calculator server:

    • Client sends: "5 + 3"
    • Server responds: "8"
    • Compare the implementations

Intermediate Exercises

  1. HTTP Client: Write a simple HTTP client that:

    • Connects to a web server
    • Sends GET request
    • Parses and displays response headers
    • Saves response body to file
  2. Chat Client: Write a client for the chat server example:

    • Send messages to server
    • Receive messages from other clients
    • Use threading to send and receive simultaneously
  3. File Transfer: Enhance the file transfer server:

    • Support uploading files (not just downloading)
    • Add progress bar for large files
    • Handle multiple clients simultaneously
  4. Port Scanner Enhancement: Improve the port scanner:

    • Add banner grabbing (identify service version)
    • Save results to JSON file
    • Add verbose mode

Advanced Exercises

  1. HTTP Server: Build a more complete HTTP server:

    • Serve files from directory
    • Support GET and POST methods
    • Parse query parameters
    • Return proper HTTP status codes (200, 404, 500)
    • Add logging
  2. Multiplayer Game: Create a simple multiplayer game:

    • Server maintains game state
    • Multiple clients can connect
    • Real-time updates (use UDP for game state, TCP for chat)
    • Handle player disconnections
  3. Proxy Server: Implement a simple HTTP proxy:

    • Client connects to proxy
    • Proxy forwards request to actual server
    • Proxy returns response to client
    • Add caching for repeated requests
  4. Secure Chat: Add encryption to the chat server:

    • Use SSL/TLS for encrypted communication
    • Implement user authentication
    • Add private messages between users
  5. Distributed System: Create a simple distributed key-value store:

    • Multiple server nodes
    • Clients can set/get values
    • Data replication across nodes
    • Handle node failures

Summary

In this reading, we explored socket programming fundamentals:

  • Sockets: Endpoints for network communication (IP + Port + Protocol)
  • Client-Server Model: Server listens, client connects
  • TCP Sockets: Reliable, connection-oriented (socket, bind, listen, accept, send/recv, close)
  • UDP Sockets: Unreliable, connectionless (socket, bind, sendto/recvfrom)
  • Python socket module: Provides complete socket API
  • Practical Applications: Chat servers, file transfer, HTTP servers, port scanners
  • Best Practices: Error handling, timeouts, proper cleanup, thread safety

Socket programming is the foundation for building networked applications, from simple clients to complex distributed systems.

Key Takeaways

  1. TCP provides reliability; UDP provides speed
  2. Always handle exceptions in network code
  3. recv() may not receive all data at once; loop until complete
  4. Use threading or async I/O for multiple clients
  5. Set socket options (SO_REUSEADDR, timeouts) appropriately
  6. Close sockets properly to free resources
  7. Security is critical: validate input, use encryption

Next Steps

Congratulations! You've completed Module 8: Networking. You now understand:

  • Network fundamentals and the OSI model
  • TCP/IP protocol suite and addressing
  • Application layer protocols (HTTP, DNS, email)
  • Network security basics
  • Socket programming in Python

Continue your learning:

  • Build real-world networked applications
  • Explore asynchronous I/O (asyncio, select, epoll)
  • Study network protocols in depth (Wireshark)
  • Learn about distributed systems and microservices
  • Investigate network security testing and penetration testing

Additional Resources

  • Python socket module documentation
  • Beej's Guide to Network Programming
  • RFC 1180: TCP/IP Tutorial
  • "Computer Networking: A Top-Down Approach" by Kurose and Ross
  • Wireshark for packet analysis
  • asyncio for asynchronous socket programming
  • Scapy for packet manipulation

This reading is part of Module 8: Networking

Module 8 Complete!

Return to Module Index or start with 01-fundamentals.md