Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python and UDP listening

Tags:

python

udp

I have an app, software defined radio, that broadcasts UDP packets on a port that tell listeners what frequency and demodulation mode have been set (among other things.)

I've written a demo python client (code below) that listens to the port, and dumps out the information in the appropriate packets to the console.

These are both running under OSX 10.6, Snow Leopard. They work there.

The question/issue I have is: the Python app has to be started before the radio app or it claims the port is already in use (ERRNO 47) during bind, and I don't understand why. The radio app is broadcasting UDP; certainly I want to accommodate multiple listeners -- that's the idea of broadcasting, or at least, so I thought.

So here's the Python code (the indent is a little messed up due to stack overflow's really dumb "make-it-code" indent, but I assure you it's ok):

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

For reference, here's the Qt code that is sending the UDP message:

Setup:

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
}
like image 608
fyngyrz Avatar asked Jun 04 '12 20:06

fyngyrz


1 Answers

You are trying to bind the same port, twice.

You bind it once in the sender:

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

and again at the receiver

s.bind(('', port))

And since these are running on the same machine, you are getting an error.

Unless you care what the source port is, you don't need to bind() on the sender, just send it and the stack will pick an appropriate outgoing source port number. In the case of a sender, when you transmit a UDP datagram you specify the destination (udpSocketSend->writeDatagram(...)), and the bind actually determines the source of the outgoing datagram. If you don't bind, thats fine, the stack will assign you a port.

If you do care what the source port is, then I suggest you use a different port number for outgoing source port and incoming destination port. Then you would be able to bind both sender and receiver without issue.

Lastly, there is the option to set the SO_REUSEADDR socket option (in whatever language you're using). This would be necessary if you want to run multiple clients on the same machine, as all the clients would have to bind to the same address. But, I'm not sure whether this socket option is cross platform (*nix works fine) and I think the above suggestions are better.

like image 105
jedwards Avatar answered Oct 27 '22 07:10

jedwards