LINUX.ORG.RU

[python]Работа с сетью


0

2

С сетью начал работать пару недель назад, раньше надобности никакой не было, решил узнать, как проще отправлять данные через сокет?
Сейчас у меня вот так:

def send(self, format, *args):
        buf = struct.pack('i') , len(format))
        self._sock.send(buf)
        buf = struct.pack(str(len(format)+'s') , format) 
        self._sock.send(struct.calcsize(str(len(format))+'s')) #Передаем размер
        self._sock.send(buf) #Передаем буфер со строкой форматирования
        buf = struct.pack(format, args)
        self._sock.send(struct.calcsize(format))
        self._sock.send(buf)
Наверно вам покажется жутким извратом
Буду признателен за советы и статьи, где можно почитать на подобную тему.
Спасибо

★★★★★

Ответ на: комментарий от tailgunner

Поправил код, просто по памяти писал. Функция pack как первый параметр принимает строку форматирования, где каждый символ соответствует каждому следующему парметру передаваемому данной функции соответственно и обозначает сишный тип.
Пример:

>>> buf = struct.pack('10si2I', 'aswer', -341, 4562, 23432)
>>> buf
'aswer\x00\x00\x00\x00\x00\x00\x00\xab\xfe\xff\xff\xd2\x11\x00\x00\x88[\x00\x00'
Тут мы запаковываем строку из 10 элементов, число соответствующие типу int и 2 числа соответствующие unsigned int
Подробности тут
Для распаковки такого на принимающей стороне мне нужно знать строку форматирования, для этого ее нужно переправить тоже, и я упаковываю ее buf = struct.pack(str(len(format)+'s') , format)[bt] Затем отправляю количество символов в строке форматирования, размер буфера со строкой форматирования, а затем саму строку(буфер с ней), далее упаковываю саму информацию, передаю размер буфера и в итоге сам буфер с информацией, после этого на принимающей стороне можно будет извлечь информацию.
Сильно не пинайте, я только учусь работе с сетью и кажется уже напортачил[;

deterok ★★★★★ ()
Ответ на: комментарий от deterok

Кхм. Спасибо за разъяснение работы модуля struct, но я немного знаю Питон :)

Но _почему_ ты работаешь с сетью именно так, я не понял. Особенно вот это:

Для распаковки такого на принимающей стороне мне нужно знать строку форматирования, для этого ее нужно переправить тоже

Вообще сетевой протокол предполагает, что формат данных известен обеим сторонам. Почитай немного о том же HTTP (а лучше - посмотри, не проще ли воспользоваться HTTP, XMLRPC или еще каким-то готовым сетевым протоколом).

tailgunner ★★★★★ ()
Ответ на: комментарий от tailgunner

Вот он и придумывает какой-то изощрённый протокол для передачи рандомных структур вместе с их описанием.

PolarFox ★★★★★ ()

Ну и в мире 100500 готовых библиотек для ipc по сети. Свой велосипед нужен только если ты на 100% уверен что существующие решения говно.

true_admin ★★★★★ ()

берёшь RPyC, Twisted, PyREX, whatever и получаешь профит, это ж питон, в нём есть батарейки

shty ★★★★★ ()
Ответ на: комментарий от anonymous

Пока решил остановится на своем варианте, уже удалось уменьшить немного код.

deterok ★★★★★ ()
Ответ на: комментарий от AIv

Я читал ваши труды, правда не понял что дает флаг useradd(вроде так назывался). Я решил отправлять данные вот по такому принципу:

def send(self, format, *args):
        buf = struct.pack('i', len(format))    #Запаковываем размер строки форматирования
        self.send(buf)    #Отправляем размер строки форматирования
        buf = struct.pack(str(len(format)+'s') , format)    #Упаковываем строку форматирования
        self._sock.send(buf)    #Отправляем пакет со строкой форматирования
        buf = struct.pack(format, args) #Упаковвываем массив аргументов, требующих отправки
        self._sock.send(buf)    #Отправляем кортеж аргументов


    def recv(self):
        buf = self._sock.recv(4)    #Принимаем размер пакета с размером строки форматирования
        len_format = struct.unpack('i', buf)    #Распаковываем строку форматирования
        buf = self._sock.recv(struct.calcsize(str(len_format)+'s')) #Принимаем пакет со строкой форматирования
        format = struct.unpack(str(len_format)+'s', buf)    #Распаковываем строку форматирования
        buf = self._sock.recv(struct.calcsize(format))  #Получаем массив аргументов
        return struct.unpack(format, buf)   #Распаковываем и возвращаем кортеж данных
Хотел использовать protobuf, но по своим соображением не стал этого делать.

deterok ★★★★★ ()
Ответ на: комментарий от deterok

не понял что дает флаг useradd(вроде так назывался)

Если Вы про SO_REUSEADDR на стороне сервера, то этот флаг позволяет после остановки сервера не ждать пока адрес освободиться по таймауту, а сразу запускать новый сервер (с привязкой к тому же адресу). Иногда адрес «залипает», несмотря на закрытие сокета... подробнее надо читать Стивенса. Флаг мне посоветовали местные Гуру кстати.

Я читал ваши труды

