使用阻塞线程上的Socket进行UPnP事件订阅

时间:2015-09-03 17:28:14

标签: python multithreading sockets upnp

我正在加入到UPnP设备(WeMo运动传感器)上的事件中。我首先向设备发送HTTP订阅请求,设备应该开始向我发送指定地址的事件通知。那部分工作正常(除了我收到太多通知;即使状态没有改变,但是对于不同的线程它是一个不同的问题)

如果我在一个单独的python进程上运行keepListening函数,一切正常。但是,当我将该函数作为线程运行时,它不起作用;

import socket
import requests
from threading import Thread

def keepListening(): #running this function on a separate process works
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.settimeout(600)
    sock.bind(('192.168.10.231',1234))
    sock.listen(5)

    while 1:
        notification = ''

    try:
        conn, addr =  sock.accept()
        conn.setblocking(1)
        notification= conn.recv(1024) 
        conn.sendall(r'''HTTP/1.1 200 OK
Content-Type: text/plain

''')
    except Exception as er:
        print er

    print notification




x = Thread(target=keepListening)
x.start()

message = {
'CALLBACK': '<http://192.168.10.231:1234>',
'NT': 'upnp:event',
'TIMEOUT': 'Second-600',
'HOST': '192.168.10.159:49153'}

k = requests.request('SUBSCRIBE','http://192.168.10.159:49153/upnp/event/basicevent1',headers=message)
print k
# keep doing other important works

每个事件通知必须以200 OK回复回复,否则设备不会发送进一步通知;事实上我学到了很多东西。我怀疑的可能是愚蠢的,当在线程中运行时,与单独的进程相反,回复消息不会及时发送,因此设备不会发送任何消息。更多通知。 值得一提的是,即使我在线程中运行该功能,我确实订阅后的初始通知(设备必须根据UPnP协议强制在订阅后立即发送初始通知),但我没有得到进一步的通知(表明我的200 OK回复没有正确通过;我确实在wireshark中看到了它)

关于在线程中运行函数(而不是单独的进程)可能有什么不同的想法让它失败?

谢谢。

2 个答案:

答案 0 :(得分:0)

我认为,发生的事情是,在线程变为活动状态并开始侦听接口之前,您最终会发送订阅请求。因此设备无法连接到插座。

答案 1 :(得分:0)

前几天我有一个wemo运动传感器,开关和RaspberryPi,所以我开始修补。

该脚本订阅了wemo-device的“binaryState”-event。 每次事件发生时,它都会打印出一个“警报”(你可以在那里做其他事情)。 250秒后,它会续订订阅。

要根据需要调整脚本,您必须更改IP:

localIp:您的计算机

remoteIp:wemo传感器或开关的ip

我是python的新手(3天前开始),所以脚本可能需要一些修改,但它可以工作。

import socket
import threading
import requests

host = ''
port = 1234
localIp = '<http://192.168.1.32:1234>' # local IP of your computer 
remoteIp = '192.168.1.47:49153' # the ip of the wemo device 

global uid # stores the uuid of the event
uid = ''

class client(threading.Thread):
    def __init__(self, conn):
        super(client, self).__init__()
        self.conn = conn
        self.data = ""

    def run(self):
        global uid
        while True:
            self.data = self.data + self.conn.recv(1024)

            if self.data.endswith(u"\r\n"):
                print self.data # data from the wemo device

                uidPos = self.data.find("uuid")
                if uidPos != -1: # data contains the uuid of the event 
                    uid = self.data[uidPos+5:uidPos+41]

                if "<BinaryState>1</BinaryState>" in self.data:
                    print "ALERT ------------------------------------------Alert"      
                    # NOTIFICATION ! 

                if "/e:propertyset" in self.data:
                    self.conn.sendall('HTTP/1.1 200 OK\r\nContent-Type:text/html\r\n\r\n')
                    return 
                self.data = ""

    def send_msg(self,msg):
        self.conn.send(msg)

    def close(self):
        self.conn.close()

class connectionThread(threading.Thread):
    def __init__(self, host, port):
        super(connectionThread, self).__init__()
        try:
            self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.s.bind((host,port))
            self.s.listen(5)

        except socket.error:
            print 'Failed to create socket'
            sys.exit()
        self.clients = []

    def run(self):
        while True:
            print uid
            conn, address = self.s.accept()
            c = client(conn) 
            c.start()
            print '[+] Client connected: {0}'.format(address[0])

def main():
    get_conns = connectionThread(host, port)
    get_conns.start()
    print get_conns.clients
    while True:
        try:
            response = raw_input() 
        except KeyboardInterrupt:
            sys.exit()

def setCalback():
  global uid
  threading.Timer(250.0, setCalback).start()
  if uid == "": # no uuid set so we subscribe to the event
    eventSubscribe()
  else:         # uuid is set, so we renew the subsciption
    eventRefresh()


def eventSubscribe(): # subscribe to the wemo-event 
    message = {
        'CALLBACK': localIp,
        'NT': 'upnp:event',
        'TIMEOUT': 'Second-300',
        'HOST': remoteIp} 
    k = requests.request('SUBSCRIBE', "http://"+remoteIp+'/upnp/event/basicevent1',headers=message)
    print k


def eventRefresh() # refresh the subscription with the known uuid
    myuid = "uuid:"+uid
    message = {
        'SID': myuid,
        'TIMEOUT': 'Second-300',
        'HOST': remoteIp } 
    k = requests.request('SUBSCRIBE',"http://"+remoteIp+'/upnp/event/basicevent1',headers=message)
    print k


if __name__ == '__main__':
    threading.Timer(2.0, setCalback).start() # wait 2 sec. then subscribe to the service
    main()