Skip to content

收发命令

命令收发是在一条已建立的 TiRTC 连接上收发业务控制数据的方式。它适合表达“发一个命令,对端按这个命令处理”,例如请求状态、执行控制、回传处理结果。

基于同一条已建立连接,命令收发天然就是双向的:

  • 客户端可以向设备端发送命令,设备端按约定处理,并按约定决定是否回发结果。
  • 设备端也可以向客户端发送命令,客户端按约定处理,并按约定决定是否回发结果。

它和收发流消息不同:流消息更适合附着在某一条 stream 上发送轻量业务数据;如果你要的是“发一个命令,对端按这个命令处理”,优先用命令收发。

什么时候用命令收发

适合:

  • 你要在设备端和客户端之间传递控制类数据。
  • 你已经知道这条消息对应什么业务动作,例如“请求状态”或“执行开关”。
  • 你希望双方都按约定好的 cmdw 进入对应分支。

不适合:

  • 你要传的是附着在某一条 stream 上的轻量业务数据。
  • 你要传大包、离线消息或长期存储的数据。
  • 你需要的不是命令语义,而是媒体侧的附带消息能力。

开始前先约定

  • 双方已经基于同一条连接互通。设备端已经拿到当前链路对应的 hconn;客户端已经拿到当前链路对应的 conn 实例。
  • 双端已经约定好 payload 格式。下面示例统一使用 UTF-8 文本。
  • 双端已经约定好业务使用的 cmdw
  • 如果你的链路里包含设备端 C SDK,自定义 cmdw 的原始取值范围是闭区间 0x20000xFFFFFFFF

怎么理解 cmdw

在这页里,你可以把 cmdw 直接理解成双方约定好的 32 位字面值。

发送侧传入什么 cmdw,接收侧就会收到同一个 cmdw。TiRTC 负责把这个 cmdw 和对应 payload 传到对端;至于这个值表示“请求”“响应”还是“单向通知”,由你的业务协议自己约定。

客户端向设备端发送命令

下面这组 cmdw 表示“客户端请求设备状态,设备端返回状态结果”:

cmdw作用
0x2000请求设备状态
0x2002返回设备状态结果

客户端发送命令并接收结果

下面的片段只保留命令处理本身,默认 conn 已经是当前链路对应的连接实例;iOS 只展示和命令收发直接相关的代码。

kotlin
private const val CMDW_GET_DEVICE_STATUS = 0x2000L
private const val CMDW_DEVICE_STATUS_RESULT = 0x2002L

conn.onCommand =
  TiRtcConnCommandListener { cmdw, data ->
    if (cmdw == CMDW_DEVICE_STATUS_RESULT) {
      val text = data.decodeToString()
      Log.i("TiRTC", "device status=$text")
    }
  }

val code = conn.sendCommand(
  cmdw = CMDW_GET_DEVICE_STATUS,
  data = "status?".encodeToByteArray(),
)
if (code != 0) {
  Log.w("TiRTC", "send command failed code=$code")
}
swift
private let CMDW_GET_DEVICE_STATUS: UInt32 = 0x2000
private let CMDW_DEVICE_STATUS_RESULT: UInt32 = 0x2002

func requestDeviceStatus(on conn: TiRtcConn) {
    let code = conn.sendCommand(cmdw: CMDW_GET_DEVICE_STATUS, data: Data("status?".utf8))
    if code != 0 {
        print("send command failed: \(code)")
    }
}

func conn(_ conn: TiRtcConn, didReceiveCommand cmdw: UInt32, data: Data) {
    if cmdw == CMDW_DEVICE_STATUS_RESULT {
        let text = String(decoding: data, as: UTF8.self)
        print("device status=\(text)")
    }
}
dart
import 'dart:convert';
import 'dart:typed_data';

const int cmdwGetDeviceStatus = 0x2000;
const int cmdwDeviceStatusResult = 0x2002;

conn.onCommand = (cmdw, data) {
  if (cmdw == cmdwDeviceStatusResult) {
    final String text = utf8.decode(data);
    debugPrint('device status=$text');
  }
};

final int code = conn.sendCommand(
  cmdw: cmdwGetDeviceStatus,
  data: Uint8List.fromList(utf8.encode('status?')),
);
if (code != 0) {
  debugPrint('send command failed: $code');
}

设备端接收命令并按约定回发结果

c
#include <stdint.h>
#include <string.h>

enum {
  CMDW_GET_DEVICE_STATUS = 0x2000,
  CMDW_DEVICE_STATUS_RESULT = 0x2002,
};

