Нашёл хороший туториал (PDF) на английском про сетевое программирование в Python — чётко изложена сама теория, кое-чего я и сам не знал, и даны хорошие базовые примеры на Python.
На сайте автора есть и другие неплохие туториалы про Python.
14 мая 2010 г.
5 мая 2010 г.
UDP-траспорт
В проекте, над которым я работаю, понадобилось использовать асинхронный UDP-траспорт. Как выяснилось, есть в этой задаче несколько подводных камней — о них и пойдёт речь.
Хотелось что-то максимально простое с помощью встроенных в Python средств.
Первый вариант — класс SocketServer.UDPServer. Минус его в том, что он использует блокирующие сокеты; для обхода этого в том же пакете есть и неблокирующие версии, реализованные с помощью тредов или даже отдельных процессов на каждого клиента. Но это не совсем асинхронность.
Второй способ — пакет asyncore, который как раз для асинхронных сообщений и предназначен. Правда, только для TCP. Создать нужный класс несложно, но есть несколько тонкостей:
Вот, собственно, и всё, все хитрости прокомментированы в коде. Естественно, нам не требуется
Хотелось что-то максимально простое с помощью встроенных в Python средств.
Первый вариант — класс SocketServer.UDPServer. Минус его в том, что он использует блокирующие сокеты; для обхода этого в том же пакете есть и неблокирующие версии, реализованные с помощью тредов или даже отдельных процессов на каждого клиента. Но это не совсем асинхронность.
Второй способ — пакет asyncore, который как раз для асинхронных сообщений и предназначен. Правда, только для TCP. Создать нужный класс несложно, но есть несколько тонкостей:
class UdpTransport(asyncore.dispatcher, object): def __init__(self, addr=None): super(UdpTransport, self).__init__() self.ignore_log_types = set() # Для вывода дополнительных ошибок в asyncore, подсмотрел в исходниках asyncore :) self.create_socket(socket.AF_INET, socket.SOCK_DGRAM) # Размер этого буфера очень важен # Если входящий пакет будет больше него, остаток отбрасывается self.ReadBufferSize = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF) if addr: self.bind(addr) # Единственное отличие серверного сокета в использовании bind() def handle_close(self): self.close() def writable(self): return False def handle_read(self): try: data, addr = self.recvfrom(self.ReadBufferSize) except socket.error: # Исключение возникает при чтении данных из сокета: когда они заканчиваются, следующая попытка вылетает pass # обработка данных def send(self, msg, toaddr): self.socket.sendto(msg, toaddr) handle_connect = handle_read # Нарыл где-то на просторах инета # Без этого asyncore не работает с UDP, первую порцию данных он интерпретирует как инициализацию подключенияНаследуемся и от object для использования super и отладки с помощью метакласса.
Вот, собственно, и всё, все хитрости прокомментированы в коде. Естественно, нам не требуется
connect()
для серверных сокетов, а вместо socket.send/recv
нужно использовать методы sendto/recvfrom
. Сперва я путался, в каких случаях что нужно применять — документация лаконична — но нашёл хороший источник с примерами, это наш родной `man`
. ;) Например, `man bind`
(или `man 2 bind`
, если быть точным) показывает документацию С-шного сетевого API, обёрткой вокруг которого socket, собственно, и является. Если на вашем *nix такой странички нет, нужно установить пакет manpages-dev (в Debian и Ubuntu, по крайней мере).
Подписаться на:
Сообщения (Atom)