Как исправить состояние блокировки sock.recv() в python

Я новичок в сетевом программировании на питоне. Я использую сценарий, найденный в Интернете, который подключается к многоадресному адресу и получает пакеты MPEG-TS. Я вижу на wireshark, что после команды sock.setsockopt на мой компьютер поступают пакеты MPEG-TS.

Скрин с wireshark
https://imgur.com/XJDwd61

Но проблема возникает, когда я хочу напечатать результат sock.recv(). Я считаю, что это из-за состояния блокировки, если я правильно понимаю документацию. После раскомментирования setblocking(0) я получил ошибку 10035. Вы знаете, что мне нужно добавить, чтобы распечатать полученные данные в терминале? Заранее спасибо.

Я попытался изменить размер буфера в sock.recv(), чтобы он был меньше, равным и таким, как сейчас - выше 1358 байтов, что составляет количество байтов одной дейтаграммы.

    import socket
    import struct
    import time

    MCAST_GRP = '239.0.1.104'
    MCAST_PORT = 12345
    IS_ALL_GROUPS = True


    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    if IS_ALL_GROUPS:
        # on this port, receives ALL multicast groups
        sock.bind(('', MCAST_PORT))
    else:
        # on this port, listen ONLY to MCAST_GRP
        sock.bind((MCAST_GRP, MCAST_PORT))

    mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)

    sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)

    #sock.setblocking(0)
    print(f"Entering while loop")
    while True:
        time.sleep(1)
        print(f"I'm in while loop")    
        print(sock.recv(4096))

person Jędrzej Kieruj    schedule 29.07.2019    source источник
comment
Рассматривали ли вы поиск ошибки Winsock 10035? И почему сон? В чем проблема с блокировкой в ​​recv() в первую очередь?   -  person user207421    schedule 29.07.2019
comment
Да, я искал эту ошибку, но установка неблокирующего сокета даже не требуется. Я только что скопировал скрипт из stackoverflow. com/questions/603852/, и я настроил его для своего многоадресного потока. Функция сна предназначена только для отображения результата за одну секунду, а не тысяч из них в терминале. Вот что я спрашиваю, что может быть вероятной причиной проблемы с этим recv или recvfrom, потому что похоже, что он запрашивает дейтаграммы, он их получает, он должен вгрызаться в полученный ток и показывать его. Но он не вгрызается в дейтаграмма на первом месте   -  person Jędrzej Kieruj    schedule 29.07.2019
comment
Итак, если неблокирующий режим не нужен, в чем ваш вопрос? И что, черт возьми, вы подразумеваете под «укусить» и «укусить в ток»? Пожалуйста, ограничьтесь понятным техническим языком.   -  person user207421    schedule 29.07.2019
comment
Я имел в виду тот факт, что в документации recv или recvfrom говорится: когда данные недоступны, блокируйте до тех пор, пока не будет доступен хотя бы один байт или пока удаленный конец не будет закрыт. Итак, похоже, что он находится в состоянии блокировки, потому что он не видит доступного байта, несмотря на то, что я вижу, что приходят дейтаграммы wireshark. Под укусом в текущую дейтаграмму я подразумевал, что программа должна получить и распечатать текущую полученную дейтаграмму.   -  person Jędrzej Kieruj    schedule 29.07.2019
comment
Я хотел бы выделить два режима блокировки. Во-первых, это setblocking, который должен блокировать сокет, а другой — состояние блокировки, то есть, другими словами, состояние замораживания, которое возникает, когда recv не видит доступных данных. В начале я думал, что это одно и то же, поэтому я хотел использовать setblocking(0), потому что я думал, что это вызывает проблему в recv. Я думаю, что recv переходит в состояние замораживания, потому что он не видит никаких доступных данных, которые должны быть доступны, потому что я вижу это на wireshark. Извините за сумбурность рассуждений, я новичок в программировании.   -  person Jędrzej Kieruj    schedule 29.07.2019


