Я пытаюсь написать небольшой двоичный файл контроллера для динамика Sonos. Для этого я хочу выполнить обнаружение SSDP.
QHostAddress groupAddress = QHostAddress("239.255.255.250");
m_socket = new QUdpSocket(this);
auto ok = m_socket->bind(QHostAddress::AnyIPv4, 56123, QUdpSocket::ShareAddress);
if (!ok)
{
return;
}
ok = m_socket->joinMulticastGroup(groupAddress);
if (!ok)
{
return;
}
QByteArray message("M-SEARCH * HTTP/1.1\r\n" \
"HOST: 239.255.255.250:1900\r\n" \
"MAN: \"ssdp:discover\"\r\n" \
"MX: 5\r\n" \
"ST: urn:smartspeaker-audio:service:SpeakerGroup:1\r\n" \
"linux/4.13 UPnP/1.1 myos/0.11.1\r\n" \
"\r\n");
for(int i=0; i<4; i++){
auto writeOk = m_socket->writeDatagram(message.data(), groupAddress, 1900);
if (writeOk == -1)
{
qDebug() << "Writing Datagram failed";
}
}
while (m_socket->hasPendingDatagrams())
{
QByteArray reply;
reply.resize(m_socket->pendingDatagramSize());
m_socket->readDatagram(reply.data(), reply.size());
qDebug() << reply.data();
}
qDebug() << "No more pending datagrams";
Однако при использовании wireshark или любого другого сетевого монитора я вижу какие-либо отправляемые пакеты. Я запустил strace в бинарнике и получил такой вывод:
socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 5
setsockopt(5, SOL_SOCKET, SO_BROADCAST, [1], 4) = 0
setsockopt(5, SOL_IP, IP_PKTINFO, [1], 4) = 0
setsockopt(5, SOL_IP, IP_RECVTTL, [1], 4) = 0
setsockopt(5, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
bind(5, {sa_family=AF_INET, sin_port=htons(56123), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
getsockname(5, {sa_family=AF_INET, sin_port=htons(56123), sin_addr=inet_addr("0.0.0.0")}, [28->16]) = 0
getpeername(5, 0x7ed3aae8, [16]) = -1 ENOTCONN (Transport endpoint is not connected)
getsockopt(5, SOL_SOCKET, SO_TYPE, [2], [4]) = 0
setsockopt(5, SOL_IP, IP_ADD_MEMBERSHIP, {imr_multiaddr=inet_addr("239.255.255.250"), imr_interface=inet_addr("0.0.0.0")}, 8) = 0
sendmsg(5, {msg_name={sa_family=AF_INET, sin_port=htons(1900), sin_addr=inet_addr("239.255.255.250")}, msg_namelen=16, msg_iov=[{iov_base="M-SEARCH * HTTP/1.1\r\nHOST: 239.2"..., iov_len=167}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, MSG_NOSIGNAL) = 167
sendmsg(5, {msg_name={sa_family=AF_INET, sin_port=htons(1900), sin_addr=inet_addr("239.255.255.250")}, msg_namelen=16, msg_iov=[{iov_base="M-SEARCH * HTTP/1.1\r\nHOST: 239.2"..., iov_len=167}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, MSG_NOSIGNAL) = 167
sendmsg(5, {msg_name={sa_family=AF_INET, sin_port=htons(1900), sin_addr=inet_addr("239.255.255.250")}, msg_namelen=16, msg_iov=[{iov_base="M-SEARCH * HTTP/1.1\r\nHOST: 239.2"..., iov_len=167}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, MSG_NOSIGNAL) = 167
sendmsg(5, {msg_name={sa_family=AF_INET, sin_port=htons(1900), sin_addr=inet_addr("239.255.255.250")}, msg_namelen=16, msg_iov=[{iov_base="M-SEARCH * HTTP/1.1\r\nHOST: 239.2"..., iov_len=167}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, MSG_NOSIGNAL) = 167
recv(5, 0x7ed3ab2b, 1, MSG_PEEK) = -1 EAGAIN (Resource temporarily unavailable)
Что бросается в глаза здесь для меня, так это сбой системного вызова recv(), а также getpeername(). Чтобы убедиться, что это не проблема с сетевыми разрешениями или что-то подобное, я написал скрипт python:
class SSDPClient:
def __init__(self):
self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
def discover(self, timeout):
request_content = ("M-SEARCH * HTTP/1.1\n"
"HOST: 239.255.255.250:1900\n"
"MAN: \"ssdp:discover\"\n"
"MX: {duration}\n"
"ST: urn:smartspeaker-audio:service:SpeakerGroup:1\n"
"USER-AGENT: {osstring} UPnP/1.1 {productstring}\n").format(duration=timeout, osstring="linux/4.13", productstring="myos/0.11.1").encode('utf-8')
[self._socket.sendto(request_content, ("239.255.255.250", 1900)) for i in range(0,3)]
self._socket.settimeout(timeout)
response, address = self._socket.recvfrom(1024)
print(response)
Этот скрипт работает. Беговая дорожка на этом:
socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_IP) = 3
sendto(3, "M-SEARCH * HTTP/1.1\nHOST: 239.25"..., 171, 0, {sa_family=AF_INET, sin_port=htons(1900), sin_addr=inet_addr("239.255.255.250")}, 16) = 171
sendto(3, "M-SEARCH * HTTP/1.1\nHOST: 239.25"..., 171, 0, {sa_family=AF_INET, sin_port=htons(1900), sin_addr=inet_addr("239.255.255.250")}, 16) = 171
sendto(3, "M-SEARCH * HTTP/1.1\nHOST: 239.25"..., 171, 0, {sa_family=AF_INET, sin_port=htons(1900), sin_addr=inet_addr("239.255.255.250")}, 16) = 171
ioctl(3, FIONBIO, [1]) = 0
clock_gettime(CLOCK_MONOTONIC, {tv_sec=10531, tv_nsec=209135513}) = 0
poll([{fd=3, events=POLLIN}], 1, 3000) = 1 ([{fd=3, revents=POLLIN}])
Что меня поражает, так это то, что код Python, похоже, не выполняет все системные вызовы setsockopt
. Может ли кто-нибудь помочь мне выяснить, в чем проблема с использованием QUdpSocket? Я знаю, что если мне это нужно, я могу реализовать с использованием необработанных сокетов, но я бы предпочел использовать встроенный материал, если я смогу заставить его работать.
m_socket->waitForReadyRead()
, но правильным решением было бы присоединить обработчик к сигналуreadyRead
и вернуться в цикл обработки событий. - person Botje   schedule 05.10.2020