HarmonyOS 鸿蒙Next中MDNS搜索不到服务

HarmonyOS 鸿蒙Next中MDNS搜索不到服务

各位大佬们为什么我写的这个mdns搜索不到任何服务:

下面是ArkTS写的mdns搜索服务:

// 开始服务发现
public async startDiscovery(
  context: common.Context,  // 使用正确的 common.Context 类型
  serviceType: string = "_http._tcp.",
  domain: string = "local."): Promise<mdns.LocalServiceInfo> {

  // 检查是否已经在搜索
  if (this.isSearching) {
    LogE(TAG, "🔗尝试开始新的服务发现,但是已经有一个正在进行");
    throw new Error("Discovery already in progress");
  }

  // 取消之前的搜索
  await this.destroy();

  // 创建并启动新的搜索任务
  this.isSearching = true;
  LogI(TAG, `🔗开始搜索服务: ${serviceType}`);

  return new Promise<mdns.LocalServiceInfo>((resolve, reject) => {
    // 保存 resolve/reject 引用
    this.resolvePromise = resolve;
    this.rejectPromise = reject;

    // 设置超时自动终止
    // const timeoutId = setTimeout(() => {
    //   LogE(TAG, `服务发现超时 (${this.timeoutMs}ms)`);
    //   this.cleanup();
    //   reject(new Error("Discovery timeout"));
    // }, this.timeoutMs);

    try {
      // 创建 mdns 实例
      this.discovery = mdns.createDiscoveryService(context, serviceType);
      // 开始搜索
      this.discovery.startSearchingMDNS();
      // 事件监听
      this.discovery.on("discoveryStart", (info: mdns.DiscoveryEventInfo) => {
        LogI(TAG, `🔗发现开始: ${info.serviceInfo.serviceType}`);
      });

      this.discovery.on("discoveryStop", () => {
        LogI(TAG, `🔗发现停止`);
      });

      this.discovery.on("serviceFound", async (info: mdns.LocalServiceInfo) => {
        LogI(TAG, `🔗发现服务: ${info.serviceName}`);
        try {
          // 解析服务详情
          const resolvedInfo = await this.resolveService(context, info);
          LogI(TAG, `🔗解析成功: ${resolvedInfo.serviceName} IP: ${resolvedInfo.host?.address}`);

          // 成功返回结果
          this.resolvePromise?.(resolvedInfo);
          this.cleanup();
          // clearTimeout(timeoutId);
        } catch (resolveErr) {
          LogE(TAG, `🔗解析失败: ${resolveErr.message}`);
          // 继续等待其他服务
        }
      });

      this.discovery.on("serviceLost", (info: mdns.LocalServiceInfo) => {
        LogW(TAG, `🔗服务丢失: ${info.serviceName}`);
      });
    } catch (e) {
      LogE(TAG, `🔗启动发现失败: ${e.message}`);
      this.rejectPromise?.(e);
      this.cleanup();
      // clearTimeout(timeoutId);
    }
  });
}

为了测试如上代码,我写了一个这个mdns服务去广播:

const os = require('os');

const http = require('http'); // ✅ 补全点 1: 引入http模块

const mdns = require('multicast-dns')();

/**
 * 获取本机的局域网IPv4地址
 * @returns {string} 本机IP地址,如果找不到则返回 '0.0.0.0'
 */
function getLocalIpAddress() {
  const interfaces = os.networkInterfaces();
  for (const name of Object.keys(interfaces)) {
    for (const iface of interfaces[name]) {
      // 跳过非IPv4地址和内部地址(如 127.0.0.1)
      if (iface.family !== 'IPv4' || iface.internal !== false) {
        continue;
      }
      return iface.address;
    }
  }
  return '0.0.0.0'; // 作为回退
}

// --- 1. 获取主机和服务的动态信息 ---
const HOST_IP = getLocalIpAddress();
const HOST_NAME = os.hostname().replace(/\.local$/, ''); // 获取主机名并移除可能存在的.local后缀

// 检查是否成功获取IP
if (HOST_IP === '0.0.0.0') {
  console.error('错误:无法找到有效的本地局域网IP地址。请检查您的网络连接。');
  process.exit(1);
}

const SERVICE = {
  name: 'drec-simulator-service', // 服务的实例名
  type: '_http._tcp',           // 服务的类型
  port: 8489,
  txt: { webdav_port: '49151' } // 附加的TXT信息
};

// --- 2. 构造符合规范的mDNS记录 ---
// 构造 TXT 记录的 Buffer
const txtData = Object.entries(SERVICE.txt).map(([k, v]) => `${k}=${v}`);

const records = [
  // 记录 A: 广播服务类型
  {
    name: '_services._dns-sd._udp.local',
    type: 'PTR',
    ttl: 28800,
    data: `${SERVICE.type}.local`
  },
  // 记录 B: 广播服务实例
  {
    name: `${SERVICE.type}.local`,
    type: 'PTR',
    ttl: 28800,
    data: `${SERVICE.name}.${SERVICE.type}.local`
  },
  // 记录 C: 定义服务的位置 (SRV)
  {
    name: `${SERVICE.name}.${SERVICE.type}.local`,
    type: 'SRV',
    ttl: 120,
    data: {
      port: SERVICE.port,
      target: `${HOST_NAME}.local`
    }
  },
  // 记录 D: 定义附加文本信息 (TXT)
  {
    name: `${SERVICE.name}.${SERVICE.type}.local`,
    type: 'TXT',
    ttl: 4500,
    data: txtData
  },
  // 记录 E: 定义主机名的IP地址 (A)
  {
    name: `${HOST_NAME}.local`,
    type: 'A',
    ttl: 120,
    data: HOST_IP
  }
];

