什么是mDNS?

mDNS(Multicast DNS)是一种零配置网络服务发现协议,允许设备在局域网内无需传统DNS服务器即可发现服务和解析主机名。在鸿蒙生态中,mDNS是实现智能设备自动发现的理想选择。

mDNS在鸿蒙中的优势

  • 零配置:设备接入网络即可自动被发现

  • 局域网发现:完美适用于智能家居、IoT设备场景

  • 原生支持:鸿蒙提供了完整的mDNS API支持

  • 低功耗:相比轮询机制更加节能

环境配置

1. 添加权限

module.json5文件中添加网络权限:

json

复制

下载

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      },
      {
        "name": "ohos.permission.GET_NETWORK_INFO"
      }
    ]
  }
}

2. 导入必要模块

typescript

复制

下载

import mdns from '@ohos.net.mdns';
import common from '@ohos.app.ability.common';
import { BusinessError } from '@ohos.base';

服务发布(服务端)

创建mDNS服务

typescript

复制

下载

class MDNSService {
  private mdnsService: mdns.Service;
  private context: common.BaseContext;
  private isPublished: boolean = false;

  constructor(context: common.BaseContext) {
    this.context = context;
    this.mdnsService = mdns.createMDNSService(this.context);
  }

  // 发布服务
  async publishService(serviceName: string, serviceType: string, port: number): Promise<boolean> {
    try {
      const serviceInfo: mdns.ServiceInfo = {
        serviceName: serviceName,
        serviceType: serviceType,
        port: port,
        // 可选:添加TXT记录
        txtRecord: {
          version: '1.0',
          protocol: 'tcp',
          description: '鸿蒙mDNS示例服务'
        }
      };

      await this.mdnsService.publish(serviceInfo);
      this.isPublished = true;
      
      console.info(`mDNS服务发布成功: ${serviceName}.${serviceType}`);
      this.setupServiceListeners();
      return true;
    } catch (error) {
      console.error(`发布mDNS服务失败: ${(error as BusinessError).message}`);
      return false;
    }
  }

  // 设置服务监听器
  private setupServiceListeners(): void {
    this.mdnsService.on('servicePublished', () => {
      console.info('服务发布事件确认');
    });

    this.mdnsService.on('serviceUnpublished', () => {
      console.info('服务取消发布');
    });

    this.mdnsService.on('serviceResolved', (serviceInfo: mdns.ServiceInfo) => {
      console.info(`服务被解析: ${JSON.stringify(serviceInfo)}`);
    });

    this.mdnsService.on('serviceStopped', () => {
      console.info('服务已停止');
      this.isPublished = false;
    });
  }

  // 更新服务TXT记录
  async updateTxtRecord(txtRecord: Record<string, string>): Promise<boolean> {
    if (!this.isPublished) {
      console.error('服务未发布,无法更新TXT记录');
      return false;
    }

    try {
      await this.mdnsService.updateTxtRecord(txtRecord);
      console.info('TXT记录更新成功');
      return true;
    } catch (error) {
      console.error(`更新TXT记录失败: ${(error as BusinessError).message}`);
      return false;
    }
  }

  // 停止服务
  async stopService(): Promise<void> {
    if (this.isPublished) {
      try {
        await this.mdnsService.unpublish();
        this.isPublished = false;
        console.info('mDNS服务已停止');
      } catch (error) {
        console.error(`停止服务失败: ${(error as BusinessError).message}`);
      }
    }
  }

  // 销毁服务
  destroy(): void {
    this.mdnsService.off('servicePublished');
    this.mdnsService.off('serviceUnpublished');
    this.mdnsService.off('serviceResolved');
    this.mdnsService.off('serviceStopped');
  }
}

服务发布示例

typescript

复制

下载

// 发布一个智能家居服务
const mdnsService = new MDNSService(getContext(this));

// 发布服务
await mdnsService.publishService('MySmartHome', '_http._tcp', 8080);

// 更新设备状态
await mdnsService.updateTxtRecord({
  version: '1.0',
  status: 'online',
  deviceType: 'light',
  room: 'livingroom'
});

