opendds初入门之模拟丢包分析链路,理解TopicQos和DataWriter/DataReader的关系

前期探索已经对opendds的相关内容有了一定的了解,关于opendds中的qos策略也进行了简单的整理。

本想对qos策略统一做练习了解,发现仅仅是可靠传输策略的开始也遇到一些问题,所以对可靠传输qos策略的验证先做笔记。

主要内容:

​ 1.了解了topicQos,如何设置以及在对端打印,了解了datawriter和Datareader如何用到topicQos,专门的默认参数支持。

​ 2.模拟丢包场景,使用wireshark抓包,配合构造rtps链路+tcp传输,查看消息现象和消息报文。

​ 3.主要是对topicQos和datawriter/datareader的qos之间的关系有了了解(topicQos是一个宏观模板,datawriter/datareader创建时使用专门参数继承topicQos)。

​ 4.monitor测试软件可以通内置主题打印到对端topicQos策略,只有在集中模式下才能打印到,非集中模式rtps是打印不到的。

1.梳理练习动作

​ 1.首先使用idl+mpc构建对应的最简demo项目(注意环境变量的配置)。

​ 2.增加必要代码,实现最简发布订阅交互。

​ 3.根据必要练习,对关注的几个qos策略进行验证。

2.按照以前的动作,简单搭建发布订阅最简demo。

​ 准备idl文件,定制mpc文件,增加相关的文件,生成对应的vs项目(参考前面的文章)。

​ 增加文件内容,编译vs项目生成对应exe,增加配置文件进行调用。

​ 分别启动发布端和测试端进行测试。

描述:简单的rtps测试,发现即使首先启动订阅端,再启动发布端,会发现前面丢失几个消息,这是可解决的,也是因为代码最简:

3.考虑必要qos策略的测试。

这里以topic相关的qos策略为切入点进行测试。

3.1 首先了解一下传输环境。

这里使用rtps进行测试,设置特定的本地地址,抓包查看。