// --- 3. 启动一个真正的HTTP服务器来响应连接 --- ✅ 补全点 2: 整个HTTP服务器逻辑
const httpServer = http.createServer((req, res) => {
  // 当从客户端(如HarmonyOS应用)收到一个HTTP请求时,此函数将被执行
  const clientInfo = `${req.socket.remoteAddress}:${req.socket.remotePort}`;
  console.log(`\n[HTTP Server] 收到来自客户端 ${clientInfo} 的请求!`);
  console.log(` &gt; 请求路径: ${req.url}`);

  // 准备要发送回客户端的消息
  const responseMessage = `你好,来自 ${clientInfo} 的客户端!你的请求我已收到。`;

  // 设置响应头,指定内容为UTF-8编码的纯文本
  res.writeHead(200, {
    'Content-Type': 'text/plain; charset=utf-8'
  });

  // 将消息作为响应发送回客户端
  res.end(responseMessage);
});

// 让HTTP服务器在mDNS服务指定的端口上开始监听
httpServer.listen(SERVICE.port, HOST_IP, () => {
  console.log(`\n[HTTP Server] 服务端已在 http://${HOST_IP}:${SERVICE.port}/ 上成功启动并监听连接。`);
});

// --- 4. 发布mDNS服务并处理进程 ---
// 打印mDNS服务信息以供调试
console.log('\n准备发布 mDNS 服务:');
console.log(` &nbsp;- 服务实例名: ${SERVICE.name}`);
console.log(` &nbsp;- 服务类型: ${SERVICE.type}.local`);
console.log(` &nbsp;- 主机名: ${HOST_NAME}.local`);
console.log(` &nbsp;- IP 地址: ${HOST_IP}`);
console.log(` &nbsp;- 端口: ${SERVICE.port}`);
console.log(` &nbsp;- TXT 记录:`, SERVICE.txt);

// 响应 mDNS 查询
mdns.on('query', (query) => {
  // 可以选择性地在这里打印查询日志,用于调试
  console.log('收到查询:', query.questions.map(q => q.name));
  mdns.respond(records);
});

console.log(`\n[mDNS Server] 服务已发布。其他设备现在应该可以发现 '${SERVICE.name}'。`);
console.log('按 Ctrl+C 停止服务。');

// 优雅地停止服务
process.on('SIGINT', () => {
  console.log('\n收到 Ctrl+C,正在关闭服务...');

  // ✅ 补全点 3: 确保HTTP服务器也被关闭
  httpServer.close(() => {
    console.log('[HTTP Server] HTTP服务器已关闭。');

    // 在HTTP服务器关闭后,再关闭mDNS服务
    mdns.destroy(() => {
      console.log('[mDNS Server] mDNS服务已取消发布。');
      process.exit();
    });
  });
});

更多关于HarmonyOS 鸿蒙Next中MDNS搜索不到服务的实战教程也可以访问 https://www.itying.com/category-93-b0.html

4 回复

好长的代码。看了下你的代码中serviceType参数传递的"_http._tcp.“多了一个点号,改为”_http._tcp"。服务端代码的_services._dns-sd._udp.local记录类型应为PTR,但数据字段应指向服务实例名称。

{
  name: '_services._dns-sd._udp.local',
  type: 'PTR',
  data: `${SERVICE.type}.local`
}

SRV记录的target字段应包含完整主机名:

{
  name: `${SERVICE.name}.${SERVICE.type}.local`,
  type: 'SRV',
  data: {
    port: SERVICE.port,
    target: `${HOST_NAME}.local` // 必须带.local后缀
  }
}

更多关于HarmonyOS 鸿蒙Next中MDNS搜索不到服务的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


无人帮忙吗,泪目了,我这mdns按照官方写的为什么搜索不到服务呢?

在HarmonyOS鸿蒙Next中MDNS搜索不到服务可能原因如下:

  1. 网络配置问题:确保设备在同一局域网且未启用AP隔离
  2. 防火墙限制:检查网络防火墙是否阻止了5353/UDP端口通信
  3. 服务注册失败:目标服务可能未正确注册mDNS服务
  4. 协议实现差异:鸿蒙Next使用Hichain可能与其他系统mDNS实现存在兼容性问题
  5. 权限问题:应用需申请ohos.permission.INTERNET权限

调试建议:

  • 使用同一网络下的其他设备验证mDNS服务是否正常广播
  • 通过抓包工具检查mDNS组播报文是否正常收发

从代码来看,你的mDNS服务发现逻辑基本正确,但有几个关键点需要检查:

  1. 确保设备在同一局域网内,并且防火墙没有阻止mDNS流量(UDP端口5353)

  2. 检查服务类型格式是否正确,在HarmonyOS中应该使用"_http._tcp"格式,不要加".local"后缀

  3. 确认你的测试设备是否支持mDNS协议,可以尝试使用其他工具(如dns-sd或avahi-browse)验证服务是否正常广播

  4. 在HarmonyOS端,确保已添加必要的权限:

"reqPermissions": [
  {
    "name": "ohos.permission.INTERNET"
  }
]
  1. 调试建议:
  • 检查discoveryStart事件是否触发
  • 尝试缩短TTL时间(如120秒)
  • 确认context参数正确传递
  • 检查日志中是否有错误输出

如果问题仍然存在,可以尝试简化代码,先只实现最基本的服务发现功能,再逐步添加其他逻辑。

回到顶部