socketserver
— Фреймворк для сетевых серверов.¶
Kod źródłowy: Lib/socketserver.py
Модуль socketserver
спрощує завдання написання мережевих серверів.
Dostępność: not WASI.
Этот модуль не работает или недоступен в WebAssembly. См. Платформы веб-сборки для получения дополнительной информации.
Існує чотири основних класи конкретних серверів:
- 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
.
Ці чотири класи обробляють запити synchronously; кожен запит має бути виконано перед початком наступного запиту. Це не підходить, якщо виконання кожного запиту займає багато часу, тому що він вимагає багато обчислень або тому, що він повертає багато даних, які клієнт повільно обробляє. Рішення полягає у створенні окремого процесу або потоку для обробки кожного запиту; змішані класи ForkingMixIn
і ThreadingMixIn
можна використовувати для підтримки асинхронної поведінки.
Створення сервера вимагає кількох кроків. По-перше, ви повинні створити клас обробника запитів, створивши підклас класу 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 - это семейство адресов.
- class socketserver.ForkingMixIn¶
- class socketserver.ThreadingMixIn¶
Розгалуження та версії потоків кожного типу сервера можна створити за допомогою цих змішаних класів. Наприклад,
ThreadingUDPServer
створюється таким чином:класс ThreadingUDPServer (ThreadingMixIn, UDPServer): проходить
Клас mix-in стоїть на першому місці, оскільки він замінює метод, визначений у
UDPServer
. Налаштування різних атрибутів також змінює поведінку базового серверного механізму.ForkingMixIn
і згадані нижче класи Forking доступні лише на платформах POSIX, які підтримуютьfork()
.- block_on_close¶
ForkingMixIn.server_close
ожидает завершения всех дочерних процессов, за исключением случаев, когда атрибутblock_on_close
имеет значениеFalse
.ThreadingMixIn.server_close
ожидает завершения всех потоков, не являющихся демонами, за исключением случаев, когда атрибутblock_on_close
имеет значениеFalse
.
- daemon_threads¶
Для
ThreadingMixIn
используйте демонические потоки, установив дляThreadingMixIn.daemon_threads
значениеTrue
, чтобы не ждать завершения потоков.
Zmienione w wersji 3.7:
ForkingMixIn.server_close
иThreadingMixIn.server_close
теперь ждут завершения всех дочерних процессов и недемонических потоков. Добавьте новый атрибут классаForkingMixIn.block_on_close
, чтобы включить поведение версии до версии 3.7.
- class socketserver.ForkingTCPServer¶
- class socketserver.ForkingUDPServer¶
- class socketserver.ThreadingTCPServer¶
- class socketserver.ThreadingUDPServer¶
- class socketserver.ForkingUnixStreamServer¶
- class socketserver.ForkingUnixDatagramServer¶
- class socketserver.ThreadingUnixStreamServer¶
- class socketserver.ThreadingUnixDatagramServer¶
Ці класи попередньо визначені за допомогою змішаних класів.
Dodane w wersji 3.12: Были добавлены классы ForkingUnixStreamServer и ForkingUnixDatagramServer.
Щоб реалізувати службу, ви повинні отримати клас від BaseRequestHandler
і перевизначити його метод handle()
. Потім ви можете запустити різні версії служби, поєднавши один із класів сервера з класом обробника запитів. Клас обробника запитів має відрізнятися для датаграм або потокових служб. Це можна приховати за допомогою підкласів обробників StreamRequestHandler
або DatagramRequestHandler
.
Звичайно, вам все одно доведеться використовувати свою голову! Наприклад, немає сенсу використовувати сервер розгалуження, якщо служба містить стан у пам’яті, який можна змінювати різними запитами, оскільки зміни в дочірньому процесі ніколи не досягнуть початкового стану, який зберігається в батьківському процесі та передається кожному дочірньому. . У цьому випадку ви можете використовувати потоковий сервер, але вам, ймовірно, доведеться використовувати блокування для захисту цілісності спільних даних.
З іншого боку, якщо ви створюєте HTTP-сервер, де всі дані зберігаються зовні (наприклад, у файловій системі), синхронний клас, по суті, надаватиме послугу „глухою”, поки обробляється один запит, що може бути дуже довго, якщо клієнт повільно отримує всі запитувані дані. Тут підійде сервер потоків або розгалуження.
У деяких випадках може бути доречним обробити частину запиту синхронно, але завершити обробку в розгалуженому дочірньому файлі залежно від даних запиту. Це можна реалізувати за допомогою синхронного сервера та виконання явного розгалуження в методі класу обробника запитів handle()
.
Другой подход к обработке нескольких одновременных запросов в среде, которая не поддерживает ни потоки, ни fork()
(или там, где они слишком дороги или не подходят для службы), состоит в том, чтобы поддерживать явную таблицу частично завершенных запросов и использовать селекторы
, чтобы решить, над каким запросом работать дальше (или обрабатывать ли новый входящий запрос). Это особенно важно для потоковых сервисов, где каждый клиент потенциально может быть подключен в течение длительного времени (если невозможно использовать потоки или подпроцессы).
Серверні об’єкти¶
- 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()
для очищення дочірніх процесів-зомбі.Zmienione w wersji 3.3: Додано виклик
service_actions
до методуserve_forever
.
- service_actions()¶
Це викликається в циклі
serve_forever()
. Цей метод може бути перевизначений підкласами або класами mixin для виконання дій, специфічних для певної служби, наприклад дій очищення.Dodane w wersji 3.3.
- shutdown()¶
Скажіть циклу
serve_forever()
зупинитися та зачекайте, доки він зупиниться.shutdown()
потрібно викликати, покиserve_forever()
працює в іншому потоці, інакше він блокується.
- server_close()¶
Очистити сервер. Може бути перевизначено.
- address_family¶
Семейство протоколов, которым принадлежит сокет сервера. Общие примеры: const: socket.af_inet,: const:` socket.af_inet6` и: const: socket.af_unix. Подкласс классов TCP или UDP -сервера в этом модуле с атрибутом класса `` address_family = af_inet6`` Установите, если вы хотите классы сервера IPv6.
- 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-кортеж, що містить новий об’єкт сокета, який буде використовуватися для зв’язку з клієнтом, і адресу клієнта.
- handle_error(request, client_address)¶
Ця функція викликається, якщо метод
handle()
примірникаRequestHandlerClass
викликає виняткову ситуацію. Дія за замовчуванням полягає в тому, щоб надрукувати відстеження стандартної помилки та продовжити обробку подальших запитів.Zmienione w wersji 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
.
Zmienione w wersji 3.6: Додано підтримку протоколу context manager. Вихід із контекстного менеджера еквівалентний виклику
server_close()
.
Об’єкти обробки запитів¶
- class socketserver.BaseRequestHandler¶
Це суперклас усіх об’єктів обробки запитів. Він визначає інтерфейс, наведений нижче. Конкретний підклас обробника запитів повинен визначати новий метод
handle()
і може перевизначати будь-які інші методи. Для кожного запиту створюється новий екземпляр підкласу.- setup()¶
Викликається перед методом
handle()
для виконання будь-яких необхідних дій ініціалізації. Стандартна реалізація нічого не робить.
- handle()¶
Эта функция должна выполнять всю работу, необходимую для обслуживания запроса. Реализация по умолчанию ничего не делает. Ему доступны несколько атрибутов экземпляра; запрос доступен как
request
; адрес клиента какclient_address
; и экземпляр сервера какserver
, на случай, если ему потребуется доступ к информации по каждому серверу.Тип
request
различен для дейтаграммных и потоковых сервисов. Для потоковых сервисовrequest
— это объект сокета; для служб дейтаграммrequest
— это пара строки и сокета.
- finish()¶
Викликається після методу
handle()
для виконання будь-яких необхідних дій очищення. Стандартна реалізація нічого не робить. Якщоsetup()
викликає виняток, ця функція не буде викликана.
- request¶
новый объект
socket.socket
, который будет использоваться для связи с клиентом.
- client_address¶
Адрес клиента, возвращаемый
BaseServer.get_request()
.
- server¶
Объект
BaseServer
, используемый для обработки запроса.
- class socketserver.StreamRequestHandler¶
- class socketserver.DatagramRequestHandler¶
Эти подклассы
BaseRequestHandler
переопределяют методыsetup()
иfinish()
и предоставляют атрибутыrfile
иwfile
.- rfile¶
Читается файловый объект, из которого поступает запрос. Поддержка читаемого интерфейса
io.BufferedIOBase
.
- wfile¶
Файловый объект, в который записывается ответ. Поддержка записываемого интерфейса
io.BufferedIOBase
.
Zmienione w wersji 3.6:
wfile
также поддерживает записываемый интерфейсio.BufferedIOBase
.
Przykłady¶
socketserver.TCPServer
Приклад¶
Це сторона сервера:
import socketserver
class MyTCPHandler(socketserver.BaseRequestHandler):
"""
The request handler 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
pieces = [b'']
total = 0
while b'\n' not in pieces[-1] and total < 10_000:
pieces.append(self.request.recv(2000))
total += len(pieces[-1])
self.data = b''.join(pieces)
print(f"Received from {self.client_address[0]}:")
print(self.data.decode("utf-8"))
# just send back the same data, but upper-cased
self.request.sendall(self.data.upper())
# after we return, the socket will be closed.
if __name__ == "__main__":
HOST, PORT = "localhost", 9999
# Create the server, binding to localhost on port 9999
with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
# Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
server.serve_forever()
Альтернативний клас обробника запитів, який використовує потоки (файлоподібні об’єкти, які спрощують зв’язок, надаючи стандартний файловий інтерфейс):
class MyTCPHandler(socketserver.StreamRequestHandler):
def handle(self):
# self.rfile is a file-like object created by the handler.
# We can now use e.g. readline() instead of raw recv() calls.
# We limit ourselves to 10000 bytes to avoid abuse by the sender.
self.data = self.rfile.readline(10000).rstrip()
print(f"{self.client_address[0]} wrote:")
print(self.data.decode("utf-8"))
# Likewise, self.wfile is a file-like object used to write back
# to the client
self.wfile.write(self.data.upper())
A diferença é que a chamada readline()
no segundo manipulador chamará recv()
várias vezes até encontrar um caractere de nova linha, enquanto o primeiro manipulador teve que usar um laço recv()
para acumular dados até uma nova linha. Se tivesse usado apenas um único recv()
sem o laço, ele teria retornado apenas o que foi recebido até agora do cliente. O TCP é baseado em fluxo: os dados chegam na ordem em que foram enviados, mas não há correlação entre as chamadas send()
ou sendall()
do cliente e o número de chamadas recv()
no servidor necessárias para recebê-los.
Це сторона клієнта:
import socket
import sys
HOST, PORT = "localhost", 9999
data = " ".join(sys.argv[1:])
# Create a socket (SOCK_STREAM means a TCP socket)
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, "utf-8"))
sock.sendall(b"\n")
# Receive data from the server and shut down
received = str(sock.recv(1024), "utf-8")
print("Sent: ", data)
print("Received:", 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):
"""
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(f"{self.client_address[0]} wrote:")
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 is the socket type to use for UDP sockets
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# As you can see, there is no connect() call; UDP has no connections.
# Instead, data is directly sent to the recipient via sendto().
sock.sendto(bytes(data + "\n", "utf-8"), (HOST, PORT))
received = str(sock.recv(1024), "utf-8")
print("Sent: ", data)
print("Received:", received)
Вихідні дані прикладу мають виглядати точно так само, як для прикладу сервера TCP.
Асинхронні міксини¶
Для створення асинхронних обробників використовуйте класи 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__":
# Port 0 means to select an arbitrary unused port
HOST, PORT = "localhost", 0
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
with server:
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.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()
.