博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JavaScript和Chrome中的SSDP发现
阅读量:3522 次
发布时间:2019-05-20

本文共 6163 字,大约阅读时间需要 20 分钟。

目录


Chrome中的HTMLJavaScript内发现使用SSDP的本地设备

介绍

在实施一些项目时,我决定用HTML实现它们,因为它可以在我感兴趣的最广泛的设备上工作。我感兴趣的项目需要发现连接到我的家庭网络的其他设备。我用SSDP进行发现。

https://www.codeproject.com/KB/HTML/1273691/SSDPDiscovery.png

什么是SSDP

SSDP)是基于UDP的协议,是UPnP的一部分,用于在网络上查找其他设备和服务。它由许多设备实现,包括网络附加存储设备,智能电视和家庭自动化系统。有很多这些设备通过JSON调用公开功能。您可以轻松地创建接口来控制这些设备。但是,由于HTMLJavaScript的标准不包含UDP接口,因此如何执行发现并不是很明显。SSDP的替代方案包括让用户手动输入感兴趣的设备的IP地址或扫描网络。这些选项中的后者可以在某些公司网络上执行时引发一些安全标记,并且可能需要很长时间。在大多数情况下,解决方案都是依赖于平台。有各种基于HTML的解决方案,允许您通过UDP进行通信。例如,BrightSign HTML5播放器通过使用支持UDPChrome通过提供UDP通信。

Chrome中使用UDP的要求

网页没有访问此接口的权限(这是有充分理由的,因为在其他方面可能会被滥用)。虽然网络应用无法访问,但Chrome扩展程序可以访问。Chrome扩展程序无法在其他浏览器中使用。但在撰写本文时,Chrome占浏览器市场份额的67%,微软已宣布将使用作为其Edge浏览器的基础。虽然这种UDP套接字实现在各种浏览器中都不可用,但它在很大程度上可供大多数用户使用,因为这是大多数桌面用户的首选浏览器。要将HTML代码作为扩展运行,需要另外两个元素:清单和后台脚本。后台脚本将创建一个窗口并将开始的HTML加载到其中。

chrome.app.runtime.onLaunched.addListener(function() {    chrome.app.window.create('index.html', {        'outerBounds': {        'width': 600,        'height': 800        }    });});

我不会详细介绍清单中的内容,但我会强调其最重要的元素。清单采用JSON格式。定义要运行的初始脚本app.background.scripts。其他重要元素是permission元素和manifest_version元素,没有permission元素,通过UDP或加入多播组进行通信的尝试将失败并且。其他元素是直观的。

{    "name": "SSDP Browser",    "version": "0.1",    "manifest_version": 2,    "minimum_chrome_version": "27",    "description": "Discovers SSDP devices on the network",    "app": {      "background": {        "scripts": [          "./scripts/background.js"        ]      }    },    "icons": {        "128": "./images/j2i-128.jpeg",        "64": "./images/j2i-64.jpeg",        "32": "./images/j2i-32.jpeg"    },    "permissions": [      "http://*/",      "storage",      {        "socket": ["udp-send-to", "udp-bind", "udp-multicast-membership"]      }    ]  }

使用UDP执行SSDP的步骤

Google已经发布了一个包装器,作为chrome.udp.sockets在网络上使用多播发布的代码示例。Google代码示例以未经更改的形式假定文本采用Unicode16位字符编码进行编码。SSDP使用8ASCII编码。我已经使用了Google的类,并对其进行了一些小改动使其可以使用ASCII而不是Unicode。要执行SSDP搜索,请执行以下步骤。

  1. 创建UDP端口并将其连接到组播组239.255.255.250
  2. 在端口1900上发送M-SEARCH查询
  3. 等待来自其他设备上的从端口1900传入的响应
  4. 解析响应
  5. 一段时间后停止监听

第一步主要由Google Multicast类处理。我们只需要将端口和地址传递给它。M-SEARCH查询是一个string。至于最后一步,当响应停止进入时,它不是决定性的。即使没有请求,某些设备似乎偶尔也会在网络上广播消息。从理论上讲,你可以继续得到回应。在某个时候,我建议不再监听。五到十秒的时间通常是足够的。M-SEARCH参数有变化,但以下内容可用于询找所有设备。还有其他查询可用于筛选具有特定功能的设备。以下是我使用的string不立即可见的是,在最后一行文本之后,有两个空行。

M-SEARCH * HTTP/1.1HOST: 239.255.255.250:1900MAN: "ssdp:discover"MX: 3ST: ssdp:allUSER-AGENT: Joel's SSDP Implementation

当响应出现时,我们分配给MulticastScoket.onDiagram的函数将使用包含响应的字节数组、响应来自的IP地址和发送响应的端口号(当前应用程序的端口号为1900)应用)来调用。在下面的代码示例中,我启动搜索并将响应打印到JavaScript控制台。

const SSDP_ADDRESS = '239.255.255.250';const SSDP_PORT = 1900;const SSDP_REQUEST_PAYLOAD =    "M-SEARCH * HTTP/1.1\r\n"+                                "HOST: 239.255.255.250:1900\r\n"+                                "MAN: \"ssdp:discover\"\r\n"+                                "MX: 3\r\n"+                                "ST: ssdp:all\r\n"+                                "USER-AGENT: Joel's SSDP Implementation\r\n\r\n";var searchSocket = null;function beginSSDPDiscovery() {     if (searchSocket)        return;    $('.responseList').empty();    searchSocket = new MulticastSocket({address:SSDP_ADDRESS, port:SSDP_PORT});    searchSocket.onDiagram = function(arrayBuffer, remote_address, remote_port) {        console.log('response from ', remote_address, " ", remote_port);        var msg = searchSocket.arrayBufferToString8(arrayBuffer);        console.log(msg);            }    searchSocket.connect({call:function(c) {        console.log('connect result',c);        searchSocket.sendDiagram(SSDP_REQUEST_PAYLOAD,{call:()=>{console.log('success')}});        setTimeout(endSSDPDiscovery, 5000);    }});    }

