SSDP发现LAN中其他机器 --- Python

如果你想要像 MS 的網路芳臨 (netbios) 一樣,讓你的軟體能自動發現 LAN 上 面其它正在執行相同軟體的機器,並在連上網路時,自動通知其它機器,那麼 SSDP 這個通訊協定能幫助你。 本文討論如何使用 miniSSDPd,幫助你的應用程式使用 SSDP。 並以 Python 為例。

SSDP -- Simple Service Discovery Protocol,如其名稱,在 LAN 環境下 提供 service 的搜尋/發現的服務。 應用程式可以透過該協定,向 LAN 裡面的其它機器廣播應用程式所提供的 service, 也可以透過 SSDP 搜尋網路上所有的特定 service。 SSDP 的功能其實就和 DNS 差不多,用以查詢 service 和位址之間的對應關係。 和 DNS 不同的是, SSDP 不需要集中化 (centeralized) 的固定 server,而是 透過 multicasting 和 P2P 的方式運作。

SSDP 其實不複雜,可以想成是在 multicasting 的 UDP 上執行 HTTP protocol (HTTPU),而一般的 HTTP protocol 是使用 TCP。 因此,你可以透過 multicasting 的方式,把 HTTP 的 request 送到 LAN 裡的 每一台執行 SSDP 的機器。 收到 request 的 SSDP service 若有足夠的資訊,則回應該 request,反之則 丟掉該 request 不回應。 因此, SSDP 不需要一個固定的 server。

SSDP 有兩個重要的 request,一個是 NOTIFY,另一個則是 MSEARCH。 NOTIFY 是向其它機器廣播服務的項目和位址,你可以透過發送 NOTIFY 通知 LAN 裡 所有機器,告知你所提供的服務項目。 而 MSEARCH 則是用來尋問其它機器,是否有提供特定的 service,以取得這些 service 的位址。 因此,為了得知其它機器提供的 service,應用程式必需收集 NOTIFY,並/或送出 MSEARCH request。 實作這兩者都需要花一點時間,而且,你必需等待其它機器送 NOTIFY 或 MSEARCH 的回應。 因此,使用上很沒有效率。 miniSSDPd 是一個獨立的 daemon,隨時幫你收集透過 LAN 送來的 NOTIFY,並記錄 下來。 因此,我們可以直接向 miniSSDPd 查詢,就能隨時取得最新的資訊,不用等待。

如何廣播你的服務?

當一個 SSDP 的服務上線之後,必需定期的在 LAN 上面進行廣播。 如此,其它機器才能知道該服務的存在。 這有點像網芳,當一台新機器上線時,在網路芳臨就會立即出現該電腦的 icon。 在 SSDP 是透過 multicast 一個 NOTIFY 的 request 到網路上。 request 的內容如下

NOTIFY *  HTTP/1.1
HOST: 239.255.255.250:1900
CACHE-CONTROL: max-age=120
NT: urn:schemas-wifialliance-org:service:WFAWLANConfig:1
USN: uuid:3b968ed4-7666-11e0-a686-002215d227b8
SERVER: UNIX/unknown UPnP/1.0 dcports/1.0
LOCATION: http://example.net/test
NTS: ssdp:alive

基本上就是一個 HTTP request, 只是 command 為 NOTIFY 而非 GET/POST/...。 將這個 request 透過一個 UDP 的 socket,傳送到 239.255.255.250:1900 這個 multicast IP 位址即可。

import socket

SSDP_PORT = 1900
SSDP_MCAST_ADDR = '239.255.255.250'

msg = 'NOTIFY * HTTP/1.1\r\n....'
msock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
msock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
msock.sendto(msg, 0, (SSDP_MCAST_ADDR, SSDP_PORT))

這個例子的 request 是告知其它機器,我有提供 IGD 的 service。 但通常,我們會定義自已的 service,這時你必需修改 NT 和 USN 欄位。 NT 是 service type,你可以定義自已的 service type,如 urn:my-company-domain:service:my-service:1 。 USN 則是一個代表該 service 的 unique number,你可以使用 uuidgen 為你的服務產生一個 USN。 而 LOCATION 欄位則是你的 service 的位址,你可以填入你的 URL 或 IP:port。 需要使用該服務的程式,就會使用連結該位址。

這個 request 必需定時發送,你必需在 max-age 到達之前,重新傳送數次,以確保 其它機器知道你的服務還在線上。

