RTPS通信使用的socket和端口
概述
在FastRTPS中,无论SenderResource还是,ReceiverResource,其内部会依赖NetworkFactory创建socket,每个socket都需要绑定socket才能正常使用。
一般来说,一个RTPSParticipant中创建的RTPSReader/RTPSWriter都是共用一组socket资源的,而这些socket的端口的值一般是RTPSParticipant的domainid和participantid相关的。
上层应用可以指定创建的RTPSParticipant用于元数据和业务数据通信的单播地址/组播地址,但是无法指定通信的端口,端口是RTPS中根据规则进行计算分配的。
下面主要是梳理一下一个RTPSParticipant会创建哪些socket,每一类socket绑定的端口规则是怎样的。
创建的socket
通过阅读RTPSReader,RTPSWriter,StatelessWriter,StatelessReader,StatefulWriter以及StatefulReader的代码可以发现,这些endpoint类是只负责处理数据,管理匹配的对端endpoint,而发送和接收数据都是依赖内部的history对象的。history中的数据则是依赖了下层的ReceiverResource和SenderResource,而ReceiverResource和SenderResource是依赖了更下层Transport中的各种ChannelResource来进行网络数据通信的,每个ChannelResource中包含了用于地层数据通信的socket对象
发送
发送的Socket在SenderResource中创建,有如下几个创建的时机点
1. 创建RTPSParticipant设置qos的时候
DomainParticipantImpl::set_qosRTPSParticipant::update_attributesRTPSParticipantImpl::update_attributescreateSenderResources(元数据组播地址)createSenderResources(元数据单播地址)createSenderResources(业务数据单播地址)NetworkFactory::build_send_resourcesUDPTransportInterface::OpenOutputChannelUDPTransportInterface::OpenAndBindUnicastOutputSocketUDPTransportInterface::createUDPSocket
2. StatelessWriter匹配到对端Reader
StatelessWriter::matched_reader_addStatelessWriter::update_reader_infoRTPSParticipantImpl::createSenderResources
3. StatefulWriter匹配到对端Reader
StatefulWriter::matched_reader_addStatefulWriter::update_reader_infoRTPSParticipantImpl::createSenderResources
4. StatefulReader匹配到对端Writer(需要创建SenderResource用于发送ACKNACK消息)
StatefulReader::matched_writer_addRTPSParticipantImpl::createSenderResources
接收
接收的Socket在ReceiverResource中创建,
元数据组播地址绑定的端口
https://gitee.com/zzl0109/Fast-DDS/blob/master/include/fastdds/rtps/common/PortParameters.h
inline uint32_t getMulticastPort(uint32_t domainId) const{uint32_t port = portBase + domainIDGain * domainId + offsetd0;...return port;}
元数据单播地址绑定的端口
https://gitee.com/zzl0109/Fast-DDS/blob/master/include/fastdds/rtps/common/PortParameters.h
inline uint32_t getUnicastPort(uint32_t domainId,uint32_t RTPSParticipantID) const{uint32_t port = portBase + domainIDGain * domainId + offsetd1 + participantIDGain * RTPSParticipantID;...return port;}
业务数据单播地址绑定端口
https://gitee.com/zzl0109/Fast-DDS/blob/master/src/cpp/rtps/participant/RTPSParticipantImpl.cpp
...
RTPSParticipantImpl::RTPSParticipantImpl(...// 上层应用未指定单播和组播地址if (m_att.defaultUnicastLocatorList.empty() && m_att.defaultMulticastLocatorList.empty()){get_default_unicast_locators();internal_default_locators_ = true;EPROSIMA_LOG_INFO(RTPS_PARTICIPANT,m_att.getName() << " Created with NO default Unicast Locator List, adding Locators:"<< m_att.defaultUnicastLocatorList);}else{// Locator with port 0, calculate port. 指定了地址,通过计算分配端口std::for_each(m_att.defaultUnicastLocatorList.begin(), m_att.defaultUnicastLocatorList.end(),[&](Locator_t& loc){ // 端口通过calculate_well_known_port计算m_network_Factory.fill_default_locator_port(domain_id_, loc, m_att, false);});m_network_Factory.NormalizeLocators(m_att.defaultUnicastLocatorList);std::for_each(m_att.defaultMulticastLocatorList.begin(), m_att.defaultMulticastLocatorList.end(),[&](Locator_t& loc){ // 端口通过calculate_well_known_port计算m_network_Factory.fill_default_locator_port(domain_id_, loc, m_att, true);});}...
}...void RTPSParticipantImpl::get_default_unicast_locators()
{m_network_Factory.getDefaultUnicastLocators(domain_id_, m_att.defaultUnicastLocatorList, m_att);m_network_Factory.NormalizeLocators(m_att.defaultUnicastLocatorList);
}...
https://gitee.com/zzl0109/Fast-DDS/blob/master/src/cpp/rtps/network/NetworkFactory.cpp
bool NetworkFactory::getDefaultUnicastLocators(uint32_t domain_id,LocatorList_t& locators,const RTPSParticipantAttributes& m_att) const
{bool result = false;for (auto& transport : mRegisteredTransports){result |= transport->getDefaultUnicastLocators(locators, calculate_well_known_port(domain_id, m_att, false));}return result;
}bool NetworkFactory::fill_default_locator_port(uint32_t domain_id,Locator_t& locator,const RTPSParticipantAttributes& m_att,bool is_multicast) const
{bool result = false;for (auto& transport : mRegisteredTransports){if (transport->IsLocatorSupported(locator)){ // 端口通过calculate_well_known_port计算result |= transport->fillUnicastLocator(locator, calculate_well_known_port(domain_id, m_att, is_multicast));}}return result;
}uint16_t NetworkFactory::calculate_well_known_port(uint32_t domain_id,const RTPSParticipantAttributes& att,bool is_multicast) const
{uint32_t port = att.port.portBase +att.port.domainIDGain * domain_id +(is_multicast ?att.port.offsetd2 :att.port.offsetd3 + att.port.participantIDGain * att.participantID);if (port > 65535){... // error logexit(EXIT_FAILURE);}return static_cast<uint16_t>(port);
}
业务数据组播地址绑定端口
业务数据的接收socket,其绑定的端口逻辑如下:
发送业务数据使用的端口
发送数据时需要通过SenderResource
bool NetworkFactory::build_send_resources(
{...for (auto& transport : mRegisteredTransports){returned_value |= transport->OpenOutputChannel(sender_resource_list, locator);}...
}bool UDPTransportInterface::OpenOutputChannel(SendResourceList& sender_resource_list, // 返回创建的SenderResource列表const Locator& locator)
{..uint16_t port = configuration()->m_output_udp_socket; //获取通信端口,默认位0if (is_interface_whitelist_empty()) // 没有设置网卡白名单的情况else {// 没有设置网卡白名单的情况,根据locator对应地址找到合适的网卡集合get_unknown_network_interfaces(sender_resource_list, locNames, true);for (const auto& infoIP : locNames) {// 创建socket(绑定infoIP对应的网卡IP),port=0则绑定随机端口eProsimaUDPSocket unicastSocket = OpenAndBindUnicastOutputSocket(generate_endpoint(infoIP.name, port), port);// 设置带外数据通信属性SetSocketOutboundInterface(unicastSocket, infoIP.name);if (first_time_open_output_channel_){ // 如果是首次创建该Transport下的senderreosurce,则设置multicast::enable_loopback,允许组播数据回送127.0.0.1getSocketPtr(unicastSocket)->set_option(ip::multicast::enable_loopback(true));first_time_open_output_channel_ = false;}// 使用前面创建的socket新建SenderResource并且返回给调用方sender_resource_list.emplace_back(static_cast<SenderResource*>(new UDPSenderResource(*this, unicastSocket, false, true)));}}...
}