Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Implementing Different Server Models in Python

Tech May 13 1

Single-Process Server Model

import socket

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(('', 8000))
server_socket.listen(128)

while True:
    client_socket, client_address = server_socket.accept()
    
    while True:
        data = client_socket.recv(1024)
        if data:
            print(f'Received from {client_address}: {data.decode()}')
        else:
            print(f'Client {client_address} disconnected')
            break
    
    client_socket.close()

server_socket.close()

Key characteristics:

  • Handles one client at a time
  • Simple but inefficient for multiple clients
  • Client disconnection detected by empty recv()

Multi-Process Server Model

import socket
from multiprocessing import Process

def client_handler(client_socket, client_address):
    while True:
        data = client_socket.recv(1024)
        if data:
            print(f'Received from {client_address}: {data.decode()}')
        else:
            print(f'Client {client_address} disconnected')
            break
    client_socket.close()

if __name__ == '__main__':
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind(('', 8000))
    server_socket.listen(128)

    while True:
        client_socket, client_address = server_socket.accept()
        Process(target=client_handler, args=(client_socket, client_address)).start()
        client_socket.close()

Key characteristics:

  • Creates new process for each client
  • Better for moderate client counts
  • Resource-intensive for many clients

Non-Blocking Single-Process Server

import socket

active_clients = []

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(('', 8000))
server_socket.listen(128)
server_socket.setblocking(False)

while True:
    try:
        client_socket, client_address = server_socket.accept()
        client_socket.setblocking(False)
        active_clients.append((client_socket, client_address))
    except BlockingIOError:
        pass

    disconnected_clients = []
    
    for client in active_clients:
        sock, addr = client
        try:
            data = sock.recv(1024)
            if data:
                print(f'Received from {addr}: {data.decode()}')
            else:
                sock.close()
                disconnected_clients.append(client)
        except BlockingIOError:
            pass
    
    for client in disconnected_clients:
        active_clients.remove(client)

Key characteristics:

  • Single process handles multiple clients
  • Uses non-blocking sockets
  • Requires polling mechanism

Select-Based Server

import select
import socket
import queue

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(('', 8080))
server_socket.listen(128)

inputs = [server_socket]
outputs = []
message_queues = {}

while inputs:
    readable, writable, exceptional = select.select(inputs, outputs, inputs)
    
    for sock in readable:
        if sock is server_socket:
            client_socket, client_address = sock.accept()
            client_socket.setblocking(False)
            inputs.append(client_socket)
            message_queues[client_socket] = queue.Queue()
        else:
            data = sock.recv(1024)
            if data:
                message_queues[sock].put(data)
                if sock not in outputs:
                    outputs.append(sock)
            else:
                if sock in outputs:
                    outputs.remove(sock)
                inputs.remove(sock)
                sock.close()
                del message_queues[sock]
    
    for sock in writable:
        try:
            next_msg = message_queues[sock].get_nowait()
        except queue.Empty:
            outputs.remove(sock)
        else:
            sock.send(next_msg)

Key characteristics:

  • Uses select() system call
  • Handles multiple clients efficient
  • Limited by FD_SETSIZE (typically 1024)

Epoll-Based Server

import select
import socket
import queue

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(('', 8080))
server_socket.listen(128)

epoll = select.epoll()
epoll.register(server_socket.fileno(), select.EPOLLIN)

connections = {}
addresses = {}
queues = {}

while True:
    events = epoll.poll(1)
    
    for fd, event in events:
        if fd == server_socket.fileno():
            client_socket, client_address = server_socket.accept()
            client_socket.setblocking(False)
            fd = client_socket.fileno()
            epoll.register(fd, select.EPOLLIN)
            connections[fd] = client_socket
            addresses[fd] = client_address
            queues[fd] = queue.Queue()
        elif event & select.EPOLLIN:
            data = connections[fd].recv(1024)
            if data:
                queues[fd].put(data)
                epoll.modify(fd, select.EPOLLOUT)
            else:
                epoll.unregister(fd)
                connections[fd].close()
                del connections[fd]
                del addresses[fd]
                del queues[fd]
        elif event & select.EPOLLOUT:
            try:
                msg = queues[fd].get_nowait()
            except queue.Empty:
                epoll.modify(fd, select.EPOLLIN)
            else:
                connections[fd].send(msg)

Key characteristics:

  • More efficient than select()
  • No FD_SETSIZE limitation
  • Edge-triggered or level-triggered modes

Coroutine-Based Server

import gevent
from gevent import socket, monkey
monkey.patch_all()

def handle_client(client_socket):
    while True:
        data = client_socket.recv(1024)
        if not data:
            client_socket.close()
            break
        print(f"Received: {data}")
        client_socket.send(data)

def server(port):
    server_socket = socket.socket()
    server_socket.bind(('', port))
    server_socket.listen(5)
    
    while True:
        client_socket, addr = server_socket.accept()
        gevent.spawn(handle_client, client_socket)

if __name__ == '__main__':
    server(7788)

Key characterisitcs:

  • Lightweight green threads
  • Automatic context switching
  • Ideal for I/O-bound applications

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.