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:
Open a transport address, as described in Opening a Transport Address.
Open a connection endpoint, as described in Opening a Connection Endpoint.
Associate its open connection with the open address by submitting a TDI_ASSOCIATE_ADDRESS IOCTL request to its underlying transport, as mentioned in Opening a Connection Endpoint and described in Packaging and Submitting IOCTL Requests.
Establish the endpoint-to-endpoint connection between them, one by making a connection offer and the other by accepting that offer, as described in Making an Endpoint-to-Endpoint Connection.
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.
Note The TDI feature is deprecated and will be removed in future versions of Microsoft Windows. Depending on how you use TDI, use either the Winsock Kernel (WSK) or Windows Filtering Platform (WFP). For more information about WFP and WSK, see Windows Filtering Platform and Winsock Kernel. For a Windows Core Networking blog entry about WSK and TDI, see Introduction to Winsock Kernel (WSK).
Sending Data on an Endpoint-to-Endpoint Connection
The following figure shows how a kernel-mode client sends data on an endpoint-to-endpoint connection through its underlying local-node transport 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_SENDIOCTL 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 that are currently pending in its underlying transport. If the transport driver supports internal buffering, its client can issue a nonblocking 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. Whether normal or expedited, the queuing transport always transmits client-requested sends over the network in FIFO order. 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 nonblocking send request due to insufficient internal buffer space in the transport, the driver calls its client's registered ClientEventSendPossiblehandler when the transport again has available buffer space for sends. Then, ClientEventSendPossible can resubmit the TDI_SEND request that the transport previously failed.
Note The TDI feature is deprecated and will be removed in future versions of Microsoft Windows. Depending on how you use TDI, use either the Winsock Kernel (WSK) or Windows Filtering Platform (WFP). For more information about WFP and WSK, see Windows Filtering Platform and Winsock Kernel. For a Windows Core Networking blog entry about WSK and TDI, see Introduction to Winsock Kernel (WSK).
Receiving Data on an Endpoint-to-Endpoint Connection
The following figure shows how a kernel-mode client receives data on an endpoint-to-endpoint connection through its local-node transport from its remote-node peer.
A local-node client can receive a TSDU, either normal or expedited, on a connection by making a TDI_RECEIVErequest 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 IoCallDriverwith 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 ClientEventChainedReceiveExpeditedhandler. 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. However, the client is responsible for calling TdiReturnChainedReceivespromptly to return the resources associated with such an indication to the NDIS miniport driver that originally allocated them.
For more information about registering ClientEventXxx handlers, see Opening a Transport Address.
Note The TDI feature is deprecated and will be removed in future versions of Microsoft Windows. Depending on how you use TDI, use either the Winsock Kernel (WSK) or Windows Filtering Platform (WFP). For more information about WFP and WSK, see Windows Filtering Platform and Winsock Kernel. For a Windows Core Networking blog entry about WSK and TDI, see Introduction to Winsock Kernel (WSK).