---
title: 开发流程
description: "介绍RPing功能实现的详细流程。"
url: https://www.hiascend.com/document/detail/zh/canncommercial/latest/others/rpingdev/RPing_02_0002.html
sourcePath: /source/zh/canncommercial/900/others/rpingdev/RPing_02_0002.html
indexId: 6d6aaf962ed11a2361cedc7d89bc3dd311b2f6188284d7de2541cf3c67742c6e64
---
# 开发流程

介绍RPing功能实现的详细流程。

#### 总体流程

RPing功能开发的流程总览如下图所示。

图1 RPing功能开发流程


#### 详细步骤

1. 初始化RPing功能，启动网络状态监测。
调用HccnRpingInit接口进行RPing功能初始化。对于每一个参与RPing监测功能的NPU，都需要调用一次该接口。且一个进程里，一个NPU仅能调用一次该接口，不支持重复调用。
初始化RPing功能前，首先需要定义HccnRpingInitAttr对象，初始化相关信息，并指定Device。

  - 针对Atlas 350 加速卡，代码示例如下：
    1 2 3 4 5 6 7 8 9 10 11 12 13 // 初始化信息配置 int ipLen_ = 32; int devId = 1; HccnRpingInitAttr *initAttr = new HccnRpingInitAttr(); initAttr->mode = HCCN_RPING_MODE_UB; // 指定网络类型为UB initAttr->port = 13886; // 监听端口，指定一个未使用的端口即可 initAttr->npuNum = 128; // 参与此次探测的全部NPU数量，即需要初始化的NPU个数 initAttr->bufferSize = 4096 * 10; // 配置值需要4K对齐，且必须大于：向每个探测目标的发包数量 * 2048 * 目标NPU数量 initAttr->ipAddr = new char[ipLen_]; strcpy(initAttr->eid, device_eid[devId]); HccnRpingCtx rpingCtx = nullptr; aclrtSetDevice(devId); // 初始化之前需要指定Device HccnRpingInit(devId, initAttr, &rpingCtx); // 调用初始化接口，获取RPingCtx

  - 针对Atlas A3 训练系列产品 / Atlas A3 推理系列产品与Atlas A2 训练系列产品 / Atlas A2 推理系列产品，代码示例如下：
    1 2 3 4 5 6 7 8 9 10 11 12 13 // 初始化信息配置 int ipLen = 16; int devId = 4; HccnRpingInitAttr *initAttr = new HccnRpingInitAttr(); initAttr->mode = HCCN_RPING_MODE_ROCE; // 指定网络类型为RoCE initAttr->port = 13886; // 监听端口，指定一个未使用的端口即可 initAttr->npuNum = 128; // 参与此次探测的全部NPU数量，即需要初始化的NPU个数 initAttr->bufferSize = 4096 * 50; // 配置值需要4K对齐，且必须大于：向每个探测目标的发包数量 * 2048 * 目标NPU数量 initAttr->ipAddr = new char[ipLen]; strcpy(initAttr->ipAddr, deviceIp[devId]); HccnRpingCtx rpingCtx = nullptr; aclrtSetDevice(devId); // 初始化之前需要指定Device HccnRpingInit(devId, initAttr, &rpingCtx); // 调用初始化接口，获取RPingCtx

2. 添加探测目标，建立发送端client NPU与接收端target NPU之间的RPing通信链路。
  调用HccnRpingAddTarget
  接口为作为client的NPU添加target，target即为需要被探测的NPU。
  - 调用该接口前，需要定义被探测的target信息，类型为    HccnRpingTargetInfo
。
  - 调用该接口之后，如果目标target超过120s还没有通过    HccnRpingInit
