|
|
/****************************************************************************/ // wtdapi.c
//
// Transport driver - Windows specific API
//
// Copyright(C) 1997-1999 Microsoft Corporation
/****************************************************************************/
#include <adcg.h>
extern "C" { #define TRC_FILE "wtdapi"
#define TRC_GROUP TRC_GROUP_NETWORK
#include <atrcapi.h>
#include <adcgfsm.h>
}
#include "autil.h"
#include "td.h"
#include "nl.h"
/****************************************************************************/ /* Name: TD_Recv */ /* */ /* Purpose: Called to receive X bytes from WinSock and store them in the */ /* buffer pointed to by pData. */ /* */ /* Returns: The number of bytes received. */ /* */ /* Params: IN pData - pointer to buffer to receive the data. */ /* IN size - number of bytes to receive. */ /****************************************************************************/ DCUINT DCAPI CTD::TD_Recv(PDCUINT8 pData, DCUINT size) { unsigned bytesToRecv; unsigned bytesCopied; unsigned BytesRecv; unsigned BytesToCopy;
DC_BEGIN_FN("TD_Recv");
/************************************************************************/ /* Check that we're being asked to receive some data, that the pointer */ /* is not NULL, that the memory range to receive the data into is */ /* writable by us and that there is data available. */ /************************************************************************/ TRC_ASSERT((pData != NULL), (TB, _T("Data pointer is NULL"))); TRC_ASSERT((size != 0), (TB, _T("No data to receive")));
TRC_ASSERT((0 == IsBadWritePtr(pData, size)), (TB, _T("Don't have write access to memory %p (size %u)"), pData, size)); TRC_ASSERT((_TD.dataInTD), (TB, _T("TD_Recv called when dataInTD is FALSE"))); TRC_DBG((TB, _T("Request for %u bytes"), size));
/************************************************************************/ /* TD has a recv buffer into which it attempts to receive sufficient */ /* data to fill the buffer. */ /* Initially this buffer is empty. On a call to TD_Recv, the following */ /* sequence occurs. */ /* Data is copied from the recv buffer to the caller's buffer. If this */ /* satisfies the caller's request, no further action is needed. */ /* If this does not satisfy the caller, ie the recv buffer was empty or */ /* had less data then requested (so is now empty), another call is made */ /* to WinSock. */ /* The buffer used for this recv is the recv buffer is the caller */ /* requires fewer bytes than the recv buffer holds, the caller's buffer */ /* otherwise. */ /* Whenever the recv buffer is used, WinSock is asked for as many bytes */ /* as the buffer holds (rather than the number of bytes the caller */ /* wants). This means there may be some data left in the recv buffer */ /* ready for the next call to TD_Recv. */ /************************************************************************/
bytesToRecv = size;
/************************************************************************/ /* Copy as much data as possible from the recv buffer. */ /************************************************************************/ // If the recv buffer contains data then copy up to bytesToCopy to the
// caller's buffer, otherwise just quit.
if (_TD.recvBuffer.dataLength == 0) { // The recv buffer is empty, so zero bytes copied.
TRC_DBG((TB, _T("recv buffer is empty, need to go to WinSock"))); bytesCopied = 0; } else { // Copy the lesser of the number of bytes requested and the number of
// bytes in the buffer.
bytesCopied = DC_MIN(bytesToRecv, _TD.recvBuffer.dataLength); TRC_ASSERT(((bytesCopied + _TD.recvBuffer.dataStart) <= _TD.recvBuffer.size), (TB, _T("Want %u bytes from buffer, but start %u, size %u"), bytesCopied, _TD.recvBuffer.dataStart, _TD.recvBuffer.size));
memcpy(pData, &_TD.recvBuffer.pData[_TD.recvBuffer.dataStart], bytesCopied); TRC_DBG((TB, _T("Copied %u bytes from recv buffer"), bytesCopied));
// Update the recv buffer to take account of the data copied.
_TD.recvBuffer.dataLength -= bytesCopied; if (0 == _TD.recvBuffer.dataLength) // Used all the data from the recv buffer so reset the start pos.
_TD.recvBuffer.dataStart = 0; else // Still some data left in recv buffer.
_TD.recvBuffer.dataStart += bytesCopied;
TRC_DBG((TB, _T("recv buffer now has %u bytes starting at %u"), _TD.recvBuffer.dataLength, _TD.recvBuffer.dataStart));
// Update the number of bytes still to receive.
bytesToRecv -= bytesCopied; if (0 == bytesToRecv) { TRC_DBG((TB, _T("Received all necessary data"))); DC_QUIT; } }
/************************************************************************/ /* Now try to get any data which may still be required by recv'ing from */ /* WinSock. Offset the address of the caller's buffer by the amount of */ /* data copied from the recv buffer. */ /************************************************************************/ TRC_ASSERT(((_TD.recvBuffer.dataStart == 0) && (_TD.recvBuffer.dataLength == 0)), (TB, _T("About to recv into buffer, but existing recv ") _T("length %u, start %u"), _TD.recvBuffer.dataStart, _TD.recvBuffer.dataLength));
// Select the buffer into which we recv the data. This is the recv
// buffer if all the data required fits into it, the caller's buffer
// otherwise.
if (bytesToRecv < _TD.recvBuffer.size) { // Caller requires less than the recv buffer size, so attempt to
// have Winsock fill the recv buffer and copy to the caller's
// buffer.
BytesRecv = TDDoWinsockRecv(_TD.recvBuffer.pData, _TD.recvBuffer.size); if (BytesRecv != 0) { // Successful WinSock recv. Copy data from the recv buffer to the
// caller's buffer (offset by bytesCopied, the end of the recv
// buffer copy).
BytesToCopy = DC_MIN(bytesToRecv, BytesRecv); memcpy(pData + bytesCopied, _TD.recvBuffer.pData, BytesToCopy); bytesCopied = BytesToCopy;
// If we copied less than we recv'ed then there is some data left
// in the recv buffer for next time.
if (BytesRecv > bytesCopied) { _TD.recvBuffer.dataLength = BytesRecv - bytesCopied; _TD.recvBuffer.dataStart = bytesCopied; TRC_DBG((TB, _T("recv buffer now has %u bytes starting %u"), _TD.recvBuffer.dataLength, _TD.recvBuffer.dataStart)); }
TRC_DBG((TB, _T("%u bytes read to recv buffer, %u copied to caller ") _T("still need %u"), BytesRecv, bytesCopied, bytesToRecv - bytesCopied)); } else { TRC_DBG((TB, _T("Didn't receive any data"))); bytesCopied = 0; }
TRC_DBG((TB, _T("%u bytes in recv buffer starting %u"), _TD.recvBuffer.dataLength, _TD.recvBuffer.dataStart)); } else { // Caller requires more than the recv buffer size, so attempt to
// have Winsock fill the caller's buffer.
bytesCopied = TDDoWinsockRecv(pData + bytesCopied, bytesToRecv); TRC_DBG((TB, _T("Read %u bytes to caller's buffer, still need %u"), bytesCopied, bytesToRecv - bytesCopied)); }
/************************************************************************/ /* Update the number of bytes to receive. */ /************************************************************************/ bytesToRecv -= bytesCopied;
DC_EXIT_POINT: /************************************************************************/ /* If we have received more than the maximum we allow without resetting */ /* the data available flag OR we didn't get all the bytes we requested */ /* then we can't allow TD to continue reporting data available. */ /************************************************************************/ if (bytesToRecv != 0 || _TD.recvByteCount >= TD_MAX_UNINTERRUPTED_RECV) { // We didn't get all the bytes that we wanted, or we have received
// more than TD_MAX_UNINTERRUPTED_RECV so need to get back to the
// message loop. So, update our global data available flag and
// reset the per-FD_READ byte count.
TRC_DBG((TB, _T("Only got %u bytes of %u requested, total %u"), size - bytesToRecv, size, _TD.recvByteCount));
_TD.dataInTD = FALSE; _TD.recvByteCount = 0; }
DC_END_FN(); return (size - bytesToRecv); } /* TD_Recv */
/****************************************************************************/ /* Name: TDDoWinsockRecv */ /* */ /* Purpose: Wrapper round the WinSock recv function which handles any */ /* errors returned. */ /* */ /* Returns: The number of bytes copied. */ /* */ /* Params: IN pData - pointer to buffer to receive the data. */ /* IN bytesToRecv - number of bytes to receive. */ /****************************************************************************/ unsigned DCINTERNAL CTD::TDDoWinsockRecv(BYTE FAR *pData, unsigned bytesToRecv) { unsigned bytesReceived; int WSAErr;
DC_BEGIN_FN("TDDoWinsockRecv");
// Check that we are requesting some bytes. This will work if we ask
// for zero bytes, but it implies there is a flaw in the logic.
TRC_ASSERT((bytesToRecv != 0), (TB, _T("Requesting recv of 0 bytes")));
// In the debug build we can constrain the amount of data received,
// to simulate low-bandwidth scenarios.
#ifdef DC_NLTEST
#pragma message("NL Test code compiled in")
bytesToRecv = 1;
#elif DC_DEBUG
// Calculate how many bytes we can receive and then decrement the count
// of bytes left to send in this period.
if (0 != _TD.hThroughputTimer) { bytesToRecv = (unsigned)DC_MIN(bytesToRecv, _TD.periodRecvBytesLeft); _TD.periodRecvBytesLeft -= bytesToRecv;
if (0 == bytesToRecv) { // We won't recv any data, but still need to make the call to
// ensure the FD_READ messages keep flowing.
TRC_ALT((TB, _T("constrained READ bytes"))); }
TRC_DBG((TB, _T("periodRecvBytesLeft:%u"), _TD.periodRecvBytesLeft)); } #endif
#ifdef OS_WINCE
// Check to see we already called WinSock recv() for this FD_READ.
if (_TD.enableWSRecv) { // set enableWSRecv to FALSE to indicate that we performed a recv()
// call for this FD_READ.
_TD.enableWSRecv = FALSE; #endif // OS_WINCE
//
// Try to get bytesToRecv bytes from WinSock.
//
bytesReceived = (unsigned)recv(_TD.hSocket, (char *)pData, (int)bytesToRecv, 0);
// Do any necessary error handling. We are OK if no error or if the
// error is WOULDBLOCK (or INPROGRESS on CE).
if (bytesReceived != SOCKET_ERROR) { // Successful WinSock recv.
TRC_DBG((TB, _T("Requested %d bytes, got %d"), bytesToRecv, bytesReceived));
// Update the performance counter.
PRF_ADD_COUNTER(PERF_BYTES_RECV, bytesReceived);
// Add this lot of data to the total amount received since last
// resetting the counter, ie since we last returned to the
// message loop.
_TD.recvByteCount += bytesReceived; } else { WSAErr = WSAGetLastError();
#ifndef OS_WINCE
if (WSAErr == WSAEWOULDBLOCK) { #else
if (WSAErr == WSAEWOULDBLOCK || WSAErr == WSAEINPROGRESS) { #endif
// On a blocking call, we simply set received length to zero and
// continue.
bytesReceived = 0; } else { // Zero bytes received on error.
bytesReceived = 0;
// Call the FSM to begin disconnect processing.
TRC_ERR((TB, _T("Error on call to recv, rc:%d"), WSAErr)); TDConnectFSMProc(TD_EVT_ERROR, NL_MAKE_DISCONNECT_ERR(NL_ERR_TDONCALLTORECV));
TRC_ALT((TB, _T("WinSock recv error"))); } }
#ifdef OS_WINCE
} else { // recv is called once for this FD_READ.
TRC_DBG((TB, _T("recv() already called."))); bytesReceived = 0; } #endif // OS_WINCE
DC_END_FN(); return bytesReceived; }
#ifdef DC_DEBUG
/****************************************************************************/ /* Name: TD_GetNetworkThroughput */ /* */ /* Purpose: Get the current network throughput setting. */ /* */ /* Returns: Current network throughput. */ /****************************************************************************/ DCUINT32 DCAPI CTD::TD_GetNetworkThroughput(DCVOID) { DCUINT32 retVal;
DC_BEGIN_FN("TD_GetNetworkThroughput");
/************************************************************************/ /* Calculate the actual throughput. This is the */ /************************************************************************/ retVal = _TD.currentThroughput * (1000 / TD_THROUGHPUTINTERVAL);
TRC_NRM((TB, _T("Returning network throughput of:%lu"), retVal));
DC_END_FN(); return(retVal); } /* TD_GetNetworkThroughput */
/****************************************************************************/ /* Name: TD_SetNetworkThroughput */ /* */ /* Purpose: Sets the network throughput. This is the number of bytes */ /* that can be passed into or out of the network layer per */ /* second. For example setting this to 3000 is roughly */ /* equivalent to a 24000bps modem connection. */ /* */ /* Params: throughput - the number of bytes to be allowed into and out */ /* of the network layer per second. */ /****************************************************************************/ DCVOID DCAPI CTD::TD_SetNetworkThroughput(DCUINT32 throughput) { DC_BEGIN_FN("TD_SetNetworkThroughput");
/************************************************************************/ /* Check to determine if the throughput throttling has been enabled */ /* or disabled. */ /************************************************************************/ if (0 == throughput) { /********************************************************************/ /* Throughput throttling has been disabled so kill the throughput */ /* timer. */ /********************************************************************/ TRC_ALT((TB, _T("Throughput throttling disabled")));
if (_TD.hThroughputTimer != 0) { TRC_NRM((TB, _T("Kill throttling timer"))); KillTimer(_TD.hWnd, TD_THROUGHPUTTIMERID); _TD.hThroughputTimer = 0; }
_TD.currentThroughput = 0; } else { /********************************************************************/ /* Throughput throttling has been enabled so update the throughput */ /* byte counts and set the timer. */ /********************************************************************/ _TD.currentThroughput = (throughput * TD_THROUGHPUTINTERVAL) / 1000; _TD.periodSendBytesLeft = _TD.currentThroughput; _TD.periodRecvBytesLeft = _TD.currentThroughput;
_TD.hThroughputTimer = SetTimer(_TD.hWnd, TD_THROUGHPUTTIMERID, TD_THROUGHPUTINTERVAL, NULL);
TRC_ALT((TB, _T("Throughput throttling enabled interval:%u"), throughput)); }
DC_END_FN(); } /* TD_SetNetworkThroughput */
#endif /* DC_DEBUG */
|