Ответы (2)


На основе примера кода из Как вы выполняете многоадресную рассылку UDP в Python? когда я пытаюсь запустить его в своей системе, он работает нормально. Итак, я изменил его с вашими изменениями, это код receiver.py,

import socket
import struct

MCAST_GRP = '239.0.1.104'
MCAST_PORT = 12345
IS_ALL_GROUPS = True

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if IS_ALL_GROUPS:
    # on this port, receives ALL multicast groups
    sock.bind(('', MCAST_PORT))
else:
    # on this port, listen ONLY to MCAST_GRP
    sock.bind((MCAST_GRP, MCAST_PORT))

mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)

sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)

print("Entering while loop")
while True:
    print("I'm in while loop")
    print(sock.recv(4096))

Обратите внимание, я немного изменил операторы печати и удалил f, так как он вызывал ошибку. А это код sender.py,

import socket

MCAST_GRP = '239.0.1.104'
MCAST_PORT = 12345
# regarding socket.IP_MULTICAST_TTL
# ---------------------------------
# for all packets sent, after two hops on the network the packet will not 
# be re-sent/broadcast (see https://www.tldp.org/HOWTO/Multicast-HOWTO-6.html)
MULTICAST_TTL = 2

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, MULTICAST_TTL)
sock.sendto(bytes("robot", 'UTF-8'), (MCAST_GRP, MCAST_PORT))

Обратите внимание, что я конвертирую строку robot в байты с кодировкой UTF-8, так как это необходимо при использовании python3 и не требуется в python2. Это мой результат выполнения,

введите здесь описание изображения

Как видите, когда я вызываю отправителя, получатель отображает строку robot и выполняет цикл while. Это работает нормально.

В случае, если вы уже получаете многоадресные пакеты, которые вы показывали на своем Wireshark, вам необходимо убедиться, что эти пакеты отправляются на тот же номер порта, на котором вы слушаете, то есть 12345 в вашем случае. Если они отправляются на другой номер порта [что, вероятно, так и есть], измените приемник, чтобы он прослушивал этот номер порта, чтобы начать получать эти пакеты.

person Saeed    schedule 29.07.2019
comment
Хорошо, я протестировал коды, и они действительно работают, если я настроил свой собственный тестовый отправитель и тестовый получатель. Поэтому я считаю, что ожидаемый приемник (тот, который я хочу использовать для MPEG-TS) должен быть скорректирован в соответствии с отправителем MPEG-TS. Интересно, есть ли здесь проблема mreq. Я не понимаю эту вещь «4s1», возможно, recvfrom ожидает другой тип данных, чем предоставляет MPEG-TS. Знаете ли вы, как я могу изменить параметр mreq, чтобы отправлять байты вместо строк? - person Jędrzej Kieruj; 29.07.2019
comment
Я считаю, что номер порта является или может быть проблемой. Я не уверен, что setsockopt указывает на правильный порт, на который отправляет многоадресный сервер. Насколько я понимаю сейчас: я пытаюсь сослаться на отправителя, указав MCAST_GRP и MCAST_PORT, что, я полагаю, сделано успешно, потому что я вижу трафик на wirehark. Проблема в том, что я не знаю, указывает ли sock.recvfrom на правильный порт, на который отправляет многоадресная рассылка. Можете ли вы объяснить, как проверить, действительно ли это так, как я говорю? - person Jędrzej Kieruj; 29.07.2019
comment
Хорошо, это не «4s1», это на самом деле «4sl», который является просто спецификатором формата для структуры. Эта строка struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY) по существу означает, что создается 4-байтовая строка, т. е. 4s из номера MCAST_GRP, а затем добавляется l, которая снова является целым числом длиной 4 байта после нее. Таким образом, созданный mreq будет иметь как минимум эти 8 байтов, но могут быть добавлены дополнительные нулевые байты в зависимости от платформы. Прочтите этот docs.python.org/2/library/struct.html# символы формата [или просто напечатайте mreq, чтобы увидеть значение] - person Saeed; 29.07.2019
comment
Я не думаю, что вам нужно изменять параметр mreq, я думаю, что он настроен правильно, вы можете распечатать возвращаемое значение setsockopt, чтобы увидеть, возвращает ли он успех или EINVAL. Мой вопрос: в Wireshark, как только вы начнете получать пакеты, вы показали это на картинке, но если вы нажмете на любой из этих пакетов, он должен показать вам детали этого пакета внизу, и он должен показать вам поле Dst Port в том же пакете. Что это за число? - person Saeed; 29.07.2019
comment
Итак, есть информация wireshark, а также результат определения функции foobar(), которая выполняет print(sock) : imgur.com/Tn8DeFz Также есть эффект изменения foobar с print(sock) на return sock. Функция определяется и вызывается после set.setsockopt и перед циклом while. imgur.com/hLX2TSw Я хотел бы поблагодарить Вас, сэр, за уделенное время. Я застрял с этим кодом больше недели. Я чувствую себя беспомощным и совершенно не понимаю, что может быть не так, как не этот порт назначения. Также мне интересно, говорят ли нам о чем-нибудь вещи, отмеченные красными и синими скобками. - person Jędrzej Kieruj; 30.07.2019

