ClientEventReceive routine

The ClientEventReceive routine is an event handler that the underlying TDI transport calls in response to an incoming receive from a remote node with which the client has an established endpoint-to-endpoint connection. Usually, this is a normal TSDU unless the client has not registered a ClientEventReceiveExpedited handler.

Syntax

NTSTATUS ClientEventReceive(
  _In_  PVOID              TdiEventContext,
  _In_  CONNECTION_CONTEXT ConnectionContext,
  _In_  ULONG              ReceiveFlags,
  _In_  ULONG              BytesIndicated,
  _In_  ULONG              BytesAvailable,
  _Out_ ULONG              *BytesTaken,
  _In_  PVOID              Tsdu,
  _Out_ PIRP               *IoRequestPacket
);

Parameters

  • TdiEventContext [in]
    Pointer to the client-supplied context provided in the IRP that was set up with TdiBuildSetEventHandler when ClientEventReceive was registered with the underlying transport.

  • ConnectionContext [in]
    Pointer to the client's context area for this connection endpoint. The client previously supplied this value to its underlying transport when its ClientEventConnect handler accepted a connection offer from the remote-node peer and/or when it opened the connection endpoint with ZwCreateFile.

  • ReceiveFlags [in]
    Specifies the nature of the receive indication as a combination (ORed) of one or more of the following flags:

    • TDI_RECEIVE_NORMAL
      The buffer at Tsdu contains normal data received from the client's remote-node peer. This flag and TDI_RECEIVE_EXPEDITED are mutually exclusive.

    • TDI_RECEIVE_EXPEDITED
      The buffer at Tsdu contains expedited data received from the client's remote-node peer. This flag can be set only if the client did not register a ClientEventReceiveExpedited and/or ClientEventChainedReceiveExpedited handler.

    • TDI_RECEIVE_ENTIRE_MESSAGE
      The buffer at Tsdu contains a full TSDU. The client should return control as quickly as possible after copying the indicated data into an internal buffer if it accepts the TSDU.

      If this flag is clear, ClientEventReceive must check the BytesIndicated and BytesAvailable parameters to determine how much of the TSDU has been provided. Although legacy TDI transports continue to set the TDI_RECEIVE_PARTIAL flag, newer transports leave clear the TDI_RECEIVE_ENTIRE_MESSAGE flag to indicate partial TSDUs to their clients.

    • TDI_RECEIVE_COPY_LOOKAHEAD
      Unless TDI_RECEIVE_ENTIRE_MESSAGE is set, BytesIndicated is something less than BytesAvailable, and ClientEventReceive should copy the number of indicated bytes into an internal buffer, set the variable at BytesTaken, and return control. The client will subsequently submit one or more TDI_RECEIVE requests to get the remainder of the TSDU or ClientEventReceive will be called again to copy the remainder of the TSDU.

    • TDI_RECEIVE_FRAGMENT
      The buffer at Tsdu contains a fragmented TSDU.

    • TDI_RECEIVE_PEEK
      The transport has buffered some receive data internally, but not yet the full TSDU. The client can examine the buffer at Tsdu to decide whether to submit a TDI_RECEIVE request for the TSDU.

  • BytesIndicated [in]
    Specifies the number of bytes of message-mode or stream-mode data in the buffer at Tsdu. This parameter is always less than or equal to the value of BytesAvailable. A TDI transport provides at least 128 bytes of data in a receive indication to its client, unless the received message or stream segment is less than 128 bytes in length. If BytesIndicated is less than BytesAvailable, the transport has received data that it does not make available when it calls ClientEventReceive.

  • BytesAvailable [in]
    Specifies the total number of bytes in the received TSDU.

  • BytesTaken [out]
    Pointer to a caller-supplied variable in which ClientEventReceive returns the number of bytes of data it copies from the TSDU.

  • Tsdu [in]
    Pointer to a buffer containing the received TSDU data.

  • IoRequestPacket [out]
    Pointer to a variable in which ClientEventReceive returns a pointer to a caller-allocated IRP that was set up with TdiBuildReceive. If this variable is set to NULL, the client will not be making a receive request for the remainder of the TSDU.

Return value

ClientEventReceive can return one of the following:

Return code Description
STATUS_SUCCESS

Indicates either that the client copied all the data in the given TSDU or that the client set BytesTaken to BytesAvailable and returned control immediately (effectively "tossing" the indication).

STATUS_MORE_PROCESSING_REQUIRED

Indicates the client has supplied an IRP requesting the remainder of the TSDU, usually after copying BytesIndicated into an internal buffer.

STATUS_DATA_NOT_ACCEPTED

Indicates this client is not interested in the TSDU.

 

Remarks

ClientEventReceive accepts or rejects a TSDU that its underlying TDI driver has received on an established endpoint-to-endpoint connection. The TSDU consists of stream-mode or message-mode data that represents a segment of a data stream, an entire message, or a piece of a message. The transport has removed the transport-layer header from the TSDU before it calls ClientEventReceive.

