socketserver.md
1> **Источник:** https://python-all.ru/3.0/library/socketserver.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# `socketserver` – фреймворк для сетевых серверов89Модуль `socketserver` упрощает задачу написания сетевых серверов.1011Существует четыре базовых класса серверов: `TCPServer` использует протокол TCP, который обеспечивает непрерывные потоки данных между клиентом и сервером. `UDPServer` использует дейтаграммы – дискретные пакеты информации, которые могут прийти не по порядку или потеряться при передаче. Реже используемые классы `UnixStreamServer` и `UnixDatagramServer` похожи, но используют сокеты домена Unix; они недоступны на платформах, отличных от Unix. Дополнительные сведения о сетевом программировании можно найти в таких книгах, как UNIX Network Programming У. Ричарда Стивенса или Win32 Network Programming Ральфа Дэвиса.1213Эти четыре класса обрабатывают запросы *синхронно*; каждый запрос должен быть завершен до того, как может начаться следующий. Это не подходит, если каждый запрос требует много времени на выполнение из-за вычислительной сложности или из-за возврата большого объема данных, которые клиент обрабатывает медленно. Решение – создавать отдельный процесс или поток для обработки каждого запроса; миксин-классы `ForkingMixIn` и `ThreadingMixIn` можно использовать для поддержки асинхронного поведения.1415Создание сервера состоит из нескольких шагов. Сначала необходимо создать класс-обработчик запросов, унаследовав его от `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) объекта сервера для обработки одного или нескольких запросов.1617При наследовании от `ThreadingMixIn` для поведения с потоками необходимо явно указать, как потоки должны вести себя при аварийном завершении. Класс `ThreadingMixIn` определяет атрибут *daemon\_threads*, который указывает, должен ли сервер ждать завершения потоков. Следует явно установить этот флаг, если требуется, чтобы потоки работали автономно; по умолчанию [`False`](https://python-all.ru/3.0/library/constants.html#False), то есть Python не завершится, пока все потоки, созданные `ThreadingMixIn`, не завершатся.1819Классы серверов имеют одни и те же внешние методы и атрибуты, независимо от того, какой сетевой протокол они используют.2021## Замечания по созданию серверов2223В иерархии наследования пять классов, четыре из которых представляют синхронные серверы четырёх типов:2425```python26+------------+27| BaseServer |28+------------+29 |30 v31+-----------+ +------------------+32| TCPServer |------->| UnixStreamServer |33+-----------+ +------------------+34 |35 v36+-----------+ +--------------------+37| UDPServer |------->| UnixDatagramServer |38+-----------+ +--------------------+39```4041Обратите внимание, что `UnixDatagramServer` наследует от `UDPServer`, а не от `UnixStreamServer` – единственное различие между IP- и Unix-стрим-сервером заключается в семействе адресов, которое просто повторяется в обоих Unix-классах серверов.4243Версии с порождением процессов и потоками для каждого типа сервера можно создать с помощью миксин-классов `ForkingMixIn` и `ThreadingMixIn`. Например, класс потокового UDP-сервера создаётся следующим образом:4445```python46class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass47```4849Класс-примесь должен идти первым, так как он переопределяет метод, определённый в `UDPServer`. Настройка различных переменных-членов также изменяет поведение базового серверного механизма.5051Чтобы реализовать службу, необходимо создать класс, производный от `BaseRequestHandler`, и переопределить его метод [`handle()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle). Затем можно запускать различные версии службы, комбинируя один из серверных классов с вашим классом обработчика запросов. Класс обработчика запросов должен быть разным для служб, работающих с дейтаграммами или потоками. Это можно скрыть, используя подклассы обработчиков `StreamRequestHandler` или `DatagramRequestHandler`.5253Конечно, нужно подходить с умом. Например, нет смысла использовать сервер с порождением процессов (forking), если служба хранит состояние в памяти, которое может изменяться разными запросами, потому что изменения в дочернем процессе никогда не попадут в исходное состояние, хранящееся в родительском процессе и передаваемое каждому дочернему. В этом случае можно использовать потоковый сервер (threading), но, скорее всего, придётся использовать блокировки для защиты целостности общих данных.5455С другой стороны, если создаётся HTTP-сервер, где все данные хранятся внешне (например, в файловой системе), синхронный класс фактически сделает службу «глухой» на время обработки одного запроса – что может занять очень много времени, если клиент медленно получает все запрошенные данные. Здесь подойдёт потоковый сервер или сервер с порождением процессов.5657В некоторых случаях часть запроса можно обрабатывать синхронно, а завершать обработку в порождённом дочернем процессе в зависимости от данных запроса. Это можно реализовать с помощью синхронного сервера и явного вызова fork в методе класса обработчика запросов [`handle()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle).5859Другой подход к обработке нескольких одновременных запросов в среде, не поддерживающей ни потоки, ни `fork()` (или где они слишком дороги или неприемлемы для данного сервиса), заключается в ведении явной таблицы частично завершённых запросов и использовании `select()` для определения, какой запрос обрабатывать следующим (или следует ли обрабатывать новый входящий запрос). Это особенно важно для потоковых сервисов, где каждый клиент может быть подключен в течение длительного времени (если невозможно использовать потоки или подпроцессы). См. [`asyncore`](https://python-all.ru/3.0/library/asyncore.html#module-asyncore) для другого способа управления этим.6061## Объекты сервера6263#### `[socketserver.fileno]socketserver.fileno()`6465Возвращает целочисленный файловый дескриптор для сокета, на котором сервер прослушивает соединения. Эта функция чаще всего передаётся в6667[`select.select()`](https://python-all.ru/3.0/library/select.html#select.select)6869, чтобы обеспечить мониторинг нескольких серверов в одном процессе.7071#### `[socketserver.handle_request]socketserver.handle_request()`7273Обрабатывает один запрос. Эта функция последовательно вызывает следующие методы:7475[`get_request()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.get_request)7677,7879[`verify_request()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.verify_request)8081и8283[`process_request()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.process_request)8485. Если предоставленный пользователем метод8687[`handle()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle)8889класса-обработчика вызывает исключение, будет вызван метод9091[`handle_error()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle_error)9293сервера. Если в течение9495`self.timeout`9697секунд не поступило ни одного запроса, будет вызван9899[`handle_timeout()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle_timeout)100101, и102103[`handle_request()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle_request)104105вернётся.106107#### `[socketserver.serve_forever]socketserver.serve_forever(poll_interval=0.5)`108109Обрабатывает запросы до тех пор, пока не поступит явный запрос110111[`shutdown()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.shutdown)112113. Опрашивает на предмет завершения каждые114115*poll\_interval*116117секунд.118119#### `[socketserver.shutdown]socketserver.shutdown()`120121Сообщает циклу122123[`serve_forever()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.serve_forever)124125о необходимости остановиться и ожидает его завершения.126127#### `[socketserver.address_family]socketserver.address_family`128129Семейство протоколов, к которому принадлежит сокет сервера. Типичные примеры:130131[`socket.AF_INET`](https://python-all.ru/3.0/library/socket.html#socket.AF_INET)132133и134135[`socket.AF_UNIX`](https://python-all.ru/3.0/library/socket.html#socket.AF_UNIX)136137.138139#### `[socketserver.RequestHandlerClass]socketserver.RequestHandlerClass`140141Класс обработчика запросов, предоставленный пользователем; экземпляр этого класса создаётся для каждого запроса.142143#### `[socketserver.server_address]socketserver.server_address`144145Адрес, на котором сервер прослушивает соединения. Формат адресов зависит от семейства протоколов; подробнее см. документацию модуля socket. Для интернет-протоколов это кортеж, содержащий строку с адресом и целочисленный номер порта:146147`('127.0.0.1', 80)`148149, например.150151#### `[socketserver.socket]socketserver.socket`152153Объект сокета, через который сервер будет принимать входящие запросы.154155Классы серверов поддерживают следующие переменные класса:156157#### `[socketserver.allow_reuse_address]socketserver.allow_reuse_address`158159Определяет, разрешает ли сервер повторное использование адреса. По умолчанию160161[`False`](https://python-all.ru/3.0/library/constants.html#False)162163, но может быть установлено в подклассах для изменения политики.164165#### `[socketserver.request_queue_size]socketserver.request_queue_size`166167Размер очереди запросов. Если обработка одного запроса занимает много времени, все запросы, поступающие во время занятости сервера, помещаются в очередь – до168169[`request_queue_size`](https://python-all.ru/3.0/library/socketserver.html#socketserver.request_queue_size)170171запросов. Когда очередь заполнена, последующие запросы от клиентов получают ошибку «Connection denied». Обычно значение по умолчанию равно 5, но его можно переопределить в подклассах.172173#### `[socketserver.socket_type]socketserver.socket_type`174175Тип сокета, используемого сервером;176177[`socket.SOCK_STREAM`](https://python-all.ru/3.0/library/socket.html#socket.SOCK_STREAM)178179и180181[`socket.SOCK_DGRAM`](https://python-all.ru/3.0/library/socket.html#socket.SOCK_DGRAM)182183– два распространённых значения.184185#### `[socketserver.timeout]socketserver.timeout`186187Длительность тайм-аута в секундах или188189[`None`](https://python-all.ru/3.0/library/constants.html#None)190191, если тайм-аут не нужен. Если192193[`handle_request()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle_request)194195не получает входящих запросов в течение тайм-аута, вызывается метод196197[`handle_timeout()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle_timeout)198199.200201Существует несколько методов сервера, которые могут быть переопределены подклассами базовых классов сервера, например `TCPServer`; эти методы не предназначены для внешних пользователей объекта сервера.202203#### `[socketserver.finish_request]socketserver.finish_request()`204205Фактически обрабатывает запрос, создавая экземпляр206207[`RequestHandlerClass`](https://python-all.ru/3.0/library/socketserver.html#socketserver.RequestHandlerClass)208209и вызывая его метод210211[`handle()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle)212213.214215#### `[socketserver.get_request]socketserver.get_request()`216217Должен принять запрос из сокета и вернуть кортеж из двух элементов, содержащий218219*новый*220221объект сокета для взаимодействия с клиентом и адрес клиента.222223#### `[socketserver.handle_error]socketserver.handle_error(request, client_address)`224225Эта функция вызывается, если метод226227[`handle()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle)228229класса230231[`RequestHandlerClass`](https://python-all.ru/3.0/library/socketserver.html#socketserver.RequestHandlerClass)232233вызывает исключение. По умолчанию она печатает трассировку в стандартный вывод и продолжает обработку следующих запросов.234235#### `[socketserver.handle_timeout]socketserver.handle_timeout()`236237Эта функция вызывается, когда атрибут238239[`timeout`](https://python-all.ru/3.0/library/socketserver.html#socketserver.timeout)240241установлен в значение, отличное от242243[`None`](https://python-all.ru/3.0/library/constants.html#None)244245, и по истечении тайм-аута не было получено ни одного запроса. В серверах с порождением процессов (forking) по умолчанию собирается статус завершившихся дочерних процессов, а в серверах с потоками этот метод ничего не делает.246247#### `[socketserver.process_request]socketserver.process_request(request, client_address)`248249Вызывает250251[`finish_request()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.finish_request)252253для создания экземпляра254255[`RequestHandlerClass`](https://python-all.ru/3.0/library/socketserver.html#socketserver.RequestHandlerClass)256257. При необходимости эта функция может создать новый процесс или поток для обработки запроса; классы258259`ForkingMixIn`260261и262263`ThreadingMixIn`264265делают это.266267#### `[socketserver.server_activate]socketserver.server_activate()`268269Вызывается конструктором сервера для его активации. Поведение по умолчанию просто вызывает270271`listen()`272273для сокета сервера. Может быть переопределено.274275#### `[socketserver.server_bind]socketserver.server_bind()`276277Вызывается конструктором сервера для привязки сокета к нужному адресу. Можно переопределить.278279#### `[socketserver.verify_request]socketserver.verify_request(request, client_address)`280281Должна возвращать логическое значение; если значение равно282283[`True`](https://python-all.ru/3.0/library/constants.html#True)284285, запрос будет обработан, а если286287[`False`](https://python-all.ru/3.0/library/constants.html#False)288289, запрос будет отклонён. Эту функцию можно переопределить для реализации контроля доступа к серверу. Реализация по умолчанию всегда возвращает290291[`True`](https://python-all.ru/3.0/library/constants.html#True)292293.294295## Объекты RequestHandler296297Класс обработчика запросов должен определять новый метод [`handle()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle) и может переопределять любой из следующих методов. Для каждого запроса создаётся новый экземпляр.298299#### `[socketserver.finish]socketserver.finish()`300301Вызывается после метода302303[`handle()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle)304305для выполнения необходимых действий по очистке. Реализация по умолчанию ничего не делает. Если306307[`setup()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.setup)308309или310311[`handle()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle)312313вызывают исключение, эта функция не будет вызвана.314315#### `[socketserver.handle]socketserver.handle()`316317Эта функция должна выполнять всю работу по обслуживанию запроса. Реализация по умолчанию ничего не делает. Для неё доступны несколько атрибутов экземпляра: запрос доступен как `self.request`; адрес клиента как `self.client_address`; и экземпляр сервера как `self.server`, на случай, если потребуется доступ к информации, специфичной для сервера.318319Тип `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` можно читать или записывать соответственно для получения данных запроса или возврата данных клиенту.320321#### `[socketserver.setup]socketserver.setup()`322323Вызывается перед методом324325[`handle()`](https://python-all.ru/3.0/library/socketserver.html#socketserver.handle)326327для выполнения необходимых действий по инициализации. Реализация по умолчанию ничего не делает.328329## Примеры330331### `socketserver.TCPServer` Пример332333Это серверная сторона:334335```python336import socketserver337338class MyTCPHandler(socketserver.BaseRequestHandler):339 """340 The RequestHandler class for our server.341342 It is instantiated once per connection to the server, and must343 override the handle() method to implement communication to the344 client.345 """346347 def handle(self):348 # self.request is the TCP socket connected to the client349 self.data = self.request.recv(1024).strip()350 print("%s wrote:" % self.client_address[0])351 print(self.data)352 # just send back the same data, but upper-cased353 self.request.send(self.data.upper())354355if __name__ == "__main__":356 HOST, PORT = "localhost", 9999357358 # Create the server, binding to localhost on port 9999359 server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)360361 # Activate the server; this will keep running until you362 # interrupt the program with Ctrl-C363 server.serve_forever()364```365366Альтернативный класс обработчика запросов, который использует потоки (объекты, подобные файлам, упрощающие взаимодействие за счет предоставления стандартного файлового интерфейса):367368```python369class MyTCPHandler(socketserver.StreamRequestHandler):370371 def handle(self):372 # self.rfile – это файлоподобный объект, созданный обработчиком;373 # теперь можно использовать, например, readline() вместо прямых вызовов recv()374 self.data = self.rfile.readline().strip()375 print("%s wrote:" % self.client_address[0])376 print(self.data)377 # Аналогично, self.wfile – это файлоподобный объект, используемый для записи ответа378 # клиенту379 self.wfile.write(self.data.upper())380```381382Разница в том, что вызов `readline()` во втором обработчике будет вызывать `recv()` несколько раз, пока не встретит символ новой строки, в то время как единственный вызов `recv()` в первом обработчике просто вернёт то, что было отправлено клиентом в одном вызове `send()`.383384Это клиентская сторона:385386```python387import socket388import sys389390HOST, PORT = "localhost", 9999391data = " ".join(sys.argv[1:])392393# Создать сокет (SOCK_STREAM означает TCP-сокет)394sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)395396# Подключиться к серверу и отправить данные397sock.connect((HOST, PORT))398sock.send(bytes(data + "\n","utf8"))399400# Получить данные от сервера и завершить работу401received = sock.recv(1024)402sock.close()403404print("Sent: %s" % data)405print("Received: %s" % received)406```407408Результат работы примера должен выглядеть примерно так:409410Сервер:411412```python413$ python TCPServer.py414127.0.0.1 wrote:415b'hello world with TCP'416127.0.0.1 wrote:417b'python is nice'418```419420Клиент:421422```python423$ python TCPClient.py hello world with TCP424Sent: hello world with TCP425Received: b'HELLO WORLD WITH TCP'426$ python TCPClient.py python is nice427Sent: python is nice428Received: b'PYTHON IS NICE'429```430431### `socketserver.UDPServer` Пример432433Это серверная сторона:434435```python436import socketserver437438class MyUDPHandler(socketserver.BaseRequestHandler):439 """440 This class works similar to the TCP handler class, except that441 self.request consists of a pair of data and client socket, and since442 there is no connection the client address must be given explicitly443 when sending data back via sendto().444 """445446 def handle(self):447 data = self.request[0].strip()448 socket = self.request[1]449 print("%s wrote:" % self.client_address[0])450 print(data)451 socket.sendto(data.upper(), self.client_address)452453if __name__ == "__main__":454 HOST, PORT = "localhost", 9999455 server = socketserver.UDPServer((HOST, PORT), MyUDPHandler)456 server.serve_forever()457```458459Это клиентская сторона:460461```python462import socket463import sys464465HOST, PORT = "localhost", 9999466data = " ".join(sys.argv[1:])467468# SOCK_DGRAM – это тип сокета для UDP-сокетов469sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)470471# Как можно видеть, вызов connect() отсутствует; в UDP нет соединений.472# Вместо этого данные отправляются напрямую получателю через sendto().473sock.sendto(bytes(data + "\n","utf8"), (HOST, PORT))474received = sock.recv(1024)475476print("Sent: %s" % data)477print("Received: %s" % received)478```479480Результат работы примера должен выглядеть точно так же, как для примера TCP-сервера.481482### Асинхронные Mixins483484Для создания асинхронных обработчиков используйте классы `ThreadingMixIn` и `ForkingMixIn`.485486Пример для класса `ThreadingMixIn`:487488```python489import socket490import threading491import socketserver492493class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):494495 def handle(self):496 data = self.request.recv(1024)497 cur_thread = threading.current_thread()498 response = bytes("%s: %s" % (cur_thread.getName(), data),'ascii')499 self.request.send(response)500501class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):502 pass503504def client(ip, port, message):505 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)506 sock.connect((ip, port))507 sock.send(message)508 response = sock.recv(1024)509 print("Received: %s" % response)510 sock.close()511512if __name__ == "__main__":513 # Port 0 means to select an arbitrary unused port514 HOST, PORT = "localhost", 0515516 server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)517 ip, port = server.server_address518519 # Start a thread with the server -- that thread will then start one520 # more thread for each request521 server_thread = threading.Thread(target=server.serve_forever)522 # Exit the server thread when the main thread terminates523 server_thread.setDaemon(True)524 server_thread.start()525 print("Server loop running in thread:", server_thread.getName())526527 client(ip, port, b"Hello World 1")528 client(ip, port, b"Hello World 2")529 client(ip, port, b"Hello World 3")530531 server.shutdown()532```533534Результат работы примера должен выглядеть примерно так:535536```python537$ python ThreadedTCPServer.py538Server loop running in thread: Thread-1539Received: b"Thread-2: b'Hello World 1'"540Received: b"Thread-3: b'Hello World 2'"541Received: b"Thread-4: b'Hello World 3'"542```543544Класс `ForkingMixIn` используется так же, за исключением того, что сервер будет создавать новый процесс для каждого запроса.545