Python service discovery: Advertise a service across a local network
Asked Answered
H

1

20

I have a "server" python script running on one of the local network machines, which waits for clients to connect, and passes them some work to do. The server and client code have both been written, and are working as expected...

The problem is, this server might be running from any machine in the local network, so I can't hard code the address in the script... I immediately wondered if I can make a machine advertise about its existence, and clients can respond to that. Is that doable in Python with the standard library? I really don't have time to download twisted or tornado and learn about them, unfortunately, so I need something simple.

I tried to think more about it, and realized I can have a single static IP machine where servers register/unregister from and clients can look for servers from there. Kind of like a torrent tracker, I think. This'll have to do if I can't do the service advertising approach easily.

Huddleston answered 13/1, 2014 at 10:43 Comment(6)
A common way of doing local service discovery is using a network broadcast. You don't need to hardcode specific IP addresses if you use it, nor have a central serviceAmatory
I am not sure if this will be easy or hard to do in python but one way to handle it is via multicast DNS.Margemargeaux
@goncalopp Thanks, I looked into that a bit, and it leads to very "deep" tutorials that often use an external library. I'll try to research the multicast DNS..Huddleston
@Huddleston it's pretty easy, actually. You should take the time to familiarize yourself with Berkeley sockets, if you haven't already done soAmatory
@goncalopp Sweet! Can you add that as an answer so others can find it easily? Thanks.Huddleston
@goncalopp Right, I read about them in the Python socket programming. It was a start, but I guess I should learn more about this.Huddleston
A
31

An easy way to do service announcement/discovery on the local network is by broadcasting UDP packets.

Constants:

PORT = 50000
MAGIC = "fna349fn" #to make sure we don't confuse or get confused by other programs

Announcement:

from time import sleep
from socket import socket, AF_INET, SOCK_DGRAM, SOL_SOCKET, SO_BROADCAST, gethostbyname, gethostname

s = socket(AF_INET, SOCK_DGRAM) #create UDP socket
s.bind(('', 0))
s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) #this is a broadcast socket
my_ip= gethostbyname(gethostname()) #get our IP. Be careful if you have multiple network interfaces or IPs

while 1:
    data = MAGIC+my_ip
    s.sendto(data, ('<broadcast>', PORT))
    print "sent service announcement"
    sleep(5)

Discovery:

from socket import socket, AF_INET, SOCK_DGRAM

s = socket(AF_INET, SOCK_DGRAM) #create UDP socket
s.bind(('', PORT))

while 1:
    data, addr = s.recvfrom(1024) #wait for a packet
    if data.startswith(MAGIC):
        print "got service announcement from", data[len(MAGIC):]

This code was adapted from the demo on python.org

Amatory answered 13/1, 2014 at 12:2 Comment(3)
Is there a way to make this work where the Discovery side does not know what port the announcer is on?Macfadyn
@SalimFadhley TCP/UDP communication always happens on a host,port pair, so my guess is that there's no way to do that (barring trying all ports in succession or capturing all traffic on the interface). Is there a reason you can't use a fixed (high) port?Amatory
Way better than all those SSDP, UPnP, SLP and whatever. Thank youOpenhearth

© 2022 - 2024 — McMap. All rights reserved.