接口初始化，会超时失败。
  - 该接口可以反复调用，并支持按照IP地址为target信息去重，只保留第一个target信息。
  - 该接口有一个target添加失败就会返回错误。
  - 该接口支持一次最多添加16个target，若需要添加的target总数超过16个，请多次调用该接口实现。


  添加探测目标的代码示例：
  - Atlas 350 加速卡
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 // 添加探测target char device_eid[8][33] = { "000000000000002000100000dfdf1701", "000000000000002000100000dfdf1702", "000000000000002000100000dfdf1703", "000000000000002000100000dfdf1704", "000000000000002000100000dfdf1705", "000000000000002000100000dfdf1706", "000000000000002000100000dfdf1707", "000000000000002000100000dfdf1708" }; int ipLen = 32; int targetNum = devices.size() - 1; HccnRpingTargetInfo *target = new HccnRpingTargetInfo[targetNum]; for (int i = 0; i < devices.size() - 1; i++) { int devTargetId = devices[i]; // 从devices向量中取出target的deviceLogicId target[i].srcPort = 0; // tcp/udp源端口 target[i].sl = 0; // RDMA网卡的service level , 取值范围：[0,7] target[i].tc = 0; // RDMA网卡的traffic class，配置为RoCE报文的DSCP值 * 4 target[i].port = 13886; // socket连接端口，与target device初始化时配置的port一致 char payload[12] = "hellotarget"; strcpy(target[i].payload, payload); target[i].addrType = 1; target[i].srcEid = new char[ipLen_]; target[i].dstEid = new char[ipLen_]; strcpy(target[i].srcEid, device_eid[devClientId]); strcpy(target[i].dstEid, device_eid[devTargetId]); } if (rpingCtx == nullptr) { printf("rpingCtx is null!\n"); return -1; } HccnRpingAddTarget(rpingCtx, targetNum, target);

    HccnRpingAddTarget接口可以按照HccnRpingTargetInfo对象中“dstEid”的值对入参中的target数组去重。targetNum需要与target数组大小一致，否则可能会导致数组访问越界。

    如需要自定义RPing超时时间，可使用HccnRpingAddTargetWithCfg接口。

  - Atlas A3 训练系列产品 / Atlas A3 推理系列产品与Atlas A2 训练系列产品 / Atlas A2 推理系列产品
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 // 添加探测target char deviceIp[8][16] = { "192.168.99.127", "192.168.99.128", "192.168.99.129", "192.168.99.130", "192.168.99.131", "192.168.99.132", "192.168.99.133", "192.168.99.134" }; int ipLen = 16; int targetNum = devices.size() - 1; HccnRpingTargetInfo *target = new HccnRpingTargetInfo[targetNum]; for (int i = 0; i < devices.size() - 1; i++) { int devTargetId = devices[i]; // 从devices向量中取出target的deviceLogicId target[i].srcPort = 0; // tcp/udp源端口 target[i].sl = 4; // RDMA网卡的service level , 取值范围：[0,7] target[i].tc = (33 & 0x3f) << 2; // RDMA网卡的traffic class，配置为RoCE报文的DSCP值 * 4 target[i].port = 13886; // socket连接端口，与target device初始化时配置的port一致 target[i].srcIp = new char[ipLen]; target[i].dstIp = new char[ipLen]; strcpy(target[i].srcIp, deviceIp[devClientId]); strcpy(target[i].dstIp, deviceIp[devTargetId]); } if (rpingCtx == nullptr) { printf("rpingCtx is null!\n"); return -1; } HccnRpingAddTarget(rpingCtx, targetNum, target); /* HccnRpingAddTargetWithCfg接口可自定义RPing超时时间，调用示例如下： HccnRpingAddTargetConfig config; config.connectTimeout = 120; // RPing超时时间,取值范围[1,3600000]，单位ms HccnRpingAddTargetWithCfg(rpingCtx, targetNum, target, &config); */

    HccnRpingAddTarget接口可以按照HccnRpingTargetInfo对象中“dstIp”的值对入参中的target数组去重。targetNum需要与target数组大小一致，否则可能会导致数组访问越界。

    如需要自定义RPing超时时间，可使用HccnRpingAddTargetWithCfg接口。


3. 发起探测请求。
  调用HccnRpingBatchPingStart
  接口发起探测请求，开启BatchPing任务，client NPU会按照该接口中配置的发包数量及间隔时间向target NPU发送探测包。
  - 该接口调用前需要确保client NPU上已添加需要探测的target NPU信息，并且未处在探测任务执行的状态（即未处在BatchPing任务的执行状态）。
  - 如果想要重复调用该接口，可以在调用前先调用    HccnRpingBatchPingStop
