На этом шаге мы рассмотрим использование библиотеки socketserver для создания такого сервера.
Вы хотите реализовать сервер, который общается с клиентами по протоколу UDP.
Как и в случае с TCP, UDP-серверы легко создать с помощью библиотеки socketserver. Например, вот простой сервер, отвечающий на запрос текущим временем:
from socketserver import BaseRequestHandler, UDPServer import time class TimeHandler(BaseRequestHandler): def handle(self): print('Got connection from', self.client_address) # Получаем сообщение и клиентский сокет msg, sock = self.request resp = time.ctime() sock.sendto(resp.encode('ascii'), self.client_address) if __name__ == '__main__': serv = UDPServer(('', 20000), TimeHandler) serv.serve_forever()
Как и ранее, вы определяете специальный класс-обработчик, в котором реализован метод handle(), предназначенный для обслуживания соединений с клиентами. Атрибут request - это кортеж, который содержит входящую датаграмму и объект сокета для сервера. client_address содержит адрес клиента.
Чтобы протестировать сервер, запустите его и откройте отдельный процесс Python, который будет посылать ему сообщения:
>>> from socket import socket, AF_INET, SOCK_DGRAM >>> s = socket(AF_INET, SOCK_DGRAM) >>> s.sendto(b'', ('localhost', 20000)) 0 >>> s.recvfrom(8192) (b'Tue Oct 20 17:11:48 2026', ('127.0.0.1', 20000))
Типичный UDP-сервер получает входящую датаграмму (сообщение) вместе с адресом клиента. Если присутствует отвечающий сервер, он посылает датаграмму обратно клиенту. Для передачи датаграмм вы должны использовать методы сокета sendto() и recvfrom(). Хотя традиционные методы send() и recv() также могут работать, два предыдущих чаще используются в UDP-соединениях.
Поскольку протокол не подразумевает установку соединения, UDP-серверы чаще оказываются проще в написании, нежели TCP-серверы. Однако UDP имеет врожденный недостаток - ненадежность ("соединение" не устанавливается, и сообщения могут быть потеряны). Поэтому именно вам придется решать, как разбираться с потерянными сообщениями. Эта тема лежит за пределами этого изложения, но обычно вам потребуется добавить в программу последовательные номера, повторы, тайм-ауты и другие механизмы подтверждения надежности, если она важна для вашего приложения. UDP часто используется в случаях, когда отсутствуют строгие требования к надежности доставки. Например, в приложениях реального времени, таких как сервисы стриминга мультимедийного контента и игры, просто отсутствует возможность отправиться назад во времени и восстановить потерянный пакет, так что программа просто пропускает его и продолжает работу.
Класс UDPServer является однопоточным, что означает возможность обслуживания только одного запроса одновременно. На практике для UDP-соединений это менее важно, чем для TCP. Однако если вы хотите конкурентной работы, создавайте экземпляры ForkingUDPServer или ThreadingUDPServer:
from socketserver import ThreadingUDPServer . . . if __name__ == '__main__': serv = ThreadingUDPServer(('', 20000), TimeHandler) serv.serve_forever()
Реализовать UDP-сервер напрямую, через сокеты, тоже несложно. Вот пример:
from socket import socket, AF_INET, SOCK_DGRAM import time def time_server(address): sock = socket(AF_INET, SOCK_DGRAM) sock.bind(address) while True: msg, addr = sock.recvfrom(8192) print('Got message from', addr) resp = time.ctime() sock.sendto(resp.encode('ascii'), addr) if __name__ == '__main__': time_server(('', 20000))
На следующем шаге мы рассмотрим генерацию диапазона IP-адресов из CIDR-адреса.