This page describes how the DGHMux fragments work.
First the local and remote applications connect using TCP. They they pass the TCP connection to DGHMux to handle. In this example, the local application will make a DGHMux connection to the remote application. This is started by the local machine sending a TYPE_CONNECT_TO_LISTENER fragment to the remote machine. The remote machine responds with a TYPE_LISTENER_ACCEPTS fragment. Every fragment is required to have the 10 bit Stream ID to identify the stream.
The local machine does not need to negociate with the remote machine to find a stream ID, because each side is given 512 stream IDs to use. The TCP "client" is given 0 through 511. The TCP "server" machine is given streams 512 through 1023.
Now each machine can send data in the TYPE_SEND_DATA fragments. Each side is required to have a buffer that can receive 5 full fragments to start with. Each fragment can have a 512 Byte payload, so the receive buffers on each side must be at least 2.5 KB. Lets say that the local machine sends 5 TYPE_SEND_DATA fragments to the remote machine. The local machine must stop until the remote machine sends a TYPE_ADD_1_BUFFER_CREDIT fragment or a TYPE_ADD_4_BUFFER_CREDITS fragment. 1 buffer credit is exactly 512 Bytes. Only after the remote machine signals that there is room in the receive buffer by using the buffer credit fragments, the local machine can send data.
At any time during the TYPE_SEND_DATA phase, either side can send the TYPE_CHANGE_PRIORITY fragment to change the priority of the stream. Priorities can range from 0 to 99 with 99 being the highest priority. There is no defined logic to how much more time is given to higher priority streams, except that the lowest priority stream must not be starved.
The TYPE_CHANGE_PRIORITY fragment does not always automatically change the priority of a stream. Only the "owner" of the stream can change the priority. The TCP client is the owner of streams 0 through 511. The TCP server is the owner of streams 512 through 1023. If the nonowner sends a TYPE_CHANGE_PRIORITY fragment, it is merely a request. The owner of the stream can ignore it, but normally does not. When the owner sends a TYPE_CHANGE_PRIORITY fragment, the other side must obey and change the priority.
At any time, either machine can send the TYPE_EOF fragment to close the connection. The other side responds with a TYPE_EOF_CONFIRM fragment to signal that the stream ID can be used again. No fragments must be sent after a TYPE_EOF fragment and no fragments must be sent after sending a TYPE_EOF_CONFIRM fragment.
If there is an error, either side can send the TYPE_ERROR fragment and the other side responds with a TYPE_ERROR_CONFIRM fragment. The connection is closed and no more fragments should be sent on this stream. The TYPE_EOF fragment should not be sent after an error.
There is a rare case when each side sends a TYPE_EOF fragment at the same time. Each side should read the incomming TYPE_EOF fragment as a TYPE_EOF_CONFIRM fragment. The same is true for a TYPE_ERROR fragment.
0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 10101010 10101010 10101010 10101010 | ||
Stream ID | Type | * | payload size | payload |
* = Has Payload Flag
All data is big endian, so the stream ID is "0101010101" in big endian, or "1010101010" in little endian, or 682 in decimal.
The type is "0101" in big endian, or "1010" or in 10 in decimal, which is a TYPE_CONNECT_TO_LISTENER fragment according to the table below.
Type | 4 bit number | Payload Required |
TYPE_SEND_DATA | 1 | Required - Payload is data to send |
TYPE_CHANGE_PRIORITY | 3 | Not Allowed |
TYPE_ADD_1_BUFFER_CREDIT | 4 | Can Have A Payload - Payload is data to send |
TYPE_ADD_4_BUFFER_CREDITS | 5 | Can Have A Payload - Payload is data to send |
TYPE_EOF | 6 | Not Allowed |
TYPE_EOF_CONFIRM | 7 | Not Allowed |
TYPE_ERROR | 8 | Can Have A Payload - Payload is the error message. Message is in UTF-8. |
TYPE_EOF_CONFIRM | 9 | Not Allowed |
TYPE_CONNECT_TO_LISTENER | 10 | Required - Payload is the listener name (only 7 bit data allowed) |
TYPE_LISTENER_ACCEPTS | 11 | Can Have A Payload - Payload is data to send |
The Has Payload Flag is 1 in this example, meaning that there is a payload.
The payload size is "011101000" in big endian, or "000101110" in little endian, or 46 in decimal. The payload is 46 Bytes. With the header, the total fragment size is 3 + 46 or 49 Bytes.
The Payload-less Fragment header is only 2 bytes.
0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | |
Stream ID | Type | * |
* = Has Payload Flag
The stream ID is the same as the stream ID above.
The type is "1101" in big endian, or "1011" in little endian, or 11 in decimal, which is the TYPE_LISTENER_ACCEPTS fragment.
The Has Fragment Flag must be 0, meaning that there is no payload.
The last bit must be zero. It has no meaning.
You may be wondering how a thread on the client machine connects with the right thread on the server machine. Depending on how much you have read on this website, you may have guessed how this is done.
The client thread connects to a listener on the server (or the server can connect to a listener on the client) and the listner has a name like "File Receiver", or "Directory Browser". When the TYPE_CONNECT_TO_LISTENER fragment is sent, it must have a payload. The payload is the name of the listener to connect to. On the server side, the server finds the listener for "File Receiver" (case sensitive). The client, nor the server application knows the stream ID. I cannot think of a situation where this is needed, but it would be better handled in the applications by sending a unique number at the beginning of the stream. Since stream IDs are reused, the stream ID may become stale.