Шаг 206.
Python: сборник рецептов.
Сети и веб-программирование. Создание UDP-сервера

    На этом шаге мы рассмотрим использование библиотеки 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-адреса.




Предыдущий шаг Содержание Следующий шаг