Попробуйте этот простой код, вы можете установить меньше BUFF_SIZE, если хотите.

#BUFF_SIZE = 4096
BUFF_SIZE = 1024
data = b''
while True:
    #chunk = s.recv(BUFF_SIZE)

    #FOR UDP (@Saeed credits)
    chuck = sock.recvfrom(BUFF_SIZE)

    data += chunk
    if len(chunk) < BUFF_SIZE:
        break

print(data)
person Wonka    schedule 29.07.2019
comment
К сожалению, тот же результат. Я читал о разбивке пакетов раньше, но в этом случае я все еще вижу пакеты, поступающие на wirehark, но программа зависает на sock.recv в отладчике :( - person Jędrzej Kieruj; 29.07.2019
comment
Попробуйте использовать BUFF_SIZE = 1024. Если вы печатаете фрагмент или данные после конкатенации в цикле, они что-то отображают? - person Wonka; 29.07.2019
comment
Я сделал это еще до того, как скомпилировал ваше изменение в первый раз, потому что знаю, что оно нормально сработает, потому что разделит 1358 байт на 2 куска. К сожалению, все тот же результат. Я стажер в компании, где, как вы, наверное, знаете, существует множество правил безопасности для брандмауэра, антивируса и т. д., поэтому в начале у меня были проблемы даже с получением дейтаграмм, поэтому я связался с отделом безопасности, они внесли изменения в мою политику безопасности, так что теперь Я их получаю, но не могу распечатать и не знаю, в чем может быть правдоподобная причина. - person Jędrzej Kieruj; 29.07.2019
comment
Хорошо, может быть, вам нужно проверить mreq, я его никогда не видел. Возможно, это тоже что-то блокирует, попробуйте не устанавливать sock.setsockopt(.., mreq) - person Wonka; 29.07.2019
comment
Вместо sock.recv(4096) используйте sock.recvfrom(4096). Прочитайте это stackoverflow.com/a/36116201/9342222 - person Saeed; 29.07.2019
comment
Это хороший ответ @Saeed. Не видел, чтобы он объявил о подключении UPD, если это решит его проблему, мой ответ будет отредактирован с вашими кредитами. - person Wonka; 29.07.2019
comment
Это ничего не меняет. Программа все равно смотрит, что зависает на sock.recvfrom point. @Wonka Но без mreq даже дейтаграммы не приходят - person Jędrzej Kieruj; 29.07.2019
comment
Я изменил его на sock.recvfrom, но он также зависает в этот момент. совершенно не понимаю что делать - person Jędrzej Kieruj; 29.07.2019