pythonsocketshttpnetwork-programmingssdp

How to Respond to SSDP Searches with Sockets in Python?


I'm trying to create a Chromecast like device that can stream video from the internet and be remotely controlled. I do the remote control with HTTP get requests to the device and listen for them with the following code:

Listening for HTTP Requests to the Device (localhost):

import socket
import sys

s = socket.socket() 
host = "localhost" 
port = 8060
s.bind(('', port))
s.listen(1)
try:
    while True:
        connection, clientAddress = s.accept()
        try:
                print clientAddress
                //Do Command
                //Reply
        except:
                print "Error"
except KeyboardInterrupt:
    print('interrupt')

I then went to implement SSDP so other devices can find my device and cast to it and was planning on using similar code to listen for the MSEARCH requests except on 239.255.255.250:1900. However, when a MSEARCH is sent the code does not pick it up.

Listening for SSDP Searches on "239.255.255.250:1900"

import socket
import sys

s = socket.socket()
host = "239.255.255.250"
port = 1900
s.bind((host, port))
s.listen(10)
try:
    while True:
        connection, clientAddress = s.accept()
        try:
                print("Trigger")
                print clientAddress
                data = connection.recv(1048)
                print data
        except:
                print "Error"
except KeyboardInterrupt:
    print('interrupt')

The Question:

So the question is why are the two acting differently (I believe this is because in the first example the device listening is also the destination for the HTTP requests whereas in the second it is not) and what is a way to fix the code so I can listen for SSDP searches.


Solution

  • SSDP is a UDP protocol, but you don't specify UDP in the socket constructor. By using the socket constructor with no arguments,

    s = socket.socket()
    

    you get the default arguments:

    socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)

    As you can see, the default for type is SOCK_STREAM, i.e. TCP. Instead, look at the server() method in this example, of which a fragment is:

    MCAST_GRP = '239.255.255.250'
    MCAST_PORT = 1900
    
    def server(timeout=5):
    
        socket.setdefaulttimeout(timeout)
    
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
        sock.bind(('', MCAST_PORT))
    
        mreq = struct.pack('4sl', socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
        sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
        # ... more code at the link