ndnSIM学习(十一)——data.cpp和interest.cpp源码分析

本文介绍NDN网络中的数据包和兴趣包的结构与处理流程,包括使用C++实现的数据类和兴趣类,涉及编码、解码及包匹配等关键功能。

前言

data.cppinterest.cpp 用于处理兴趣包和数据包。

data.hpp

删掉了部分我认为不重要的东西,比如 class Error 。这里面的 wireEncode 属于是经典的模板编程的例子,看这种代码真是吐了。不过看懂了其实还好,这个代码的意思就是先用 EncodingEstimator 计算出需要的 buffer 的大小,然后再用 EncodingBuffer 造出一个真正的 buffer ,对这个 buffer 执行 wireDecode 函数,更新自己的成员函数。

class Data : public PacketBase, public std::enable_shared_from_this<Data>
{
public:
  // 构造一个Name为name的空Data包
  explicit Data(const Name& name = Name());
  // 通过对Block进行解码,构造出一个Data包
  explicit Data(const Block& wire);

  // 对内容进行Encode,其中TAG分为EncodingEstimator和EncodingBuffer
  template<encoding::Tag TAG>
  size_t wireEncode(EncodingImpl<TAG>& encoder, bool wantUnsignedPortionOnly = false) const;
  const Block& wireEncode(EncodingBuffer& encoder, const Block& signatureValue) const;
  // 先执行wireEncode<EncodingEstimator>,这部分用于计算buffer的大小,不执行操作
  // 再执行wireEncode<EncodingBuffer>,这部分用于真正创建一个buffer,并对它进行Encode操作
  // 最后把buffer用于执行wireDecode,更新本Data的成员函数
  const Block& wireEncode() const;
  
  // 按照Name->MetaInfo(?)->Content(?)->SignatureInfo->SignatureValue的顺序解码到自己的成员函数里
  void wireDecode(const Block& wire);

  bool hasWire() const { return m_wire.hasWire(); }
  const Name& getFullName() const;  // return m_name(以及对应的ImplicitSha256Digest组件)

public: // 数据的Get和Set
  const Name& getName() const { return m_name; }
  Data& setName(const Name& name);  // return m_name
  const MetaInfo& getMetaInfo() const { return m_metaInfo; }
  Data& setMetaInfo(const MetaInfo& metaInfo);
  const Block& getContent() const;
  Data& setContent(const Block& block);
  Data& setContent(const uint8_t* value, size_t valueSize);
  Data& setContent(ConstBufferPtr value);
  const Signature& getSignature() const { return m_signature; }
  Data& setSignature(const Signature& signature);
  Data& setSignatureValue(const Block& value);

public: // MetaInfo的Get和Set
  uint32_t getContentType() const { return m_metaInfo.getType(); }
  Data& setContentType(uint32_t type);
  time::milliseconds getFreshnessPeriod() const { return m_metaInfo.getFreshnessPeriod(); }
  Data& setFreshnessPeriod(time::milliseconds freshnessPeriod);
  const optional<name::Component>& getFinalBlock() const { return m_metaInfo.getFinalBlock(); }
  Data& setFinalBlock(optional<name::Component> finalBlockId);

protected:
  void resetWire();    // 每次set后都要重置wire

private:
  Name m_name;
  MetaInfo m_metaInfo;
  Block m_content;
  Signature m_signature;

  mutable Block m_wire;
  mutable Name m_fullName; ///< cached FullName computed from m_wire
};

#ifndef DOXYGEN
extern template size_t
Data::wireEncode<encoding::EncoderTag>(EncodingBuffer&, bool) const;

extern template size_t
Data::wireEncode<encoding::EstimatorTag>(EncodingEstimator&, bool) const;
#endif

std::ostream& operator<<(std::ostream& os, const Data& data);

bool operator==(const Data& lhs, const Data& rhs);

inline bool operator!=(const Data& lhs, const Data& rhs) { return !(lhs == rhs); }

关于具体的编解码过程,请参考ndnSIM学习(五)——data包和interest包的tlv编码、解码过程以及如何判断包的类型这篇文章,这里面主要讲了 tlv 相关的东西。ndnSIM学习(八)——apps之ndn-producer.cpp和ndn-consumer.cpp源码分析这篇文章里主要讲了发包和收包的过程。

interest.hpp

删掉了部分我认为不重要的东西,比如 class Error 。大部分内容和 Data 是类似的,不过其中关于 SHA256 加密这块我还是没怎么搞明白。

// 默认interest生存时间为4s
const time::milliseconds DEFAULT_INTEREST_LIFETIME = 4_s;

class Interest : public PacketBase, public std::enable_shared_from_this<Interest>
{
public:
  // 初始化Name&lifetime
  explicit Interest(const Name& name = Name(), time::milliseconds lifetime = DEFAULT_INTEREST_LIFETIME);
  // 用Block造一个Interest
  explicit Interest(const Block& wire);