Невнимательно читали судя по всему;-) Протокол у Вас интересный, трафик меньше моего и типа можно напрямую вязаться с сишными клиентами, мой протокол притормаживает но зато он гораздо гибче. Но никто не обещал, что recv с первого вызова вернет столько данных, сколько попросили. Под виндой это во всяком случае не так (для больших объемов), т.е. надо читать пока не получите именно столько, сколько надо.

И меня смутно гложет три сенда за одну отправку, хотя у меня так же... Это надо тестить, возможно оно менее эффективно чем один сенд от суммы трех строк, фиг знает как оно там организовано внутрях.

AIv ★★★★★ ()
Ответ на: комментарий от AIv

У меня к вам вопрос(этак скил смотрю у вас побольше)

Здесь
я выложил функции посылки/приема данных, но меня интересует возможность проверки на ошибки приема/передачи. Как бы сказать правильно, я хочу остановить прием на принимающей стороне, при ошибке на отправляющей и наоборот.
Фактически можно отправить сразу все запакованное в один пакет, а потом читать до тех пор, пока кол-во прочитанного из сетевого буфера не будет = 0, далее, так как все это является буфером, распаковываем сначала первый int, в котором содержится размер следующей строковой переменной, далее распаковываем этот же буфер с такой строкой форматирования 'i{0}s'.format(i), где i первая переменная из этого буфера, итого у нас будет строка форматирования для наших данных, ради которых мы все это устроили, и последний раз распаковываем буфер с такой строкой форматирования: 'i{0}s{1}'.format(i, f), где s строка форматирования, в итоге в 3 элементе полученного кортежа мы получаем кортеж наших данных, кривовато, но должно работать. Все надо тестировать.

deterok ★★★★★ ()
Ответ на: комментарий от deterok

Да, надо завязывать с начинанием написанием поста ночью, а закачиванием написания утром. Ну вроде мысль ясна моя.

deterok ★★★★★ ()
Ответ на: комментарий от deterok

Здесь это ГДЕ? У меня оно читается просто как слово здесь;-)

насчет многократной распаковки одного-и-того-же не понял.

У вас в заголовке передается длина строки формата (4 байта), строка формата и аргументы (длина аргументов считается по строке формата). Послдать можно одним куском или частями, доставать можно токо по частям (длину, строку формата, аргументы - длина каждой след части определяется на основе предыдущей). Можно вначале запихать общую длину сообщения, а потом как у Вас - тогда достать можно двумя частями, сначала длину, затем само сообщение.

Достали сообщение, это срока. Взяли из него длину строки формата, строку формата и аргументы, распаковали?

data = sock.recv(len_data)
format_len = unpack('i',data[:4])[0] 
args = unpack( data[4:4+format_len], data[4+fromat_len:] )

как то так....

AIv ★★★★★ ()
Ответ на: комментарий от deterok

про tsl/ssl - не знаю, не пробовал.

Про передачу файлов вопрос интересный. В принципе уже есть 100500 протоколов для передачи файлов, и надо их использовать а не городить свой. Но я таки делал свой, потому как у меня передача с винды на линукс и обратно, и трад протоколы настраивать было лень. Передаете длину файла, затем пихаете его кусками оговоренной длины, пока все не передадите. Куда сложнее вписать передачу файла в общую структуру приложения (иногда надо открывать доп сокет и вести передачу в нем в отдельной нити, что бы все не висело). Но это уже от задачи зависит... и да, под виндой файлы надо обязательно открывать как «rb» или «wb» ;-)

AIv ★★★★★ ()
Ответ на: комментарий от deterok

Вы прям меня сподвигнете на дописывание второй части... тем более что в итоге проект оказался весьма пользительным, и пошел в серию (на нем построена клиент-серверная система управления счетом синтетических сейсмограмм).

AIv ★★★★★ ()
Ответ на: комментарий от AIv

И меня смутно гложет три сенда за одну отправку, хотя у меня так же... Это надо тестить, возможно оно менее эффективно чем один сенд от суммы трех строк, фиг знает как оно там организовано внутрях.

В обсчем три сенда за одну отправку это трындец. Мой протокол (два сенда за одну отправку) ощутимо тормозил - при каждом нажатии на кнопочку клиента система подвисала. Потестил, в рамках одного хоста по 127.0.0.1 запрос на вызов метода сервера со стороны клиента (там несколько пересылок туда сюда) - 0.24 сек. Простая замена

def send_string( connect, string ) :
    connect.send( '%08i'%len(string) )
    if string : connect.send( string )
на
def send_string( connect, string ) :
    connect.send( '%08i'%len(string) + string )
с одной стороны (пофик с какой) дает уже 0.12 сек

Замена с обоих сторон дает 0.0012 сек, т.е. выигрыш в ДВЕСТИ раз! Надо будет потестить в боевых условиях конечно еще, но тенденция ясна;-)

AIv ★★★★★ ()
Ответ на: комментарий от AIv

Я уже понял:) Тестировал в разных условиях и один send дал наилучший результат.
Теперь мысли мои направлены в сторону ssl и сжатия данных при передачи крупных объектов.

deterok ★★★★★ ()
Ответ на: комментарий от deterok

Жмется все zlib-ом без проблем (zlib.compress/decompress) Если с ssl разберетесь, напишите как - тоже любопытно.

AIv ★★★★★ ()
Ответ на: комментарий от AIv

Я переосмыслил то, что я хочу:)
Что будет если клиент просто отключится во время передачи данных?
Как правильно обработать эту ситуацию?

deterok ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.