ClientEventReceive copies normal data the TDI driver receives over the network. The transport does not call ClientEvent(Chained)Receive while the client has an outstanding normal receive request or has rejected previously indicated data for a particular incoming normal receive until that receive is done. However, a transport that supports expedited data can call ClientEvent(Chained)ReceiveExpedited in the process of indicating a normal TSDU if an expedited TSDU comes in from the remote-node peer.

A message-mode transport delimits messages using end-of-record marks. Such a transport can make a sequence of separate receive indications for each block of data terminated with an end-of-record mark within a particular received TSDU. Such a receive usually indicates that the remote-node client issued a sequence of sends to transmit the TSDU.

When ClientEventReceive is called, it can do one of the following:

  • Reject the indicated TSDU if it has no use for the data by returning STATUS_DATA_NOT_ACCEPTED

  • Copy all of the data into an internal buffer if TDI_RECEIVE_ENTIRE_MESSAGE is set in the ReceiveFlags and return STATUS_SUCCESS

  • If TDI_RECEIVE_ENTIRE_MESSAGE is clear (or TDI_RECEIVE_PARTIAL is set), copy some or all of the indicated data and return either of the following, depending on which operation it carries out:

    STATUS_MORE_PROCESSING_REQUIRED if the client is supplying a TDI_RECEIVE request at IoRequestPacket to obtain the remaining TSDU data

    STATUS_SUCCESS if the transport is expected to call ClientEventReceive again with the remaining TSDU data

When it has finished copying receive data, ClientEventReceive sets the variable at BytesTaken to the number of bytes of data accepted before it returns control.

If ClientEventReceive supplies a TDI_RECEIVE request at IoRequestPacket for the underlying driver, the transport retrieves the data that it has not yet delivered to the client, along with any new data that is available. Retrieval continues until the client-supplied buffer in the IRP is full or the transport encounters an end-of-record mark. The transport always completes the given IRP before it issues a subsequent call to ClientEvent(Chained)Receive handler for this client. In effect, a client processes each normal TSDU received to completion before the transport will indicate a subsequent normal receive to the client. This allows each client to synchronize receive data correctly.

If ClientEventReceive returns STATUS_DATA_NOT_ACCEPTED after the client has accepted some data from a TSDU, the TDI driver proceeds according to the amount of indicated data the client has accepted and the availability of additional data, as follows:

  • If the driver has passed all indicated data and no more data was available at the time of the indication, the driver calls ClientEventReceive again when it receives more data for the TSDU from the network.

  • If the driver has not passed all indicated data to the client, or if it has additional data available that it did not make available in the preceding call to ClientEventReceive, the transport assumes that its client has no internal buffer space for the data and makes no further indications until the client issues an explicit TDI_RECEIVE request.

    In these circumstances, a transport can optionally initiate protocol-flow-control action to prevent retransmissions of data from the remote-node transport until the local-node client makes the receive request.

A TDI transport is not required to correlate data reception from the network with TDI_EVENT_RECEIVE indications. If the driver provides internal buffering, it can acknowledge data at any time and make receive indications as necessary, for instance, when it has received a certain amount of data or when its internal buffers are nearly full. A buffering driver should retain any data that its client does not accept in an indication until that client notifies the transport that the client's receive request is satisfied. A transport that does not support internal data buffering can acknowledge received data after its client has accepted the data from ClientEventReceive or after the driver has transferred the received data into a client-supplied IRP.

A received TSDU can be any length up to a transport-specific limit, which the client can find by submitting a request set up with TdiBuildQueryInformation for the QType TDI_QUERY_CONNECTION_INFO and examining the value returned for ReceiveBufferSize if the transport buffers received data internally. However, ClientEventReceive does not necessarily receive an entire TSDU, or even the available portion of a TSDU, each time this handler is called. Each TDI transport can set its own limits on the minimum and maximum amounts of data it will indicate at each receive to its clients, as long as its minimum is at least 128 bytes. The client can find these limits by submitting another query-information request for the QType TDI_QUERY_PROVIDER_INFO and examining the values returned for MinimumLookaheadData and MaximumLookaheadData.

ClientEventReceive must be capable of carrying out its operations at IRQL = DISPATCH_LEVEL.

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).

 

Requirements

Target platform

Desktop

Header

Tdikrnl.h (include TdiKrnl.h)

IRQL

DISPATCH_LEVEL (see Remarks section)

See also

ClientEventChainedReceive

ClientEventReceiveExpedited

ClientEventReceiveDatagram

TdiBuildInternalDeviceControlIrp

TdiBuildQueryInformation

TdiBuildReceive

TdiBuildSetEventHandler

 

 

Send comments about this topic to Microsoft