From c21c3b0befeb46a51b6bf3758ffa30813bea0ff0 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 9 Mar 2024 14:19:22 +0100 Subject: Adding upstream version 1.44.3. Signed-off-by: Daniel Baumann --- ml/dlib/examples/bridge_ex.cpp | 365 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 365 insertions(+) create mode 100644 ml/dlib/examples/bridge_ex.cpp (limited to 'ml/dlib/examples/bridge_ex.cpp') diff --git a/ml/dlib/examples/bridge_ex.cpp b/ml/dlib/examples/bridge_ex.cpp new file mode 100644 index 000000000..bc772ccbb --- /dev/null +++ b/ml/dlib/examples/bridge_ex.cpp @@ -0,0 +1,365 @@ +// The contents of this file are in the public domain. See LICENSE_FOR_EXAMPLE_PROGRAMS.txt + + +/* + This is an example showing how to use the bridge object from from the + dlib C++ Library to send messages via TCP/IP. + + In particular, this example will walk you through four progressively + more complex use cases of the bridge object. Note that this example + program assumes you are already familiar with the pipe object and at + least the contents of the pipe_ex_2.cpp example program. +*/ + + +// =========== Example program output =========== +/* + ---- Running example 1 ---- + dequeued value: 1 + dequeued value: 2 + dequeued value: 3 + + ---- Running example 2 ---- + dequeued value: 1 + dequeued value: 2 + dequeued value: 3 + + ---- Running example 3 ---- + dequeued int: 1 + dequeued int: 2 + dequeued struct: 3 some string + + ---- Running example 4 ---- + bridge 1 status: is_connected: true + bridge 1 status: foreign_ip: 127.0.0.1 + bridge 1 status: foreign_port: 43156 + bridge 2 status: is_connected: true + bridge 2 status: foreign_ip: 127.0.0.1 + bridge 2 status: foreign_port: 12345 + dequeued int: 1 + dequeued int: 2 + dequeued struct: 3 some string + bridge 1 status: is_connected: false + bridge 1 status: foreign_ip: 127.0.0.1 + bridge 1 status: foreign_port: 12345 +*/ + + +#include +#include +#include + +using namespace dlib; +using namespace std; + +// ---------------------------------------------------------------------------------------- + +void run_example_1(); +void run_example_2(); +void run_example_3(); +void run_example_4(); + +// ---------------------------------------------------------------------------------------- + +int main() +{ + run_example_1(); + run_example_2(); + run_example_3(); + run_example_4(); +} + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + +void run_example_1( +) +{ + cout << "\n ---- Running example 1 ---- " << endl; + + /* + The idea of the bridge is basically to allow two different dlib::pipe objects + to be connected together via a TCP connection. This is best illustrated by + the following short example. In it we create two pipes, in and out. When + an object is enqueued into the out pipe it will be automatically sent + through a TCP connection and once received at the other end it will be + inserted into the in pipe. + */ + dlib::pipe in(4), out(4); + + + // This bridge will listen on port 12345 for an incoming TCP connection. Then + // it will read data from that connection and put it into the in pipe. + bridge b2(listen_on_port(12345), receive(in)); + + // This bridge will initiate a TCP connection and then start dequeuing + // objects from out and transmitting them over the connection. + bridge b1(connect_to_ip_and_port("127.0.0.1", 12345), transmit(out)); + + // As an aside, in a real program, each of these bridges and pipes would be in a + // separate application. But to make this example self contained they are both + // right here. + + + + // Now let's put some things into the out pipe + int value = 1; + out.enqueue(value); + + value = 2; + out.enqueue(value); + + value = 3; + out.enqueue(value); + + + // Now those 3 ints can be dequeued from the in pipe. They will show up + // in the same order they were inserted into the out pipe. + in.dequeue(value); + cout << "dequeued value: "<< value << endl; + in.dequeue(value); + cout << "dequeued value: "<< value << endl; + in.dequeue(value); + cout << "dequeued value: "<< value << endl; +} + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + +void run_example_2( +) +{ + cout << "\n ---- Running example 2 ---- " << endl; + + /* + This example makes a simple echo server on port 12345. When an object + is inserted into the out pipe it will be sent over a TCP connection, get + put into the echo pipe and then immediately read out of the echo pipe and + sent back over the TCP connection where it will finally be placed into the in + pipe. + */ + + dlib::pipe in(4), out(4), echo(4); + + // Just like TCP connections, a bridge can send data both directions. The directionality + // of a pipe is indicated by the receive() and transmit() type decorations. Also, the order + // they are listed doesn't matter. + bridge echo_bridge(listen_on_port(12345), receive(echo), transmit(echo)); + + // Note that you can also specify the ip and port as a string by using connect_to(). + bridge b1(connect_to("127.0.0.1:12345"), transmit(out), receive(in)); + + + int value = 1; + out.enqueue(value); + + value = 2; + out.enqueue(value); + + value = 3; + out.enqueue(value); + + + in.dequeue(value); + cout << "dequeued value: "<< value << endl; + in.dequeue(value); + cout << "dequeued value: "<< value << endl; + in.dequeue(value); + cout << "dequeued value: "<< value << endl; +} + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + +struct my_example_object +{ + /* + All objects passing through a dlib::bridge must be serializable. This + means there must exist global functions called serialize() and deserialize() + which can convert an object into a bit stream and then reverse the process. + + This example object illustrates how this is done. + */ + + int value; + std::string str; +}; + +void serialize (const my_example_object& item, std::ostream& out) +{ + /* + serialize() just needs to write the state of item to the output stream. + You can do this however you like. Below, I'm using the serialize functions + for int and std::string which come with dlib. But again, you can do whatever + you want here. + */ + dlib::serialize(item.value, out); + dlib::serialize(item.str, out); +} + +void deserialize (my_example_object& item, std::istream& in) +{ + /* + deserialize() is just the inverse of serialize(). Again, you can do + whatever you want here so long as it correctly reconstructs item. This + also means that deserialize() must always consume as many bytes as serialize() + generates. + */ + dlib::deserialize(item.value, in); + dlib::deserialize(item.str, in); +} + +// ---------------------------------------------------------------------------------------- + +void run_example_3( +) +{ + cout << "\n ---- Running example 3 ---- " << endl; + + /* + In this example we will just send ints and my_example_object objects + over a TCP connection. Since we are sending more than one type of + object through a pipe we will need to use the type_safe_union. + */ + + typedef type_safe_union tsu_type; + + dlib::pipe in(4), out(4); + + // Note that we don't have to start the listening bridge first. If b2 + // fails to make a connection it will just keep trying until successful. + bridge b2(connect_to("127.0.0.1:12345"), receive(in)); + // We don't have to configure a bridge in it's constructor. If it's + // more convenient we can do so by calling reconfigure() instead. + bridge b1; + b1.reconfigure(listen_on_port(12345), transmit(out)); + + tsu_type msg; + + msg = 1; + out.enqueue(msg); + + msg = 2; + out.enqueue(msg); + + msg.get().value = 3; + msg.get().str = "some string"; + out.enqueue(msg); + + + // dequeue the three objects we sent and print them on the screen. + for (int i = 0; i < 3; ++i) + { + in.dequeue(msg); + if (msg.contains()) + { + cout << "dequeued int: "<< msg.get() << endl; + } + else if (msg.contains()) + { + cout << "dequeued struct: "<< msg.get().value << " " + << msg.get().str << endl; + } + } +} + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + +void run_example_4( +) +{ + cout << "\n ---- Running example 4 ---- " << endl; + + /* + This final example is the same as example 3 except we will also now be getting + status messages from the bridges. These bridge_status messages tell us the + state of the TCP connection associated with a bridge. Is it connected or not? + Who it is connected to? + + The way you get these status messages is by ensuring that your receive pipe is + capable of storing bridge_status objects. If it is then the bridge will + automatically insert bridge_status messages into your receive pipe whenever + there is a status change. + + There are only two kinds of status changes. The establishment of a connection + or the closing of a connection. Also, a connection which closes due to you + calling clear(), reconfigure(), or destructing a bridge does not generate a + status message since, in this case, you already know about it and just want + the bridge to destroy itself as quickly as possible. + */ + + + typedef type_safe_union tsu_type; + + dlib::pipe in(4), out(4); + dlib::pipe b1_status(4); + + // setup both bridges to have receive pipes capable of holding bridge_status messages. + bridge b1(listen_on_port(12345), transmit(out), receive(b1_status)); + // Note that we can also use a hostname with connect_to() instead of supplying an IP address. + bridge b2(connect_to("localhost:12345"), receive(in)); + + tsu_type msg; + bridge_status bs; + + // Once a connection is established it will generate a status message from each bridge. + // Let's get those and print them. + b1_status.dequeue(bs); + cout << "bridge 1 status: is_connected: " << boolalpha << bs.is_connected << endl; + cout << "bridge 1 status: foreign_ip: " << bs.foreign_ip << endl; + cout << "bridge 1 status: foreign_port: " << bs.foreign_port << endl; + + in.dequeue(msg); + bs = msg.get(); + cout << "bridge 2 status: is_connected: " << bs.is_connected << endl; + cout << "bridge 2 status: foreign_ip: " << bs.foreign_ip << endl; + cout << "bridge 2 status: foreign_port: " << bs.foreign_port << endl; + + + + msg = 1; + out.enqueue(msg); + + msg = 2; + out.enqueue(msg); + + msg.get().value = 3; + msg.get().str = "some string"; + out.enqueue(msg); + + + // Read the 3 things we sent over the connection. + for (int i = 0; i < 3; ++i) + { + in.dequeue(msg); + if (msg.contains()) + { + cout << "dequeued int: "<< msg.get() << endl; + } + else if (msg.contains()) + { + cout << "dequeued struct: "<< msg.get().value << " " + << msg.get().str << endl; + } + } + + // cause bridge 1 to shutdown completely. This will close the connection and + // therefore bridge 2 will generate a status message indicating the connection + // just closed. + b1.clear(); + in.dequeue(msg); + bs = msg.get(); + cout << "bridge 1 status: is_connected: " << bs.is_connected << endl; + cout << "bridge 1 status: foreign_ip: " << bs.foreign_ip << endl; + cout << "bridge 1 status: foreign_port: " << bs.foreign_port << endl; +} + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + -- cgit v1.2.3