1.使用本机进行测试,设置特定的ip和端口方便测试(如果报错删除#后面的注释)

ini 复制代码
#这里是pub端的配置文件,主要是注释的部分,然后sub端类似,ip相反。
[common]
DCPSGlobalTransportConfig=$file
DCPSDefaultDiscovery=DEFAULT_RTPS

[transport/the_rtps_transport]
transport_type=rtps_udp
local_address=192.168.1.161:12346    #设置本地地址
unicast_addresses=192.168.1.161:12345    #设置单播,
use_multicast=false  #禁用组播

2.查看一下本地端口:

bash 复制代码
PS C:\Users\LEGION> netstat -aon|findstr 1234
  UDP    192.168.1.161:12345    *:*                                    5932
  UDP    192.168.1.161:12346    *:*                                    2724

3.使用抓包简单查看,为后续做前置分析。

可以看到,能正常抓到rtps的包,能看到pub端发布的消息。

3.2 试试tcp的设置传输测试。

1.修改配置文件。

ini 复制代码
[common]
DCPSGlobalTransportConfig=$file
DCPSDefaultDiscovery=DEFAULT_RTPS

[transport/the_tcp_transport]
transport_type=tcp
local_address=192.168.1.161:12346

2.运行测试观察。

3.抓包查看消息。

这里忘了关注,tcp在建立连接后,开始也是使用rtps进行交互链路,抓包同样可以看到协议。

3.3 设置可靠传输,然后构造丢包场景测试。

1. 首先使用工具模拟丢包,查看丢包场景下的现象。

使用丢包工具clumsy模拟丢包场景,工具使用如下:

2. 同样的场景进行测试。

观察实际接收情况,发现真的有收不到的包了哎。

配合上述场景,查看抓包工具分析,发现真的是没有发送这些报文。 这里的心跳交互之类的,待研究。

接下来该考虑的是使用可靠传输策略处理该问题,首先发现qos策略是要在代码层适配的,或者通过qos.xml中逻辑设置。

3.4 试试tcp通信丢包场景,看效果(tcp可靠传输)

设置tcp场景下的丢包后,构造发布订阅交互测试,发现tcp在丢包场景下,确实能正常收到消息(消息日志是连续的)。

分析抓包现象,可以配合分析tcp快速重传逻辑,可以看到这里触发了重传标识为38(丢失,直接没发送)的那个包。

=====》tcp快速重传理论上只触发传输丢失的那个包,这里重新把丢失包后面没来得及回应的两个序号为39和40的包也进行了重传。

4.配置可靠传输qos策略。

4.1 首先查看datareader,datawriter,topic中可靠传输相关关键字段。

使用opendds中的内置主题打印相关的对端qos策略,可以直接在本地打印自己的qos策略。

1.订阅端订阅内置主题,打印相关的qos策略。

首先关注订阅端打印发布端的qos策略,以topic策略为切入点,使用其对应的内置主题获取对端的qos策略。

C++ 复制代码
//测试时,发现使用topic对应的内置主题时,发现无法获取到topic内置主题,使用rtps_udp的方式时。 
//  ====>同时 topic设置qos,订阅端使用内置主题读取 一直也没有正确。
//这里还是基于datawriter和datareader进行测试吧。
//1.首先是pub端,增加qos策略,这里只关注可靠传输设置。
	DDS::DataWriterQos dw_qos;
    pub->get_default_datawriter_qos(dw_qos);
    dw_qos.reliability.kind = DDS::RELIABLE_RELIABILITY_QOS;

    DDS::DataWriter_var dw = pub->create_datawriter(topic.in(), dw_qos, DDS::DataWriterListener::_nil(), ::OpenDDS::DCPS::DEFAULT_STATUS_MASK);
    if (CORBA::is_nil(dw.in())) {
        cerr << "create_datawriter failed." << endl;
        exit(1);
    }

//2.sub端,这里测试时,发现也必须DataReaderQos设置对应的策略,否则貌似不生效。
	DDS::DataReaderQos dr_qos;
    sub->get_default_datareader_qos(dr_qos);
    //dr_qos.reliability.kind = DDS::BEST_EFFORT_RELIABILITY_QOS;
    dr_qos.reliability.kind = DDS::RELIABLE_RELIABILITY_QOS; //特别注意这里内置主题要对应
    DDS::DataReader_var dr = sub->create_datareader(topic.in(), dr_qos, listener.in(), ::OpenDDS::DCPS::DEFAULT_STATUS_MASK);

	//增加内置主题读取信息。
	{
        DDS::Subscriber_var bit_sub = participant->get_builtin_subscriber();
        DDS::DataReader_var dr_pub = bit_sub->lookup_datareader(OpenDDS::DCPS::BUILT_IN_PUBLICATION_TOPIC);
        DDS::DataReaderQos dr_qos;
        bit_sub->get_default_datareader_qos(dr_qos);
        dr_qos.reliability.kind = DDS::RELIABLE_RELIABILITY_QOS;
        dr_qos.history.kind = DDS::KEEP_ALL_HISTORY_QOS;
        dr_pub->set_qos(dr_qos);

        //这里相关的监控主题是不一样的 
        DDS::DataReaderListener_var listener = new publishQosBitListener();
        dr_pub->set_listener(listener, DDS::DATA_AVAILABLE_STATUS | DDS::SUBSCRIPTION_MATCHED_STATUS | DDS::SAMPLE_LOST_STATUS);
	}

	//这里我为了简单,直接统一使用监听的方式进行读取消息。
	class publishQosBitListener : public virtual OpenDDS::DCPS::LocalObject<OpenDDS::DCPS::DataReaderListener>
{
public:
	publishQosBitListener() {};
	virtual ~publishQosBitListener(void) {};

	void on_requested_deadline_missed(DDS::DataReader_ptr reader, const DDS::RequestedDeadlineMissedStatus& status)
	{};
	void on_requested_incompatible_qos(DDS::DataReader_ptr reader, const DDS::RequestedIncompatibleQosStatus& status)
	{};
	void on_liveliness_changed(DDS::DataReader_ptr reader, const DDS::LivelinessChangedStatus& status)
	{};
	void on_subscription_matched(DDS::DataReader_ptr reader, const DDS::SubscriptionMatchedStatus& status)
	{};
	void on_sample_rejected(DDS::DataReader_ptr reader, const DDS::SampleRejectedStatus& status)
	{};
	void on_data_available(DDS::DataReader_ptr reader) override;
	void on_sample_lost(DDS::DataReader_ptr reader, const DDS::SampleLostStatus& status)
	{};
	void on_subscription_disconnected(DDS::DataReader_ptr reader, const ::OpenDDS::DCPS::SubscriptionDisconnectedStatus& status)
	{};
	void on_subscription_reconnected(DDS::DataReader_ptr reader, const ::OpenDDS::DCPS::SubscriptionReconnectedStatus& status)
	{};
	void on_subscription_lost(DDS::DataReader_ptr reader, const ::OpenDDS::DCPS::SubscriptionLostStatus& status)
	{};
	void on_budget_exceeded(DDS::DataReader_ptr reader, const ::OpenDDS::DCPS::BudgetExceededStatus& status)
	{};

private:
	std::string print_octet_array16(const DDS::OctetArray16& arr);
	void print_publication_builtin_topic_data(const DDS::PublicationBuiltinTopicData& pub_data);
};

void publishQosBitListener::on_data_available(DDS::DataReader_ptr reader) {
	try {
        //这里注意使用对应的内置主题datareader
		DDS::PublicationBuiltinTopicDataDataReader_var  sub_bit = DDS::PublicationBuiltinTopicDataDataReader::_narrow(reader);
		DDS::PublicationBuiltinTopicDataSeq data;
		DDS::SampleInfoSeq infos;
		DDS::ReturnCode_t ret = sub_bit->take(data, infos, DDS::LENGTH_UNLIMITED, DDS::ANY_SAMPLE_STATE, DDS::ANY_VIEW_STATE, DDS::ALIVE_INSTANCE_STATE);
		if (ret != DDS::RETCODE_OK) {
			ACE_ERROR((LM_ERROR, ACE_TEXT("ERROR: take failed: %d\n"), ret));
			return;
		}
		for (CORBA::ULong i = 0; i < data.length(); ++i) {
			//if (infos[i].valid_data)
			{
				const DDS::PublicationBuiltinTopicData& pub_data = data[i];
				print_publication_builtin_topic_data(pub_data);
			}
		}
		sub_bit->return_loan(data, infos);
	}
	catch (const CORBA::Exception& e) {
		e._tao_print_exception("ERROR:");
	}
	catch (...) {
		ACE_ERROR((LM_ERROR, ACE_TEXT("ERROR: Unknown exception in on_data_available\n")));
	}
}

std::string publishQosBitListener::print_octet_array16(const DDS::OctetArray16& arr) {
	std::ostringstream oss;
	oss << std::hex << std::setfill('0');
	for (size_t i = 0; i < 16; ++i) {
		oss << std::setw(2) << static_cast<int>(arr[i]);
		if (i != 15) oss << " ";
	}
	return oss.str();
}

void publishQosBitListener::print_publication_builtin_topic_data(const DDS::PublicationBuiltinTopicData& pub_data)
{
	std::cout << "PublicationBuiltinTopicData at " << std::time(nullptr) << ":\n";
	// 1. key
	std::cout << "  Key: " << print_octet_array16(pub_data.key.value) << "\n";
	// 2. participant_key
	std::cout << "  Participant Key: " << print_octet_array16(pub_data.participant_key.value) << "\n";
	// 3. topic_name
	std::cout << "  Topic Name: " << (pub_data.topic_name.in() ? pub_data.topic_name.in() : "null") << "\n";
	// 4. type_name
	std::cout << "  Type Name: " << (pub_data.type_name.in() ? pub_data.type_name.in() : "null") << "\n";
	// 5. durability
	std::cout << "  Durability: kind=" << (pub_data.durability.kind == DDS::VOLATILE_DURABILITY_QOS ? "VOLATILE" : "TRANSIENT_LOCAL") << "\n";
	// 6. durability_service
	std::cout << "  Durability Service: service_cleanup_delay=" << pub_data.durability_service.service_cleanup_delay.sec << "s "
		<< pub_data.durability_service.service_cleanup_delay.nanosec << "ns, "
		<< "history_kind=" << (pub_data.durability_service.history_kind == DDS::KEEP_LAST_HISTORY_QOS ? "KEEP_LAST" : "KEEP_ALL") << ", "
		<< "history_depth=" << pub_data.durability_service.history_depth << ", "
		<< "max_samples=" << pub_data.durability_service.max_samples << ", "
		<< "max_instances=" << pub_data.durability_service.max_instances << ", "
		<< "max_samples_per_instance=" << pub_data.durability_service.max_samples_per_instance << "\n";

	// 7. deadline
	std::cout << "  Deadline: period=" << pub_data.deadline.period.sec << "s "
		<< pub_data.deadline.period.nanosec << "ns\n";
	// 8. latency_budget
	std::cout << "  Latency Budget: duration=" << pub_data.latency_budget.duration.sec << "s "
		<< pub_data.latency_budget.duration.nanosec << "ns\n";

	// 9. liveliness
	std::cout << "  Liveliness: kind=" << (pub_data.liveliness.kind == DDS::AUTOMATIC_LIVELINESS_QOS ? "AUTOMATIC" :
		pub_data.liveliness.kind == DDS::MANUAL_BY_PARTICIPANT_LIVELINESS_QOS ? "MANUAL_BY_PARTICIPANT" :
		"MANUAL_BY_TOPIC") << ", "
		<< "lease_duration=" << pub_data.liveliness.lease_duration.sec << "s "
		<< pub_data.liveliness.lease_duration.nanosec << "ns\n";

	// 10. reliability
	std::cout << "  Reliability: kind=" << (pub_data.reliability.kind == DDS::RELIABLE_RELIABILITY_QOS ? "RELIABLE" : "BEST_EFFORT") << ", "
		<< "max_blocking_time=" << pub_data.reliability.max_blocking_time.sec << "s "
		<< pub_data.reliability.max_blocking_time.nanosec << "ns\n";

	// 11. lifespan
	std::cout << "  Lifespan: duration=" << pub_data.lifespan.duration.sec << "s "
		<< pub_data.lifespan.duration.nanosec << "ns\n";

	// 12. user_data
	std::cout << "  User Data: ";
	for (CORBA::ULong j = 0; j < pub_data.user_data.value.length(); ++j) {
		std::cout << static_cast<char>(pub_data.user_data.value[j]);
	}
	std::cout << "\n";
	// 13. ownership
	std::cout << "  Ownership: kind=" << (pub_data.ownership.kind == DDS::SHARED_OWNERSHIP_QOS ? "SHARED" : "EXCLUSIVE") << "\n";
	// 14. ownership_strength
	std::cout << "  Ownership Strength: value=" << pub_data.ownership_strength.value << "\n";
	// 15. destination_order
	std::cout << "  Destination Order: kind=" << (pub_data.destination_order.kind == DDS::BY_RECEPTION_TIMESTAMP_DESTINATIONORDER_QOS ? "BY_RECEPTION_TIMESTAMP" : "BY_SOURCE_TIMESTAMP") << "\n";
	// 16. presentation
	std::string access_scope;
	switch (pub_data.presentation.access_scope) {
	case DDS::INSTANCE_PRESENTATION_QOS: access_scope = "INSTANCE"; break;
	case DDS::TOPIC_PRESENTATION_QOS: access_scope = "TOPIC"; break;
	case DDS::GROUP_PRESENTATION_QOS: access_scope = "GROUP"; break;
	default: access_scope = "UNKNOWN"; break;
	}
	std::cout << "  Presentation: access_scope=" << access_scope << ", "
		<< "coherent_access=" << (pub_data.presentation.coherent_access ? "true" : "false") << ", "
		<< "ordered_access=" << (pub_data.presentation.ordered_access ? "true" : "false") << "\n";

	// 17. partition
	std::cout << "  Partition: ";
	for (CORBA::ULong j = 0; j < pub_data.partition.name.length(); ++j) {
		std::cout << (pub_data.partition.name[j].in() ? pub_data.partition.name[j].in() : "null");
		if (j < pub_data.partition.name.length() - 1) std::cout << ", ";
	}
	std::cout << "\n";

	// 18. topic_data
	std::cout << "  Topic Data: ";
	for (CORBA::ULong j = 0; j < pub_data.topic_data.value.length(); ++j) {
		std::cout << static_cast<char>(pub_data.topic_data.value[j]);
	}
	std::cout << "\n";

	// 19. group_data
	std::cout << "  Group Data: ";
	for (CORBA::ULong j = 0; j < pub_data.group_data.value.length(); ++j) {
		std::cout << static_cast<char>(pub_data.group_data.value[j]);
	}
	std::cout << "\n";

	// 20. representation
	std::cout << "  Data Representation: ";
	for (CORBA::ULong j = 0; j < pub_data.representation.value.length(); ++j) {
		std::cout << pub_data.representation.value[j];
		if (j < pub_data.representation.value.length() - 1) std::cout << ", ";
	}
	std::cout << "\n";
}

通过打印对内置主题的使用细节进行观察:

  1. 发现始终没有触发topic对应的qos策略的打印,这里用的rtps_udp,未观察到影响因素(monitor程序貌似能获取到topic的内置主题)。

  2. 单纯关注可靠性设置,发现topic设置为尽力传输(这里的覆盖关系是什么),但是datawriter发现还是可靠传输(这里通过获取默认qos策略,发现datawriter端的qos默认是可靠传输,但是订阅端datareader默认是尽力传输)。

  3. 验证,使用丢包工具+日志打印的方式验证,发现订阅端如果不设置可靠传输策略,丢包严重。

  4. 给订阅端datareader设置qos策略为可靠传输,发现不丢包。

    C++ 复制代码
    DDS::DataReaderQos dr_qos;
    sub->get_default_datareader_qos(dr_qos);
    dr_qos.reliability.kind = DDS::RELIABLE_RELIABILITY_QOS;
    cout << (dr_qos.reliability.kind == DDS::RELIABLE_RELIABILITY_QOS ? "RELIABLE" : "BEST_EFFORT") << endl;

    这里又引发一个思考,qos策略定制的报文如果丢失呢?也是需要给他设置可靠传输的。

2.是否有必要试试发布端订阅内置主题,打印对端相关qos策略。

暂时不关注,发布端获取订阅端的qos策略,实际上和订阅端获取发布端是一样的,只是一个用的pub一个用的sub的内置主题。

3.如果多节点交互,实际上qos策略需要分链路控制的,需要识别到节点信息的。

这里在思考的是,发布和订阅端实际上是多对多成网状交互的,设置qos策略时需要考虑自己的业务场景。

但是细想了一下,设置策略一般没有这种细化的场景吧,直接都是以主题链路进行统一策略定制吧。

4.2 测试datawriter/datareader与topic设置qos的覆盖场景。

dds/version.h中查看版本号,是3.26.0

从上述测试场景看,发现主要生效的是datawriter和datareader之间链路qos策略设置生效,但是遗留有问题:

1.topic的qos是没有生效的,没有从对端正确获取并进行输出的?(只有集中模式inforepo下才生效?)

遗留问题:为什么无法获取到远端的topicqos。

一直在订阅端增加了topicqos对应的内置主题的监听,但是非集中模式一直获取不到,手头无措。

最终发现,集中模式下才能正常~

C++ 复制代码
//这里简单描述测试代码: 发布端,设置topicqos,
		DDS::TopicQos topic_qos;
		participant->get_default_topic_qos(topic_qos);
		topic_qos.reliability.kind = DDS::BEST_EFFORT_RELIABILITY_QOS; // 可靠传输
		topic_qos.history.kind = DDS::KEEP_ALL_HISTORY_QOS;         // 保留所有样本
		topic_qos.deadline.period.sec = 1;                          // 截止时间 1 秒
		topic_qos.deadline.period.nanosec = 0;
		topic_qos.durability.kind = DDS::TRANSIENT_LOCAL_DURABILITY_QOS; // 瞬时本地持久性
		topic_qos.liveliness.kind = DDS::AUTOMATIC_LIVELINESS_QOS;
		topic_qos.liveliness.lease_duration.sec = 10;               // 存活租期 10 秒
		topic_qos.liveliness.lease_duration.nanosec = 0;

        DDS::Topic_var topic = participant->create_topic("Movie Discussion List", type_name.in(), topic_qos, DDS::TopicListener::_nil(), ::OpenDDS::DCPS::DEFAULT_STATUS_MASK);

	//.... 省略不关注代码
	//设置datawriter继承自topic的qos  使用DATAWRITER_QOS_USE_TOPIC_QOS
	DDS::DataWriter_var dw = pub->create_datawriter(topic.in(), DATAWRITER_QOS_USE_TOPIC_QOS, DDS::DataWriterListener::_nil(), ::OpenDDS::DCPS::DEFAULT_STATUS_MASK);
       
//订阅端,主要设置获取发布端的qos,这里同样要注意datareader的qos策略,可能导致链路不通。
	//这里使用DATAREADER_QOS_DEFAULT是可以的, DATAREADER_QOS_USE_TOPIC_QOS也可以,其他待测试
	DDS::DataReader_var dr = sub->create_datareader(topic.in(), DATAREADER_QOS_DEFAULT, listener.in(), ::OpenDDS::DCPS::DEFAULT_STATUS_MASK);

	//使用内置主题监听对端topicqos
	{//这里处理sub对应的内置主题:
        DDS::Subscriber_var bit_topic = participant->get_builtin_subscriber();
        if (CORBA::is_nil(bit_topic.in())) {
            std::cerr << "获取内置 Subscriber 失败" << std::endl;
            return 1;
        }

        DDS::DataReader_var dr_topic = bit_topic->lookup_datareader(OpenDDS::DCPS::BUILT_IN_TOPIC_TOPIC);
        if (CORBA::is_nil(dr_topic.in())) {
            std::cerr << "查找 DCPSPublication DataReader 失败" << std::endl;
            return 1;
        }

        DDS::DataReaderQos dr_qos;
        bit_topic->get_default_datareader_qos(dr_qos);
        dr_qos.reliability.kind = DDS::RELIABLE_RELIABILITY_QOS;
        dr_qos.history.kind = DDS::KEEP_ALL_HISTORY_QOS;
        dr_topic->set_qos(dr_qos);

        DDS::DataReaderListener_var topicqos_bit_listener = new TopicQosBitListener();
        dr_topic->set_listener(topicqos_bit_listener, DDS::DATA_AVAILABLE_STATUS | DDS::SAMPLE_LOST_STATUS | DDS::SUBSCRIPTION_MATCHED_STATUS); //该sub,datareader也是可以设置qos的通过set_qos
    }

	//对应的TopicQosBitListener的监听代码:
	
class TopicQosBitListener: public virtual OpenDDS::DCPS::LocalObject<OpenDDS::DCPS::DataReaderListener>
{
public:
	TopicQosBitListener() {};
	virtual ~TopicQosBitListener(void) {};
 
	void on_requested_deadline_missed(DDS::DataReader_ptr reader, const DDS::RequestedDeadlineMissedStatus& status)
	{};
	void on_requested_incompatible_qos(DDS::DataReader_ptr reader, const DDS::RequestedIncompatibleQosStatus& status)
	{};
    void on_liveliness_changed(DDS::DataReader_ptr reader, const DDS::LivelinessChangedStatus& status)
    {};
    void on_subscription_matched(DDS::DataReader_ptr reader, const DDS::SubscriptionMatchedStatus& status)
    {};
    void on_sample_rejected(DDS::DataReader_ptr reader, const DDS::SampleRejectedStatus& status)
    {};
    void on_data_available(DDS::DataReader_ptr reader) override;
    void on_sample_lost(DDS::DataReader_ptr reader, const DDS::SampleLostStatus& status)
    {};
    void on_subscription_disconnected( DDS::DataReader_ptr reader, const ::OpenDDS::DCPS::SubscriptionDisconnectedStatus& status)
    {};
    void on_subscription_reconnected(DDS::DataReader_ptr reader, const ::OpenDDS::DCPS::SubscriptionReconnectedStatus& status)
    {};
    void on_subscription_lost(DDS::DataReader_ptr reader, const ::OpenDDS::DCPS::SubscriptionLostStatus& status)
    {};
    void on_budget_exceeded(DDS::DataReader_ptr reader, const ::OpenDDS::DCPS::BudgetExceededStatus& status)
    {};
};

#include <dds/DCPS/BuiltInTopicUtils.h>
void TopicQosBitListener::on_data_available(DDS::DataReader_ptr reader) {
	std::cout << "---------------start TopicQosBitListener::on_data_available------------------------" << std::endl;
	DDS::TopicBuiltinTopicDataDataReader_var topic_dr = DDS::TopicBuiltinTopicDataDataReader::_narrow(reader);
	if (CORBA::is_nil(topic_dr.in())) {
		std::cerr << "缩小 TopicBuiltinTopicDataDataReader 失败" << std::endl;
		return;
	}

	DDS::TopicBuiltinTopicDataSeq topic_data;
	DDS::SampleInfoSeq infos;
	DDS::ReturnCode_t ret = topic_dr->take(topic_data, infos, DDS::LENGTH_UNLIMITED,DDS::ANY_SAMPLE_STATE, DDS::ANY_VIEW_STATE, DDS::ANY_INSTANCE_STATE);

	if (ret != DDS::RETCODE_OK && ret != DDS::RETCODE_NO_DATA) {
		std::cerr << "读取数据失败,错误码: " << ret << std::endl;
		return;
	}

	for (CORBA::ULong i = 0; i < topic_data.length(); ++i) {
		if (infos[i].valid_data) {
			const DDS::TopicBuiltinTopicData& data = topic_data[i];
			std::cout << "发现对端 Topic:" << std::endl;
			std::cout << "  - Topic 名称: " << data.name << std::endl;
			std::cout << "  - 类型名称: " << data.type_name << std::endl;
			std::cout << "  - QoS 策略:" << std::endl;
			std::cout << "    - Durability: kind = " << data.durability.kind << std::endl;
			std::cout << "    - Deadline: period = " << data.deadline.period.sec << " 秒 " << data.deadline.period.nanosec << " 纳秒" << std::endl;
			std::cout << "    - Latency Budget: duration = " << data.latency_budget.duration.sec << " 秒 " << data.latency_budget.duration.nanosec << " 纳秒" << std::endl;
			std::cout << "    - Liveliness: kind = " << data.liveliness.kind << ", lease_duration = " << data.liveliness.lease_duration.sec << " 秒 " << data.liveliness.lease_duration.nanosec << " 纳秒" << std::endl;
			std::cout << "    - Reliability: kind = " << data.reliability.kind << std::endl;
			std::cout << "    - Transport Priority: value = " << data.transport_priority.value << std::endl;
			std::cout << "    - Lifespan: duration = " << data.lifespan.duration.sec << " 秒 " << data.lifespan.duration.nanosec << " 纳秒" << std::endl;
			std::cout << "    - Destination Order: kind = " << data.destination_order.kind << std::endl;
			std::cout << "    - History: kind = " << data.history.kind << ", depth = " << data.history.depth << std::endl;
			std::cout << "    - Resource Limits: max_samples = " << data.resource_limits.max_samples << ", max_instances = " << data.resource_limits.max_instances << ", max_samples_per_instance = " << data.resource_limits.max_samples_per_instance << std::endl;
			std::cout << "    - Ownership: kind = " << data.ownership.kind << std::endl;
			std::cout << "    - Topic Data: " << (data.topic_data.value.length() > 0 ? "有数据" : "无数据") << std::endl;
			std::cout << "---------------------------------------" << std::endl;
		}
	}

	topic_dr->return_loan(topic_data, infos);
}

集中模式下真能打印到topicQos:

关注一下内容,可以发现打印的内容和设置的一致:

2.topic和这里datawriter/datareader之间qos策略的关系,是如何的覆盖关系?

======》topicQos是给这类主题的一个默认qos策略的设置,可以通过DATAWRITER_QOS_USE_TOPIC_QOS或者DATAREADER_QOS_USE_TOPIC_QOS让其继承自topicQos去用。

====》从某种程度而言,topic对应的qos是不需要的,一直不得其意,最终理解,topicQos策略是给datawriter和datareader做宏观的一个默认模板,用于统一设置而用。

====》datawriter/datareader的qos也要互相对应的兼容。

C++ 复制代码
//最终发现  datawriter或者datareader的qos要继承自topic,需要按设置来 这里有设置默认接口
#define PARTICIPANT_QOS_DEFAULT     \
  OpenDDS::DCPS::Marked_Default_Qos::marked_default_DomainParticipantQos ()
#define TOPIC_QOS_DEFAULT           \
  OpenDDS::DCPS::Marked_Default_Qos::marked_default_TopicQos ()
#define PUBLISHER_QOS_DEFAULT       \
  OpenDDS::DCPS::Marked_Default_Qos::marked_default_PublisherQos ()
#define SUBSCRIBER_QOS_DEFAULT      \
  OpenDDS::DCPS::Marked_Default_Qos::marked_default_SubscriberQos ()
#define DATAWRITER_QOS_DEFAULT      \
  OpenDDS::DCPS::Marked_Default_Qos::marked_default_DataWriterQos ()
#define DATAREADER_QOS_DEFAULT      \
  OpenDDS::DCPS::Marked_Default_Qos::marked_default_DataReaderQos ()
#define DATAWRITER_QOS_USE_TOPIC_QOS    \
  OpenDDS::DCPS::Marked_Default_Qos::marked_default_DataWriter_Use_TopicQos ()
#define DATAREADER_QOS_USE_TOPIC_QOS    \
  OpenDDS::DCPS::Marked_Default_Qos::marked_default_DataReader_Use_TopicQos ()

//也就是说修改代码: pub端和sub端
DDS::DataWriter_var dw = pub->create_datawriter(topic.in(), DATAWRITER_QOS_USE_TOPIC_QOS, DDS::DataWriterListener::_nil(), ::OpenDDS::DCPS::DEFAULT_STATUS_MASK);
       
DDS::DataReader_var dr = sub->create_datareader(topic.in(), DATAREADER_QOS_USE_TOPIC_QOS, listener.in(), ::OpenDDS::DCPS::DEFAULT_STATUS_MASK);
//同时注意:这里在测试的时候,发现统一设置为默认链路能通,但是单独给一端独立设置时,会有直接链路不通的情况。 需要注意兼容

诡异的问题:使用家里的网络进行测试时,发现集中模式一直启动不起来。

​ 使用公司的网络时,发现本能运行起来的非集中模式下的测试又有问题。 ====》这里与底层网络配置相关,待探究。

相关推荐
yun68539921 个月前
opendds初入门之对inforepo模式运行探索
opendds
jason成都3 个月前
ubuntu编译opendds开发(C#)
linux·ubuntu·c#·opendds
林夕072 年前
OpenDDS之QosXml库编译(Windows + VS2019)
编译·dds·opendds