Skip to content
IRC-CodingIRC-Coding
Network protocolsTCP UDP IPDNS HTTP HTTPSOSI modelTCP IP stack

Network Protocols: TCP, UDP, IP, DNS, HTTP/HTTPS

Learn network protocol fundamentals: TCP, UDP, IP, DNS, HTTP/HTTPS. OSI model and TCP/IP stack with practical examples.

S

schutzgeist

25 min read
Network Protocols: TCP, UDP, IP, DNS, HTTP/HTTPS

Network Protocols: TCP, UDP, IP, DNS, HTTP/HTTPS & OSI/TCP-IP Models

This guide is a comprehensive introduction to network protocols — covering TCP, UDP, IP, DNS, HTTP/HTTPS, the OSI model, and the TCP/IP stack with practical examples.

In a Nutshell

Network protocols define how computers communicate. TCP/IP is the foundational protocol stack, the OSI model describes seven layers, HTTP/HTTPS enable web communication, and DNS translates domain names into IP addresses.

Quick Reference

Network protocols are standardized rules and conventions that govern data communication between computers on a network.

Key protocols:

TCP (Transmission Control Protocol)

  • Type: Connection-oriented, reliable
  • Features: Three-way handshake, flow control, error correction
  • Use cases: Web, email, file transfer
  • Ports: 80 (HTTP), 443 (HTTPS), 25 (SMTP)

UDP (User Datagram Protocol)

  • Type: Connectionless, unreliable
  • Features: Simple, fast, no guarantees
  • Use cases: Streaming, gaming, DNS
  • Ports: 53 (DNS), 123 (NTP), 67 (DHCP)

IP (Internet Protocol)

  • Type: Packet switching, routing
  • Versions: IPv4 (32-bit), IPv6 (128-bit)
  • Features: Addressing, fragmentation
  • Use cases: Foundation of the internet

DNS (Domain Name System)

  • Type: Name resolution
  • Function: Domain → IP address
  • Record types: A, AAAA, MX, NS, CNAME
  • Port: 53 (UDP/TCP)

HTTP/HTTPS (Hypertext Transfer Protocol)

  • Type: Application protocol for the web
  • Methods: GET, POST, PUT, DELETE
  • Status codes: 200 OK, 404 Not Found, 500 Server Error
  • Ports: 80 (HTTP), 443 (HTTPS)

Exam Essentials

These points form the backbone of IT certification exams (IHK AP1/AP2). Exams don’t just ask you to define terms — you need to explain differences, match use cases, and connect concepts.

  • Network protocols: Rules for computer-based communication. Why it matters: Without standardized protocols, different systems couldn’t talk to each other. Protocols define format, sequence, error handling, and confirmation.
  • TCP: Connection-oriented, reliable, three-way handshake (SYN → SYN-ACK → ACK). Why it matters: TCP guarantees complete, accurate data delivery. In exams, you need to explain the handshake and know when to use TCP (web, email, FTP).
  • UDP: Connectionless, fast, unreliable. Why it matters: UDP sends without establishing a connection and without waiting for acknowledgment. In exams, contrast it with TCP and name applications where speed beats reliability (DNS, VoIP, gaming, streaming).
  • IP: Packet switching, routing, IPv4 (32-bit) vs IPv6 (128-bit). Why it matters: IP is the foundation of the internet. You should explain the difference between IPv4 (limited addresses, NAT required) and IPv6 (virtually unlimited addresses) and understand how routers forward packets.
  • DNS: Name resolution, domain → IP address. Why it matters: DNS translates human-readable names into machine addresses. In exams, explain the hierarchical structure (root → TLD → authoritative) and record types (A, AAAA, MX, CNAME).
  • HTTP/HTTPS: Web communication, request/response. Why it matters: Explain the methods (GET, POST, PUT, DELETE) and status codes (200, 404, 500), and distinguish between HTTP (port 80, unencrypted) and HTTPS (port 443, TLS).
  • OSI Model: Seven-layer model. Why it matters: In exams, name the layers and assign them correctly: Layer 2 = Data Link (switch), Layer 3 = Network (router, IP), Layer 4 = Transport (TCP, UDP), Layer 7 = Application (HTTP, DNS).
  • TCP/IP Stack: Four-layer model. Why it matters: TCP/IP is the practical model used on the internet. You should be able to map the four layers to the seven OSI layers (e.g., TCP/IP Application = OSI 5–7).
  • Exam relevance: Foundation for network administration and development. Why it matters: AP1 and AP2 exams regularly test networks, protocols, and security. Repeated study leads to real understanding.

Core Components

These core components form the technical foundation of network communication. Master these building blocks, and you can not only describe protocols but also troubleshoot network problems and build your own networked applications.

  1. OSI Model: Theoretical seven-layer model. Why it matters: The OSI model gives you a shared language for describing network communication. When a colleague says “The problem is on Layer 3,” you immediately know routing and IP are involved. It helps you systematically narrow down faults.
  2. TCP/IP Stack: Practical four-layer model. Why it matters: TCP/IP is what actually runs the internet. It’s leaner than OSI and directly implementable. Understand TCP/IP, and you understand how the internet really works.
  3. Transport Protocols: TCP and UDP for data delivery. Why it matters: Your choice of transport protocol determines how reliably your application communicates. A banking app needs TCP; a live stream needs UDP. As a developer, you make this decision actively.
  4. Network Protocols: IP for routing and addressing. Why it matters: IP addresses are like postal addresses in your network. Without correct addressing, data doesn’t arrive. IPv4 versus IPv6 migration is a live topic in many organizations.
  5. Application Protocols: HTTP, DNS, SMTP for services. Why it matters: These protocols sit at the boundary between user and network. Every webpage uses HTTP, every domain name needs DNS. As a developer, you use them every day.
  6. Port Numbers: Identification of services. Why it matters: An IP address alone isn’t enough — multiple services often run on one server. Ports (like 80 for HTTP, 443 for HTTPS) route traffic to the right program. Know well-known ports for the exam.
  7. Socket Programming: Network communication. Why it matters: Sockets are the programming interface to networks. Understand sockets, and you can build your own servers and clients — that’s what separates a real developer from a user.
  8. Network Security: Firewalls, VPN, TLS. Why it matters: Every open network is a target. TLS encrypts data, firewalls block unwanted traffic. In exams and on the job, security isn’t optional — it’s essential.

