@achingbrain/ssdp

中文文档
4.2.4发布于 2025/06/24Apache-2.0 OR MITTypeScriptES Module构建来源已验证近 30 天下载 11.1万

SSDP针对节点的又一执行

中文使用文档
文档目录11 节
  1. 示例 - 找到服务
  2. 示例 - 查找所有服务
  3. 示例 - 广告服务
  4. 与现有的 HTTP 服务器整合
  5. 示例 - Hapi
  6. 示例 - 快捷键
  7. 实例 - 优雅地关闭
  8. 完整 API 和选项
  9. 设备描述文档
  10. 我想看所有协议信息
  11. 参考资料

@achingbrain/ sdp (英语)

codecov CI

SSDP针对节点的又一执行

关于

首先,导入模块,调用函数并设置一个错误处理器:

import ssdp from '@achingbrain/ssdp'

const bus = await ssdp()

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

示例 - 找到服务

传出 a serviceType 页:1 discover 方法 - 当发现服务事件时会释放:

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

for await (const service of bus.discover({ serviceType })) {
  // 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
})

示例 - 查找所有服务

不要给任何选择 discover 方法(n.b.您还将接收与协议相关的事件):

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

示例 - 广告服务

// 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()

关于全部选项,见 自由/广告/选择

与现有的 HTTP 服务器整合

默认情况下,您创建广告时会创建 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')
  }
})

示例 - 快捷键

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)
  }
})

实例 - 优雅地关闭

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)
  })
})

完整 API 和选项

import ssdp from '@achingbrain/ssdp'

// all arguments are optional
var bus = ssdp({
  udn: '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 serviceType = 'urn:schemas-upnp-org:service:ContentDirectory:1'

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

}

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()

设备描述文档

在 UPnP 设备发现过程中,客户端可以请求 a 说明您所提供服务的各种能力。 。 。 . 要做到这一点,您可以存储一个 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'
  }
})

或者提供一个描述对象,让这个模块进行举重(n.b. 您的对象将运行于 xml2js 构建器:

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。 。 。 .

我想看所有协议信息

没问题,试试这个

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'))
})

或见 试验/固定/全部js

参考资料

安装

$ npm i @achingbrain/ssdp

API 文档

许可证

根据其中任何一项颁发的许可证

捐款

除非另有明确声明,Apache-2.0许可证中界定的、有意提交供列入作品的任何捐助,应具有上述双重许可证,不附加任何条件.

版本与发布记录

@achingbrain/ssdp 共发布 40 个版本,首次发布于 2015/07/17;过去 12 个月发布了 0 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
4.2.4latest2025/06/241.4 年前
4.2.32025/06/241.4 年前
4.2.22025/04/081.6 年前
4.2.12024/11/301.9 年前
4.2.02024/11/291.9 年前
4.1.32024/11/271.9 年前

关于 @achingbrain/ssdp 的常见问题

@achingbrain/ssdp 是什么?

SSDP针对节点的又一执行

如何安装 @achingbrain/ssdp?

在项目目录运行 npm install @achingbrain/ssdp,或使用 pnpm add / yarn add / bun add。

@achingbrain/ssdp 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@achingbrain/ssdp 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@achingbrain/ssdp 可以商用吗?

@achingbrain/ssdp 使用 Apache-2.0 OR MIT 许可。可在 Apache-2.0、MIT 中任选其一遵守。选择 Apache-2.0 时,宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@achingbrain/ssdp 的下载量有多少?

根据 npm 官方下载统计,@achingbrain/ssdp 在 2026/10/05 – 2026/11/03 这 30 天内被下载 111,340 次,日均约 3,711 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@achingbrain/ssdp 还在维护吗?

最新版本 4.2.4 发布于 2025/06/24(1.4 年前);至今共发布 40 个版本,其中过去 12 个月发布了 0 个;首次发布于 2015/07/17。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。