并不是解析响应string很困难,无论如何,如果响应是JSON对象会更方便。我已经创建了一个函数,可以快速转换响应,因此我可以像任何其他JSON对象一样使用它。

function discoveryStringToDiscoveryDictionary(str) {    var lines = str.split('\r');    var retVal = {}    lines.forEach((l) => {        var del = l.indexOf(':');        if(del>1) {            var key = l.substring(0,del).trim().toLowerCase();            var value = l.substring(del+1).trim();            retVal[key]=value;        }    });    return retVal;}

完成此转换后,我网络上的Roku流媒体播放器返回了以下响应。(我改变了序列号。)

{    cache-control: "max-age=3600",    device-group.roku.com: "D1E000C778BFF26AD000",    ext: "",    location: "http://192.168.1.163:8060/",    server: "Roku UPnP/1.0 Roku/9.0.0",    st: "roku:ecp",    usn: "uuid:roku:ecp:1XX000000000",    wakeup: "MAC=08:05:81:17:9d:6d;Timeout=10"    ,}

已经为要使用的示例分享了足够的代码,但是我将更改示例以在UI中显示响应,而不是依赖于开发JavaScript控制台。为了简单起见,我定义了HTML结构,将每个结果用作palettediv元素的子元素。这个元素是隐藏的,但对于每个响应,我将克隆ssdpDevice类的div元素将改变一些子成员并将其附加到页面的可见部分。

  

            
address:
location:
server:
search target:

改变后的函数现在将在HTML中显示SSDP响应,如下所示:

function beginSSDPDiscovery() {    if (searchSocket)        return;    $('.responseList').empty();    searchSocket = new MulticastSocket({address:SSDP_ADDRESS, port:SSDP_PORT});    searchSocket.onDiagram = function(arrayBuffer, remote_address, remote_port) {        console.log('response from ', remote_address, " ", remote_port);        var msg = searchSocket.arrayBufferToString8(arrayBuffer);        console.log(msg);        discoveryData = discoveryStringToDiscoveryDictionary(msg);        console.log(discoveryData);        var template = $('.palette').find('.ssdpDevice').clone();        $(template).find('.ipAddress').text(remote_address);        $(template).find('.location').text(discoveryData.location);        $(template).find('.server').text(discoveryData.server);        $(template).find('.searchTarget').text(discoveryData.st)        $('.responseList').append(template);    }    searchSocket.connect({call:function(c) {        console.log('connect result',c);        searchSocket.sendDiagram(SSDP_REQUEST_PAYLOAD,{call:()=>{console.log('success')}});        setTimeout(endSSDPDiscovery, 5000);    }});}

发现设备!现在怎么办?

从这里出发取决于他们的意图和设备的能力。不同的设备实现不同的API以访问其功能。您可以找到有关在UPnP规范下实现的常规API的更多信息,但许多允许以这种方式发现它们的设备可以通过它们自己的API提供功能。

 

原文地址:

转载地址:http://juzhj.baihongyu.com/

你可能感兴趣的文章
牛客寒假算法基础集训营4 G:Applese 的毒气炸弹(Kruskal求最小生成树)
查看>>
Codeforces Round #536 (Div. 2) B. Lunar New Year and Food Ordering(思维)
查看>>
Codeforces Round #536 (Div. 2) D. Lunar New Year and a Wander(基础图论)
查看>>
牛客寒假算法基础集训营6 A:出题(思维)+B:煤气灶(二分)+C:项链(简单贪心)+D:美食(模拟)
查看>>
牛客寒假算法基础集训营6 E:海啸(二维树状数组 or 前缀和 +容斥定理)
查看>>
G:区间或和(思维)
查看>>
牛客寒假算法基础集训营6 I:wzoi(stack的应用)
查看>>
牛客寒假算法基础集训营5 A:炫酷双截棍+G:炫酷数字(唯一分解定理+埃式筛法)+J:炫酷数学(快速幂)
查看>>
牛客寒假算法基础集训营5 I:炫酷镜子(模拟 or 记忆化搜索)+D:炫酷路途(贪心求最短路)
查看>>
C:小a与星际探索(线性基 or 搜索bfs or 背包dp)
查看>>
牛客寒假算法基础集训营1 D:小a与黄金街道(欧拉函数+快速幂)+G:小a的排列(思维)
查看>>
学习使我快乐学习使我升华只要你爱学习我们就是一辈子的好朋友
查看>>
牛客寒假算法基础集训营2 A:处女座的签到题(排序)+C:处女座的砝码(思维)+J:处女座的期末复习(贪心)
查看>>
牛客寒假算法基础集训营2 B : 处女座与cf (巨坑模拟题)
查看>>
牛客寒假算法基础集训营2 G:处女座与复读机(搜索dfs or dp)+H:处女座的测验(一)(质数+构造)
查看>>
codevs 1102:采药(记忆化搜索 or dp)+1144:守望者的逃离(记忆化搜索 or 贪心)
查看>>
牛客练习赛39 A:走方格 (思维)
查看>>
牛客练习赛39 B:选点 (dfs序+LIS)
查看>>
牛客练习赛39 C:流星雨 (概率dp+除法逆元)
查看>>
牛客练习赛39 D:动态连通块(并查集+bitset优化)
查看>>