Hands-On Examples

1. TCP Socket Programming with Java

What does this code do? This Java program shows a complete TCP server with thread pool management that handles multiple clients concurrently. It also includes a TCP client and performance tests for latency and throughput.

Why understand this? TCP is the most commonly used transport protocol on the internet. Learn how a TCP server works, and you understand how web servers, mail servers, and databases function at a fundamental level. The three-way handshake, connection management, and thread handling are core exam topics and everyday concepts on the job.

What will you learn?

  • How a ServerSocket accepts connections and hands them off to separate threads
  • How to exchange data bidirectionally using BufferedReader and PrintWriter
  • Why a thread pool matters — so you don’t create thousands of threads
  • How to measure latency and throughput programmatically
  • How to cleanly close client connections and free resources

If you don’t know Java or haven’t learned it yet, read through this example anyway. Focus on the structure instead: when are global variables set, when does the actual client/connection happen, when do try statements appear and why exactly there, and so on. You’ll find a Python example further down.

import java.io.*;
import java.net.*;
import java.util.concurrent.*;

public class TCPServerDemo {
    
    private static final int PORT = 8080;
    private static final int MAX_CLIENTS = 10;
    
    public static void main(String[] args) {
        System.out.println("=== TCP Server Demo ===");
        
        // Thread pool for client connections
        ExecutorService threadPool = Executors.newFixedThreadPool(MAX_CLIENTS);
        
        try (ServerSocket serverSocket = new ServerSocket(PORT)) {
            System.out.println("TCP Server started on port " + PORT);
            
            while (true) {
                // Wait for client connections
                Socket clientSocket = serverSocket.accept();
                System.out.println("New client connected: " + clientSocket.getInetAddress());
                
                // Handle client in separate thread
                threadPool.execute(new ClientHandler(clientSocket));
            }
            
        } catch (IOException e) {
            System.err.println("Server error: " + e.getMessage());
        } finally {
            threadPool.shutdown();
        }
    }
    
    // Client handler
    static class ClientHandler implements Runnable {
        private final Socket clientSocket;
        
        public ClientHandler(Socket socket) {
            this.clientSocket = socket;
        }
        
        @Override
        public void run() {
            try (
                BufferedReader in = new BufferedReader(
                    new InputStreamReader(clientSocket.getInputStream()));
                PrintWriter out = new PrintWriter(
                    clientSocket.getOutputStream(), true)
            ) {
                
                String clientAddress = clientSocket.getInetAddress().toString();
                System.out.println("Handler for " + clientAddress + " started");
                
                // Send greeting
                out.println("Welcome to TCP Server!");
                out.println("Type 'exit' to disconnect");
                
                // Receive messages from client and respond
                String inputLine;
                while ((inputLine = in.readLine()) != null) {
                    System.out.println("Message from " + clientAddress + ": " + inputLine);
                    
                    if ("exit".equalsIgnoreCase(inputLine.trim())) {
                        out.println("Goodbye!");
                        break;
                    }
                    
                    // Echo with timestamp
                    String response = "Echo: " + inputLine + " [" + 
                                   java.time.LocalDateTime.now() + "]";
                    out.println(response);
                }
                
            } catch (IOException e) {
                System.err.println("Error in client handler: " + e.getMessage());
            } finally {
                try {
                    clientSocket.close();
                    System.out.println("Client connection closed");
                } catch (IOException e) {
                    System.err.println("Error closing connection: " + e.getMessage());
                }
            }
        }
    }
    
    // TCP Client
    static class TCPClient {
        private final String hostname;
        private final int port;
        
        public TCPClient(String hostname, int port) {
            this.hostname = hostname;
            this.port = port;
        }
        
        public void start() {
            try (
                Socket socket = new Socket(hostname, port);
                BufferedReader in = new BufferedReader(
                    new InputStreamReader(socket.getInputStream()));
                PrintWriter out = new PrintWriter(
                    socket.getOutputStream(), true);
                BufferedReader stdIn = new BufferedReader(
                    new InputStreamReader(System.in))
            ) {
                
                System.out.println("Connected to server");
                
                // Receive server response
                String serverResponse = in.readLine();
                System.out.println("Server: " + serverResponse);
                
                // Interactive communication
                String userInput;
                while ((userInput = stdIn.readLine()) != null) {
                    out.println(userInput);
                    
                    serverResponse = in.readLine();
                    System.out.println("Server: " + serverResponse);
                    
                    if ("exit".equalsIgnoreCase(userInput.trim())) {
                        break;
                    }
                }
                
            } catch (UnknownHostException e) {
                System.err.println("Unknown host: " + hostname);
            } catch (IOException e) {
                System.err.println("I/O error: " + e.getMessage());
            }
        }
    }
    
    // TCP Performance Test
    static class TCPPerformanceTest {
        
        public static void testTCPLatency(String hostname, int port, int iterations) {
            try (Socket socket = new Socket(hostname, port);
                 PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
                 BufferedReader in = new BufferedReader(
                     new InputStreamReader(socket.getInputStream()))) {
                
                System.out.println("=== TCP Latency Test ===");
                
                long totalTime = 0;
                
                for (int i = 0; i < iterations; i++) {
                    long startTime = System.nanoTime();
                    
                    out.println("ping");
                    String response = in.readLine();
                    
                    long endTime = System.nanoTime();
                    long latency = (endTime - startTime) / 1_000_000; // ms
                    
                    totalTime += latency;
                    
                    if (i % 10 == 0) {
                        System.out.printf("Ping %d: %dms%n", i + 1, latency);
                    }
                }
                
                double avgLatency = (double) totalTime / iterations;
                System.out.printf("Average latency: %.2fms%n", avgLatency);
                
            } catch (IOException e) {
                System.err.println("Error in latency test: " + e.getMessage());
            }
        }
        
