Серверное программирование в Python: создание и настройка серверов

Раздел: Сети -> Серверное программирование

Основные подходы к созданию сервера на Python

Как быстро запустить простой HTTP сервер для раздачи статических файлов?

Наиболее простой способ создать HTTP сервер на Python - использовать встроенный модуль http.server. Этот модуль позволяет в несколько строк поднять сервер, который отдает файлы из текущей директории. Пример запуска из командной строки:

python -m http.server 8000

создание сервера python (создание сервера на python)

После выполнения сервер будет доступен по адресу http://localhost:8000. Для программного создания сервера можно написать скрипт:


from http.server import HTTPServer, SimpleHTTPRequestHandler

server_address = ('', 8000)
httpd = HTTPServer(server_address, SimpleHTTPRequestHandler)
print('Сервер запущен на порту 8000...')
httpd.serve_forever()

Типичная проблема: порт занят другим процессом. Ошибка OSError: [Errno 98] Address already in use. Решение: указать другой порт или завершить процесс, использующий порт. Также может возникнуть ошибка доступа к файлам, если сервер запущен без прав на чтение каталога.

Цели использования: быстрая раздача статических файлов, тестирование веб-приложений, прототипирование. Не подходит для промышленной эксплуатации из-за однопоточности и отсутствия безопасности.

Как создать простой TCP сервер для передачи данных по собственному протоколу?

Для создания сервера, работающего на уровне транспортного протокола TCP, используется модуль socket. Приведем пример эхо-сервера, который возвращает клиенту полученные данные:


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(('localhost', 12345))
server_socket.listen(5)
print('TCP сервер запущен на порту 12345...')

while True:
    client_socket, addr = server_socket.accept()
    print(f'Подключен клиент: {addr}')
    data = client_socket.recv(1024)
    if data:
        client_socket.sendall(data)  # эхо
    client_socket.close()

Типичные ошибки: забыть установить SO_REUSEADDR - после перезапуска сервера может быть ошибка Address already in use. Необходимо корректно закрывать сокеты, иначе утечка дескрипторов. Однопоточная обработка блокирует подключения.

Цели использования: создание сетевых приложений с произвольным протоколом (чат, игры, обмен данными).

Как реализовать масштабируемый сервер с неблокирующим вводом-выводом?

Для асинхронной обработки множества соединений используется модуль asyncio. Пример асинхронного эхо-сервера:


import asyncio

async def handle_client(reader, writer):
    addr = writer.get_extra_info('peername')
    print(f'Подключен {addr}')
    while True:
        data = await reader.read(100)
        if not data:
            break
        writer.write(data)
        await writer.drain()
    writer.close()

async def main():
    server = await asyncio.start_server(handle_client, 'localhost', 8888)
    print('Асинхронный сервер запущен на порту 8888...')
    async with server:
        await server.serve_forever()

asyncio.run(main())

Проблема: при использовании asyncio необходимо понимать корутины и event loop. Ошибка RuntimeError: Event loop is closed часто возникает при неправильной остановке сервера. Также следует избегать блокирующих вызовов внутри корутин.

Цели использования: высоконагруженные сетевые сервисы, где требуется обработка тысяч соединений без создания потоков.

Как создать веб-приложение с динамическими маршрутами и бизнес-логикой?

Для создания полноценного веб-сервера с поддержкой шаблонов, маршрутизации и обработки запросов удобно использовать микрофреймворк Flask. Простейшее приложение:


from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
    return 'Привет, мир!'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

Типичные ошибки: забыть __name__ == '__main__' - Flask может запуститься дважды при некоторых WSGI серверах. Перегрузка приложения блокирующими операциями - сервер станет неотзывчивым. Для production использовать WSGI сервер, а не встроенный.

Цели использования: создание веб-сайтов, API, микросервисов. Быстрое прототипирование и гибкая архитектура.

Расширенные примеры создания серверов на Python

Многопоточный TCP сервер с использованием threading

Этот пример демонстрирует сервер, обрабатывающий каждое соединение в отдельном потоке, что позволяет параллельно обслуживать нескольких клиентов.

Пример

import socket
import threading

def handle_client(client_socket, addr):
    print(f'Обработка клиента: {addr}')
    try:
        while True:
            data = client_socket.recv(1024)
            if not data:
                break
            client_socket.sendall(data.upper())
    except Exception as e:
        print(f'Ошибка: {e}')
    finally:
        client_socket.close()
        print(f'Клиент {addr} отключен')

def main():
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind(('0.0.0.0', 9999))
    server.listen(10)
    print('Многопоточный сервер запущен на порту 9999...')

    while True:
        client, addr = server.accept()
        thread = threading.Thread(target=handle_client, args=(client, addr))
        thread.start()

if __name__ == '__main__':
    main()

Результат: после запуска сервера можно подключиться несколькими клиентами (например, через telnet). Каждое соединение обрабатывается в своем потоке. Пример вывода в консоль сервера:

Многопоточный сервер запущен на порту 9999...
Обработка клиента: ('127.0.0.1', 54321)
Обработка клиента: ('127.0.0.1', 54322)
Клиент ('127.0.0.1', 54321) отключен

Асинхронный echo сервер с asyncio и обработкой таймаутов

Расширенный вариант асинхронного сервера с таймаутом на чтение и логированием.

Пример

import asyncio
import logging

logging.basicConfig(level=logging.INFO)

async def echo_handler(reader, writer):
    addr = writer.get_extra_info('peername')
    logging.info(f'Новое соединение: {addr}')
    try:
        while True:
            data = await asyncio.wait_for(reader.read(100), timeout=30)
            if not data:
                break
            writer.write(data)
            await writer.drain()
    except asyncio.TimeoutError:
        logging.warning(f'Таймаут соединения с {addr}')
    except Exception as e:
        logging.error(f'Ошибка: {e}')
    finally:
        writer.close()
        logging.info(f'Соединение с {addr} закрыто')

async def main():
    server = await asyncio.start_server(echo_handler, 'localhost', 7777)
    addr = server.sockets[0].getsockname()
    logging.info(f'Сервер запущен на {addr}')
    async with server:
        await server.serve_forever()

if __name__ == '__main__':
    asyncio.run(main())

Результат: сервер принимает соединения, отвечает эхом и завершает при неактивности более 30 секунд. В лог выводится информация.

INFO:root:Сервер запущен на ('127.0.0.1', 7777)
INFO:root:Новое соединение: ('127.0.0.1', 60001)
INFO:root:Соединение с ('127.0.0.1', 60001) закрыто

Сервер на FastAPI с обработкой JSON запросов и документацией

FastAPI - современный асинхронный веб-фреймворк с автоматической генерацией документации OpenAPI.

Пример

from fastapi import FastAPI
from pydantic import BaseModel
import uvicorn

app = FastAPI()

class Item(BaseModel):
    name: str
    price: float

@app.post('/items/')
async def create_item(item: Item):
    # Имитация сохранения в БД
    return {'id': 123, 'name': item.name, 'price': item.price}

@app.get('/')
async def root():
    return {'message': 'Сервер работает!'}

if __name__ == '__main__':
    uvicorn.run(app, host='0.0.0.0', port=8000)

Результат: сервер запускается на порту 8000. Документация доступна по адресу /docs. Пример запроса через curl:

$ curl -X POST http://localhost:8000/items/ -H "Content-Type: application/json" -d '{"name":"Шляпа", "price": 100}'
{"id":123,"name":"Шляпа","price":100.0}

Автоматически генерируется интерактивная документация Swagger.

Создание сервера на Python - comments

En
создание сервера python (python)