5.6 Sending and Receiving Connection-Oriented Data
TDI clients on separate nodes of the network must establish an endpoint-to-endpoint connection between them to send and receive connection-oriented data. Before either client sends data to the other, each client on its own node must do the following:
1.Open a transport address, as already described in Section 5.1.
2.Open a connection endpoint, as already described in Section 5.2.
3.Associate its open connection with the open address by submitting a TDI_ASSOCIATE_ADDRESS IOCTL request to its underlying transport, as mentioned in Section 5.2 and described in Section 5.3.
4.Establish the endpoint-to-endpoint connection between them, one by making a connection offer and the other by accepting that offer, as described in Section 5.5.
In fact, the client that accepts a connection offer can receive data from its remote-node peer as soon as the local-node transport sends a connection-acceptance frame to the remote-node transport, which can occur before the accepting client's TDI_LISTEN IRP has been fully completed back to that client.
After an endpoint-to-endpoint connection has been made by the underlying transports, either client can send data to the other across the network.
Sending Data on an Endpoint-to-Endpoint Connection
Figure 5.7 show how a kernel-mode client sends data on an endpoint-to-endpoint connection through its underlying local-node transport to a remote-node peer.
Figure 5.7 Sending Data to a Remote-Node Peer
A local-node TDI client issues a send request to transmit data from its connection endpoint to the remote-node connection endpoint. To do this, the local-node client submits a TDI_SEND IOCTL request to its transport. This IRP, which the client sets up with TdiBuildSend, contains a pointer to a client-supplied buffer containing a stream-oriented or message-oriented TSDU. This buffer can be any size up to the maximum the TDI transport driver allows. If the transport supports expedited sends, the client can request that the TSDU be transmitted as expedited data, ahead of any preceding normal sends it has already submitted to its underlying transport. If the transport driver supports internal buffering, its client can issue a non-blocking send.
The client's call to IoCallDriver with the TDI_SEND IRP forwards the IRP to the underlying transport's TdiDispatchInternalDeviceControl routine. This routine checks the MinorFunction code in the transport's I/O stack location of the IRP and usually calls a send-specific internal driver function to process the IRP further. For send requests, the internal driver function usually queues the IRP if the client has already submitted other send requests that have not yet been transmitted over the network to the remote node. The transport always queues requests to send expedited data ahead of requests to send normal TSDUs to the client's remote-node peer. The transport either copies the client-supplied data into its internal buffers or sends the specified data on the network before it completes each client-submitted TDI_SEND IRP.
If the underlying transport has failed a non-blocking send request due to insufficient internal buffer space in the transport, the driver calls its client’s registered ClientEventSendPossible handler when the transport again has available buffer space for sends. Then, ClientEventSendPossible can resubmit the TDI_SEND request that the transport previously failed.
Receiving Data on an Endpoint-to-Endpoint Connection
Figure 5.8 shows how a kernel-mode client receives data on an endpoint-to-endpoint connection through its local-node transport from its remote-node peer.
Figure 5.8 Receiving Data from a Remote-Node Peer
A local-node client can receive a TSDU, either normal or expedited, on a connection by making a TDI_RECEIVE request to the underlying transport. This IRP, which the client sets up with TdiBuildReceive, contains a pointer to a client-supplied buffer into which the transport copies all or part of the TSDU data it received from the client's remote-node peer. This buffer can be any size up to the maximum the TDI transport driver allows.
The client's call to IoCallDriver with the TDI_RECEIVE IRP forwards the IRP to the underlying transport's TdiDispatchInternalDeviceControl routine. This routine checks the MinorFunction code in the transport's I/O stack location of the IRP and usually calls a receive-specific internal driver function to process the IRP further. The internal driver function transfers received data into the client-supplied buffer until it is full or until the received TSDU data is exhausted.
However, expedited data takes precedence over normal data during receive operations. If an expedited TSDU comes in from the remote node while the transport is processing a client-submitted receive request for normal data, the transport completes the IRP back to its client with whatever normal TSDU data has already been transferred into the client-supplied buffer. Then, the transport processes the expedited receive to completion, and the client must issue another TDI_RECEIVE request to obtain the remainder of the normal TSDU.
A client can also receive data from its remote-node peer as an event notification from the underlying TDI transport driver. For these notifications, the driver removes the transport layer header from the TSDU that it receives from the remote node and calls the client’s registered ClientEventReceive, ClientEventChainedReceive, ClientEventReceiveExpedited, or ClientEventChainedReceiveExpedited handler. The client’s event handler can then copy as much of the data as possible. If ClientEventReceive or ClientEventReceiveExpedited does not receive all the data, it can do one of the following:
·Return a not-accepted status immediately, effectively telling the transport that the received TSDU is not of interest to the client.
·Make another TDI_RECEIVE receive request to obtain the remainder of the TSDU data.
·Rely on subsequent driver receive-event notifications to obtain the remainder of the data.
The ClientEventChainedReceive and ClientEventChainedReceiveExpedited handlers are always given read-only access to a full TSDU by the underlying transport. Consequently, these routines have no need to issue a sequence of TDI_RECEIVE requests to the underlying transport or to process partial indications of received TSDUs.
For more information about registering ClientEventXxx handlers, see Section 5.1.