# Generic Source & Sink¶

This introductory example shows how to use the generic source and sink classes. No data is transmitted over the network, and the source and sink objects are parts of the same program for the sake of simplicity. This example can be a starting point if you want to integrate score into an application that already has a custom socket framework.

The complete example is shown below.

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 // Copyright (c) 2017 Steinwurf ApS // Distributed under the "STEINWURF EVALUATION LICENSE 1.0". // See accompanying file LICENSE.rst or // http://www.steinwurf.com/licensing #include #include #include #include #include #include #include #include #include /// @example pure_source_sink.cpp /// /// This example shows how to use the generic API of the pure source and /// sink classes. No data is transmitted over the network, and the source /// and sink are parts of the same program. int main() { // Create a pure source and sink object score::api::manual_source source(score::api::source_profile::stream); score::api::sink sink; // We will generate 10 random messages and pass them through the source // and sink uint32_t message_count = 10; std::vector> messages; for (uint32_t i = 0; i < message_count; i++) { // Generate a random message and store it for later verification std::vector message(1 + rand() % 1000); std::generate(message.begin(), message.end(), rand); messages.push_back(message); source.read_message(message.data(), message.size()); // If this is the last message, we also flush the source to indicate // that no more data will be added if (i == message_count - 1) source.flush(); // Check if the source has any outgoing packets while (source.has_data_packet()) { // Get the data packet from the source std::vector data_packet(source.data_packet_size()); source.write_data_packet(data_packet.data()); // This packet could be transmitted over the network, but in this // example we forward it directly to the sink std::error_code error; sink.read_data_packet( data_packet.data(), data_packet.size(), error); if (error) { std::cerr << "sink.read_message() error: " << error.message() << std::endl; return error.value(); } // After processing a data packet, the sink might generate // some snack packets while (sink.has_snack_packet()) { std::vector snack_packet( sink.snack_packet_size()); sink.write_snack_packet(snack_packet.data()); // The snack packet is forwarded directly to the source. // In a real application, this would be sent over the network. source.read_snack_packet( snack_packet.data(), snack_packet.size(), error); if (error) { std::cerr << "source.read_snack_packet() error: " << error.message() << std::endl; return error.value(); } } } } // After processing all packets generated by the source, the sink // should have all the original messages available uint32_t received_messages = 0; while (sink.has_message()) { // Retrieve the original messages in-order using get_data() std::error_code error; std::vector message(sink.message_size()); sink.write_message(message.data(), error); if (error) { std::cerr << "sink.get_data() error: " << error.message() << std::endl; return error.value(); } // Verify the decoded messages against the original data if (message == messages[received_messages]) { std::cout << "Message " << received_messages << ": data verified" << std::endl; } else { std::cout << "Message " << received_messages << ": data corrupted" << std::endl; } received_messages++; } if (received_messages == message_count) std::cout << "All messages received " << std::endl; return 0; } 

First we create a manual_source and a sink object. The manual source allows us to configure each protocol parameter manually, whereas the auto_sender class can automatically adjust various protocol parameters using automatic controllers. In this example, we use the manual source with the stream profile which is optimized for low delay.

After the initialization, we generate a small number of random messages and we push each message to the source using the read_message function. The source transforms these user messages into outgoing data packets and we retrieve these packets with the write_data_packet function. Of course, these packet buffers could be transmitted over the network using any socket interface, but in this example we immediately forward these to the sink using read_data_packet.

The sink might generate SNACK packets to provide feedback to the source. We get these packets from the sink using write_snack_packet, and then they are delivered to the source using read_snack_packet.

After processing all packets generated by the source, the sink should have all the original messages available. We retrieve these messages in order using the sink.write_message function and we also verify the decoded messages against the original data.

In a real application, this interaction between the source and sink objects can be realized with an asynchronous socket interface. The receiver socket should listen for data packets that should be passed to the sink and the same socket can be used to transmit SNACK packets back to the source. Therefore the sender socket should also continuously listen for these feedback packets, and the source might generate additional data packets reacting to the feedback.