@achingbrain/ssdp 中文文档教程

发布于 3年前 浏览 26 项目主页 更新于 3年前

SSDP

构建状态覆盖状态依赖状态

node.js 的另一个 SSDP 实现

SSDP 是一种服务发现协议,它使用由通过 UDP 发送的 HTTP 样式标头组成的消息。 它的作用与 mDNS 类似,但不需要额外的库,并且完全用 JavaScript 实现。

使用 SSDP,服务将广播其可用性并通过 UDP 响应搜索消息,并提供包含其提供的功能详细信息的描述文档。 然后客户可以搜索可用的服务并根据需要使用它们。

Installation

npm install @achingbrain/ssdp

Usage

首先,导入模块,调用函数并设置错误处理程序:

import ssdp from '@achingbrain/ssdp'

const bus = await ssdp()

// print error messages to the console
bus.on('error', console.error)

Find a service

usn 传递给 discover 方法 - 当找到服务时,将发出事件:

// this is the unique service name we are interested in:
const usn = 'urn:schemas-upnp-org:service:ContentDirectory:1'

for await (const service of bus.discover(usn)) {
  // search for instances of a specific service
}

bus.on('service:discover', service => {
  // receive a notification about discovery of a service
})

bus.on('service:update', service => {
  // receive a notification when that service is updated - nb. this will only happen
  // after the service max-age is reached and if the service's device description
  // document has changed
})

Find all services

不要将任何选项传递给 discover 方法(注意,您还将收到与协议相关的事件):

for await (const service of bus.discover()) {
  // receive a notification about all service types
}

Advertise a service

// advertise a service

const advert = await bus.advertise({
  usn: 'urn:schemas-upnp-org:service:ContentDirectory:1',
  details: {
    URLBase: 'https://192.168.0.1:8001'
  }
})

// stop advertising a service
await advert.stop()

有关完整选项,请参阅 lib/advertise/ parse-options.js

Integrate with existing HTTP servers

默认情况下,当您创建广告时,会创建一个 HTTP 服务器来为描述您的服务的 details.xml 文档提供服务。 要改为使用现有服务器,请执行以下操作:

Hapi

const advert = await bus.advertise({
  usn: 'urn:schemas-upnp-org:service:ContentDirectory:1',
  location: {
    udp4: 'http://192.168.0.1:8000/ssdp/details.xml'
  },
  details: {
    URLBase: 'https://192.168.0.1:8001'
  }
})

server.route({
  method: 'GET',
  path: '/ssdp/details.xml',
  handler: (request, reply) => {
    reply(advert.service.details())
      .type('text/xml')
  }
})

Express

const advert = await bus.advertise({
  usn: 'urn:schemas-upnp-org:service:ContentDirectory:1',
  location: {
    udp4: 'http://192.168.0.1:8000/ssdp/details.xml'
  },
  details: {
    URLBase: 'https://192.168.0.1:8001'
  }
})

app.get('/ssdp/details.xml', async (request, response) => {
  response.set('Content-Type', 'text/xml')

  try {
    const details = await advert.service.details()
    response.send(details)
  } catch (err) {
    response.set('Content-Type', 'text/xml')
    response.send(err)
  }
})

Shutting down gracefully

ssdp 打开多个端口以与您网络上的其他设备通信,要关闭它们,请执行以下操作:

process.on('SIGINT',() => {
  // stop the server(s) from running - this will also send ssdp:byebye messages for all
  // advertised services however they'll only have been sent once the callback is
  // invoked so it won't work with process.on('exit') as you can only perform synchronous
  // operations there
  bus.stop(error => {
    process.exit(error ? 1 : 0)
  })
})

Full API and options

import ssdp from '@achingbrain/ssdp'

// all arguments are optional
var bus = ssdp({
  usn: 'unique-identifier', // defaults to a random UUID
  // a string to identify the server by
  signature: 'node.js/0.12.6 UPnP/1.1 @achingbrain/ssdp/1.0.0',
  retry {
    times: 5, // how many times to attempt joining the UDP multicast group
    interval: 5000 // how long to wait between attempts
  },
  // specify one or more sockets to listen on
  sockets: [{
    type: 'udp4', // or 'udp6'
    broadcast: {
      address: '239.255.255.250', // or 'FF02::C'
      port: 1900 // SSDP broadcast port
    },
    bind: {
      address: '0.0.0.0', // or '0:0:0:0:0:0:0:0'
      port: 1900
    },
    maxHops: 4 // how many network segments packets are allow to travel through (UDP TTL)
  }]
})
bus.on('error', console.error)

// this is the type of service we are interested in
var usn = 'urn:schemas-upnp-org:service:ContentDirectory:1'