服务发现(客户端)

创建服务浏览器

typescript

复制

下载

class MDNSBrowser {
  private mdnsService: mdns.Service;
  private context: common.BaseContext;
  private discoveredServices: Map<string, mdns.ServiceInfo> = new Map();
  private discoveryCallback?: (service: mdns.ServiceInfo) => void;
  private removalCallback?: (serviceName: string) => void;

  constructor(context: common.BaseContext) {
    this.context = context;
    this.mdnsService = mdns.createMDNSService(this.context);
  }

  // 开始发现服务
  async startDiscovery(serviceType: string): Promise<boolean> {
    try {
      await this.mdnsService.startDiscovering(serviceType);
      console.info(`开始发现服务类型: ${serviceType}`);
      this.setupDiscoveryListeners();
      return true;
    } catch (error) {
      console.error(`启动服务发现失败: ${(error as BusinessError).message}`);
      return false;
    }
  }

  // 设置发现监听器
  private setupDiscoveryListeners(): void {
    this.mdnsService.on('serviceFound', (serviceInfo: mdns.ServiceInfo) => {
      console.info(`发现服务: ${serviceInfo.serviceName}`);
      console.info(`服务详情: ${JSON.stringify(serviceInfo)}`);
      
      this.discoveredServices.set(serviceInfo.serviceName, serviceInfo);
      
      if (this.discoveryCallback) {
        this.discoveryCallback(serviceInfo);
      }
    });

    this.mdnsService.on('serviceLost', (serviceInfo: mdns.ServiceInfo) => {
      console.info(`服务丢失: ${serviceInfo.serviceName}`);
      
      this.discoveredServices.delete(serviceInfo.serviceName);
      
      if (this.removalCallback) {
        this.removalCallback(serviceInfo.serviceName);
      }
    });

    this.mdnsService.on('serviceResolved', (serviceInfo: mdns.ServiceInfo) => {
      console.info(`服务解析完成: ${serviceInfo.serviceName}`);
      console.info(`解析详情: ${JSON.stringify(serviceInfo)}`);
      
      // 更新服务信息
      this.discoveredServices.set(serviceInfo.serviceName, serviceInfo);
    });
  }

  // 设置发现回调
  setDiscoveryCallbacks(
    discoveryCallback: (service: mdns.ServiceInfo) => void,
    removalCallback?: (serviceName: string) => void
  ): void {
    this.discoveryCallback = discoveryCallback;
    this.removalCallback = removalCallback;
  }

  // 获取所有发现的服务
  getDiscoveredServices(): mdns.ServiceInfo[] {
    return Array.from(this.discoveredServices.values());
  }

  // 停止发现
  async stopDiscovery(): Promise<void> {
    try {
      await this.mdnsService.stopDiscovering();
      this.discoveredServices.clear();
      console.info('服务发现已停止');
    } catch (error) {
      console.error(`停止服务发现失败: ${(error as BusinessError).message}`);
    }
  }

  // 销毁浏览器
  destroy(): void {
    this.mdnsService.off('serviceFound');
    this.mdnsService.off('serviceLost');
    this.mdnsService.off('serviceResolved');
  }
}

实战示例:智能家居设备发现

设备发布者(服务端)

typescript

复制

下载

class SmartHomeDevice {
  private mdnsService: MDNSService;
  private deviceInfo: DeviceInfo;

  constructor(context: common.BaseContext, deviceInfo: DeviceInfo) {
    this.mdnsService = new MDNSService(context);
    this.deviceInfo = deviceInfo;
  }

  // 启动设备服务
  async startDevice(): Promise<boolean> {
    const serviceType = '_smarthome._tcp';
    const success = await this.mdnsService.publishService(
      this.deviceInfo.name,
      serviceType,
      this.deviceInfo.port
    );

    if (success) {
      // 更新设备特定的TXT记录
      await this.mdnsService.updateTxtRecord({
        deviceId: this.deviceInfo.id,
        type: this.deviceInfo.type,
        version: this.deviceInfo.version,
        status: 'online',
        capabilities: this.deviceInfo.capabilities.join(',')
      });
    }

    return success;
  }

