Серверное программирование в 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.