查詢網路上的服務和其位址

這可以透過 miniSSDPd 幫我們查詢。 miniSSDPd 在上線之後,會持續收集 NOTIFY,因此我們只需向它查詢即可。 miniSSDPd 會在 "/var/run/minissdpd.sock" 這個位址上,開啟一個 unix domain 的 socket。 因此,我們只需開一個 socket,連到該位址,就可以向 miniSSDPd 發出查詢要求。 而透過該 socket 傳送的 command 格式請見http://miniupnp.free.fr/minissdpd.html

下面是一個 Python 的範例

001	import socket
002	
003	def _encode_len(n):
004	    r = ''
005	    if n >= 268435456:
006	        r = r + chr(((n >> 28) & 0x7f) | 0x80)
007	        pass
008	    if n >= 2097152:
009	        r = r + chr(((n >> 21) & 0x7f)| 0x80)
010	        pass
011	    if n >= 16384:
012	        r = r + chr(((n >> 14) & 0x7f)| 0x80)
013	        pass
014	    if n >= 128:
015	        r = r + chr(((n >> 7) & 0x7f)| 0x80)
016	        pass
017	    r = r + chr(n & 0x7f)
018	    return r
019	
020	def _decode_len(s):
021	    n = 0
022	    for c in s:
023	        n = (n << 7) | (ord(c) & 0x7f)
024	        if not (ord(c) & 0x80):
025	            break
026	        pass
027	    return n
028	
029	def _encode_str(s):
030	    n = len(s)
031	    r = _encode_len(n) + s
032	    return r
033	
034	def _decode_str(s):
035	    n = _decode_len(s)
036	    skip = len(_encode_len(n))
037	    r = s[skip: skip + n]
038	    
039	    return r, skip + n
040	
041	def query_service(sock, st):
042	    q = chr(1) + _encode_str(st)
043	    sock.send(q)
044	
045	    rep = sock.recv(10240)
046	
047	    n = _decode_len(rep)
048	    skip = len(_encode_len(n))
049	    
050	    result = []
051	    remain = rep[skip:]
052	    for i in range(n):
053	        location, skip = _decode_str(remain)
054	        remain = remain[skip:]
055	        
056	        r_st, skip = _decode_str(remain)
057	        remain = remain[skip:]
058	        
059	        r_usn, skip = _decode_str(remain)
060	        remain = remain[skip:]
061	        
062	        result.append((location, r_st, r_usn))
063	        pass
064	
065	    return result
066	
067	sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0)
068	sock.connect('/var/run/minissdpd.sock')
069	r = query_service(sock,
070	    'urn:schemas-wifialliance-org:service:WFAWLANConfig:1')
071	print r
072	

這個範例會連到 miniSSDPd 的 unix domain socket,然後查詢網路上有那些機器提供 urn:schemas-wifialliance-org:service:WFAWLANConfig:1 這個服務。 通常這是 AP 或 NAT 機器等 IP 設備。 最後會傳回一個 list,包括網路上提供該 service 的設備資訊。 相同的,你也可以查詢你自已定義的 service。

結論

miniSSDPd 是一個簡單的 daemon,只有 18Kbytes (i386), 而且 request 的格式很簡單,很容易和應用程式整合。 如果你也需要這種自動發現、通知的功能,不妨試試 miniSSDPd。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]的代码是一个使用Python编写的程序,用于发送查询请求并监听支持设备的应答。该程序使用多播地址发送查询请求,并监听其他设备的应答。程序使用socket库创建一个UDP套接字,并设置超时时间为10秒。然后,程序发送一个包含查询请求的消息到指定的多播地址和端口。接着,程序通过循环接收应答数据,并打印出应答的地址和数据。这段代码实现了使用SSDP协议发送单播报文的功能。 问题: python ssdp协议单播报文 回答: 要使用Python发送SSDP协议的单播报文,可以使用socket库创建一个UDP套接字,并使用sendto方法发送报文。报文的内容可以参考引用\[1\]的代码,其包括了M-SEARCH请求的头部信息,如Host、ST、Man和MX等。可以根据需要修改这些头部信息,并将报文发送到目标地址和端口。 #### 引用[.reference_title] - *1* [为什么说 Windows 10 不会被 DDoS SSDP反射攻击利用](https://blog.csdn.net/cg_i/article/details/128016460)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [python笔试110题(Interview questions)](https://blog.csdn.net/beauthy/article/details/114394166)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值