socketserver
— Фреймворк для сетевых серверов¶
Исходный код: Lib/socketserver.py
Модуль socketserver
упрощает задачу написания сетевых серверов.
Существует четыре основных класса серверов:
-
class
socketserver.
TCPServer
(server_address, RequestHandlerClass, bind_and_activate=True)¶ Используется протокол TCP интернета, обеспечивающий непрерывные потоки данных между клиентом и сервером. Если bind_and_activate имеет значение true, конструктор автоматически пытается вызвать
server_bind()
иserver_activate()
. Другие параметры передаются базовому классуBaseServer
.
-
class
socketserver.
UDPServer
(server_address, RequestHandlerClass, bind_and_activate=True)¶ Используются дейтаграммы, которые представляют собой дискретные пакеты информации, которые могут прийти не по порядку или быть потеряны во время транспортировки. Параметры те же, что и для
TCPServer
.
-
class
socketserver.
UnixStreamServer
(server_address, RequestHandlerClass, bind_and_activate=True)¶ -
class
socketserver.
UnixDatagramServer
(server_address, RequestHandlerClass, bind_and_activate=True)¶ Эти более редко используемый классы аналогичны классам TCP и UDP, но используют доменные сокеты Unix; они недоступны на платформах, отличных от Unix. Параметры те же, что и для
TCPServer
.
Вышеуказанные четыре класса обрабатывают запросы синхронно; каждый запрос должен быть
выполнен до запуска следующего запроса. Это не подходит, если каждый запрос
занимает много времени, потому что он требует большого количества вычислений,
или потому что он возвращает много данных, которые клиент медленно
обрабатывает. Решение состоит в создании отдельного процесса или поток для
обработки каждого запроса; классы ForkingMixIn
и ThreadingMixIn
mix-in могут быть
используемый для поддержки асинхронного поведения.
Создание сервера требует нескольких шагов. Во-первых, необходимо создать класс
обработчик запроса путем подкласса класса BaseRequestHandler
и переопределения его
метода handle()
; этот метод будет обрабатывать входящие запросы. Во-вторых,
необходимо создать экземпляр одного из классов сервера, передав ему адрес
сервера и класс обработчик запроса. Рекомендуется использовать сервер в
with
инструкции. Затем вызвать метод handle_request()
или serve_forever()
объекта
сервера для обработки одного или нескольких запросов. Наконец, назовите
server_close()
, чтобы закрыть сокет (если вы используете with
инструкцию).
При наследовании от ThreadingMixIn
для поведения многопоточных соединений следует
явным образом объявить поведение потоки при внезапном отключении. Класс
ThreadingMixIn
определяет атрибут daemon_threads, который указывает, должен ли сервер
ожидать завершения поток. Если вы хотите, чтобы потоки вел себя
автономно, необходимо явно установить флаг; значение по умолчанию - False
,
что означает, что Python не завершит работу до тех пор, пока не завершат
работу все потоки, созданные ThreadingMixIn
.
Классы серверов имеют одинаковые внешние методы и атрибуты, независимо от того, какой сетевой протокол они используют.
Примечания к созданию сервера¶
На схеме наследования имеется пять классов, четыре из которых представляют синхронные серверы четырёх типов:
+------------+
| BaseServer |
+------------+
|
v
+-----------+ +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+ +------------------+
|
v
+-----------+ +--------------------+
| UDPServer |------->| UnixDatagramServer |
+-----------+ +--------------------+
Обратите внимание, что UnixDatagramServer
происходит из UDPServer
, не из UnixStreamServer
—, единственная разница между IP и сервером потока Unix - семья
адреса, которая просто повторена в обоих классах сервера Unix.
-
class
socketserver.
ForkingMixIn
¶ -
class
socketserver.
ThreadingMixIn
¶ Форкинг и многопоточность версий каждого типа сервера могут быть созданы с использованием этих смешанных классов. Для сущность
ThreadingUDPServer
создается следующим образом:class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
Класс mix-in занимает первое место, поскольку он переопределяет метод, определенный в
UDPServer
. Установка различных атрибуты также изменяет поведение базового серверного механизма.Классы
ForkingMixIn
и Forking, упомянутые ниже, доступны только на платформах POSIX, поддерживающихfork()
.socketserver.ForkingMixIn.server_close()
ожидает завершения всех дочерних процессов, за исключением случаев, когдаsocketserver.ForkingMixIn.block_on_close
атрибут имеет значение false.socketserver.ThreadingMixIn.server_close()
ждет до всего недемона полный потоки, кроме того, еслиsocketserver.ThreadingMixIn.block_on_close
атрибут ложный. Используйте демонические потоки, установив дляThreadingMixIn.daemon_threads
значениеTrue
, чтобы не ждать завершения потоки.Изменено в версии 3.7:
socketserver.ForkingMixIn.server_close()
иsocketserver.ThreadingMixIn.server_close()
теперь ждет завершения всех дочерних процессов и недемонических потоки. Добавьте новый атрибут классаsocketserver.ForkingMixIn.block_on_close
для регистрации поведения до 3.7.
-
class
socketserver.
ForkingTCPServer
¶ -
class
socketserver.
ForkingUDPServer
¶ -
class
socketserver.
ThreadingTCPServer
¶ -
class
socketserver.
ThreadingUDPServer
¶ Эти классы предварительно определены с использованием смешанных классов.
Для реализации службы необходимо вывести класс из BaseRequestHandler
и переопределить
его метод handle()
. Затем можно запустить различные версии службы, объединив
один из классов сервера с классом обработчик запроса. Класс обработчик
запросов должен отличаться для служб дейтаграмм или потоков. Это можно скрыть с
помощью обработчик подклассы StreamRequestHandler
или DatagramRequestHandler
.
Конечно, ты все еще должен использовать голову! для сущность, нет смысла использовать сервер форкинга, если служба содержит состояние в памяти, которые могут быть изменены различными запросами, так как изменения в дочернем процессе никогда не достигнут начального состояние, сохраненного в родительском процессе и переданного каждому дочернему процессу. В этом случае можно использовать многопоточный сервер, но для защиты целостности общих данных, вероятно, придется использовать блокировки.
С другой стороны, при создании HTTP-сервера, на котором все данные хранятся извне (для сущность, в файловой системе), синхронный класс по существу предоставит «глухой» сервис, в то время как один запрос обрабатывается - который может быть в течение очень долгого времени, если клиент не спешит получать все данные, это просило. Здесь подходит сервер многопоточности или вилки.
В некоторых случаях может оказаться целесообразным обрабатывать часть запроса
синхронно, но закончить обработку в вилочном нижестоящем элементе в зависимости
от данных запроса. Это может быть реализовано с помощью синхронного сервера и
выполнения явной форки в методе request обработчика class handle()
.
Другой подход к обработке нескольких одновременных запросов в среде, которая не
поддерживает ни потоки, ни fork()
(или где они слишком дороги или
неуместны для службы), заключается в ведении явной таблицы частично завершенных
запросов и использовании selectors
для принятия решения о том, над каким
запросом работать дальше (или обрабатывать ли новый входящий запрос). Это
особенно важно для потоковых сервисов, где каждый клиент потенциально может быть
подключен в течение длительного времени (если потоки или подпроцессы не
могут быть используемый). См. asyncore
другой способ управления этим.
Объекты Server¶
-
class
socketserver.
BaseServer
(server_address, RequestHandlerClass)¶ Суперкласс всех объектов сервера в модуле. Он определяет интерфейс, приведенный ниже, но не реализует большинство методов, что делается в подклассы. Эти два параметра хранятся в соответствующих
server_address
иRequestHandlerClass
атрибуты.-
fileno
()¶ Возвращает целочисленный дескриптор файла для сокет, на котором слушается сервер. Эта функция чаще всего передается
selectors
, чтобы обеспечить мониторинг нескольких серверов в одном процессе.
-
handle_request
()¶ Обработка одного запроса. Эта функция вызывает следующие методы по порядку:
get_request()
,verify_request()
иprocess_request()
. Если предоставленный пользователем методhandle()
класса обработчик вызывает исключение, вызывается методhandle_error()
сервера. Если запрос не получен в течениеtimeout
секунд,handle_timeout()
будет вызван иhandle_request()
возвращает.
-
serve_forever
(poll_interval=0.5)¶ Обрабатывать запросы до явного запроса
shutdown()
. Опрашивайте завершение работы каждые poll_interval секунд. Игнорируетtimeout
атрибут. Он также вызываетservice_actions()
, который может быть используемый подкласс или смесью для обеспечения действий, специфичных для данной услуги. Например, классForkingMixIn
используетservice_actions()
для очистки дочерних процессов зомби.Изменено в версии 3.3: Добавлен
service_actions
вызов методаserve_forever
.
-
service_actions
()¶ Вызывается в цикле
serve_forever()
. Этот метод может быть переопределен классами подклассы или mixin для выполнения действий, специфичных для данной службы, таких как действия очистки.Добавлено в версии 3.3.
-
shutdown
()¶ Передайте петлю
serve_forever()
, чтобы она остановилась, и дождитесь ее завершения.shutdown()
необходимо вызывать, когдаserve_forever()
работает в другом поток, в противном случае это приведет к взаимоблокировке.
-
server_close
()¶ Очистить сервер. Может быть переопределен.
-
address_family
¶ Семейство протоколов, к которому относится сокет сервера. Общими примерами являются
socket.AF_INET
иsocket.AF_UNIX
.
-
RequestHandlerClass
¶ Предоставленный пользователем запрос обработчик класс; для каждого запроса создается сущность этого класса.
-
server_address
¶ Адрес, по которому прослушивается сервер. Формат адресов зависит от семейства протоколов; для получения более подробной информации см. документацию по модулю
socket
. Для интернет-протоколов это кортеж, содержащий строка, дающий адрес, и целочисленный номер порта:('127.0.0.1', 80)
, например.
-
socket
¶ Объект сокет, на котором сервер будет прослушивать входящие запросы.
Классы сервера поддерживают следующие переменные классов:
-
allow_reuse_address
¶ Разрешает ли сервер повторное использование адреса. Это значение по умолчанию равно
False
и может быть задано в подклассы для изменения политики.
-
request_queue_size
¶ Размер очереди запросов. Если обработка одного запроса занимает много времени, любые запросы, поступающие во время занятости сервера, помещаются в очередь, вплоть до
request_queue_size
запросов. После заполнения очереди дальнейшие запросы от клиентов получат ошибку «Подключение запрещено». Обычно значение по умолчанию составляет 5, но это может быть переопределено подклассы.
-
socket_type
¶ Тип сокет, используемый сервером;
socket.SOCK_STREAM
иsocket.SOCK_DGRAM
являются двумя общими значения.
-
timeout
¶ Длительность тайм-аута, измеренная в секундах, или
None
, если тайм-аут не требуется. Еслиhandle_request()
не получает входящих запросов в течение периода тайм-аута, вызывается методhandle_timeout()
.
Существуют различные серверные методы, которые могут быть переопределены подклассы базовых серверных классов, таких как
TCPServer
; эти методы не полезны для внешних пользователей объекта сервера.-
finish_request
(request, client_address)¶ Фактически обрабатывает запрос путем создания экземпляра
RequestHandlerClass
и вызова методаhandle()
.
-
get_request
()¶ Необходимо принять запрос от сокет и возвращает 2-кортеж, содержащий объект new сокет, который будет используемый для связи с клиентом, и адрес клиента.
-
handle_error
(request, client_address)¶ Эта функция вызывается, если метод
handle()
RequestHandlerClass
сущность вызывает исключение. Действие по умолчанию заключается в печати трейсбэк до стандартной ошибки и продолжении обработки дальнейших запросов.Изменено в версии 3.6: Теперь вызывается только исключение, производное от класса
Exception
.
-
handle_timeout
()¶ Эта функция вызывается, когда
timeout
атрибут установлен в значение, отличное отNone
, и период тайм-аута прошел без получения запросов. Действие по умолчанию для раскачивающих серверов заключается в сборе состояния всех дочерних процессов, которые были завершены, в то время как в многопоточных серверах этот метод не выполняет никаких действий.
-
process_request
(request, client_address)¶ Вызывает
finish_request()
для создания сущностьRequestHandlerClass
. При необходимости эта функция может создать новый процесс или поток для обработки запроса; это делаютForkingMixIn
иThreadingMixIn
классы.
-
server_activate
()¶ Вызывается конструктором сервера для активации сервера. Поведение по умолчанию для TCP-сервера просто вызывает
listen()
на сокет сервера. Может быть переопределен.
-
server_bind
()¶ Вызывается конструктором сервера для привязки сокет к требуемому адресу. Может быть переопределен.
-
verify_request
(request, client_address)¶ Должен возвращает логическим значение; если значение
True
, запрос будет обработан, а если онFalse
, запрос будет отклонен. Эта функция может быть переопределена для реализации управления доступом к серверу. Реализация по умолчанию всегда возвращаетTrue
.
Изменено в версии 3.6: Добавлена поддержка протокола контекстного менеджера. Выход из диспетчера контекст эквивалентен вызову
server_close()
.-
Объекты обработчика запросов¶
-
class
socketserver.
BaseRequestHandler
¶ Суперкласс всех объектов обработчик запросов. Он определяет интерфейс, приведенный ниже. Конкретный запрос обработчик подкласс должен определять новый метод
handle()
и может переопределять любой другой метод. Для каждого запроса создается новый сущность подкласс.-
setup
()¶ Вызывается до метода
handle()
для выполнения любых необходимых действий инициализации. Реализация по умолчанию ничего не делает.
-
handle
()¶ Эта функция должна выполнять всю работу, необходимую для обслуживания запроса. Реализация по умолчанию ничего не делает. Ему доступны несколько сущность атрибуты; запрос доступен в виде
self.request
; адрес клиента какself.client_address
; и сервер сущность какself.server
, в случае, если ему необходим доступ к информации о каждом сервере.Тип
self.request
отличается для служб дейтаграмм или потоков. Для потоковых сервисовself.request
является объектом сокет; для служб дейтаграммself.request
является парой строка и сокет.
-
-
class
socketserver.
StreamRequestHandler
¶ -
class
socketserver.
DatagramRequestHandler
¶ Эти
BaseRequestHandler
подклассы переопределяют методыsetup()
иfinish()
и обеспечиваютself.rfile
иself.wfile
атрибуты.self.rfile
иself.wfile
атрибуты могут быть считаны или записаны, соответственно, для получения данных запроса или данных возвращает клиенту.rfile
атрибуты обоих классов поддерживает удобочитаемый интерфейсio.BufferedIOBase
, иDatagramRequestHandler.wfile
поддерживает перезаписываемый интерфейсio.BufferedIOBase
.Изменено в версии 3.6:
StreamRequestHandler.wfile
также поддерживает интерфейс, доступный для записиio.BufferedIOBase
.
Примеры¶
Пример socketserver.TCPServer
¶
Это серверная сторона:
import socketserver
class MyTCPHandler(socketserver.BaseRequestHandler):
"""
Класс обработчика запроса для нашего сервера.
Он создается один раз для каждого подключения к серверу и должен переопределять
метод handle() для реализации связи с клиентом.
"""
def handle(self):
# self.request - это TCP - сокет, подключенный к клиенту
self.data = self.request.recv(1024).strip()
print("{} wrote:".format(self.client_address[0]))
print(self.data)
# просто отправьте обратно те же данные, но в верхнем регистре
self.request.sendall(self.data.upper())
if __name__ == "__main__":
HOST, PORT = "localhost", 9999
# Создать серверный биндинг localhost на порту 9999
with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
# Активировать сервер; это будет продолжаться до тех пор, пока вы не прервете
# программу с помощью Ctrl-C
server.serve_forever()
Альтернативный класс обработчик запросов, использующий потоки (файловые объекты, упрощающие обмен данными, обеспечивая стандартный файловый интерфейс):
class MyTCPHandler(socketserver.StreamRequestHandler):
def handle(self):
# self.rfile - файлообразный объект, созданный обработчик; теперь мы можем
# использовать, например, readline() вместо необработанных вызовов recv()
self.data = self.rfile.readline().strip()
print("{} wrote:".format(self.client_address[0]))
print(self.data)
# Аналогично, self.wfile является файловым объектом, используемый для обратной
# записи клиенту
self.wfile.write(self.data.upper())
Различие - то, что требование readline()
во втором обработчик назовет
recv()
многократно, пока это не столкнется с newline символ, в то время
как единственное требование recv()
в первом обработчик будет просто
возвращает, что послали от клиента в одном требовании sendall()
.
Это клиентская сторона:
import socket
import sys
HOST, PORT = "localhost", 9999
data = " ".join(sys.argv[1:])
# Создать сокет (SOCK_STREAM означает TCP сокет)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
# Connect to server and send data
sock.connect((HOST, PORT))
sock.sendall(bytes(data + "\n", "utf-8"))
# Получение данных с сервера и завершение работы
received = str(sock.recv(1024), "utf-8")
print("Sent: {}".format(data))
print("Received: {}".format(received))
Выходные данные примера должны выглядеть примерно так:
Сервер:
$ python TCPServer.py
127.0.0.1 wrote:
b'hello world with TCP'
127.0.0.1 wrote:
b'python is nice'
Клиент:
$ python TCPClient.py hello world with TCP
Sent: hello world with TCP
Received: HELLO WORLD WITH TCP
$ python TCPClient.py python is nice
Sent: python is nice
Received: PYTHON IS NICE
Пример socketserver.UDPServer
¶
Это серверная сторона:
import socketserver
class MyUDPHandler(socketserver.BaseRequestHandler):
"""
Класс работает аналогично классу TCP обработчика, за исключением того, что
self.request состоит из пары данных и клиентских сокет, и поскольку соединение
отсутствует, адрес клиента должен быть задан явно при отправке данных обратно
через sendto().
"""
def handle(self):
data = self.request[0].strip()
socket = self.request[1]
print("{} wrote:".format(self.client_address[0]))
print(data)
socket.sendto(data.upper(), self.client_address)
if __name__ == "__main__":
HOST, PORT = "localhost", 9999
with socketserver.UDPServer((HOST, PORT), MyUDPHandler) as server:
server.serve_forever()
Это клиентская сторона:
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", "utf-8"), (HOST, PORT))
received = str(sock.recv(1024), "utf-8")
print("Sent: {}".format(data))
print("Received: {}".format(received))
Выходные данные примера должны выглядеть точно так же, как и в примере TCP- сервера.
Асинхронные Mixins¶
Для создания асинхронных обработчики используйте классы ThreadingMixIn
и
ForkingMixIn
.
Пример для класса ThreadingMixIn
:
import socket
import threading
import socketserver
class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
def handle(self):
data = str(self.request.recv(1024), 'ascii')
cur_thread = threading.current_thread()
response = bytes("{}: {}".format(cur_thread.name, data), 'ascii')
self.request.sendall(response)
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
def client(ip, port, message):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((ip, port))
sock.sendall(bytes(message, 'ascii'))
response = str(sock.recv(1024), 'ascii')
print("Received: {}".format(response))
if __name__ == "__main__":
# Порт 0 означает выбор произвольного неиспользуемого порта
HOST, PORT = "localhost", 0
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
with server:
ip, port = server.server_address
# Начать поток с сервера, далее поток запустит еще один поток
# для каждого запроса
server_thread = threading.Thread(target=server.serve_forever)
# Exit the server thread when the main thread terminates
server_thread.daemon = True
server_thread.start()
print("Server loop running in thread:", server_thread.name)
client(ip, port, "Hello World 1")
client(ip, port, "Hello World 2")
client(ip, port, "Hello World 3")
server.shutdown()
Выходные данные примера должны выглядеть примерно так:
$ python ThreadedTCPServer.py
Server loop running in thread: Thread-1
Received: Thread-2: Hello World 1
Received: Thread-3: Hello World 2
Received: Thread-4: Hello World 3
Класс ForkingMixIn
используемый таким же образом, за исключением того, что сервер
создаст новый процесс для каждого запроса. Доступно только на платформах POSIX,
поддерживающих fork()
.