Iray Programmer's Manual

Networking modes

This topic introduces:

  1. The different networking modes:
  2. The program example_networking.cpp, which shows the basic configuration options to set up the different networking modes. Further configuration options related to networking are provided by the mi::neuraylib::INetwork_configuration interface. A simple implementation of the mi::neuraylib::IHost_callback interface is used to track network changes.

See Networking configuration for a general discussion about the advantages and disadvantages of the available networking modes.

No networking

This mode is the default, since it does not require any additional configuration. In this mode each instance of the Iray API is separate. There are no attempts to connect to other instances. This mode was already used in the previous examples. It is part of this example program just for a matter of completeness.

UDP mode

The UDP mode is the simplest networking mode (apart from no networking at all). All it requires is a UDP multicast address and port number. All hosts with instances of the Iray API than can be reached via this multicast address join a single cluster. The cluster can be restricted to certain sets of hosts by using an optional discovery identifier. This option allows a fine-grained control over potential members of a particular cluster.

TCP mode

In TCP mode it is necessary to specify all hosts participating in the cluster in advance. Apart from your address and port, you have to specify the addresses and ports of all other hosts. Since this mode is not very flexible it is recommended to use TCP mode with discovery instead.

TCP mode with discovery

This mode is similar to TCP, but there is a discovery mechanism which eliminates the need to specify the addresses and ports of all other hosts. There are two variants of this mode: discovery via multicast or discovery via a master node. In the multicast variant you have to specify a common multicast address which is used by all hosts during the discovery phase to join the cluster. In the master variant you have to specify a common unicast address. The host with the given IP address acts as master during the discovery phase, all other hosts connect to this host to join the cluster. In both variants TCP is used as in the TCP mode without discovery after the host connected to the cluster. Similar to the UDP mode an optional discovery identifier can be used to restrict the cluster to a certain set of hosts.

example_networking.cpp