// search for one type of service
for await (const service of bus.discover(usn)) {

}

bus.on('service:discover', service => {
  // receive a notification when a service of the passed type is discovered
})

bus.on('service:update', service => {
  // receive a notification when that service is updated
})

// search for all types of service
for await (const service of bus.discover()) {

}

// advertise a service
const advert = await bus.advertise({
  usn: 'a-usn', // unique service name
  interval: 10000, // how often to broadcast service adverts in ms
  ttl: 1800000, // how long the advert is valid for in ms
  ipv4: true, // whether or not to broadcast the advert over IPv4
  ipv6: true, // whether or not to broadcast the advert over IPv6
  location: { // where the description document(s) are available - omit to have an http server automatically created
    udp4: 'http://192.168.0.1/details.xml', // where the description document is available over ipv4
    udp6: 'http://FE80::0202:B3FF:FE1E:8329/details.xml' // where the description document is available over ipv6
  },
  details: { // the contents of the description document
    specVersion: {
      major: 1,
      minor: 1
    },
    URLBase: 'http://example.com',
    device: {
      deviceType: 'a-usn',
      friendlyName: 'A friendly device name',
      manufacturer: 'Manufactuer name',
      manufacturerURL: 'http://example.com',
      modelDescription: 'A description of the device',
      modelName: 'A model name',
      modelNumber: 'A vendor specific model number',
      modelURL: 'http://example.com',
      serialNumber: 'A device specific serial number',
      UDN: 'unique-identifier' // should be the same as the bus USN
      presentationURL: 'index.html'
    }
  }
})

// stop advertising a service
advert.stop()

Device description document

在 UPnP 设备发现期间,客户端可以请求 < a href="http://jan.newmarch.name/internetdevices/upnp/upnp-devices.html">描述您的服务提供的各种功能。 为此,您可以存储一个 xml 文档并将广告的 location 字段设置为指向该文档 或者让它自动生成。

例如,创建一个文档 description.xml 并将其放在位于 http://server.com/path/to/description.xml 的服务器上:

<root xmlns="urn:schemas-upnp-org:device-1-0">
  <specVersion>
    <major>1</major>
    <minor>0</minor>
  </specVersion>
  <URLBase>http://192.168.1.41:80</URLBase>
  <device>
    <deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>
    <friendlyName>I am a light controller</friendlyName>
    <manufacturer>Royal Philips Electronics</manufacturer>
    <manufacturerURL>http://www.philips.com</manufacturerURL>
    <modelDescription>Philips hue Personal Wireless Lighting</modelDescription>
    <modelName>Philips hue bridge 2012</modelName>
    <modelNumber>23409823049823</modelNumber>
    <modelURL>http://www.meethue.com</modelURL>
    <serialNumber>asd09f8s90832</serialNumber>
    <UDN>uuid:2f402f80-da50-12321-9b23-2131298129</UDN>
    <presentationURL>index.html</presentationURL>
  </device>
</root>

然后创建您的广告:

bus.advertise({
  usn: 'urn:schemas-upnp-org:device:Basic:1',
  location: {
    udp4: 'http://192.168.1.40/path/to/description.xml'
  }
})

或者提供一个描述符对象,让这个模块做繁重的工作(nb 您的对象将通过 xml2js Builder 运行):

bus.advertise({
  usn: 'urn:schemas-upnp-org:device:Basic:1',
  details: {
   '$': {
      'xmlns': 'urn:schemas-upnp-org:device-1-0'
    },
    'specVersion': {
      'major': '1',
      'minor': '0'
    },
    'URLBase': 'http://192.168.1.41:80',
    'device': {
      'deviceType': 'urn:schemas-upnp-org:device:Basic:1',
      'friendlyName': 'I am a light controller',
      'manufacturer': 'Royal Philips Electronics',
      'manufacturerURL': 'http://www.philips.com',
      'modelDescription': 'Philips hue Personal Wireless Lighting',
      'modelName': 'Philips hue bridge 2012',
      'modelNumber': '23409823049823',
      'modelURL': 'http://www.meethue.com',
      'serialNumber': 'asd09f8s90832',
      'UDN': 'uuid:2f402f80-da50-12321-9b23-2131298129',
      'presentationURL': 'index.html'
    }
  }
})

将选择一个随机的高端口,http 服务器将侦听该端口并提供描述符和 LOCATION 标头将在所有 ssdp 消息中适当设置。

当您调用 advert.stop 时,服务器将关闭。

I want to see all protocol messages

没问题,试试这个:

bus.on('transport:outgoing-message', (socket, message, remote) => {
  console.info('-> Outgoing to %s:%s via %s', remote.address, remote.port, socket.type)
  console.info(message.toString('utf8'))
})
bus.on('transport:incoming-message', (message, remote) => {
  console.info('<- Incoming from %s:%s', remote.address, remote.port)
  console.info(message.toString('utf8'))
})

或者查看 test/fixtures/all.js

References

SSDP

Build StatusCoverage StatusDependency Status

Yet another SSDP implementation for node.js

SSDP is a service discovery protocol that uses messages composed from HTTP-style headers sent over UDP. It fulfills a similar role to mDNS but needs no extra libraries and is implemented completely in JavaScript.

With SSDP a service will broadcast it's availability and respond to search messages over UDP and also present a description document that contains details of the capabilities it offers. A client can then search for available services and use them as required.

Installation

npm install @achingbrain/ssdp

Usage

First, import the module, call the function and set up an error handler:

import ssdp from '@achingbrain/ssdp'

const bus = await ssdp()

// print error messages to the console
bus.on('error', console.error)

Find a service

Pass a usn to the discover method - when services are found events will be emitted:

// this is the unique service name we are interested in:
const usn = 'urn:schemas-upnp-org:service:ContentDirectory:1'

for await (const service of bus.discover(usn)) {
  // search for instances of a specific service
}

bus.on('service:discover', service => {
  // receive a notification about discovery of a service
})

bus.on('service:update', service => {
  // receive a notification when that service is updated - nb. this will only happen
  // after the service max-age is reached and if the service's device description
  // document has changed
})

Find all services

Don't pass any options to the discover method (n.b. you will also receive protocol related events):

for await (const service of bus.discover()) {
  // receive a notification about all service types
}

Advertise a service

// advertise a service

const advert = await bus.advertise({
  usn: 'urn:schemas-upnp-org:service:ContentDirectory:1',
  details: {
    URLBase: 'https://192.168.0.1:8001'
  }
})

// stop advertising a service
await advert.stop()

For full options, see lib/advertise/parse-options.js

Integrate with existing HTTP servers

By default when you create an advertisment an HTTP server is created to serve the details.xml document that describes your service. To use an existing server instead, do something like:

Hapi

const advert = await bus.advertise({
  usn: 'urn:schemas-upnp-org:service:ContentDirectory:1',
  location: {
    udp4: 'http://192.168.0.1:8000/ssdp/details.xml'
  },
  details: {
    URLBase: 'https://192.168.0.1:8001'
  }
})

server.route({
  method: 'GET',
  path: '/ssdp/details.xml',
  handler: (request, reply) => {
    reply(advert.service.details())
      .type('text/xml')
  }
})

Express

const advert = await bus.advertise({
  usn: 'urn:schemas-upnp-org:service:ContentDirectory:1',
  location: {
    udp4: 'http://192.168.0.1:8000/ssdp/details.xml'
  },
  details: {
    URLBase: 'https://192.168.0.1:8001'
  }
})

app.get('/ssdp/details.xml', async (request, response) => {
  response.set('Content-Type', 'text/xml')

  try {
    const details = await advert.service.details()
    response.send(details)
  } catch (err) {
    response.set('Content-Type', 'text/xml')
    response.send(err)
  }
})

Shutting down gracefully

ssdp opens several ports to communicate with other devices on your network, to shut them down, do something like:

process.on('SIGINT',() => {
  // stop the server(s) from running - this will also send ssdp:byebye messages for all
  // advertised services however they'll only have been sent once the callback is
  // invoked so it won't work with process.on('exit') as you can only perform synchronous
  // operations there
  bus.stop(error => {
    process.exit(error ? 1 : 0)
  })
})

Full API and options

import ssdp from '@achingbrain/ssdp'

// all arguments are optional
var bus = ssdp({
  usn: 'unique-identifier', // defaults to a random UUID
  // a string to identify the server by
  signature: 'node.js/0.12.6 UPnP/1.1 @achingbrain/ssdp/1.0.0',
  retry {
    times: 5, // how many times to attempt joining the UDP multicast group
    interval: 5000 // how long to wait between attempts
  },
  // specify one or more sockets to listen on
  sockets: [{
    type: 'udp4', // or 'udp6'
    broadcast: {
      address: '239.255.255.250', // or 'FF02::C'
      port: 1900 // SSDP broadcast port
    },
    bind: {
      address: '0.0.0.0', // or '0:0:0:0:0:0:0:0'
      port: 1900
    },
    maxHops: 4 // how many network segments packets are allow to travel through (UDP TTL)
  }]
})
bus.on('error', console.error)

// this is the type of service we are interested in
var usn = 'urn:schemas-upnp-org:service:ContentDirectory:1'

// search for one type of service
for await (const service of bus.discover(usn)) {

}

