> **Источник:** https://python-all.ru/3.0/library/socketserver.html
>
> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.

---

# `socketserver` – фреймворк для сетевых серверов

Модуль `socketserver` упрощает задачу написания сетевых серверов.

Существует четыре базовых класса серверов: `TCPServer` использует протокол TCP, который обеспечивает непрерывные потоки данных между клиентом и сервером. `UDPServer` использует дейтаграммы – дискретные пакеты информации, которые могут прийти не по порядку или потеряться при передаче. Реже используемые классы `UnixStreamServer` и `UnixDatagramServer` похожи, но используют сокеты домена Unix; они недоступны на платформах, отличных от Unix. Дополнительные сведения о сетевом программировании можно найти в таких книгах, как UNIX Network Programming У. Ричарда Стивенса или Win32 Network Programming Ральфа Дэвиса.

Эти четыре класса обрабатывают запросы *синхронно*; каждый запрос должен быть завершен до того, как может начаться следующий. Это не подходит, если каждый запрос требует много времени на выполнение из-за вычислительной сложности или из-за возврата большого объема данных, которые клиент обрабатывает медленно. Решение – создавать отдельный процесс или поток для обработки каждого запроса; миксин-классы `ForkingMixIn` и `ThreadingMixIn` можно использовать для поддержки асинхронного поведения.

Создание сервера состоит из нескольких шагов. Сначала необходимо создать класс-обработчик запросов, унаследовав его от `BaseRequestHandler` и переопределив его метод [`handle()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle); этот метод будет обрабатывать входящие запросы. Затем нужно создать экземпляр одного из серверных классов, передав ему адрес сервера и класс обработчика запросов. И наконец, вызвать метод [`handle_request()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle_request) или [`serve_forever()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.serve_forever) объекта сервера для обработки одного или нескольких запросов.

При наследовании от `ThreadingMixIn` для поведения с потоками необходимо явно указать, как потоки должны вести себя при аварийном завершении. Класс `ThreadingMixIn` определяет атрибут *daemon\_threads*, который указывает, должен ли сервер ждать завершения потоков. Следует явно установить этот флаг, если требуется, чтобы потоки работали автономно; по умолчанию [`False`](https://python-all.ru/3.0/library/constants.html#False), то есть Python не завершится, пока все потоки, созданные `ThreadingMixIn`, не завершатся.

Классы серверов имеют одни и те же внешние методы и атрибуты, независимо от того, какой сетевой протокол они используют.

## Замечания по созданию серверов

В иерархии наследования пять классов, четыре из которых представляют синхронные серверы четырёх типов:

```python
+------------+
| BaseServer |
+------------+
      |
      v
+-----------+        +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+        +------------------+
      |
      v
+-----------+        +--------------------+
| UDPServer |------->| UnixDatagramServer |
+-----------+        +--------------------+
```

Обратите внимание, что `UnixDatagramServer` наследует от `UDPServer`, а не от `UnixStreamServer` – единственное различие между IP- и Unix-стрим-сервером заключается в семействе адресов, которое просто повторяется в обоих Unix-классах серверов.

Версии с порождением процессов и потоками для каждого типа сервера можно создать с помощью миксин-классов `ForkingMixIn` и `ThreadingMixIn`. Например, класс потокового UDP-сервера создаётся следующим образом:

```python
class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
```

Класс-примесь должен идти первым, так как он переопределяет метод, определённый в `UDPServer`. Настройка различных переменных-членов также изменяет поведение базового серверного механизма.

Чтобы реализовать службу, необходимо создать класс, производный от `BaseRequestHandler`, и переопределить его метод [`handle()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle). Затем можно запускать различные версии службы, комбинируя один из серверных классов с вашим классом обработчика запросов. Класс обработчика запросов должен быть разным для служб, работающих с дейтаграммами или потоками. Это можно скрыть, используя подклассы обработчиков `StreamRequestHandler` или `DatagramRequestHandler`.