  // 停止设备服务
  async stopDevice(): Promise<void> {
    await this.mdnsService.stopService();
  }
}

interface DeviceInfo {
  id: string;
  name: string;
  type: string;
  port: number;
  version: string;
  capabilities: string[];
}

设备发现者(客户端)

typescript

复制

下载

class SmartHomeController {
  private mdnsBrowser: MDNSBrowser;
  private devices: Map<string, SmartHomeDeviceInfo> = new Map();

  constructor(context: common.BaseContext) {
    this.mdnsBrowser = new MDNSBrowser(context);
    this.setupDiscoveryCallbacks();
  }

  // 开始发现智能家居设备
  async startDiscovery(): Promise<boolean> {
    return await this.mdnsBrowser.startDiscovery('_smarthome._tcp');
  }

  // 设置发现回调
  private setupDiscoveryCallbacks(): void {
    this.mdnsBrowser.setDiscoveryCallbacks(
      // 设备发现回调
      (service: mdns.ServiceInfo) => {
        const deviceInfo = this.parseDeviceInfo(service);
        this.devices.set(deviceInfo.id, deviceInfo);
        
        console.info(`发现新设备: ${deviceInfo.name}`);
        this.onDeviceDiscovered(deviceInfo);
      },
      // 设备丢失回调
      (serviceName: string) => {
        console.info(`设备离线: ${serviceName}`);
        this.onDeviceLost(serviceName);
      }
    );
  }

  // 解析设备信息
  private parseDeviceInfo(service: mdns.ServiceInfo): SmartHomeDeviceInfo {
    return {
      id: service.txtRecord?.['deviceId'] || 'unknown',
      name: service.serviceName,
      type: service.txtRecord?.['type'] || 'unknown',
      address: service.host?.address || 'unknown',
      port: service.port,
      version: service.txtRecord?.['version'] || '1.0',
      status: service.txtRecord?.['status'] || 'unknown',
      capabilities: service.txtRecord?.['capabilities']?.split(',') || []
    };
  }

  // 设备发现处理
  private onDeviceDiscovered(device: SmartHomeDeviceInfo): void {
    // 这里可以添加设备发现后的逻辑
    // 比如更新UI、建立连接等
    console.info(`设备详情: ${JSON.stringify(device)}`);
  }

  // 设备丢失处理
  private onDeviceLost(serviceName: string): void {
    // 处理设备离线逻辑
    this.devices.delete(serviceName);
  }

  // 获取所有发现的设备
  getDiscoveredDevices(): SmartHomeDeviceInfo[] {
    return Array.from(this.devices.values());
  }

  // 停止发现
  async stopDiscovery(): Promise<void> {
    await this.mdnsBrowser.stopDiscovery();
  }
}

interface SmartHomeDeviceInfo {
  id: string;
  name: string;
  type: string;
  address: string;
  port: number;
  version: string;
  status: string;
  capabilities: string[];
}

UI界面实现

typescript

复制

下载

// DeviceDiscoveryUI.ets
@Entry
@Component
struct DeviceDiscoveryPage {
  private controller: SmartHomeController = new SmartHomeController(getContext(this));
  @State devices: SmartHomeDeviceInfo[] = [];
  @State isDiscovering: boolean = false;

  aboutToAppear() {
    // 设置设备更新回调
    this.setupDeviceUpdate();
  }

  build() {
    Column({ space: 20 }) {
      // 标题和控制区域
      Row({ space: 10 }) {
        Text('智能家居设备发现')
          .fontSize(24)
          .fontWeight(FontWeight.Bold)
          .layoutWeight(1)

        Button(this.isDiscovering ? '停止发现' : '开始发现')
          .onClick(() => {
            if (this.isDiscovering) {
              this.stopDiscovery();
            } else {
              this.startDiscovery();
            }
          })
      }
      .width('100%')
      .padding(10)

      // 设备列表
      List({ space: 10 }) {
        ForEach(this.devices, (device: SmartHomeDeviceInfo) => {
          ListItem() {
            DeviceItem({ device: device })
          }
        }, (device: SmartHomeDeviceInfo) => device.id)
      }
      .layoutWeight(1)
      .width('100%')

      // 状态显示
      Text(this.isDiscovering ? `发现 ${this.devices.length} 个设备` : '点击开始发现设备')
        .fontSize(16)
        .fontColor(Color.Gray)
    }
    .width('100%')
    .height('100%')
    .padding(20)
  }