001 /******************************************************************************
002  * © 1986, 2015 NVIDIA ARC GmbH. All rights reserved.
003  *****************************************************************************/
004 
005 // examples/example_networking.cpp
006 //
007 // Demonstrates usage of the different networking modes
008 //
009 // The example expects the following command line arguments:
010 //
011 //   example_networking <mode> <multicast_address> <cluster_interface> <discovery_address>
012 //                     [<host1> <host2> ... <hostN>]
013 //
014 // mode                          "OFF", "UDP", "TCP", or "TCP_WITH_DISCOVERY"
015 // multicast_address             IPv4 or IPv6 address and port, or "."
016 // cluster_interface             IPv4 or IPv6 address and port, or "."
017 // discovery_address             IPv4 or IPv6 address and port, or "."
018 //
019 // The special value "." represents the "don't care" value for any address.
020 // If mode is "TCP", the remaining arguments are interpreted as hosts to configure.
021 //
022 // Examples:
023 //
024 //   No networking
025 //
026 //   @any host:    ./example_networking OFF . . .
027 //
028 //   UDP mode with three hosts
029 //
030 //   @192.168.1.1: ./example_networking UDP 224.1.1.1:1111 . .
031 //   @192.168.1.2: ./example_networking UDP 224.1.1.1:1111 . .
032 //   @192.168.1.3: ./example_networking UDP 224.1.1.1:1111 . .
033 //
034 //   TCP mode with three hosts
035 //
036 //   @192.168.1.1: ./example_networking TCP . 192.168.1.1:1111 . 192.168.1.2:2222 192.168.1.3:3333
037 //   @192.168.1.2: ./example_networking TCP . 192.168.1.2:2222 . 192.168.1.1:1111 192.168.1.3:3333
038 //   @192.168.1.3: ./example_networking TCP . 192.168.1.3:3333 . 192.168.1.1:1111 192.168.1.2:2222
039 //
040 //   TCP_WITH_DISCOVERY mode with three hosts (via multicast)
041 //
042 //   @192.168.1.1: ./example_networking TCP_WITH_DISCOVERY . 192.168.1.1:1111 224.1.1.1:4444
043 //   @192.168.1.2: ./example_networking TCP_WITH_DISCOVERY . 192.168.1.2:2222 224.1.1.1:4444
044 //   @192.168.1.3: ./example_networking TCP_WITH_DISCOVERY . 192.168.1.3:3333 224.1.1.1:4444
045 //
046 //   TCP_WITH_DISCOVERY mode with three hosts (via a head node)
047 //   Note that the client needs to be the only node running on its IP address.
048 //
049 //   @192.168.1.1: ./example_networking TCP_WITH_DISCOVERY . 192.168.1.1:1111 192.168.1.1:5555
050 //   @192.168.1.2: ./example_networking TCP_WITH_DISCOVERY . 192.168.1.2:2222 192.168.1.1:5555
051 //   @192.168.1.3: ./example_networking TCP_WITH_DISCOVERY . 192.168.1.3:3333 192.168.1.1:5555
052 
053 #include <iostream>
054 
055 #include <mi/neuraylib.h>
056 
057 // Include code shared by all examples.
058 #include "example_shared.h"
059 
060 // This example implementation of the IHost_callback interface just prints a line
061 // for every event.
062 class Host_callback : public mi::base::Interface_implement<mi::neuraylib::IHost_callback>
063 {
064 public:
065     void connection_callback( mi::Uint32 host_id, bool flag)
066     {
067         if( flag) {
068             fprintf( stderr,
069                 "The connection to the cluster was established. Own host id is %u.\n", host_id);
070             m_own_host_id = host_id;
071         }
072         else
073             fprintf( stderr, "The connection to the cluster was lost.\n");
074     }
075     void membership_callback( mi::Uint32 host_id, bool flag)
076     {
077         if( flag)
078             fprintf( stderr, "Host %u joined the cluster.\n", host_id);
079         else
080             fprintf( stderr, "Host %u left the cluster.\n", host_id);
081     }
082     void property_callback( mi::Uint32 host_id, const mi::neuraylib::IHost_properties* properties)
083     {
084         fprintf( stderr, "Host %u communicated its properties:\n", host_id);
085         mi::base::Handle<const mi::IString> value( properties->get_property( "application_name"));
086         if( value.is_valid_interface())
087             fprintf( stderr, "    application_name: %s\n", value->get_c_str());
088     }
089     void synchronizer_callback( mi::Uint32 host_id)
090     {
091         fprintf( stderr, "The synchronizer is now host %u", host_id);
092         if( m_own_host_id == host_id)
093             fprintf( stderr, " (this host)");
094         fprintf( stderr, ".\n");
095     }
096     void database_status_callback( const char* status)
097     {
098         fprintf( stderr, "The database reports its status as \"%s\".\n", status);
099     }
100 
101 private:
102     mi::Uint32 m_own_host_id;
103 };
104 
105 void configuration( mi::base::Handle<mi::neuraylib::INeuray> neuray,
106                     mi::base::Handle<mi::neuraylib::IHost_callback> host_callback,
107                     int argc,
108                     char* argv[])
109 {
110     mi::base::Handle<mi::neuraylib::INetwork_configuration> network_configuration(
111         neuray->get_api_component<mi::neuraylib::INetwork_configuration>());
112 
113     // Register the callback handler
114     network_configuration->register_host_callback( host_callback.get());
115 
116     // Set networking mode
117     const char* mode = argv[1];
118     if( strcmp( mode, ".") == 0) {
119         ;
120     } else if( strcmp( mode, "OFF") == 0) {
121         check_success( network_configuration->set_mode(
122             mi::neuraylib::INetwork_configuration::MODE_OFF) == 0);
123     } else if( strcmp( mode, "TCP") == 0) {
124         check_success( network_configuration->set_mode(
125             mi::neuraylib::INetwork_configuration::MODE_TCP) == 0);
126     } else if( strcmp( mode, "UDP") == 0) {
127         check_success( network_configuration->set_mode(
128             mi::neuraylib::INetwork_configuration::MODE_UDP) == 0);
129     } else if( strcmp( mode, "TCP_WITH_DISCOVERY") == 0) {
130         check_success( network_configuration->set_mode(
131             mi::neuraylib::INetwork_configuration::MODE_TCP_WITH_DISCOVERY) == 0);
132     } else {
133         check_success( false);
134     }
135 
136     // Set the multicast address
137     if( strcmp( argv[2], ".") != 0)
138         check_success( network_configuration->set_multicast_address( argv[2]) == 0);
139 
140     // Set the cluster interface
141     if( strcmp( argv[3], ".") != 0)
142         check_success( network_configuration->set_cluster_interface( argv[3]) == 0);
143 
144     // Set the discovery address
145     if( strcmp( argv[4], ".") != 0)
146         check_success( network_configuration->set_discovery_address( argv[4]) == 0);
147 
148     // Add configured hosts
149     for( int i = 5; i < argc; ++i)
150         check_success( network_configuration->add_configured_host( argv[i]) == 0);
151 
152     // Set a host property
153     mi::base::Handle<mi::neuraylib::IGeneral_configuration> general_configuration(
154         neuray->get_api_component<mi::neuraylib::IGeneral_configuration>());
155     general_configuration->set_host_property( "application_name", "example_networking");
156 
157     // Load the Iray Photoreal plugin. This is only relevant when the example is used in conjunction
158     // with the node manager example.
159     mi::base::Handle<mi::neuraylib::IPlugin_configuration> plugin_configuration(
160         neuray->get_api_component<mi::neuraylib::IPlugin_configuration>());
161 #ifndef MI_PLATFORM_WINDOWS
162     check_success( plugin_configuration->load_plugin_library( "libiray.so") == 0);
163 #else
164     check_success( plugin_configuration->load_plugin_library( "libiray.dll") == 0);
165 #endif
166 }
167 
168 void print_configuration( mi::base::Handle<mi::neuraylib::INeuray> neuray)
169 {
170     mi::base::Handle<mi::neuraylib::INetwork_configuration> network_configuration(
171         neuray->get_api_component<mi::neuraylib::INetwork_configuration>());
172 
173     // Print networking mode
174     mi::neuraylib::INetwork_configuration::Mode mode = network_configuration->get_mode();
175     switch( mode) {
176         case mi::neuraylib::INetwork_configuration::MODE_OFF:
177             fprintf( stderr, "mode: OFF\n");
178             break;
179         case mi::neuraylib::INetwork_configuration::MODE_TCP:
180             fprintf( stderr, "mode: TCP\n");
181             break;
182         case mi::neuraylib::INetwork_configuration::MODE_UDP:
183             fprintf( stderr, "mode: UDP\n");
184             break;
185         case mi::neuraylib::INetwork_configuration::MODE_TCP_WITH_DISCOVERY:
186             fprintf( stderr, "mode: TCP_WITH_DISCOVERY\n");
187             break;
188         default:
189             fprintf( stderr, "mode: error\n");
190             break;
191     }
192 
193     // Print the multicast address
194     mi::base::Handle<const mi::IString> string( network_configuration->get_multicast_address());
195     fprintf( stderr, "multicast address: %s\n", string->get_c_str());
196 
197     // Print the cluster interface
198     string = network_configuration->get_cluster_interface();
199     fprintf( stderr, "cluster interface: %s\n", string->get_c_str());
200 
201     // Print the discovery address
202     string = network_configuration->get_discovery_address();
203     fprintf( stderr, "discovery address: %s\n", string->get_c_str());
204 
205     // Print the configured hosts
206     fprintf( stderr, "configured hosts: ");
207     for( mi::Uint32 i = 0; i < network_configuration->get_number_of_configured_hosts(); ++i) {
208         string = network_configuration->get_configured_host( i);
209         fprintf( stderr, "%s ", string->get_c_str());
210     }
211     fprintf( stderr, "\n");
212 }
213 
214 int main( int argc, char* argv[])
215 {
216     // Collect command line parameters
217     if( argc < 5) {
218         std::cerr << "Usage: example_networking <mode> <multicast_address> \\" << std::endl;
219         std::cerr << "         <cluster_interface> <discovery_address> \\" << std::endl;
220         std::cerr << "         [<host1> <host2> ... <hostN>]" << std::endl;
221         keep_console_open();
222         return EXIT_FAILURE;
223     }
224 
225     // Access the neuray library
226     mi::base::Handle<mi::neuraylib::INeuray> neuray( load_and_get_ineuray());
227     check_success( neuray.is_valid_interface());
228 
229     // Create callback handler
230     mi::base::Handle<mi::neuraylib::IHost_callback> host_callback( new Host_callback());
231 
232     // Configure the neuray library
233     configuration( neuray, host_callback, argc, argv);
234 
235     // Print the configuration
236     print_configuration( neuray);
237 
238     // Start the neuray library
239     check_success( neuray->start() == 0);
240 
241     // Wait for other hosts to join/leave the cluster. The Host_callback objects prints
242     // messages of joining or leaving hosts.
243     sleep_seconds( 30);
244 
245     // Shut down the neuray library
246     check_success( neuray->shutdown() == 0);
247 
248     // Unregister the callback handler again
249     mi::base::Handle<mi::neuraylib::INetwork_configuration> network_configuration(
250         neuray->get_api_component<mi::neuraylib::INetwork_configuration>());
251     network_configuration->unregister_host_callback( host_callback.get());
252     network_configuration = 0;
253     neuray = 0;
254 
255     // Unload the neuray library
256     check_success( unload());
257 
258     keep_console_open();
259     return EXIT_SUCCESS;
260 }