Конечно, нужно подходить с умом. Например, нет смысла использовать сервер с порождением процессов (forking), если служба хранит состояние в памяти, которое может изменяться разными запросами, потому что изменения в дочернем процессе никогда не попадут в исходное состояние, хранящееся в родительском процессе и передаваемое каждому дочернему. В этом случае можно использовать потоковый сервер (threading), но, скорее всего, придётся использовать блокировки для защиты целостности общих данных.

С другой стороны, если создаётся HTTP-сервер, где все данные хранятся внешне (например, в файловой системе), синхронный класс фактически сделает службу «глухой» на время обработки одного запроса – что может занять очень много времени, если клиент медленно получает все запрошенные данные. Здесь подойдёт потоковый сервер или сервер с порождением процессов.

В некоторых случаях часть запроса можно обрабатывать синхронно, а завершать обработку в порождённом дочернем процессе в зависимости от данных запроса. Это можно реализовать с помощью синхронного сервера и явного вызова fork в методе класса обработчика запросов [`handle()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle).

Другой подход к обработке нескольких одновременных запросов в среде, не поддерживающей ни потоки, ни `fork()` (или где они слишком дороги или неприемлемы для данного сервиса), заключается в ведении явной таблицы частично завершённых запросов и использовании `select()` для определения, какой запрос обрабатывать следующим (или следует ли обрабатывать новый входящий запрос). Это особенно важно для потоковых сервисов, где каждый клиент может быть подключен в течение длительного времени (если невозможно использовать потоки или подпроцессы). См. [`asyncore`](https://python-all.ru/3.0/library/asyncore.html#module-asyncore) для другого способа управления этим.

## Объекты сервера

#### `[socketserver.fileno]socketserver.fileno()`

Возвращает целочисленный файловый дескриптор для сокета, на котором сервер прослушивает соединения. Эта функция чаще всего передаётся в

[`select.select()`](https://python-all.ru/3.0/library/select.html#select.select)

, чтобы обеспечить мониторинг нескольких серверов в одном процессе.

#### `[socketserver.handle_request]socketserver.handle_request()`

Обрабатывает один запрос. Эта функция последовательно вызывает следующие методы:

[`get_request()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.get_request)

,

[`verify_request()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.verify_request)

и

[`process_request()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.process_request)

. Если предоставленный пользователем метод

[`handle()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle)

класса-обработчика вызывает исключение, будет вызван метод

[`handle_error()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle_error)

сервера. Если в течение

`self.timeout`

секунд не поступило ни одного запроса, будет вызван

[`handle_timeout()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle_timeout)

, и

[`handle_request()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle_request)

вернётся.

#### `[socketserver.serve_forever]socketserver.serve_forever(poll_interval=0.5)`

Обрабатывает запросы до тех пор, пока не поступит явный запрос

[`shutdown()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.shutdown)

. Опрашивает на предмет завершения каждые

*poll\_interval*

секунд.

#### `[socketserver.shutdown]socketserver.shutdown()`

Сообщает циклу

[`serve_forever()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.serve_forever)

о необходимости остановиться и ожидает его завершения.

#### `[socketserver.address_family]socketserver.address_family`

Семейство протоколов, к которому принадлежит сокет сервера. Типичные примеры:

[`socket.AF_INET`](https://python-all.ru/3.0/library/socket.html#socket.AF_INET)

и

[`socket.AF_UNIX`](https://python-all.ru/3.0/library/socket.html#socket.AF_UNIX)

.

#### `[socketserver.RequestHandlerClass]socketserver.RequestHandlerClass`

Класс обработчика запросов, предоставленный пользователем; экземпляр этого класса создаётся для каждого запроса.

#### `[socketserver.server_address]socketserver.server_address`

Адрес, на котором сервер прослушивает соединения. Формат адресов зависит от семейства протоколов; подробнее см. документацию модуля socket. Для интернет-протоколов это кортеж, содержащий строку с адресом и целочисленный номер порта:

`('127.0.0.1', 80)`

, например.

#### `[socketserver.socket]socketserver.socket`

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

Классы серверов поддерживают следующие переменные класса:

#### `[socketserver.allow_reuse_address]socketserver.allow_reuse_address`

Определяет, разрешает ли сервер повторное использование адреса. По умолчанию

[`False`](https://python-all.ru/3.0/library/constants.html#False)

, но может быть установлено в подклассах для изменения политики.

#### `[socketserver.request_queue_size]socketserver.request_queue_size`

Размер очереди запросов. Если обработка одного запроса занимает много времени, все запросы, поступающие во время занятости сервера, помещаются в очередь – до

[`request_queue_size`](https://python-all.ru/3.0/library/socketserver.html#socketserver.request_queue_size)

запросов. Когда очередь заполнена, последующие запросы от клиентов получают ошибку «Connection denied». Обычно значение по умолчанию равно 5, но его можно переопределить в подклассах.

#### `[socketserver.socket_type]socketserver.socket_type`

Тип сокета, используемого сервером;

[`socket.SOCK_STREAM`](https://python-all.ru/3.0/library/socket.html#socket.SOCK_STREAM)

и

[`socket.SOCK_DGRAM`](https://python-all.ru/3.0/library/socket.html#socket.SOCK_DGRAM)

– два распространённых значения.

#### `[socketserver.timeout]socketserver.timeout`

Длительность тайм-аута в секундах или

[`None`](https://python-all.ru/3.0/library/constants.html#None)

, если тайм-аут не нужен. Если

[`handle_request()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle_request)

не получает входящих запросов в течение тайм-аута, вызывается метод

[`handle_timeout()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle_timeout)

.

Существует несколько методов сервера, которые могут быть переопределены подклассами базовых классов сервера, например `TCPServer`; эти методы не предназначены для внешних пользователей объекта сервера.

#### `[socketserver.finish_request]socketserver.finish_request()`

Фактически обрабатывает запрос, создавая экземпляр

[`RequestHandlerClass`](https://python-all.ru/3.0/library/socketserver.html#socketserver.RequestHandlerClass)

и вызывая его метод

[`handle()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle)

.

#### `[socketserver.get_request]socketserver.get_request()`

Должен принять запрос из сокета и вернуть кортеж из двух элементов, содержащий

*новый*

объект сокета для взаимодействия с клиентом и адрес клиента.

#### `[socketserver.handle_error]socketserver.handle_error(request, client_address)`

Эта функция вызывается, если метод

[`handle()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle)

класса

[`RequestHandlerClass`](https://python-all.ru/3.0/library/socketserver.html#socketserver.RequestHandlerClass)

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

#### `[socketserver.handle_timeout]socketserver.handle_timeout()`

Эта функция вызывается, когда атрибут

[`timeout`](https://python-all.ru/3.0/library/socketserver.html#socketserver.timeout)

установлен в значение, отличное от

[`None`](https://python-all.ru/3.0/library/constants.html#None)

, и по истечении тайм-аута не было получено ни одного запроса. В серверах с порождением процессов (forking) по умолчанию собирается статус завершившихся дочерних процессов, а в серверах с потоками этот метод ничего не делает.

#### `[socketserver.process_request]socketserver.process_request(request, client_address)`

Вызывает

[`finish_request()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.finish_request)

для создания экземпляра

[`RequestHandlerClass`](https://python-all.ru/3.0/library/socketserver.html#socketserver.RequestHandlerClass)

. При необходимости эта функция может создать новый процесс или поток для обработки запроса; классы

`ForkingMixIn`

и

`ThreadingMixIn`

делают это.

#### `[socketserver.server_activate]socketserver.server_activate()`

Вызывается конструктором сервера для его активации. Поведение по умолчанию просто вызывает

`listen()`

для сокета сервера. Может быть переопределено.

#### `[socketserver.server_bind]socketserver.server_bind()`

Вызывается конструктором сервера для привязки сокета к нужному адресу. Можно переопределить.

#### `[socketserver.verify_request]socketserver.verify_request(request, client_address)`

Должна возвращать логическое значение; если значение равно

[`True`](https://python-all.ru/3.0/library/constants.html#True)

, запрос будет обработан, а если

[`False`](https://python-all.ru/3.0/library/constants.html#False)

, запрос будет отклонён. Эту функцию можно переопределить для реализации контроля доступа к серверу. Реализация по умолчанию всегда возвращает

[`True`](https://python-all.ru/3.0/library/constants.html#True)

.

## Объекты RequestHandler

Класс обработчика запросов должен определять новый метод [`handle()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle) и может переопределять любой из следующих методов. Для каждого запроса создаётся новый экземпляр.

#### `[socketserver.finish]socketserver.finish()`

Вызывается после метода

[`handle()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle)

для выполнения необходимых действий по очистке. Реализация по умолчанию ничего не делает. Если

[`setup()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.setup)

или

[`handle()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle)

вызывают исключение, эта функция не будет вызвана.

#### `[socketserver.handle]socketserver.handle()`

Эта функция должна выполнять всю работу по обслуживанию запроса. Реализация по умолчанию ничего не делает. Для неё доступны несколько атрибутов экземпляра: запрос доступен как `self.request`; адрес клиента как `self.client_address`; и экземпляр сервера как `self.server`, на случай, если потребуется доступ к информации, специфичной для сервера.

Тип `self.request` различается для датаграммных и потоковых служб. Для потоковых служб `self.request` – это объект сокета; для датаграммных – `self.request` представляет собой пару из строки и сокета. Однако это можно скрыть, используя подклассы обработчика запросов `StreamRequestHandler` или `DatagramRequestHandler`, которые переопределяют методы [`setup()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.setup) и [`finish()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.finish) и предоставляют атрибуты `self.rfile` и `self.wfile`. `self.rfile` и `self.wfile` можно читать или записывать соответственно для получения данных запроса или возврата данных клиенту.

#### `[socketserver.setup]socketserver.setup()`

Вызывается перед методом

[`handle()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle)

для выполнения необходимых действий по инициализации. Реализация по умолчанию ничего не делает.

## Примеры

### `socketserver.TCPServer` Пример

Это серверная сторона:

```python
import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):
    """
    The RequestHandler class for our server.

    It is instantiated once per connection to the server, and must
    override the handle() method to implement communication to the
    client.
    """

    def handle(self):
        # self.request is the TCP socket connected to the client
        self.data = self.request.recv(1024).strip()
        print("%s wrote:" % self.client_address[0])
        print(self.data)
        # just send back the same data, but upper-cased
        self.request.send(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999

    # Create the server, binding to localhost on port 9999
    server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)

    # Activate the server; this will keep running until you
    # interrupt the program with Ctrl-C
    server.serve_forever()
```

Альтернативный класс обработчика запросов, который использует потоки (объекты, подобные файлам, упрощающие взаимодействие за счет предоставления стандартного файлового интерфейса):

```python
class MyTCPHandler(socketserver.StreamRequestHandler):

    def handle(self):
        # self.rfile – это файлоподобный объект, созданный обработчиком;
        # теперь можно использовать, например, readline() вместо прямых вызовов recv()
        self.data = self.rfile.readline().strip()
        print("%s wrote:" % self.client_address[0])
        print(self.data)
        # Аналогично, self.wfile – это файлоподобный объект, используемый для записи ответа
        # клиенту
        self.wfile.write(self.data.upper())
```

Разница в том, что вызов `readline()` во втором обработчике будет вызывать `recv()` несколько раз, пока не встретит символ новой строки, в то время как единственный вызов `recv()` в первом обработчике просто вернёт то, что было отправлено клиентом в одном вызове `send()`.

Это клиентская сторона:

```python
import socket
import sys

HOST, PORT = "localhost", 9999
data = " ".join(sys.argv[1:])

# Создать сокет (SOCK_STREAM означает TCP-сокет)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Подключиться к серверу и отправить данные
sock.connect((HOST, PORT))
sock.send(bytes(data + "\n","utf8"))

# Получить данные от сервера и завершить работу
received = sock.recv(1024)
sock.close()

print("Sent:     %s" % data)
print("Received: %s" % received)
```

Результат работы примера должен выглядеть примерно так:

Сервер:

```python
$ python TCPServer.py
127.0.0.1 wrote:
b'hello world with TCP'
127.0.0.1 wrote:
b'python is nice'
```

Клиент:

```python
$ python TCPClient.py hello world with TCP
Sent:     hello world with TCP
Received: b'HELLO WORLD WITH TCP'
$ python TCPClient.py python is nice
Sent:     python is nice
Received: b'PYTHON IS NICE'
```

### `socketserver.UDPServer` Пример

Это серверная сторона:

```python
import socketserver

class MyUDPHandler(socketserver.BaseRequestHandler):
    """
    This class works similar to the TCP handler class, except that
    self.request consists of a pair of data and client socket, and since
    there is no connection the client address must be given explicitly
    when sending data back via sendto().
    """

    def handle(self):
        data = self.request[0].strip()
        socket = self.request[1]
        print("%s wrote:" % self.client_address[0])
        print(data)
        socket.sendto(data.upper(), self.client_address)

if __name__ == "__main__":
   HOST, PORT = "localhost", 9999
   server = socketserver.UDPServer((HOST, PORT), MyUDPHandler)
   server.serve_forever()
```

Это клиентская сторона:

```python
import socket
import sys

HOST, PORT = "localhost", 9999
data = " ".join(sys.argv[1:])

# SOCK_DGRAM – это тип сокета для UDP-сокетов
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# Как можно видеть, вызов connect() отсутствует; в UDP нет соединений.
# Вместо этого данные отправляются напрямую получателю через sendto().
sock.sendto(bytes(data + "\n","utf8"), (HOST, PORT))
received = sock.recv(1024)

print("Sent:     %s" % data)
print("Received: %s" % received)
```

Результат работы примера должен выглядеть точно так же, как для примера TCP-сервера.

### Асинхронные Mixins

Для создания асинхронных обработчиков используйте классы `ThreadingMixIn` и `ForkingMixIn`.

Пример для класса `ThreadingMixIn`:

```python
import socket
import threading
import socketserver

class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):

    def handle(self):
        data = self.request.recv(1024)
        cur_thread = threading.current_thread()
        response = bytes("%s: %s" % (cur_thread.getName(), data),'ascii')
        self.request.send(response)

class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    pass

def client(ip, port, message):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((ip, port))
    sock.send(message)
    response = sock.recv(1024)
    print("Received: %s" % response)
    sock.close()

if __name__ == "__main__":
    # Port 0 means to select an arbitrary unused port
    HOST, PORT = "localhost", 0

    server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
    ip, port = server.server_address

    # Start a thread with the server -- that thread will then start one
    # more thread for each request
    server_thread = threading.Thread(target=server.serve_forever)
    # Exit the server thread when the main thread terminates
    server_thread.setDaemon(True)
    server_thread.start()
    print("Server loop running in thread:", server_thread.getName())

    client(ip, port, b"Hello World 1")
    client(ip, port, b"Hello World 2")
    client(ip, port, b"Hello World 3")

    server.shutdown()
```

Результат работы примера должен выглядеть примерно так:

```python
$ python ThreadedTCPServer.py
Server loop running in thread: Thread-1
Received: b"Thread-2: b'Hello World 1'"
Received: b"Thread-3: b'Hello World 2'"
Received: b"Thread-4: b'Hello World 3'"
```

Класс `ForkingMixIn` используется так же, за исключением того, что сервер будет создавать новый процесс для каждого запроса.
