2012-06-04 25 views
7

Tengo una aplicación, Software Defined Radio, que transmite paquetes UDP en un puerto que decirle a los oyentes qué frecuencia y modo de demodulación se han establecido (entre otras cosas).Python y UDP escuchando

He escrito una demo cliente de python (código a continuación) que escucha el puerto y descarga la información en los paquetes apropiados a la consola.

Ambos funcionan bajo OSX 10.6, Snow Leopard. Ellos trabajan allí.

La pregunta/problema que tengo es: la aplicación Python debe iniciarse antes de la aplicación de radio o afirma que el puerto ya está en uso (ERRNO 47) durante el enlace, y no entiendo por qué. La aplicación de radio está transmitiendo UDP; ciertamente quiero acomodar a múltiples oyentes: esa es la idea de la radiodifusión, o al menos, así lo pensé.

Así que aquí está el código Python (el guión es un poco desordenado debido al desbordamiento de pila es muy tonto "hacer-él-código" guión, pero le aseguro que está bien):

#!/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' 

Como referencia, aquí está el código de Qt que está enviando el mensaje UDP:

Configuración:

bcast = new QHostAddress("192.168.1.255"); 
if (bcast) 
{ 
    udpSocketSend = new QUdpSocket(0); 
    if (udpSocketSend) 
    { 
     udpSocketSend->bind(*bcast, txudp); 
    } 
} 

Broadcast:

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 
} 
+0

Estas dos aplicaciones se están ejecutando en la misma máquina, ¿sí? – jedwards

+0

Debe corregir la sangría. Después de 'try:' no puede haber una línea que tenga el mismo nivel de sangría que la instrucción 'try:'. – ThiefMaster

+0

@jedwards, sí. ThiefMaster: como dije explícitamente justo encima del código de Python, el formateo de stackoverflow ensuciaba la sangría. El código está sangrado correctamente. No hay preocupaciones allí. – fyngyrz

Respuesta

6

Está intentando vincular el mismo puerto, dos veces.

Enlaza una vez en el emisor:

if (udpSocketSend) 
{ 
    udpSocketSend->bind(*bcast, txudp); 
} 

y otra vez en el receptor

s.bind(('', port)) 

Y puesto que éstos se están ejecutando en la misma máquina, que está recibiendo un error.

A menos que le importe cuál es el puerto de origen, no necesita bind() en el remitente, simplemente envíelo y la pila elegirá un número de puerto de origen saliente apropiado. En el caso de un emisor, cuando transmite un datagrama UDP, usted especifica el destino (udpSocketSend->writeDatagram(...)), y el bind realmente determina la fuente del datagrama saliente. Si no tiene bind, está bien, la pila le asignará un puerto.

Si do importa cuál es el puerto de origen, entonces le sugiero que utilice un número de puerto diferente para el puerto de origen de salida y el puerto de destino de entrada. Entonces usted podría unir tanto al emisor como al receptor sin problema.

Por último, existe la opción de configurar la opción de socket SO_REUSEADDR (en el idioma que esté utilizando). Esto sería necesario si desea ejecutar clientes múltiples en la misma máquina, ya que todos los clientes tendrían que vincularse a la misma dirección. Pero no estoy seguro de si esta opción de socket es multiplataforma (* nix funciona bien) y creo que las sugerencias anteriores son mejores.

+1

+1, pero para hacerlo totalmente explícito, en comunicación TCP/IP, casi nunca le importa cuál es el puerto de origen. Casi la única vez que te importa es si intentas negociar un conjunto particular de reglas de firewall. –

+0

Russell: esto es UDP. No TCP. @jedwards: Necesito la aplicación (única) para enviar una transmisión que pueden recibir múltiples clientes. Necesito que los clientes, posiblemente varios de ellos, puedan enviar mensajes a la aplicación. En ningún caso quiero que el usuario realmente tenga que configurar nada. Parecía que la idea de la transmisión encajaba en ambos extremos (y nada más realmente parecía). ¿Estoy equivocado? – fyngyrz