        public static void testTCPThroughput(String hostname, int port, int dataSize) {
            try (Socket socket = new Socket(hostname, port);
                 PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
                 BufferedReader in = new BufferedReader(
                     new InputStreamReader(socket.getInputStream()))) {
                
                System.out.println("=== TCP Throughput Test ===");
                
                // Create test data
                StringBuilder testData = new StringBuilder();
                for (int i = 0; i < dataSize; i++) {
                    testData.append("A");
                }
                
                long startTime = System.nanoTime();
                
                out.println("throughput:" + testData.toString());
                String response = in.readLine();
                
                long endTime = System.nanoTime();
                long duration = (endTime - startTime) / 1_000_000; // ms
                
                double throughput = (double) dataSize / (duration / 1000.0) / 1024.0; // KB/s
                System.out.printf("Throughput: %.2f KB/s (%d bytes in %dms)%n", 
                                 throughput, dataSize, duration);
                
            } catch (IOException e) {
                System.err.println("Error in throughput test: " + e.getMessage());
            }
        }
    }
    
    // Main for client tests
    public static void main(String[] args) {
        // Start server
        if (args.length == 0) {
            TCPServerDemo.main(args);
        } else {
            // Client tests
            TCPClient client = new TCPClient("localhost", PORT);
            
            // Interactive client
            new Thread(() -> client.start()).start();
            
            // Performance tests
            try {
                Thread.sleep(2000); // Wait for server to be ready
                TCPPerformanceTest.testTCPLatency("localhost", PORT, 100);
                TCPPerformanceTest.testTCPThroughput("localhost", PORT, 10240);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

2. UDP Socket Programming with Python

What does this code do? This Python program demonstrates a UDP server with broadcast capability, an interactive UDP client, performance tests for latency, throughput, and packet loss, plus a UDP multicast demo.

Why is this worth understanding? UDP is the opposite of TCP: fast but unreliable. Understanding UDP tells you when speed matters more than reliability. Broadcasting and multicast are fundamental concepts for network discovery, streaming, and IoT communication. On an exam, you need to be able to contrast TCP and UDP.

What will you learn?

  • How to create UDP sockets using socket.SOCK_DGRAM
  • Why UDP requires no connection setup and how recvfrom returns sender addresses
  • How broadcasting reaches all clients and multicast reaches a specific group
  • Why UDP experiences packet loss and how to measure it
  • How to send and receive simultaneously using threads

Pay special attention to the structure and layout of this example. Analyze the imports carefully and ask yourself why each one is imported and what purpose it serves.

import socket
import threading
import time
import json
from datetime import datetime

# UDP Server
class UDPServer:
    def __init__(self, host='localhost', port=9999):
        self.host = host
        self.port = port
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.clients = set()
        self.running = False
        
    def start(self):
        """Start the UDP server"""
        self.server_socket.bind((self.host, self.port))
        self.running = True
        print(f"UDP Server running on {self.host}:{self.port}")
        
        while self.running:
            try:
                # Receive data
                data, client_address = self.server_socket.recvfrom(2048)
                
                # Add client to list
                self.clients.add(client_address)
                
                print(f"Message from {client_address}: {data.decode()}")
                
                # Send message to all clients (broadcast)
                self.broadcast(data, client_address)
                
            except Exception as e:
                print(f"Error receiving: {e}")
    
    def broadcast(self, message, sender_address):
        """Send message to all clients"""
        timestamp = datetime.now().strftime("%H:%M:%S")
        broadcast_message = f"[{timestamp}] {sender_address[0]}: {message.decode()}"
        
        for client in self.clients:
            if client != sender_address:
                try:
                    self.server_socket.sendto(broadcast_message.encode(), client)
                except Exception as e:
                    print(f"Error sending to {client}: {e}")
    
    def stop(self):
        """Stop the server"""
        self.running = False
        self.server_socket.close()
        print("UDP Server stopped")

# UDP Client
class UDPClient:
    def __init__(self, host='localhost', port=9999):
        self.host = host
        self.port = port
        self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.running = False
        
    def start(self, username):
        """Start the UDP client"""
        self.running = True
        print(f"UDP Client connected to {self.host}:{self.port}")
        
        # Start receive thread
        receive_thread = threading.Thread(target=self.receive_messages)
        receive_thread.daemon = True
        receive_thread.start()
        
        try:
            while self.running:
                # Input message
                message = input(f"{username}: ")
                
                if message.lower() == 'exit':
                    break
                
                # Send message
                full_message = f"{username}: {message}"
                self.client_socket.sendto(full_message.encode(), (self.host, self.port))
                
        except KeyboardInterrupt:
            print("\nClient terminated")
        finally:
            self.stop()
    
    def receive_messages(self):
        """Receive messages from server"""
        while self.running:
            try:
                data, server = self.client_socket.recvfrom(2048)
                print(f"\r{data.decode()}")
                print("Message: ", end="", flush=True)
                
            except Exception as e:
                if self.running:
                    print(f"Error receiving: {e}")
                break
    
    def stop(self):
        """Stop the client"""
        self.running = False
        self.client_socket.close()
        print("UDP Client stopped")

# UDP Performance Test
class UDPPerformanceTest:
    def __init__(self, host='localhost', port=9999):
        self.host = host
        self.port = port
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        
    def test_latency(self, iterations=100):
        """UDP latency test"""
        print(f"=== UDP Latency Test ===")
        
        latencies = []
        
        for i in range(iterations):
            start_time = time.time()
            
            # Send ping message
            self.socket.sendto(b"ping", (self.host, self.port))
            
            # Receive response
            try:
                data, _ = self.socket.recvfrom(1024)
                end_time = time.time()
                
                latency = (end_time - start_time) * 1000  # ms
                latencies.append(latency)
                
                if i % 10 == 0:
                    print(f"Ping {i+1}: {latency:.2f}ms")
                    
            except socket.timeout:
                print(f"Timeout on ping {i+1}")
        
        if latencies:
            avg_latency = sum(latencies) / len(latencies)
            min_latency = min(latencies)
            max_latency = max(latencies)
            
            print(f"\nLatency statistics:")
            print(f"  Average: {avg_latency:.2f}ms")
            print(f"  Minimum: {min_latency:.2f}ms")
            print(f"  Maximum: {max_latency:.2f}ms")
    
    def test_throughput(self, data_size=1024, duration=5):
        """UDP throughput test"""
        print(f"\n=== UDP Throughput Test ===")
        
        # Create test data
        test_data = b'A' * data_size
        
        start_time = time.time()
        packets_sent = 0
        
        while time.time() - start_time < duration:
            try:
                self.socket.sendto(test_data, (self.host, self.port))
                packets_sent += 1
                
                # Brief pause to avoid overwhelming the server
                time.sleep(0.001)
                
            except Exception as e:
                print(f"Error sending: {e}")
                break
        
        end_time = time.time()
        total_time = end_time - start_time
        
        throughput = (packets_sent * data_size) / total_time / 1024  # KB/s
        packets_per_second = packets_sent / total_time
        
        print(f"Throughput statistics:")
        print(f"  Packets sent: {packets_sent}")
        print(f"  Total duration: {total_time:.2f}s")
        print(f"  Packets/second: {packets_per_second:.2f}")
        print(f"  Throughput: {throughput:.2f} KB/s")
    
    def test_packet_loss(self, packets=1000):
        """UDP packet loss test"""
        print(f"\n=== UDP Packet Loss Test ===")
        
        packets_lost = 0
        
        for i in range(packets):
            try:
                # Send sequential packet number
                packet = f"packet_{i}".encode()
                self.socket.sendto(packet, (self.host, self.port))
                
                # Brief pause
                time.sleep(0.001)
                
            except Exception as e:
                packets_lost += 1
        
        loss_rate = (packets_lost / packets) * 100
        print(f"Packet loss statistics:")
        print(f"  Packets sent: {packets}")
        print(f"  Packets lost: {packets_lost}")
        print(f"  Loss rate: {loss_rate:.2f}%")
    
    def close(self):
        """Close socket"""
        self.socket.close()

# UDP Multicast Demo
class UDPMulticastDemo:
    def __init__(self, multicast_group='224.0.0.1', port=5000):
        self.multicast_group = multicast_group
        self.port = port
        
    def create_multicast_sender(self):
        """Create multicast sender"""
        sender = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        
        # Set TTL (time-to-live: how many hops the packet can make)
        ttl = struct.pack('b', 1)
        sender.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl)
        
        return sender
    
    def create_multicast_receiver(self):
        """Create multicast receiver"""
        receiver = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        
        # Bind socket to multicast address
        receiver.bind(('', self.port))
        
        # Join the multicast group
        group = socket.inet_aton(self.multicast_group)
        receiver.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, group + socket.inet_aton('0.0.0.0'))
        
        return receiver
    
    def send_multicast(self, message):
        """Send multicast message"""
        sender = self.create_multicast_sender()
        
        try:
            sender.sendto(message.encode(), (self.multicast_group, self.port))
            print(f"Multicast sent: {message}")
        finally:
            sender.close()
    
    def receive_multicast(self):
        """Receive multicast messages"""
        receiver = self.create_multicast_receiver()
        
        try:
            print(f"Multicast receiver listening on {self.multicast_group}:{self.port}")
            
            while True:
                try:
                    data, address = receiver.recvfrom(1024)
                    print(f"Multicast from {address}: {data.decode()}")
                    
                except KeyboardInterrupt:
                    break
                    
        finally:
            # Leave the group
            group = socket.inet_aton(self.multicast_group)
            receiver.setsockopt(socket.SOL_IP, socket.IP_DROP_MEMBERSHIP, group + socket.inet_aton('0.0.0.0'))
            receiver.close()

# Main for UDP demos
def main():
    import sys
    
    if len(sys.argv) > 1:
        mode = sys.argv[1]
        
        if mode == "server":
            # Start UDP server
            server = UDPServer()
            try:
                server.start()
            except KeyboardInterrupt:
                server.stop()
                
        elif mode == "client":
            # Start UDP client
            username = input("Your name: ")
            client = UDPClient()
            client.start(username)
            
        elif mode == "performance":
            # Performance tests
            test = UDPPerformanceTest()
            test.test_latency(100)
            test.test_throughput(1024, 5)
            test.test_packet_loss(1000)
            test.close()
            
        elif mode == "multicast_send":
            # Multicast sender
            multicast = UDPMulticastDemo()
            while True:
                message = input("Multicast message (or 'exit'): ")
                if message.lower() == 'exit':
                    break
                multicast.send_multicast(message)
                
        elif mode == "multicast_receive":
            # Multicast receiver
            multicast = UDPMulticastDemo()
            multicast.receive_multicast()
            
        else:
            print("Usage: python udp_demo.py [server|client|performance|multicast_send|multicast_receive]")
    else:
        print("Usage: python udp_demo.py [server|client|performance|multicast_send|multicast_receive]")

if __name__ == "__main__":
    main()

3. DNS Lookups Across Different Programming Languages

What does this code do? This Java program demonstrates various DNS lookup techniques: standard name resolution (domain → IP), reverse DNS (IP → domain), retrieving all IP addresses for a hostname, DNS performance testing, and DNS caching comparison (first lookup vs. subsequent lookup).

Why is this worth understanding? DNS is the internet’s phonebook. Without it, you’d need to remember IP addresses. In exams and on the job, you’ll need to diagnose DNS problems—whether it’s a misconfigured A record, a stale cache entry, or an unreachable nameserver. Master DNS and you’ll understand why some websites are reachable and others aren’t.

What you’ll learn:

  • How InetAddress.getByName() translates domain names to IP addresses
  • How reverse DNS works using the same class
  • Why DNS caching matters and how it affects performance
  • How to benchmark DNS lookups programmatically
  • The difference between IPv4 (Inet4Address) and IPv6 (Inet6Address)
import java.net.*;
import java.util.*;

public class DNSLookupDemo {
    
    // Simple DNS lookup
    public static String dnsLookup(String hostname) {
        try {
            InetAddress address = InetAddress.getByName(hostname);
            return address.getHostAddress();
        } catch (UnknownHostException e) {
            return "Unknown host: " + hostname;
        }
    }
    
    // Reverse DNS lookup (IP → Hostname)
    public static String reverseDNSLookup(String ipAddress) {
        try {
            InetAddress address = InetAddress.getByName(ipAddress);
            return address.getHostName();
        } catch (UnknownHostException e) {
            return "Unknown IP: " + ipAddress;
        }
    }
    
    // Get all IP addresses for a hostname
    public static List<String> getAllIPAddresses(String hostname) {
        List<String> addresses = new ArrayList<>();
        
        try {
            InetAddress[] allAddresses = InetAddress.getAllByName(hostname);
            for (InetAddress addr : allAddresses) {
                addresses.add(addr.getHostAddress());
            }
        } catch (UnknownHostException e) {
            addresses.add("Unknown host: " + hostname);
        }
        
        return addresses;
    }
    
    // Query DNS record types (simplified)
    public static void dnsLookupWithTypes(String hostname) {
        System.out.println("=== DNS Lookup for " + hostname + " ===");
        
        // A record (IPv4)
        String ipv4 = dnsLookup(hostname);
        System.out.println("A record (IPv4): " + ipv4);
        
        // AAAA record (IPv6)
        try {
            InetAddress ipv6Address = InetAddress.getByName(hostname);
            if (ipv6Address instanceof Inet6Address) {
                System.out.println("AAAA record (IPv6): " + ipv6Address.getHostAddress());
            }
        } catch (UnknownHostException e) {
            System.out.println("AAAA record (IPv6): Not found");
        }
        
        // Reverse lookup
        if (!ipv4.equals("Unknown host: " + hostname)) {
            String reverse = reverseDNSLookup(ipv4);
            System.out.println("PTR record (Reverse): " + reverse);
        }
        
        // MX records (Mail Exchange)
        dnsLookupMX(hostname);
        
        // NS records (Name Server)
        dnsLookupNS(hostname);
    }
    
    // Query MX records
    public static void dnsLookupMX(String hostname) {
        try {
            // In practice, use DNS libraries like dnsjava for complete queries
            // Here we demonstrate the concept
            System.out.println("MX records: Use DNS libraries for full queries");
            
            // Simple logic example
            if (hostname.endsWith(".com")) {
                System.out.println("  MX: mail." + hostname + " (example)");
            }
            
        } catch (Exception e) {
            System.out.println("MX records: Query error - " + e.getMessage());
        }
    }
    
    // Query NS records
    public static void dnsLookupNS(String hostname) {
        try {
            System.out.println("NS records: Use DNS libraries for full queries");
            
            // Simple logic example
            String[] commonNS = {"ns1." + hostname, "ns2." + hostname};
            for (String ns : commonNS) {
                System.out.println("  NS: " + ns + " (example)");
            }
            
        } catch (Exception e) {
            System.out.println("NS records: Query error - " + e.getMessage());
        }
    }
    
    // DNS performance test
    public static void dnsPerformanceTest(String hostname, int iterations) {
        System.out.println("=== DNS Performance Test ===");
        
        long totalTime = 0;
        int successfulLookups = 0;
        
        for (int i = 0; i < iterations; i++) {
            long startTime = System.nanoTime();
            
            String result = dnsLookup(hostname);
            
            long endTime = System.nanoTime();
            long duration = (endTime - startTime) / 1_000_000; // ms
            
            if (!result.startsWith("Unknown host")) {
                totalTime += duration;
                successfulLookups++;
            }
            
            if (i % 10 == 0) {
                System.out.printf("Lookup %d: %dms%n", i + 1, duration);
            }
        }
        
        if (successfulLookups > 0) {
            double avgTime = (double) totalTime / successfulLookups;
            System.out.printf("Average DNS lookup time: %.2fms%n", avgTime);
            System.out.printf("Successful lookups: %d/%d%n", successfulLookups, iterations);
        }
    }
    
    // DNS caching demo
    public static void dnsCachingDemo(String hostname) {
        System.out.println("=== DNS Caching Demo ===");
        
        // First lookup (cache miss)
        long start = System.nanoTime();
        String result1 = dnsLookup(hostname);
        long firstLookup = (System.nanoTime() - start) / 1_000_000;
        
        // Second lookup (cache hit)
        start = System.nanoTime();
        String result2 = dnsLookup(hostname);
        long secondLookup = (System.nanoTime() - start) / 1_000_000;
        
        System.out.println("First lookup: " + firstLookup + "ms");
        System.out.println("Second lookup: " + secondLookup + "ms");
        
        if (secondLookup < firstLookup) {
            System.out.println("DNS caching active (second lookup faster)");
        } else {
            System.out.println("No DNS caching detected");
        }
        
        // Compare results
        System.out.println("Result 1: " + result1);
        System.out.println("Result 2: " + result2);
        System.out.println("Results match: " + result1.equals(result2));
    }
    
    // IPv6 support test
    public static void ipv6SupportTest(String hostname) {
        System.out.println("=== IPv6 Support Test ===");
        
        try {
            // Resolve IPv6 address
            InetAddress[] addresses = InetAddress.getAllByName(hostname);
            
            boolean hasIPv4 = false;
            boolean hasIPv6 = false;
            
            for (InetAddress addr : addresses) {
                if (addr instanceof Inet4Address) {
                    hasIPv4 = true;
                    System.out.println("IPv4: " + addr.getHostAddress());
                } else if (addr instanceof Inet6Address) {
                    hasIPv6 = true;
                    System.out.println("IPv6: " + addr.getHostAddress());
                }
            }
            
            System.out.println("IPv4 support: " + (hasIPv4 ? "Yes" : "No"));
            System.out.println("IPv6 support: " + (hasIPv6 ? "Yes" : "No"));
            
        } catch (UnknownHostException e) {
            System.out.println("IPv6 support test failed: " + e.getMessage());
        }
    }
    
    public static void main(String[] args) {
        String hostname = "google.com";
        
        if (args.length > 0) {
            hostname = args[0];
        }
        
        // Various DNS lookups
        dnsLookupWithTypes(hostname);
        
        // Performance test
        dnsPerformanceTest(hostname, 50);
        
        // Caching demo
        dnsCachingDemo(hostname);
        
        // IPv6 support test
        ipv6SupportTest(hostname);
    }
}

4. HTTP/HTTPS Client with Advanced Features

What does this code do? This Python program demonstrates a complete HTTP/HTTPS client with advanced capabilities: retry logic, various HTTP methods (GET, POST, HEAD, OPTIONS), SSL/TLS certificate validation, cookie handling, redirect tracking, file downloads with progress reporting, performance benchmarking, and a REST API client for common endpoint operations.

Why is this worth understanding? HTTP/HTTPS is the fundamental protocol driving the web. Every API call, every page load, and every data sync depends on it. Understanding how requests, responses, status codes, headers, and sessions work lets you debug APIs effectively, spot security issues, and build performant web applications. HTTP methods and status codes are standard exam material.

What you’ll learn:

  • How to build a reusable connection with requests.Session()
  • Why retry strategies matter and how to implement them with urllib3.util.retry
  • How to read SSL certificate information and TLS versions
  • How cookies are automatically passed between requests
  • How to follow or block redirects
  • How to efficiently download large files using stream=True
  • How to build a generic REST API client with CRUD operations
import requests
import json
import time
import ssl
import urllib3
from urllib3.util.retry import Retry
from datetime import datetime

# HTTP Client with advanced features
class HTTPClient:
    def __init__(self):
        self.session = requests.Session()
        self.setup_session()
        
    def setup_session(self):
        """Configure the session"""
        # Retry strategy
        retry_strategy = Retry(
            total=3,
            backoff_factor=1,
            status_forcelist=[429, 500, 502, 503, 504],
            allowed_methods=["HEAD", "GET", "OPTIONS"]
        )
        
        adapter = urllib3.HTTPAdapter(max_retries=retry_strategy)
        self.session.mount("https://", adapter)
        self.session.mount("http://", adapter)
        
        # Headers
        self.session.headers.update({
            'User-Agent': 'HTTPClient-Demo/1.0',
            'Accept': 'application/json',
            'Accept-Language': 'de-DE,de;q=0.9,en;q=0.8',
            'Cache-Control': 'no-cache'
        })
        
        # Timeout
        self.session.timeout = 30
        
        print("HTTP Client configured")
    
    def get_request(self, url, params=None):
        """GET request with error handling"""
        try:
            print(f"GET Request: {url}")
            
            start_time = time.time()
            response = self.session.get(url, params=params)
            end_time = time.time()
            
            self.print_response_info(response, end_time - start_time)
            return response
            
        except requests.exceptions.RequestException as e:
            print(f"GET request failed: {e}")
            return None
    
    def post_request(self, url, data=None, json_data=None):
        """POST request with data"""
        try:
            print(f"POST Request: {url}")
            
            if json_data:
                print(f"JSON data: {json.dumps(json_data, indent=2)}")
            
            start_time = time.time()
            
            if json_data:
                response = self.session.post(url, json=json_data)
            else:
                response = self.session.post(url, data=data)
            
            end_time = time.time()
            
            self.print_response_info(response, end_time - start_time)
            return response
            
        except requests.exceptions.RequestException as e:
            print(f"POST request failed: {e}")
            return None
    
    def print_response_info(self, response, duration):
        """Print response information"""
        print(f"Status Code: {response.status_code}")
        print(f"Response Time: {duration:.2f}s")
        print(f"Content-Type: {response.headers.get('Content-Type', 'N/A')}")
        print(f"Content-Length: {response.headers.get('Content-Length', 'N/A')}")
        
        # Print headers
        if response.headers:
            print("Response Headers:")
            for key, value in response.headers.items():
                print(f"  {key}: {value}")
    
    def test_http_methods(self, url):
        """Test different HTTP methods"""
        print(f"=== HTTP Methods Test for {url} ===")
        
        # GET
        print("\n--- GET ---")
        response = self.get_request(url)
        
        # HEAD
        print("\n--- HEAD ---")
        try:
            response = self.session.head(url)
            print(f"HEAD Status: {response.status_code}")
            print(f"HEAD Headers: {dict(response.headers)}")
        except Exception as e:
            print(f"HEAD request failed: {e}")
        
        # OPTIONS
        print("\n--- OPTIONS ---")
        try:
            response = self.session.options(url)
            print(f"OPTIONS Status: {response.status_code}")
            allowed_methods = response.headers.get('Allow', 'N/A')
            print(f"Allowed methods: {allowed_methods}")
        except Exception as e:
            print(f"OPTIONS request failed: {e}")
    
    def test_https_features(self, url):
        """Test HTTPS features"""
        print(f"=== HTTPS Features Test for {url} ===")
        
        try:
            # SSL certificate information
            response = self.session.get(url)
            
            # Print SSL information
            cert_info = response.raw._connection.peer.cert
            if cert_info:
                print("SSL Certificate Information:")
                for cert in cert_info:
                    print(f"  Subject: {cert.subject}")
                    print(f"  Issuer: {cert.issuer}")
                    print(f"  Valid from: {cert.not_valid_before}")
                    print(f"  Valid until: {cert.not_valid_after}")
                    print(f"  Serial: {cert.serial_number}")
            
            # TLS version
            tls_version = response.raw._connection.tls_version
            print(f"TLS Version: {tls_version}")
            
            # Cipher suite
            cipher_suite = response.raw._connection.cipher
            print(f"Cipher Suite: {cipher_suite}")
            
        except Exception as e:
            print(f"HTTPS features test failed: {e}")
    
    def test_authentication(self, url, username, password):
        """Test HTTP authentication"""
        print(f"=== Authentication Test for {url} ===")
        
        # Basic authentication
        try:
            from requests.auth import HTTPBasicAuth
            
            auth = HTTPBasicAuth(username, password)
            response = self.session.get(url, auth=auth)
            
            print(f"Basic Auth Status: {response.status_code}")
            
            if response.status_code == 200:
                print("Basic authentication successful")
            else:
                print("Basic authentication failed")
                
        except Exception as e:
            print(f"Authentication test failed: {e}")
    
    def test_cookies(self, url):
        """Test cookie handling"""
        print(f"=== Cookie Test for {url} ===")
        
        try:
            # First request without cookies
            response1 = self.session.get(url)
            print(f"First request status: {response1.status_code}")
            print(f"Cookies after first request: {len(self.session.cookies)}")
            
            # Second request with cookies
            response2 = self.session.get(url)
            print(f"Second request status: {response2.status_code}")
            print(f"Cookies after second request: {len(self.session.cookies)}")
            
            # Cookie information
            if self.session.cookies:
                print("Cookie Details:")
                for cookie in self.session.cookies:
                    print(f"  {cookie.name}: {cookie.value}")
                    print(f"    Domain: {cookie.domain}")
                    print(f"    Path: {cookie.path}")
                    print(f"    Secure: {cookie.secure}")
                    print(f"    HttpOnly: {cookie.has_attr('HttpOnly')}")
            
        except Exception as e:
            print(f"Cookie test failed: {e}")
    
    def test_redirects(self, url):
        """Test redirect handling"""
        print(f"=== Redirect Test for {url} ===")
        
        try:
            # Don't follow redirects
            response = self.session.get(url, allow_redirects=False)
            print(f"Status without redirects: {response.status_code}")
            
            if 300 <= response.status_code < 400:
                location = response.headers.get('Location', 'N/A')
                print(f"Redirect to: {location}")
            
            # Follow redirects
            response = self.session.get(url, allow_redirects=True)
            print(f"Status with redirects: {response.status_code}")
            print(f"Final URL: {response.url}")
            
            # Redirect history
            if hasattr(response.history, '__iter__'):
                print("Redirect history:")
                for i, resp in enumerate(response.history):
                    print(f"  {i+1}. {resp.status_code} -> {resp.url}")
            
        except Exception as e:
            print(f"Redirect test failed: {e}")
    
    def test_download(self, url, save_path=None):
        """Test file download"""
        print(f"=== Download Test for {url} ===")
        
        try:
            response = self.session.get(url, stream=True)
            
            content_length = int(response.headers.get('content-length', 0))
            print(f"File size: {content_length} bytes")
            
            if save_path:
                with open(save_path, 'wb') as f:
                    downloaded = 0
                    chunk_size = 8192
                    
                    for chunk in response.iter_content(chunk_size=chunk_size):
                        f.write(chunk)
                        downloaded += chunk_size
                        
                        # Show progress
                        progress = (downloaded / content_length) * 100 if content_length > 0 else 0
                        print(f"\rDownload: {progress:.1f}%", end="", flush=True)
                
                print(f"\nFile saved: {save_path}")
            
        except Exception as e:
            print(f"Download failed: {e}")
    
    def performance_test(self, url, iterations=10):
        """Performance test"""
        print(f"=== Performance Test for {url} ===")
        
        times = []
        
        for i in range(iterations):
            try:
                start_time = time.time()
                response = self.session.get(url)
                end_time = time.time()
                
                duration = end_time - start_time
                times.append(duration)
                
                if i % 5 == 0:
                    print(f"Request {i+1}: {duration:.3f}s")
                
            except Exception as e:
                print(f"Request {i+1} failed: {e}")
        
        if times:
            avg_time = sum(times) / len(times)
            min_time = min(times)
            max_time = max(times)
            
            print(f"\nPerformance Statistics:")
            print(f"  Requests: {len(times)}")
            print(f"  Average: {avg_time:.3f}s")
            print(f"  Minimum: {min_time:.3f}s")
            print(f"  Maximum: {max_time:.3f}s")
            print(f"  Requests/second: {1/avg_time:.2f}")

# API client for REST APIs
class APIClient(HTTPClient):
    def __init__(self, base_url):
        super().__init__()
        self.base_url = base_url.rstrip('/')
        
        # API-specific headers
        self.session.headers.update({
            'Content-Type': 'application/json',
            'Accept': 'application/json'
        })
    
    def get_resource(self, resource, params=None):
        """GET resource"""
        url = f"{self.base_url}/{resource.lstrip('/')}"
        return self.get_request(url, params)
    
    def create_resource(self, resource, data):
        """POST resource"""
        url = f"{self.base_url}/{resource.lstrip('/')}"
        return self.post_request(url, json_data=data)
    
    def update_resource(self, resource, data):
        """PUT resource"""
        url = f"{self.base_url}/{resource.lstrip('/')}"
        try:
            response = self.session.put(url, json=data)
            self.print_response_info(response, 0)
            return response
        except Exception as e:
            print(f"PUT request failed: {e}")
            return None
    
    def delete_resource(self, resource):
        """DELETE resource"""
        url = f"{self.base_url}/{resource.lstrip('/')}"
        try:
            response = self.session.delete(url)
            self.print_response_info(response, 0)
            return response
        except Exception as e:
            print(f"DELETE request failed: {e}")
            return None

# Main for HTTP demos
def main():
    import sys
    
    # HTTP client demo
    client = HTTPClient()
    
    # HTTP methods test
    client.test_http_methods("https://httpbin.org")
    
    # HTTPS features test
    client.test_https_features("https://google.com")
    
    # Redirect test
    client.test_redirects("http://httpbin.org/redirect/1")
    
    # Cookie test
    client.test_cookies("https://httpbin.org/cookies/set/test/value")
    
    # Performance test
    client.performance_test("https://httpbin.org/get", 20)
    
    # API client demo
    api_client = APIClient("https://jsonplaceholder.typicode.com")
    
    # GET users
    users_response = api_client.get_resource("/users")
    if users_response:
        print(f"Users: {users_response.json()}")
    
    # Create post
    new_post = {
        "title": "Test Post",
        "body": "Test Body",
        "userId": 1
    }
    create_response = api_client.create_resource("/posts", new_post)
    
    # Download test
    client.test_download("https://httpbin.org/bytes/1024", "test_download.bin")

if __name__ == "__main__":
    main()

OSI Model vs TCP/IP Stack

OSI Model (7 Layers)

LayerNameFunctionExamples
7ApplicationApplication protocolsHTTP, FTP, SMTP
6PresentationData transformationSSL/TLS, JPEG
5SessionSession managementNetBIOS, RPC
4TransportEnd-to-end communicationTCP, UDP
3NetworkRouting and addressingIP, ICMP
2Data LinkFrame transmissionEthernet, WiFi
1PhysicalBit transmissionCables, wireless

TCP/IP Stack (4 Layers)

LayerNameOSI EquivalentProtocols
4Application5-7HTTP, FTP, DNS
3Transport4TCP, UDP
2Internet3IP, ICMP
1Network Access1-2Ethernet, WiFi

Important Port Numbers

Well-Known Ports (0-1023)

PortProtocolService
20FTPFile Transfer (Data)
21FTPFile Transfer (Control)
22SSHSecure Shell
23TelnetRemote Terminal
25SMTPE-Mail
53DNSDomain Name System
80HTTPWeb (unencrypted)
110POP3E-Mail (Post Office)
143IMAPE-Mail (Internet Message)
443HTTPSWeb (encrypted)
993IMAPSIMAP over SSL
995POP3SPOP3 over SSL

Registered Ports (1024-49151)

PortProtocolService
3306MySQLDatabase
5432PostgreSQLDatabase
8080HTTP-AltWeb (alternative)
8443HTTPS-AltWeb (alternative)
27017MongoDBNoSQL database

TCP vs UDP Comparison

PropertyTCPUDP
ConnectionConnection-orientedConnectionless
ReliabilityGuaranteedNot guaranteed
OrderGuaranteedNot guaranteed
Flow ControlYesNo
Error CorrectionYesNo
OverheadHighLow
SpeedSlowerFaster
Use CasesWeb, EmailStreaming, Gaming

HTTP Status Codes Overview

1xx Informational

  • 100 Continue: Request accepted, continue sending
  • 101 Switching Protocols: Protocol upgrade in progress

2xx Success

  • 200 OK: Request successful
  • 201 Created: Resource created
  • 204 No Content: Request successful, no content to return

3xx Redirection

  • 301 Moved Permanently: Resource permanently moved
  • 302 Found: Resource temporarily moved
  • 304 Not Modified: Cached resource still valid

4xx Client Error

  • 400 Bad Request: Invalid request syntax
  • 401 Unauthorized: Authentication required
  • 403 Forbidden: Access denied
  • 404 Not Found: Resource not found
  • 429 Too Many Requests: Rate limit exceeded

5xx Server Error

  • 500 Internal Server Error: Server-side error
  • 502 Bad Gateway: Invalid response from upstream
  • 503 Service Unavailable: Server temporarily unavailable

DNS Record Types

TypeDescriptionExample
AIPv4 addressexample.com → 93.184.216.34
AAAAIPv6 addressexample.com → 2606:2800:220:1
MXMail Exchangeexample.com → mail.example.com
NSName Serverexample.com → ns1.example.com
CNAMECanonical Namewww.example.com → example.com
TXTText recordexample.com → “v=spf1 include:_spf.google.com ~all”
PTRPointer (Reverse)93.184.216.34 → example.com

Socket Programming Basics

TCP Socket Lifecycle

// Server
1. Create ServerSocket
2. Wait for connection (accept)
3. Create InputStream/OutputStream
4. Exchange data
5. Close connection

// Client
1. Create Socket
2. Connect to server
3. Create InputStream/OutputStream
4. Exchange data
5. Close connection

UDP Socket Lifecycle

// Server
1. Create DatagramSocket
2. Wait for packets (receive)
3. Process packet
4. Send response (send)

// Client
1. Create DatagramSocket
2. Send packet (send)
3. Wait for response (receive)
4. Close socket

Advantages and Disadvantages

Benefits of Network Protocols

  • Standardization: Systems can interoperate reliably
  • Scalability: Support for networks of any size
  • Reliability: Error handling and recovery mechanisms
  • Flexibility: Different protocols for different purposes

Drawbacks

  • Complexity: Multiple layers and protocols to manage
  • Overhead: Additional data required for protocol metadata
  • Security: Attack surface for malicious actors
  • Performance: Network latencies impact throughput

Common Exam Questions

  1. What’s the difference between TCP and UDP? TCP is connection-oriented and reliable, while UDP is connectionless and faster but offers no delivery guarantees.

  2. Explain the OSI model! The OSI model describes seven layers for network communication, ranging from Physical at the bottom to Application at the top.

  3. What is DNS and how does it work? DNS translates domain names to IP addresses and vice versa using a hierarchical system of nameservers.

  4. When do you use HTTP versus HTTPS? HTTP for unencrypted communication, HTTPS for encrypted communication secured with SSL/TLS.

Key Resources

  1. https://www.ietf.org/rfc/rfc791.html (TCP)
  2. https://www.ietf.org/rfc/rfc768.html (UDP)
  3. https://www.ietf.org/rfc/rfc1035.html (DNS)
  4. https://www.ietf.org/rfc/rfc2616.html (HTTP/1.1)
Back to Blog
Share:

Related Posts