/******************************************/ /* transport.c 0.4.0 (1999-Dec-09-Thu) */ /* Adam M. Costello */ /******************************************/ /* Example implementation of the interface of transport.h 0.3.x. */ /* This implementation is not reliable; it just illustrates the */ /* roles of the various functions. */ /* This is ANSI C code. */ #include #include "bytestream.h" #include "network.h" #include "timer.h" #include "transport.h" enum transport_extension_flags transport_extensions(void) { return 0; /* We implement no extensions. */ /* If we were implementing all the project extensions, */ /* we would return transport_flow_control_efficiency | */ /* transport_multiplexing | transport_congestion_control. */ } /* transport source state: */ struct transport_source { struct transport_connection connection; void *byte_source; bytestream_go_ahead_method *go_ahead; /* 0 if we're not connected. */ size_t buffer_max; }; void *transport_source_create(const struct network_address source_address) { static struct transport_source source_storage, *source = &source_storage; /* If we wanted to allow for multiple transport sources */ /* per simulation process, we would dynamically allocate */ /* the transport_source structure. See streamers.c for */ /* an example of this technique. */ source->go_ahead = 0; /* Indicate that we're not connected. */ return source; } void transport_source_create_connection( void *transport_source, struct transport_connection connection, void *byte_source, bytestream_go_ahead_method *go_ahead, size_t buffer_max ) { struct transport_source *source = transport_source; /* Obviously, if we wanted to support multiple connections, */ /* we'd have to do something more complex than this: */ source->connection = connection; source->byte_source = byte_source; source->go_ahead = go_ahead; source->buffer_max = buffer_max; } void transport_source_delete_connection( void *transport_source, void *byte_source ) { struct transport_source *source = transport_source; /* Make sure this is a connection we currently have open: */ if (source->byte_source != byte_source) return; /* Indicate that we're not connected: */ source->go_ahead = 0; /* In an actual reliable implementation, there would */ /* probably be other state that needs to be cleaned up. */ } size_t transport_source_receive_bytes( void *transport_source, /* sink */ void *byte_source, /* source */ const unsigned char *buffer, size_t length ) { struct transport_source *source = transport_source; struct network_packet packet; /* A correct implementation would have to save a copy of the */ /* data for possible retransmission, respecting buffer_max. */ /* It would have to provide for error detection, respect */ /* network_max_payload_length(), avoid sending too fast for the */ /* sink, and use the port numbers if multiple connections were */ /* being supported. This example just sends all the data as soon */ /* as it arrives, and never retransmits. A correct implementation */ /* might not be able to accept the entire buffer right now, it */ /* which case it must return the number of bytes it does accept, */ /* and call go_ahead() later when it can accept more. */ /* Make sure we're connected: */ if (!source->go_ahead) return length; /* Since this implementation is unreliable anyway, */ /* we returned length rather than 0 so we wouldn't */ /* be obligated to call go_ahead() later. */ packet.source = source->connection.source.address; packet.destination = source->connection.sink.address; packet.payload = buffer; packet.payload_length = length; network_send_packet(packet); return length; } void transport_source_receive_packet( void *transport_source, struct network_packet packet ) { /* In a correct implementation the transport */ /* source would need to handle ack packets, but */ /* in this example there is nothing to do here. */ } /* transport sink state: */ struct transport_sink { struct transport_connection connection; void *byte_sink; bytestream_transfer_method *transfer; /* 0 if we're not connected. */ size_t buffer_max; }; void *transport_sink_create(const struct network_address sink_address) { static struct transport_sink sink_storage, *sink = &sink_storage; /* If we wanted to allow for multiple transport sinks */ /* per simulation process, we would dynamically allocate */ /* the transport_sink structure. See streamers.c for an */ /* example of this technique. */ sink->transfer = 0; /* Indicate that we're not connected. */ return sink; } void transport_sink_create_connection( void *transport_sink, struct transport_connection connection, void *byte_sink, bytestream_transfer_method *transfer, size_t buffer_max ) { struct transport_sink *sink = transport_sink; /* Obviously, if we wanted to support multiple connections, */ /* we'd have to do something more complex than this: */ sink->connection = connection; sink->byte_sink = byte_sink; sink->transfer = transfer; sink->buffer_max = buffer_max; } void transport_sink_delete_connection( void *transport_sink, void *byte_sink ) { struct transport_sink *sink = transport_sink; /* Make sure this is a connection we currently have open: */ if (sink->byte_sink != byte_sink) return; /* Indicate that we're not connected: */ sink->transfer = 0; /* In an actual reliable implementation, there would */ /* probably be other state that needs to be cleaned up. */ } static void sink_transfer(double now, void *transport_sink, void *byte_sink) /* Transfer buffered data from the transport sink to the byte sink. */ { /* struct transport_sink *sink = transport_sink; */ /* In a correct implementation, the byte sink might not always be */ /* able to accept data, so the transport sink must buffer it. This */ /* function would attempt to transfer the buffered data to the byte */ /* sink. In this example, the transport sink does not buffer data, */ /* so there is nothing to do here. */ } void transport_sink_okay_to_deliver( void *transport_sink, /* source */ void *byte_sink /* sink */ ) { timer_set(sink_transfer, timer_now(), transport_sink, byte_sink); } void transport_sink_receive_packet( void *transport_sink, struct network_packet packet ) { struct transport_sink *sink = transport_sink; /* A correct implementation would need to detect errors, buffer */ /* data, assemble it in the right order, send acks, use the port */ /* numbers if multiple connections are supported, etc. This */ /* example just sends all the data to the byte sink as it arrives. */ /* Make sure we're connected: */ if (!sink->transfer) return; sink->transfer(sink->byte_sink, transport_sink, packet.payload, packet.payload_length); }