static void on_command(
    tirtc_conn_t hconn,
    uint32_t cmdw,
    const void *data,
    uint32_t len) {
  const char *request = "status?";

  if (cmdw != CMDW_GET_DEVICE_STATUS ||
      data == NULL ||
      len != (uint32_t)strlen(request) ||
      memcmp(data, request, len) != 0) {
    return;
  }

  const char *response = "device-ok";
  TiRtcSendCommand(
      hconn,
      CMDW_DEVICE_STATUS_RESULT,
      response,
      (uint32_t)strlen(response));
}

如果这条命令不需要响应,设备端处理完后可以直接结束,不必再回发另一条命令。

设备端向客户端发送命令

下面这组 cmdw 表示“设备端请求客户端状态,客户端返回状态结果”:

cmdw作用
0x2004请求客户端状态
0x2006返回客户端状态结果

设备端发送命令并接收结果

c
#include <stdint.h>
#include <stdio.h>
#include <string.h>

enum {
  CMDW_GET_CLIENT_STATUS = 0x2004,
  CMDW_CLIENT_STATUS_RESULT = 0x2006,
};

static int request_client_status(tirtc_conn_t hconn) {
  const char *payload = "client-status?";
  return TiRtcSendCommand(
      hconn,
      CMDW_GET_CLIENT_STATUS,
      payload,
      (uint32_t)strlen(payload));
}

static void on_command(
    tirtc_conn_t hconn,
    uint32_t cmdw,
    const void *data,
    uint32_t len) {
  (void)hconn;

  if (cmdw != CMDW_CLIENT_STATUS_RESULT || data == NULL) {
    return;
  }

  char text[128] = {0};
  uint32_t n = len < (uint32_t)(sizeof(text) - 1)
      ? len
      : (uint32_t)(sizeof(text) - 1);

  memcpy(text, data, n);
  printf("client status=%s\n", text);
}

客户端接收命令并按约定回发结果

下面的片段只保留命令处理本身,默认 conn 已经是当前链路对应的连接实例;iOS 只展示和命令收发直接相关的代码。

kotlin
private const val CMDW_GET_CLIENT_STATUS = 0x2004L
private const val CMDW_CLIENT_STATUS_RESULT = 0x2006L

conn.onCommand =
  TiRtcConnCommandListener { cmdw, data ->
    if (cmdw == CMDW_GET_CLIENT_STATUS) {
      val response = "foreground".encodeToByteArray()
      val code = conn.sendCommand(cmdw = CMDW_CLIENT_STATUS_RESULT, data = response)
      if (code != 0) {
        Log.w("TiRTC", "send response failed code=$code")
      }
    }
  }
swift
private let CMDW_GET_CLIENT_STATUS: UInt32 = 0x2004
private let CMDW_CLIENT_STATUS_RESULT: UInt32 = 0x2006

func conn(_ conn: TiRtcConn, didReceiveCommand cmdw: UInt32, data: Data) {
    if cmdw == CMDW_GET_CLIENT_STATUS {
        let response = Data("foreground".utf8)
        let code = conn.sendCommand(cmdw: CMDW_CLIENT_STATUS_RESULT, data: response)
        if code != 0 {
            print("send response failed: \(code)")
        }
    }
}
dart
import 'dart:convert';
import 'dart:typed_data';

const int cmdwGetClientStatus = 0x2004;
const int cmdwClientStatusResult = 0x2006;

conn.onCommand = (cmdw, data) {
  if (cmdw == cmdwGetClientStatus) {
    final int code = conn.sendCommand(
      cmdw: cmdwClientStatusResult,
      data: Uint8List.fromList(utf8.encode('foreground')),
    );
    if (code != 0) {
      debugPrint('send response failed: $code');
    }
  }
};

如果这条命令不需要响应,客户端处理完后也可以直接结束,不必再回发另一条命令。

常见问题

一端已经发送,但另一端没有收到

先检查这三项:

  • 双端约定的是否是同一个 cmdw
  • payload 编码是否一致,例如一端按 UTF-8 文本发,另一端却按别的格式解析。
  • 当前发送使用的是否是这条链路对应的 hconnconn

双端都收到了命令,但分支没有命中

先检查比较的是否就是双方约定好的那个原始 cmdw

什么时候该看流消息

如果你的数据需要跟某一条 stream 一起理解,而不是表达一个明确的业务命令,改看收发流消息

相关文档

Ti RTC 开发文档