  template<encoding::Tag TAG>
  size_t wireEncode(EncodingImpl<TAG>& encoder) const;
  // 先执行wireEncode<EncodingEstimator>,这部分用于计算buffer的大小,不执行操作
  // 再执行wireEncode<EncodingBuffer>,这部分用于真正创建一个buffer,并对它进行Encode操作
  // 最后把buffer用于执行wireDecode,更新本Data的成员函数
  const Block& wireEncode() const;

  // Name(SHA256?)->CanBePrefix?->MustBeFresh?->ForwardingHint?->Nonce->InterestLifetime?->HopLimit?->ApplicationParameters?
  void wireDecode(const Block& wire);

  bool hasWire() const { return m_wire.hasWire(); }
  std::string toUri() const;

public: // 匹配
  // 看Interest是否能匹配上Data
  bool matchesData(const Data& data) const;
  // 比较Name和CanBePrefix和MustBeFresh
  bool matchesInterest(const Interest& other) const;

public: // element access
  const Name& getName() const noexcept { return m_name; }
  // set m_name,并且如果自己的m_parameters有SHA256参数,则添加到m_name
  Interest& setName(const Name& name);
  static void setDefaultCanBePrefix(bool canBePrefix) { s_defaultCanBePrefix = canBePrefix; }
  bool getCanBePrefix() const noexcept { return m_canBePrefix; }
  Interest& setCanBePrefix(bool canBePrefix) {
    m_canBePrefix = canBePrefix;
    m_wire.reset();
    m_isCanBePrefixSet = true;
    return *this;
  }
  bool getMustBeFresh() const noexcept { return m_mustBeFresh; }
  Interest& setMustBeFresh(bool mustBeFresh) {
    m_mustBeFresh = mustBeFresh;
    m_wire.reset();
    return *this;
  }
  const DelegationList& getForwardingHint() const noexcept { return m_forwardingHint; }
  Interest& setForwardingHint(const DelegationList& value);
  template<typename Modifier>
  Interest& modifyForwardingHint(const Modifier& modifier) {
    modifier(m_forwardingHint);
    m_wire.reset();
    return *this;
  }
  bool hasNonce() const noexcept { return m_nonce.has_value(); }
  uint32_t getNonce() const;
  Interest& setNonce(uint32_t nonce);
  void refreshNonce();
  time::milliseconds getInterestLifetime() const noexcept { return m_interestLifetime; }
  Interest& setInterestLifetime(time::milliseconds lifetime);
  optional<uint8_t> getHopLimit() const noexcept { return m_hopLimit; }
  Interest& setHopLimit(optional<uint8_t> hopLimit);
  bool hasApplicationParameters() const noexcept { return !m_parameters.empty(); }
  Block getApplicationParameters() const {
    if (m_parameters.empty())
      return {};
    else
      return m_parameters.front();
  }
  Interest& setApplicationParameters(const Block& parameters);
  Interest& setApplicationParameters(const uint8_t* value, size_t length);
  Interest& setApplicationParameters(ConstBufferPtr value);
  Interest& unsetApplicationParameters();

public: // ParametersSha256DigestComponent support
  static bool getAutoCheckParametersDigest() { return s_autoCheckParametersDigest; }
  static void setAutoCheckParametersDigest(bool b) { s_autoCheckParametersDigest = b; }
  bool isParametersDigestValid() const;

private:
  void setApplicationParametersInternal(Block parameters);
  NDN_CXX_NODISCARD shared_ptr<Buffer> computeParametersDigest() const;
  // 添加SHA256组件
  void addOrReplaceParametersDigestComponent();
  // 如果有1个SHA256组件,返回pos;没有返回-1;多个返回-2
  static ssize_t findParametersDigestComponent(const Name& name);

#ifdef NDN_CXX_HAVE_TESTS
public:
  /// If true, not setting CanBePrefix results in an error in wireEncode().
  static bool s_errorIfCanBePrefixUnset;
#endif // NDN_CXX_HAVE_TESTS

private:
  static boost::logic::tribool s_defaultCanBePrefix;
  static bool s_autoCheckParametersDigest;

  Name m_name;
  DelegationList m_forwardingHint;
  mutable optional<uint32_t> m_nonce;
  time::milliseconds m_interestLifetime;
  optional<uint8_t> m_hopLimit;
  mutable bool m_isCanBePrefixSet = false;
  bool m_canBePrefix = true;
  bool m_mustBeFresh = false;

  std::vector<Block> m_parameters;

  mutable Block m_wire;
};

// 输出CanBePrefix和MustBeFresh和Nonce和Lifetime和HopLimit
std::ostream& operator<<(std::ostream& os, const Interest& interest);

总结

里面内容细节太多了,其中部分我在其他文章中分析过,所以只给 .hpp 文件加了注释,没有分析 .cpp 文件的函数。这篇文章写得有点水……