  async startDiscovery() {
    this.isDiscovering = await this.controller.startDiscovery();
    if (this.isDiscovering) {
      this.devices = this.controller.getDiscoveredDevices();
    }
  }

  async stopDiscovery() {
    await this.controller.stopDiscovery();
    this.isDiscovering = false;
  }

  private setupDeviceUpdate() {
    // 定期更新设备列表
    setInterval(() => {
      if (this.isDiscovering) {
        this.devices = [...this.controller.getDiscoveredDevices()];
      }
    }, 1000);
  }
}

@Component
struct DeviceItem {
  @Prop device: SmartHomeDeviceInfo;

  build() {
    Row({ space: 15 }) {
      // 设备图标
      Image($r('app.media.device_icon'))
        .width(40)
        .height(40)

      // 设备信息
      Column({ space: 5 }) {
        Text(this.device.name)
          .fontSize(18)
          .fontWeight(FontWeight.Medium)
          .textAlign(TextAlign.Start)

        Text(`${this.device.type} | ${this.device.address}:${this.device.port}`)
          .fontSize(12)
          .fontColor(Color.Gray)
      }
      .layoutWeight(1)

      // 设备状态
      Text(this.device.status === 'online' ? '在线' : '离线')
        .fontSize(14)
        .fontColor(this.device.status === 'online' ? Color.Green : Color.Red)
    }
    .width('100%')
    .padding(15)
    .backgroundColor(Color.White)
    .borderRadius(10)
    .shadow({ radius: 2, color: Color.Black, offsetX: 0, offsetY: 1 })
  }
}

常见服务类型

在mDNS中,服务类型遵循特定格式。以下是一些常见的服务类型:

typescript

复制

下载

const CommonServiceTypes = {
  HTTP: '_http._tcp',
  HTTPS: '_https._tcp',
  SSH: '_ssh._tcp',
  FTP: '_ftp._tcp',
  PRINTER: '_printer._tcp',
  AIRPLAY: '_airplay._tcp',
  HOMEKIT: '_homekit._tcp',
  SMART_HOME: '_smarthome._tcp',
  MEDIA_SERVER: '_media-server._tcp'
} as const;

最佳实践和注意事项

1. 错误处理

typescript

复制

下载

class MDNSErrorHandler {
  static handleError(error: BusinessError): void {
    const errorCode = error.code;
    
    switch (errorCode) {
      case 201:
        console.error('权限不足');
        break;
      case 202:
        console.error('服务类型格式错误');
        break;
      case 203:
        console.error('服务名称已存在');
        break;
      case 204:
        console.error('网络不可用');
        break;
      default:
        console.error(`mDNS错误: ${error.message}`);
    }
  }
}

2. 性能优化

  • 合理设置发现间隔:避免频繁的服务发现

  • 及时清理资源:页面销毁时停止服务发现

  • 使用合适的服务类型:精确指定需要发现的服务类型

3. 安全性考虑

  • 验证服务身份:通过TXT记录验证设备身份

  • 加密通信:发现后建立加密连接

  • 权限控制:确保只有授权设备可以发现服务

总结

mDNS在鸿蒙Next中为设备发现提供了强大的支持,特别适合智能家居、IoT设备等场景。通过本文的指南,您可以:

  1. 轻松发布mDNS服务供其他设备发现

  2. 实现自动化的设备发现和管理

  3. 构建零配置的智能设备网络

  4. 创建用户友好的设备发现界面

mDNS的零配置特性让用户体验更加流畅,用户只需将设备接入网络即可自动被发现和使用,这正符合鸿蒙分布式理念的设计哲学。

开启新对话

深度思考

联网搜索

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