接口终止BatchPing任务。


  发起探测请求的代码片段示例如下：

  1 2 3 4 uint32_t pktNum = 10; // 发给每个target的报文数量 uint32_t interval = 1; // 发送报文的间隔时间，单位为ms uint32_t timeout = 100; // client从发包到收包的等待的最大时长，单位为ms HccnRpingBatchPingStart(rpingCtx, pktNum, interval, timeout);

  HccnRpingBatchPingStart接口在发起探测之前会校验HccnRpingInit接口配置的bufferSize值，bufferSize值需要大于“pktNum * 2048 * targetNum”，其中pktNum为向每个探测目标的发包数量，targetNum为该client要探测的target NPU总数。

4. 调用HccnRpingGetResult接口获取探测结果。
  探测结果以数组的形式呈现，每一个元素中存储的是探测target返回的信息。该接口不支持去重，因此需要确保入参中的“target”不包含重复信息。探测结果中的“state”字段代表了该探测结果的有效性，每个target result都会有一个对应的state，当其值为“HCCN_RPING_RESULT_STATE_VALID”时，代表结果真实有效，否则结果无效。

  获取探测结果的代码片段示例如下：

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 // 获取探测结果 HccnRpingResultInfo *result = new HccnRpingResultInfo[targetNum]; HccnResult hccnRet = HCCN_E_AGAIN; while(hccnRet == HCCN_E_AGAIN) { sleep(1); hccnRet = HccnRpingGetResult(rpingCtx, targetNum, target, result); } for (int i = 0; i < targetNum; i++) { printf("txPkt[%u] rxPkt[%u] minRTT[%u] maxRTT[%u] avgRTT[%u] state[%u] payloadLen[%u]\n", result[i].txPkt, // 发送报文数 result[i].rxPkt, // 接收报文数 result[i].minRTT,// 最小收发包时延 result[i].maxRTT,// 最大收发包时延 result[i].avgRTT,// 平均收发包时延 result[i].state);// 获取结果的状态 } unsigned int payloadLenOutput = 0; void* payloadOutput; HccnRpingGetPayload(rpingCtx, &payloadOutput, &payloadLenOutput); int payloadNum = payloadLenOutput / 2048; for (int i = 0; i < payloadNum; i++) { HccnRpingPayloadHead *head = static_cast<HccnRpingPayloadHead*>(payloadOutput); printf("[%dth] srcIp:%s, dstIp:%s, payloadLen:%d, t1:%llu %llu, t2:%llu %llu, t3:%llu %llu, t4:%llu %llu, task id:%u\n", i, head->srcIp, head->dstIp, head->payloadLen, head->t1.sec, head->t1.usec, head->t2.sec, head->t2.usec, head->t3.sec, head->t3.usec, head->t4.sec, head->t4.usec, head->rpingBatchId); char* ptrTmp = static_cast<char*>(payloadOutput); ptrTmp += 2048; payloadOutput = ptrTmp; }

5. 中止探测任务
  获取结果之后，调用HccnRpingBatchPingStop接口中止探测任务。

  1 2 // 中止探测任务 HccnRpingBatchPingStop(rpingCtx);

6. 移除探测目标
结束探测任务之前，移除添加的探测目标，断开先前建立的RPing通信链路。

  - 该接口调用前须确保没有探测任务在执行。
  - 该接口调用前要保证有target信息添加过。
```
// 删除探测目标
    HccnRpingRemoveTarget(rpingCtx, targetNum, target);
```

7. 释放RPing资源。
进程结束前需要调用HccnRpingDeinit接口释放内存资源。

  - 该接口必须在    HccnRpingInit
接口调用并返回成功的情形下才可以调用，否则会释放不存在的内存资源导致异常。
  - 该接口无法重复调用，重复调用会导致对内存的重复释放，可能造成段错误甚至coredump。
```
// 释放内存
    HccnRpingDeinit(rpingCtx);
```
