Прослушивание Python и UDP
у меня есть приложение, программно определенное радио, которое транслирует UDP-пакеты на порт, который сообщает слушателям, какая частота и режим демодуляции были установлены (среди прочего.)
Я написал демо-клиент python (код ниже), который слушает порт и выгружает информацию в соответствующих пакетах на консоль.
Они оба работают под OSX 10.6, Snow Leopard. Они там работают.
вопрос / проблема у меня есть: приложение Python имеет для запуска перед приложением радио или он утверждает, что порт уже используется (ERRNO 47) во время привязки, и я не понимаю, почему. Радио приложение вещает UDP; конечно, я хочу разместить несколько слушателей - это идея вещания, или, по крайней мере, так я думал.
Итак, вот код Python (отступ немного перепутан из-за переполнения стека действительно тупой отступ "make-it-code", но я уверяю вас, что все в порядке):
#!/usr/bin/python
import select, socket
# AA7AS - for SdrDx UDP broadcast
# this is a sample python script that captures the UDP messages
# that are coming from SdrDx. SdrDx tells you what frequency and
# mode it has been set to. This, in turn, would be used to tell
# another radio to tune to that frequency and mode.
# UDP packet from SdrDx is zero terminated, but receiving the
# packet makes it seem like the string contains zeros all the
# way out to the 1024th character. This function extracts a
# python string up to the point where it hits the first zero,
# then returns that string.
# -----------------------------------------------------------
def zeroterm(msg):
counter = 0;
for c in msg:
if ord(c) != 0:
counter += 1
strn = msg[:counter]
return strn
port = 58083 # port where we expect to get a msg
bufferSize = 1024 # room for message
# Create port to listen upon
# --------------------------
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
s.bind(('', port))
except:
print 'failure to bind'
s.close()
raise
s.setblocking(0)
# Listen for messages
# -------------------
looping = True
while looping:
try:
result = select.select([s],[],[])
except: # you can kill the client here with control-c in its shell
s.close() # must close socket
print 'Closing, exception encountered during select' # warn
raise SystemExit # and quit
msg = result[0][0].recv(bufferSize) # actually fetch the UDP data
msg = zeroterm(msg) # convert content into python string
# in next line, [] contain optional repeats
# message format is keyword:data[|keyword:data]
# where from 1...n keyword:data pairs may appear, up to 1024 bytes
# ----------------------------------------------------------------
try:
msgs = msg.split('|') # can be more than one message in packet
except: # failed to split
msgs = [] # on the other hand, can just be one. :)
msgs.append(msg) # so build array with that one.
for m in msgs: # now, for every message we have
keyw,data = m.split(':') # break into keyword and data
print keyw + "-->" + data # you'd do something with this
if keyw == "closing": # Our client terminates when SdrDx does
looping = False # loop stops
s.close() # must close socket
print 'Normal termination'
Для справки, вот Qt код, который отправляет сообщение UDP:
настройка:
bcast = new QHostAddress("192.168.1.255");
if (bcast)
{
udpSocketSend = new QUdpSocket(0);
if (udpSocketSend)
{
udpSocketSend->bind(*bcast, txudp);
}
}
сообщения:
if (udpSocketSend)
{
QByteArray *datagram = new QByteArray(1024,0); // datagram is full of zeroes
strcpy(datagram->data(),msg); // except where text message is in it at beginning
udpSocketSend->writeDatagram(*datagram, QHostAddress::Broadcast,txudp); // send
}
1 ответов
вы пытаетесь связать один и тот же порт, дважды.
вы привязываете его один раз в отправителе:
if (udpSocketSend)
{
udpSocketSend->bind(*bcast, txudp);
}
и снова у приемника
s.bind(('', port))
и поскольку они работают на одной машине, вы получаете ошибку.
если вас не волнует, что такое исходный порт, вам не нужно bind()
на отправителе просто отправьте его, и стек выберет соответствующий номер порта исходящего источника. В случае отправителя при передаче дейтаграммы UDP вы указываете пункт назначения (udpSocketSend->writeDatagram(...)
), и bind
фактически определяет источник исходящей дейтаграммы. Если вы не bind
, это нормально, стек назначит вам порт.
если вы do заботьтесь о том, какой порт источника, тогда я предлагаю вам использовать другой номер порта для исходящего порта источника и входящего порта назначения. Тогда вы сможете связать как отправителя, так и получателя без проблем.
наконец, есть возможность чтобы установить SO_REUSEADDR
опция сокета (на любом языке, который вы используете). Это было бы необходимо, если вы хотите запустить несколько клиентов на той же машине, так как все клиенты должны были бы привязываться к тому же адресу. Но я не уверен, является ли этот вариант сокета кросс-платформенным (*nix отлично работает), и я думаю, что вышеуказанные предложения лучше.