这个错误信息表明你的程序在运行时抛出了一个 OpenCV 的异常,具体是由于 `cv::Exception` 导致的崩溃。我们来逐步分析错误原因、解决方法,并提供相应的建议。 --- ### ❌ 错误信息解析: ``` OpenCV(4.2.0) /home/pi/Downloads/opencv-4.2.0/modules/core/src/matrix.cpp:757: error: (-215:Assertion failed) dims <= 2 && step[0] > 0 in function 'locateROI' ``` #### 关键点解释: - **文件位置**:`matrix.cpp:757` 是 OpenCV 源码中处理矩阵(`cv::Mat`)的部分。 - **断言失败**:`dims <= 2 && step[0] > 0` - `dims <= 2`:表示矩阵维度必须是 1D 或 2D(即最多二维),OpenCV 大多数图像操作只支持 1D/2D 矩阵。 - `step[0] > 0`:表示第一维的“步长”必须为正数,用于内存访问计算。如果 `step[0] == 0` 或未正确初始化,则会触发此错误。 - **函数上下文**:`locateROI` 表示你正在对一个 ROI(Region of Interest)进行定位操作,可能是调用了 `.col()`、`.row()`、`.operator()` 提取子区域,或者使用了指针访问等操作。 --- ### 🧨 常见导致该问题的原因: 1. **空的或无效的 `cv::Mat` 被访问** ```cpp cv::Mat mat; mat.col(0); // 对空矩阵操作 → 触发 assert ``` 2. **图像加载失败但仍被处理** ```cpp cv::Mat img = cv::imread("nonexistent.jpg"); if (img.empty()) { // 忘记检查,继续使用 img → 后续操作崩溃 } ``` 3. **越界访问子矩阵** ```cpp cv::Mat img = cv::Mat::ones(3, 3, CV_8UC1); cv::Mat sub = img(cv::Range(0, 5), cv::Range(0, 5)); // 越界! ``` 4. **多线程中共享 `cv::Mat` 且未加锁,造成结构破坏** 5. **使用已经释放或 move 过的 `cv::Mat`** 6. **自定义数据构造 `cv::Mat` 时 `step` 设置错误** ```cpp unsigned char* data = ...; size_t step = 0; // 错误!应该等于一行字节数 cv::Mat mat(rows, cols, CV_8UC3, data, step); // step[0] == 0 → 断言失败 ``` --- ### ✅ 解决方案最佳实践: #### 1. **始终检查图像是否成功加载** ```cpp cv::Mat img = cv::imread("path/to/image.jpg"); if (img.empty()) { std::cerr << "Error: Could not load image!" << std::endl; return -1; } ``` #### 2. **避免对空矩阵做任何操作前先验证** ```cpp if (!mat.empty()) { cv::Mat col = mat.col(0); // 安全访问 } ``` #### 3. **确保子区域(ROI)不越界** ```cpp cv::Rect roi(0, 0, 100, 100); if (roi.x + roi.width <= mat.cols && roi.y + roi.height <= mat.rows) { cv::Mat sub = mat(roi); } else { std::cerr << "ROI out of bounds!" << std::endl; } ``` #### 4. **构造外部数据绑定 Mat 时正确设置 step** ```cpp // 假设 width=640, 3通道 int width = 640; int height = 480; unsigned char* myData = new unsigned char[height * width * 3]; size_t step = width * 3; // 每行字节数 cv::Mat mat(height, width, CV_8UC3, myData, step); // 正确设置 step ``` #### 5. **启用调试日志定位出错位置** 在关键位置打印尺寸信息: ```cpp std::cout << "Mat size: " << mat.size() << ", dims: " << mat.dims << std::endl; std::cout << "Step[0]: " << mat.step[0] << std::endl; ``` #### 6. **使用智能指针或作用域管理资源(尤其多线程)** --- ### 💡 示例修复代码(典型场景) ```cpp #include <opencv2/opencv.hpp> #include <iostream> int main() { cv::Mat img = cv::imread("test.jpg"); // 替换为你的真实路径 if (img.empty()) { std::cerr << "Failed to load image!" << std::endl; return -1; } std::cout << "Image loaded: " << img.cols << "x" << img.rows << ", channels: " << img.channels() << ", step[0]=" << img.step[0] << std::endl; // 安全提取第一列 if (img.cols > 0) { try { cv::Mat col = img.col(0); std::cout << "First column extracted." << std::endl; } catch (const cv::Exception& e) { std::cerr << "OpenCV Exception: " << e.what() << std::endl; } } return 0; } ``` --- ### 🔍 如何快速定位问题? 1. 使用 `gdb` 回溯: ```bash gdb ./car run # 当崩溃时输入: bt ``` 查看哪一行代码引发了 `locateROI` 调用。 2. 在每个 `cv::Mat` 操作前添加 `.empty()` 判断。 3. 检查所有 `imread`, `VideoCapture >>`, 自定义 Mat 构造逻辑。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值