bus.on('service:discover', service => {
  // receive a notification when a service of the passed type is discovered
})

bus.on('service:update', service => {
  // receive a notification when that service is updated
})

// search for all types of service
for await (const service of bus.discover()) {

}

// advertise a service
const advert = await bus.advertise({
  usn: 'a-usn', // unique service name
  interval: 10000, // how often to broadcast service adverts in ms
  ttl: 1800000, // how long the advert is valid for in ms
  ipv4: true, // whether or not to broadcast the advert over IPv4
  ipv6: true, // whether or not to broadcast the advert over IPv6
  location: { // where the description document(s) are available - omit to have an http server automatically created
    udp4: 'http://192.168.0.1/details.xml', // where the description document is available over ipv4
    udp6: 'http://FE80::0202:B3FF:FE1E:8329/details.xml' // where the description document is available over ipv6
  },
  details: { // the contents of the description document
    specVersion: {
      major: 1,
      minor: 1
    },
    URLBase: 'http://example.com',
    device: {
      deviceType: 'a-usn',
      friendlyName: 'A friendly device name',
      manufacturer: 'Manufactuer name',
      manufacturerURL: 'http://example.com',
      modelDescription: 'A description of the device',
      modelName: 'A model name',
      modelNumber: 'A vendor specific model number',
      modelURL: 'http://example.com',
      serialNumber: 'A device specific serial number',
      UDN: 'unique-identifier' // should be the same as the bus USN
      presentationURL: 'index.html'
    }
  }
})

// stop advertising a service
advert.stop()

Device description document

During UPnP device discovery, clients can request a description of the various capabilities your service offers. To do this you can either store an xml document and set the location field of your advert to point at that document or have it automatically generated.

E.g., create a document, description.xml and put it on a server at http://server.com/path/to/description.xml:

<root xmlns="urn:schemas-upnp-org:device-1-0">
  <specVersion>
    <major>1</major>
    <minor>0</minor>
  </specVersion>
  <URLBase>http://192.168.1.41:80</URLBase>
  <device>
    <deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>
    <friendlyName>I am a light controller</friendlyName>
    <manufacturer>Royal Philips Electronics</manufacturer>
    <manufacturerURL>http://www.philips.com</manufacturerURL>
    <modelDescription>Philips hue Personal Wireless Lighting</modelDescription>
    <modelName>Philips hue bridge 2012</modelName>
    <modelNumber>23409823049823</modelNumber>
    <modelURL>http://www.meethue.com</modelURL>
    <serialNumber>asd09f8s90832</serialNumber>
    <UDN>uuid:2f402f80-da50-12321-9b23-2131298129</UDN>
    <presentationURL>index.html</presentationURL>
  </device>
</root>

Then create your advert:

bus.advertise({
  usn: 'urn:schemas-upnp-org:device:Basic:1',
  location: {
    udp4: 'http://192.168.1.40/path/to/description.xml'
  }
})

Alternatively provide an descriptor object and let this module do the heavy lifting (n.b. your object will be run through the xml2js Builder):

bus.advertise({
  usn: 'urn:schemas-upnp-org:device:Basic:1',
  details: {
   '$': {
      'xmlns': 'urn:schemas-upnp-org:device-1-0'
    },
    'specVersion': {
      'major': '1',
      'minor': '0'
    },
    'URLBase': 'http://192.168.1.41:80',
    'device': {
      'deviceType': 'urn:schemas-upnp-org:device:Basic:1',
      'friendlyName': 'I am a light controller',
      'manufacturer': 'Royal Philips Electronics',
      'manufacturerURL': 'http://www.philips.com',
      'modelDescription': 'Philips hue Personal Wireless Lighting',
      'modelName': 'Philips hue bridge 2012',
      'modelNumber': '23409823049823',
      'modelURL': 'http://www.meethue.com',
      'serialNumber': 'asd09f8s90832',
      'UDN': 'uuid:2f402f80-da50-12321-9b23-2131298129',
      'presentationURL': 'index.html'
    }
  }
})

A random high port will be chosen, a http server will listen on that port and serve the descriptor and the LOCATION header will be set appropriately in all ssdp messages.

The server will be shut down when you call advert.stop.

I want to see all protocol messages

No problem, try this:

bus.on('transport:outgoing-message', (socket, message, remote) => {
  console.info('-> Outgoing to %s:%s via %s', remote.address, remote.port, socket.type)
  console.info(message.toString('utf8'))
})
bus.on('transport:incoming-message', (message, remote) => {
  console.info('<- Incoming from %s:%s', remote.address, remote.port)
  console.info(message.toString('utf8'))
})

Alternatively see test/fixtures/all.js

References

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文