Идиома для сокета получить в Python - PullRequest
6 голосов
/ 21 марта 2011

У меня есть некоторый опыт программирования сокетов с использованием API сокетов Berkeley на C. Как правило, любое программирование сокетов требует стратегии, позволяющей получающему сокету знать, сколько данных он должен получить. Это может быть достигнуто либо с помощью полей длины заголовка, либо с помощью символов-разделителей. Как правило, я предпочитаю поле заголовка, которое содержит длину.

Конечно, нам также необходимо знать размер самого поля заголовка длины, которое является просто фиксированным значением размера, которое должно быть согласовано как отправителем, так и получателем. В C это легко реализовать, потому что собственные целочисленные типы имеют фиксированный размер и в двоичном формате, так что вы можете просто сказать что-то вроде:

uint16_t bytes_to_receive;
recv(sock, &bytes_to_receive, sizeof(bytes_to_receive), 0);
bytes_to_receive = ntohs(bytes_to_receive);
// Now receive 'bytes_to_receive' bytes...

Но как такого рода идиомы достигаются с помощью сокетов Python? В Python целые числа являются объектами, а засоленные целые числа - байтовыми массивами переменной длины. Поэтому мы не можем использовать засеченное целое число в качестве поля заголовка длины, потому что мы не можем быть уверены в его размере в байтах.

Конечно, я всегда мог отправить байтовый массив известного размера, содержащий двоичное целое число, например b'\x05\x00', чтобы создать 16-разрядное двоичное целое число со значением 5 в формате с прямым порядком байтов, но это действительно не кажется как правильный подход.

Итак, как это обычно выполняется в Python?

Ответы [ 3 ]

5 голосов
/ 21 марта 2011

Вы можете использовать модуль struct для преобразования целых чисел Python в строки и байтовые массивы. Просто прочитайте количество байтов, которое соответствует размеру заголовка типа, и преобразуйте его с помощью модуля struct, и все будет хорошо. (примечание: обязательно используйте правильные порядковые флаги при кодировании / декодировании)

0 голосов
/ 21 марта 2011

Модуль ctypes может предоставить sizeof() для типа C uint16, который вы используете в своем примере:

>>> import ctypes
>>> ctypes.sizeof(ctypes.c_uint16)
2
0 голосов
/ 21 марта 2011

Модуль sys предоставляет функцию getsizeof(), которая возвращает размер объекта в байтах (с использованием метода objects __sizeof__).Если вы работаете с пользовательскими объектами, вам нужно тщательно протестировать реализацию __sizeof__, но, похоже, это должно хорошо работать для стандартных типов.

В качестве альтернативы, вы также можете сериализовать данные в * 1009.* или json и подсчитайте количество символов в строке, хотя это может повлечь за собой снижение производительности.

Используя любой из этих методов, если вы передаете данные переменной длины, сначала передайте размер, а затем используйте это значениечтобы определить, сколько еще данных нужно прочитать.

Другие примечания:

  • Если вы еще этого не сделали, вам также следует прочитать документацию по API для сокетов .
  • Имейте в виду, что составные типы, такие как списки, требуют дополнительного пробела, поэтому:
    >>> import sys
    >>> a = [1,3,4]
    >>> sys.getsizeof(a)
    96
    >>> l = 0
    >>> for i in a:
    ...     l += sys.getsizeof(i)
    ... 
    >>> print l
    72
    >>>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...