You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1366 lines
59 KiB
1366 lines
59 KiB
/****************************************************************************/
|
|
// wtdint.cpp
|
|
//
|
|
// Transport driver - Windows specific internal functions.
|
|
//
|
|
// Copyright (C) 1997-1999 Microsoft Corp.
|
|
/****************************************************************************/
|
|
|
|
#include <adcg.h>
|
|
extern "C" {
|
|
#define TRC_FILE "wtdint"
|
|
#define TRC_GROUP TRC_GROUP_NETWORK
|
|
#include <atrcapi.h>
|
|
}
|
|
|
|
#include "autil.h"
|
|
#include "td.h"
|
|
#include "xt.h"
|
|
#include "nl.h"
|
|
#include "wui.h"
|
|
#include "objs.h"
|
|
|
|
/****************************************************************************/
|
|
/* Name: TDInit */
|
|
/* */
|
|
/* Purpose: Initializes _TD. This function allocates the send buffers, */
|
|
/* creates the TD window and then initializes WinSock. */
|
|
/* */
|
|
/* Operation: On error this function calls the UT fatal error handler. */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CTD::TDInit(DCVOID)
|
|
{
|
|
DCUINT i;
|
|
DCUINT pubSndBufSizes[TD_SNDBUF_PUBNUM] = TD_SNDBUF_PUBSIZES;
|
|
DCUINT priSndBufSizes[TD_SNDBUF_PRINUM] = TD_SNDBUF_PRISIZES;
|
|
WORD versionRequested;
|
|
WSADATA wsaData;
|
|
int intRC;
|
|
|
|
DC_BEGIN_FN("TDInit");
|
|
|
|
/************************************************************************/
|
|
/* Allocate a buffer into which data will be recieved from Winsock. */
|
|
/************************************************************************/
|
|
_TD.recvBuffer.pData = (PDCUINT8)UT_Malloc( _pUt, TD_RECV_BUFFER_SIZE);
|
|
if (NULL != _TD.recvBuffer.pData)
|
|
{
|
|
// Got the buffer memory. Record the buffer size. Note we need to
|
|
// record slightly less than the allocated size to account for
|
|
// the fact that the current MPPC decompression code looks ahead one
|
|
// byte, which means that a fault can occur if this lookahead goes
|
|
// over the page boundary of this buffer. Leaving a couple of bytes
|
|
// at the end of the buffer prevents this. This does not seriously
|
|
// affect decoding efficiency -- the server itself sends less than
|
|
// a full 8K buffer per send.
|
|
TRC_NRM((TB, _T("Allocated %u bytes for recv buffer"),
|
|
TD_RECV_BUFFER_SIZE));
|
|
_TD.recvBuffer.size = TD_RECV_BUFFER_SIZE - 2;
|
|
}
|
|
else
|
|
{
|
|
/********************************************************************/
|
|
/* Didn't get the memory. We can live without it, just keep the */
|
|
/* size set at zero. */
|
|
/********************************************************************/
|
|
TRC_ALT((TB, _T("Failed to alloc %u bytes for recv buffer"),
|
|
TD_RECV_BUFFER_SIZE));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Now loop through the public send buffer array and initialize the */
|
|
/* array members. */
|
|
/************************************************************************/
|
|
for (i = 0; i < TD_SNDBUF_PUBNUM; i++)
|
|
{
|
|
/********************************************************************/
|
|
/* Initialize the buffer information structure and allocate memory */
|
|
/* for the actual buffer. */
|
|
/********************************************************************/
|
|
TDInitBufInfo(&_TD.pubSndBufs[i]);
|
|
TDAllocBuf(&_TD.pubSndBufs[i], pubSndBufSizes[i]);
|
|
|
|
TRC_DBG((TB, _T("Initialised public buffer:%u size:%u"),
|
|
i,
|
|
pubSndBufSizes[i]));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Loop through the private send buffer array and initialize the array */
|
|
/* members. */
|
|
/************************************************************************/
|
|
for (i = 0; i < TD_SNDBUF_PRINUM; i++)
|
|
{
|
|
/********************************************************************/
|
|
/* Initialize the buffer. */
|
|
/********************************************************************/
|
|
TDInitBufInfo(&_TD.priSndBufs[i]);
|
|
TDAllocBuf(&_TD.priSndBufs[i], priSndBufSizes[i]);
|
|
|
|
TRC_DBG((TB, _T("Initialised private buffer:%u size:%u"),
|
|
i,
|
|
priSndBufSizes[i]));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Create the TD window. */
|
|
/************************************************************************/
|
|
TDCreateWindow();
|
|
|
|
#ifdef OS_WINCE
|
|
#if (_WIN32_WCE > 300)
|
|
if (NULL == (_TD.hevtAddrChange = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
|
{
|
|
TRC_ABORT((TB, _T("Failed to create addr change notify event:%d"), GetLastError()));
|
|
_pUi->UI_FatalError(DC_ERR_OUTOFMEMORY);
|
|
DC_QUIT;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
/************************************************************************/
|
|
/* We want to use version 1.1 of WinSock. */
|
|
/************************************************************************/
|
|
versionRequested = MAKEWORD(1, 1);
|
|
|
|
/************************************************************************/
|
|
/* Initialize WinSock. */
|
|
/************************************************************************/
|
|
intRC = WSAStartup(versionRequested, &wsaData);
|
|
|
|
if (intRC != 0)
|
|
{
|
|
/********************************************************************/
|
|
// Trace out the error code - note that we can't use WSAGetLastError
|
|
// at this point as WinSock has failed to initialize and so
|
|
// WSAGetLastError will just fail.
|
|
/********************************************************************/
|
|
TRC_ABORT((TB, _T("Failed to initialize WinSock rc:%d"), intRC));
|
|
_pUi->UI_FatalError(DC_ERR_WINSOCKINITFAILED);
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Now confirm that this WinSock supports version 1.1. Note that if */
|
|
/* the DLL supports versions greater than 1.1 in addition to 1.1 then */
|
|
/* it will still return 1.1 in the version information as that is the */
|
|
/* version requested. */
|
|
/************************************************************************/
|
|
if ((LOBYTE(wsaData.wVersion) != 1) ||
|
|
(HIBYTE(wsaData.wVersion) != 1))
|
|
{
|
|
/********************************************************************/
|
|
/* Oops - this WinSock doesn't support version 1.1. */
|
|
/********************************************************************/
|
|
WSACleanup();
|
|
|
|
TRC_ABORT((TB, _T("This WinSock doesn't support version 1.1")));
|
|
_pUi->UI_FatalError(DC_ERR_WINSOCKINITFAILED);
|
|
DC_QUIT;
|
|
}
|
|
|
|
TRC_NRM((TB, _T("WinSock init version %u:%u"),
|
|
HIBYTE(wsaData.wVersion),
|
|
LOBYTE(wsaData.wVersion)));
|
|
|
|
TRC_NRM((TB, _T("TD successfully initialized")));
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
} /* TDInit */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: TDTerm */
|
|
/* */
|
|
/* Purpose: Terminates _TD. It frees the send buffers, cleans up WinSock, */
|
|
/* destroys the TD window and then unregisters the TD window */
|
|
/* class. */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CTD::TDTerm(DCVOID)
|
|
{
|
|
DCUINT i;
|
|
int intRC;
|
|
|
|
DC_BEGIN_FN("TDTerm");
|
|
|
|
/************************************************************************/
|
|
/* Loop through the public and private send buffers and free the */
|
|
/* memory. */
|
|
/************************************************************************/
|
|
for (i = 0; i < TD_SNDBUF_PUBNUM; i++)
|
|
{
|
|
UT_Free( _pUt, _TD.pubSndBufs[i].pBuffer);
|
|
}
|
|
|
|
for (i = 0; i < TD_SNDBUF_PRINUM; i++)
|
|
{
|
|
UT_Free( _pUt, _TD.priSndBufs[i].pBuffer);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Cleanup WinSock. */
|
|
/************************************************************************/
|
|
intRC = WSACleanup();
|
|
|
|
if (SOCKET_ERROR == intRC)
|
|
{
|
|
TRC_ALT((TB, _T("Failed to cleanup WinSock:%d"), WSAGetLastError()));
|
|
}
|
|
|
|
#ifdef OS_WINCE
|
|
#if (_WIN32_WCE > 300)
|
|
TRC_ASSERT((_TD.hevtAddrChange), (TB, _T("hevtAddrChange is null")));
|
|
|
|
CloseHandle(_TD.hevtAddrChange);
|
|
_TD.hevtAddrChange = NULL;
|
|
#endif
|
|
#endif
|
|
|
|
/************************************************************************/
|
|
/* Destroy the window. */
|
|
/************************************************************************/
|
|
intRC = DestroyWindow(_TD.hWnd);
|
|
_TD.hWnd = NULL;
|
|
|
|
if (0 == intRC)
|
|
{
|
|
TRC_SYSTEM_ERROR("Destroy Window");
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Unregister the class. */
|
|
/************************************************************************/
|
|
UnregisterClass(TD_WNDCLASSNAME, _pUi->UI_GetInstanceHandle());
|
|
|
|
/************************************************************************/
|
|
/* Release the recv buffer (if allocated). */
|
|
/************************************************************************/
|
|
if (0 != _TD.recvBuffer.size)
|
|
{
|
|
TRC_ASSERT((!IsBadWritePtr(_TD.recvBuffer.pData, _TD.recvBuffer.size)),
|
|
(TB, _T("recv buffer %p size %u is invalid"),
|
|
_TD.recvBuffer.pData,
|
|
_TD.recvBuffer.size));
|
|
|
|
UT_Free( _pUt, _TD.recvBuffer.pData);
|
|
_TD.recvBuffer.pData = NULL;
|
|
}
|
|
|
|
TRC_NRM((TB, _T("TD successfully terminated")));
|
|
|
|
DC_END_FN();
|
|
} /* TDTerm */
|
|
|
|
|
|
LRESULT CALLBACK CTD::StaticTDWndProc(HWND hwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
CTD* pTD = (CTD*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
if(WM_CREATE == message)
|
|
{
|
|
//pull out the this pointer and stuff it in the window class
|
|
LPCREATESTRUCT lpcs = (LPCREATESTRUCT) lParam;
|
|
pTD = (CTD*)lpcs->lpCreateParams;
|
|
|
|
SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG_PTR)pTD);
|
|
}
|
|
|
|
//
|
|
// Delegate the message to the appropriate instance
|
|
//
|
|
|
|
return pTD->TDWndProc(hwnd, message, wParam, lParam);
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: TDWndProc */
|
|
/* */
|
|
/* Purpose: The TD window procedure. */
|
|
/* */
|
|
/* Params: See Windows documentation. */
|
|
/****************************************************************************/
|
|
LRESULT CALLBACK CTD::TDWndProc(HWND hWnd,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
LRESULT rc = 0;
|
|
WORD eventWSA;
|
|
WORD errorWSA;
|
|
u_long address;
|
|
|
|
DC_BEGIN_FN("TDWndProc");
|
|
|
|
// Trace the interesting parameters.
|
|
TRC_DBG((TB, _T("uMsg:%u wP:%u lP:%lu"), uMsg, wParam, lParam));
|
|
|
|
// Special-case FD_READ (most important) and FD_WRITE (happens often).
|
|
if (uMsg == TD_WSA_ASYNC) {
|
|
if (WSAGETSELECTEVENT(lParam) == FD_READ) {
|
|
TRC_DBG((TB, _T("FD_READ recvd")));
|
|
|
|
// Check for an error.
|
|
if (WSAGETSELECTERROR(lParam) == 0) {
|
|
// If we're no longer connected, we just ignore the data.
|
|
if (_TD.fsmState == TD_ST_CONNECTED) {
|
|
// There is now some data available so set the
|
|
// global variable.
|
|
_TD.dataInTD = TRUE;
|
|
|
|
#ifdef OS_WINCE
|
|
// Enable Winsock receive. We perform only one
|
|
// WinSock recv per FD_READ.
|
|
TD_EnableWSRecv();
|
|
#endif // OS_WINCE
|
|
|
|
// Tell XT.
|
|
_pXt->XT_OnTDDataAvailable();
|
|
}
|
|
else {
|
|
TRC_NRM((TB, _T("FD_READ when not connected")));
|
|
}
|
|
}
|
|
else {
|
|
TRC_ALT((TB, _T("WSA_ASYNC error:%hu"),
|
|
WSAGETSELECTERROR(lParam)));
|
|
}
|
|
|
|
DC_QUIT;
|
|
}
|
|
else if (WSAGETSELECTEVENT(lParam) == FD_WRITE) {
|
|
TRC_NRM((TB, _T("FD_WRITE received")));
|
|
|
|
// Check for an error.
|
|
if (WSAGETSELECTERROR(lParam) == 0) {
|
|
// Make sure we're still connected.
|
|
if (_TD.fsmState == TD_ST_CONNECTED) {
|
|
// We're on the receiver thread, notify sender
|
|
// thread to flush the send queue.
|
|
|
|
|
|
_pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, this,
|
|
CD_NOTIFICATION_FUNC(CTD,TDFlushSendQueue), 0);
|
|
|
|
// Call up to XT to inform the higher layers
|
|
// that the back pressure situation has been
|
|
// relieved.
|
|
_pXt->XT_OnTDBufferAvailable();
|
|
}
|
|
else {
|
|
TRC_ALT((TB, _T("FD_WRITE when not connected")));
|
|
}
|
|
}
|
|
else {
|
|
TRC_ALT((TB, _T("WSA_ASYNC error:%hu"),
|
|
WSAGETSELECTERROR(lParam)));
|
|
}
|
|
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
// Now switch on the message type for other messages.
|
|
switch (uMsg) {
|
|
case WM_TIMER:
|
|
/****************************************************************/
|
|
/* Check that the ID of the timer is as expected. */
|
|
/****************************************************************/
|
|
if (TD_TIMERID == wParam)
|
|
{
|
|
/************************************************************/
|
|
/* OK it is our connection time out timer, so call the */
|
|
/* state machine. */
|
|
/************************************************************/
|
|
TRC_NRM((TB, _T("WM_TIMER recvd")));
|
|
TDConnectFSMProc(TD_EVT_WMTIMER,
|
|
NL_MAKE_DISCONNECT_ERR(NL_ERR_TDTIMEOUT));
|
|
}
|
|
#ifdef DC_DEBUG
|
|
else if (TD_THROUGHPUTTIMERID == wParam)
|
|
{
|
|
/************************************************************/
|
|
/* This is the throughput throttling timer. Reset the */
|
|
/* byte counts. */
|
|
/************************************************************/
|
|
TRC_DBG((TB, _T("Throughput timer, reset byte counts to:%u"),
|
|
_TD.currentThroughput));
|
|
_TD.periodSendBytesLeft = _TD.currentThroughput;
|
|
_TD.periodRecvBytesLeft = _TD.currentThroughput;
|
|
|
|
/************************************************************/
|
|
/* If we're connected then generate FD_READ and FD_WRITE */
|
|
/* messages to get the network layer running along. */
|
|
/************************************************************/
|
|
if (TD_ST_CONNECTED == _TD.fsmState)
|
|
{
|
|
PostMessage(_TD.hWnd,
|
|
TD_WSA_ASYNC,
|
|
(WPARAM)0,
|
|
(LPARAM)MAKELONG(FD_READ, 0));
|
|
PostMessage(_TD.hWnd,
|
|
TD_WSA_ASYNC,
|
|
(WPARAM)0,
|
|
(LPARAM)MAKELONG(FD_WRITE, 0));
|
|
}
|
|
}
|
|
#endif /* DC_DEBUG */
|
|
else
|
|
{
|
|
TRC_ALT((TB, _T("Unexpected timer message id:%u"), wParam));
|
|
}
|
|
break;
|
|
|
|
|
|
case TD_WSA_ASYNC:
|
|
/****************************************************************/
|
|
/* We've received a WSAAsyncSelect() FD_x notification message. */
|
|
/* Parse the message to extract the FD_ value and error value */
|
|
/* (if there is one). */
|
|
/****************************************************************/
|
|
eventWSA = WSAGETSELECTEVENT(lParam);
|
|
errorWSA = WSAGETSELECTERROR(lParam);
|
|
|
|
TRC_DBG((TB, _T("WSA_ASYNC event:%#hx error:%hu"),
|
|
eventWSA,
|
|
errorWSA));
|
|
|
|
/****************************************************************/
|
|
/* Everything is OK so now switch on the event. */
|
|
/****************************************************************/
|
|
switch (eventWSA) {
|
|
|
|
case FD_CONNECT:
|
|
TRC_NRM((TB, _T("FD_CONNECT recvd")));
|
|
/********************************************************/
|
|
/* Under some circumstances, we can receive FD_CONNECT */
|
|
/* for a socket which we have lost interest in. */
|
|
/********************************************************/
|
|
if (wParam != _TD.hSocket)
|
|
{
|
|
TRC_ALT((TB, _T("FD_CONNECT for socket %d, using %d"),
|
|
wParam, _TD.hSocket));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/********************************************************/
|
|
/* Check for an error. */
|
|
/********************************************************/
|
|
if (0 != errorWSA)
|
|
{
|
|
TRC_ALT((TB, _T("WSA_ASYNC error:%hu"), errorWSA));
|
|
TDConnectFSMProc(TD_EVT_ERROR,
|
|
NL_MAKE_DISCONNECT_ERR(NL_ERR_TDSKTCONNECTFAILED));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/********************************************************/
|
|
/* Advance the state machine. */
|
|
/********************************************************/
|
|
TDConnectFSMProc(TD_EVT_OK, 0);
|
|
break;
|
|
|
|
|
|
case FD_CLOSE:
|
|
{
|
|
DCBOOL keepOnReceiving = TRUE;
|
|
int intRC;
|
|
|
|
TRC_NRM((TB, _T("FD_CLOSE recvd")));
|
|
|
|
/********************************************************/
|
|
/* Check for the remote system aborting the connection. */
|
|
/********************************************************/
|
|
if (0 != errorWSA)
|
|
{
|
|
/****************************************************/
|
|
/* The server sends a TCP RST instead of a FIN, */
|
|
/* even when a clean disconnection is made. */
|
|
/* However, this is handled by the UI (see */
|
|
/* UIGoDisconnected). */
|
|
/****************************************************/
|
|
TRC_ALT((TB, _T("Abortive server close:%hu"), errorWSA));
|
|
|
|
TDConnectFSMProc(TD_EVT_ERROR,
|
|
NL_MAKE_DISCONNECT_ERR(NL_ERR_TDFDCLOSE));
|
|
|
|
DC_QUIT;
|
|
}
|
|
|
|
/********************************************************/
|
|
/* If we get here then this a response to a graceful */
|
|
/* close (i.e. we made a call to shutdown(SD_SEND) */
|
|
/* earlier). */
|
|
/* */
|
|
/* All of the data should have already been read from */
|
|
/* the socket before WinSock posted the FD_CLOSE, but */
|
|
/* to be safe we loop on recv. */
|
|
/********************************************************/
|
|
while (keepOnReceiving)
|
|
{
|
|
intRC = recv(_TD.hSocket,
|
|
(char *)_TD.priSndBufs[0].pBuffer,
|
|
_TD.priSndBufs[0].size,
|
|
0);
|
|
|
|
if ((0 == intRC) || (SOCKET_ERROR == intRC))
|
|
{
|
|
keepOnReceiving = FALSE;
|
|
TRC_ALT((TB, _T("No more data in WS (rc:%d)"),
|
|
intRC));
|
|
}
|
|
else
|
|
{
|
|
TRC_ALT((TB, _T("Throwing away %d bytes from WS"),
|
|
intRC));
|
|
}
|
|
}
|
|
|
|
/********************************************************/
|
|
/* Finally call the FSM. */
|
|
/********************************************************/
|
|
TDConnectFSMProc(TD_EVT_OK, NL_DISCONNECT_LOCAL);
|
|
}
|
|
break;
|
|
|
|
|
|
default:
|
|
TRC_ALT((TB, _T("Unknown FD event %hu recvd"), eventWSA));
|
|
break;
|
|
}
|
|
break;
|
|
|
|
|
|
case TD_WSA_GETHOSTBYNAME:
|
|
/****************************************************************/
|
|
/* We've received the result of a WSAAsyncGetHostByName */
|
|
/* operation. Split the message apart and call the FSM. */
|
|
/****************************************************************/
|
|
errorWSA = WSAGETASYNCERROR(lParam);
|
|
|
|
if (0 != errorWSA)
|
|
{
|
|
TRC_ALT((TB, _T("GHBN failed:%hu"), errorWSA));
|
|
|
|
/************************************************************/
|
|
/* Call the state machine with the error event. */
|
|
/************************************************************/
|
|
TDConnectFSMProc(TD_EVT_ERROR,
|
|
NL_MAKE_DISCONNECT_ERR(NL_ERR_TDGHBNFAILED));
|
|
break;
|
|
}
|
|
|
|
/****************************************************************/
|
|
/* Now get the primary interface address. */
|
|
/****************************************************************/
|
|
address = *((u_long DCPTR)
|
|
(((struct hostent DCPTR)_TD.priSndBufs[0].pBuffer)->h_addr));
|
|
|
|
TRC_ASSERT((address != 0),
|
|
(TB, _T("GetHostByName returned success but 0 address")));
|
|
|
|
TRC_NRM((TB, _T("GHBN - address is:%#lx"), address));
|
|
|
|
TDConnectFSMProc(TD_EVT_OK, (DCUINT32)address);
|
|
break;
|
|
|
|
#if (defined(OS_WINCE) && (_WIN32_WCE > 300))
|
|
case TD_WSA_NETDOWN:
|
|
TRC_NRM((TB, _T("TD_WSA_NETDOWN recvd")));
|
|
TDConnectFSMProc(TD_EVT_ERROR, NL_MAKE_DISCONNECT_ERR(NL_ERR_TDONCALLTOSEND));
|
|
break;
|
|
#endif
|
|
default:
|
|
rc = DefWindowProc(hWnd, uMsg, wParam, lParam);
|
|
break;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return(rc);
|
|
} /* TDWndProc */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: TDCreateWindow */
|
|
/* */
|
|
/* Purpose: Creates the TD window. This function registers the TD */
|
|
/* window class and then creates a window of that class. On */
|
|
/* error it calls UI_FatalError. */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CTD::TDCreateWindow(DCVOID)
|
|
{
|
|
WNDCLASS wc;
|
|
WNDCLASS tmpWndClass;
|
|
ATOM intRC;
|
|
|
|
DC_BEGIN_FN("TDCreateWindow");
|
|
|
|
if(!GetClassInfo(_pUi->UI_GetInstanceHandle(),TD_WNDCLASSNAME, &tmpWndClass))
|
|
{
|
|
/************************************************************************/
|
|
/* Fill in the class structure. */
|
|
/************************************************************************/
|
|
wc.style = 0;
|
|
wc.lpfnWndProc = StaticTDWndProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = sizeof(void*); //for instance pointer
|
|
wc.hInstance = _pUi->UI_GetInstanceHandle();
|
|
wc.hIcon = NULL;
|
|
wc.hCursor = NULL;
|
|
wc.hbrBackground = NULL;
|
|
wc.lpszMenuName = NULL;
|
|
wc.lpszClassName = TD_WNDCLASSNAME;
|
|
|
|
/************************************************************************/
|
|
/* Register the class used by the TD window. */
|
|
/************************************************************************/
|
|
intRC = RegisterClass(&wc);
|
|
|
|
if (0 == intRC)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to register WinSock window class")));
|
|
_pUi->UI_FatalError(DC_ERR_CLASSREGISTERFAILED);
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Now create the window. */
|
|
/************************************************************************/
|
|
_TD.hWnd = CreateWindow(TD_WNDCLASSNAME, /* class name */
|
|
NULL, /* window title */
|
|
0, /* window style */
|
|
0, /* x-pos */
|
|
0, /* y-pos */
|
|
0, /* width */
|
|
0, /* height */
|
|
NULL, /* parent */
|
|
NULL, /* menu */
|
|
_pUi->UI_GetInstanceHandle(), /* instance */
|
|
this); /* ptr to creation data */
|
|
|
|
if (NULL == _TD.hWnd)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to create TD window")));
|
|
_pUi->UI_FatalError(DC_ERR_WINDOWCREATEFAILED);
|
|
DC_QUIT;
|
|
}
|
|
|
|
TRC_NRM((TB, _T("Created window:%p"), _TD.hWnd));
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
} /* TDCreateWindow */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: TDBeginDNSLookup */
|
|
/* */
|
|
/* Purpose: Starts the address resolution process. On error this */
|
|
/* function calls into the state machine with an error code. */
|
|
/* */
|
|
/* Params: IN pServerAddress - pointer to the server address name. */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CTD::TDBeginDNSLookup(PDCACHAR pServerAddress)
|
|
{
|
|
DC_BEGIN_FN("TDBeginDNSLookup");
|
|
|
|
/************************************************************************/
|
|
/* This is an asynchronous operation and will result in us getting a */
|
|
/* callback sometime later. We need to provide a buffer which can be */
|
|
/* filled in with DNS information, so we make use of the first private */
|
|
/* send buffer. */
|
|
/************************************************************************/
|
|
TRC_ASSERT((_TD.priSndBufs[0].size >= MAXGETHOSTSTRUCT),
|
|
(TB, _T("Private snd buf size (%u) too small for DNS lookup (need:%u)"),
|
|
_TD.priSndBufs[0].size,
|
|
MAXGETHOSTSTRUCT));
|
|
|
|
/************************************************************************/
|
|
/* Issue the call. */
|
|
/************************************************************************/
|
|
_TD.hGHBN = WSAAsyncGetHostByName(_TD.hWnd,
|
|
TD_WSA_GETHOSTBYNAME,
|
|
pServerAddress,
|
|
(char DCPTR) _TD.priSndBufs[0].pBuffer,
|
|
MAXGETHOSTSTRUCT);
|
|
if (0 == _TD.hGHBN)
|
|
{
|
|
/********************************************************************/
|
|
/* We failed to initiate the operation - so find out what went */
|
|
/* wrong. */
|
|
/********************************************************************/
|
|
TRC_ALT((TB, _T("Failed to initiate GetHostByName - GLE:%d"),
|
|
WSAGetLastError()));
|
|
|
|
/********************************************************************/
|
|
/* Call the state machine with an error. */
|
|
/********************************************************************/
|
|
TDConnectFSMProc(TD_EVT_ERROR,
|
|
NL_MAKE_DISCONNECT_ERR(NL_ERR_TDDNSLOOKUPFAILED));
|
|
DC_QUIT;
|
|
}
|
|
|
|
TRC_NRM((TB, _T("Initiated GetHostByName OK")));
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
} /* TDBeginDNSLookup */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: TDBeginSktConnectWithConnectedEndpoint */
|
|
/* */
|
|
/* Purpose: Establish connection with server already connect */
|
|
/* on some socket */
|
|
/* */
|
|
/* Params: */
|
|
/* */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CTD::TDBeginSktConnectWithConnectedEndpoint()
|
|
{
|
|
DCBOOL failure = FALSE;
|
|
#ifndef OS_WINCE
|
|
int lastError;
|
|
#endif
|
|
int intRC;
|
|
|
|
DC_BEGIN_FN("TDBeginSktConnectWithConnectedEndpoint");
|
|
|
|
/************************************************************************/
|
|
/* Socket already connect, setup FD_XXX event with our window */
|
|
/* we are assuming mstscax client already make necessary error checking */
|
|
/************************************************************************/
|
|
_TD.hSocket = _pUi->UI_GetTDSocket();
|
|
|
|
TRC_ASSERT(
|
|
(_TD.hSocket != INVALID_SOCKET),
|
|
(TB, _T("Connected socket not setup properly")) );
|
|
|
|
if( INVALID_SOCKET == _TD.hSocket )
|
|
{
|
|
failure = TRUE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Set the required options on this socket. We do the following: */
|
|
/* */
|
|
/* - disable the NAGLE algorithm. */
|
|
/* - enable the don't linger option. This means the closesocket call */
|
|
/* will return immediately while any data queued for transmission */
|
|
/* will be sent, if possible, before the underlying socket is */
|
|
/* closed. */
|
|
/* */
|
|
/* Note that further options are set when the connection is */
|
|
/* established. */
|
|
/************************************************************************/
|
|
TDSetSockOpt(IPPROTO_TCP, TCP_NODELAY, 1);
|
|
TDSetSockOpt(SOL_SOCKET, SO_DONTLINGER, 1);
|
|
|
|
/************************************************************************/
|
|
/* Now request async notifications for all events on this socket. */
|
|
/************************************************************************/
|
|
intRC = WSAAsyncSelect(_TD.hSocket,
|
|
_TD.hWnd,
|
|
TD_WSA_ASYNC,
|
|
FD_READ | FD_WRITE | FD_CLOSE);
|
|
|
|
if (SOCKET_ERROR == intRC)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to select async - GLE:%d"), WSAGetLastError()));
|
|
failure = TRUE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
if (failure)
|
|
{
|
|
TRC_ALT((TB, _T("Failed to begin socket connection process")));
|
|
|
|
/********************************************************************/
|
|
/* Call the FSM. */
|
|
/********************************************************************/
|
|
TDConnectFSMProc(TD_EVT_ERROR,
|
|
NL_MAKE_DISCONNECT_ERR(NL_ERR_TDSKTCONNECTFAILED));
|
|
}
|
|
else
|
|
{
|
|
// use existing code path to setup rest.
|
|
PostMessage(_TD.hWnd,
|
|
TD_WSA_ASYNC,
|
|
(WPARAM) _TD.hSocket,
|
|
(LPARAM)MAKELONG(FD_CONNECT, 0));
|
|
}
|
|
|
|
DC_END_FN();
|
|
} /* TDBeginSktConnectWithConnectedEndpoint */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: TDBeginSktConnect */
|
|
/* */
|
|
/* Purpose: Issues a connect at the WinSock socket level. */
|
|
/* */
|
|
/* Params: IN address - the address to call (this is a numeric value */
|
|
/* in network (big-endian) byte order). */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CTD::TDBeginSktConnect(u_long address)
|
|
{
|
|
DCBOOL failure = FALSE;
|
|
int intRC;
|
|
int lastError;
|
|
SOCKADDR_IN stDstAddr;
|
|
|
|
DC_BEGIN_FN("TDBeginSktConnect");
|
|
|
|
/************************************************************************/
|
|
/* First of all get a socket. */
|
|
/************************************************************************/
|
|
_TD.hSocket = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
if (INVALID_SOCKET == _TD.hSocket)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to get a socket - GLE:%d"), WSAGetLastError()));
|
|
DC_QUIT;
|
|
}
|
|
|
|
_pUi->UI_SetTDSocket(_TD.hSocket);
|
|
|
|
TRC_NRM((TB, _T("Acquired socket:%#x"), _TD.hSocket));
|
|
|
|
/************************************************************************/
|
|
/* Set the required options on this socket. We do the following: */
|
|
/* */
|
|
/* - disable the NAGLE algorithm. */
|
|
/* - enable the don't linger option. This means the closesocket call */
|
|
/* will return immediately while any data queued for transmission */
|
|
/* will be sent, if possible, before the underlying socket is */
|
|
/* closed. */
|
|
/* */
|
|
/* Note that further options are set when the connection is */
|
|
/* established. */
|
|
/************************************************************************/
|
|
TDSetSockOpt(IPPROTO_TCP, TCP_NODELAY, 1);
|
|
TDSetSockOpt(SOL_SOCKET, SO_DONTLINGER, 1);
|
|
|
|
/************************************************************************/
|
|
/* Now request async notifications for all events on this socket. */
|
|
/************************************************************************/
|
|
intRC = WSAAsyncSelect(_TD.hSocket,
|
|
_TD.hWnd,
|
|
TD_WSA_ASYNC,
|
|
FD_CONNECT | FD_READ | FD_WRITE | FD_CLOSE);
|
|
|
|
if (SOCKET_ERROR == intRC)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to select async - GLE:%d"), WSAGetLastError()));
|
|
failure = TRUE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Now kick off a timer - if the connect does not complete before we */
|
|
/* get the WM_TIMER message then we'll abort the connection attempt. */
|
|
/************************************************************************/
|
|
/* _TD.hTimer = TDSetTimer(TD_CONNECTTIMEOUT);
|
|
|
|
if (0 == _TD.hTimer)
|
|
{
|
|
failure = TRUE;
|
|
DC_QUIT;
|
|
}
|
|
*/
|
|
/************************************************************************/
|
|
/* Fill in the address of the remote system we want to connect to. */
|
|
/************************************************************************/
|
|
stDstAddr.sin_family = PF_INET;
|
|
stDstAddr.sin_port = htons(_pUi->UI_GetMCSPort());
|
|
stDstAddr.sin_addr.s_addr = (u_long) address;
|
|
|
|
#ifdef OS_WINCE
|
|
#if (_WIN32_WCE > 300)
|
|
TRC_ASSERT((_TD.hevtAddrChange), (TB, _T("hevtAddrChange is null")));
|
|
TRC_ASSERT((_TD.hAddrChangeThread == NULL), (TB, _T("hAddrChangeThread is not null")));
|
|
_TD.hAddrChangeThread = CreateThread(NULL, 0, TDAddrChangeProc, &_TD, 0, NULL);
|
|
if (_TD.hAddrChangeThread == NULL)
|
|
{
|
|
TRC_ERR((TB, _T("CreatThread failed - GLE:%d"), GetLastError()));
|
|
failure = TRUE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
#endif
|
|
#endif
|
|
|
|
/************************************************************************/
|
|
/* We're now in a state where we can try to connect to the remote */
|
|
/* system so issue the connect now. We expect this call to fail with */
|
|
/* an error code of WSAEWOULDBLOCK - any other error code is a genuine */
|
|
/* problem. */
|
|
/************************************************************************/
|
|
intRC = connect(_TD.hSocket,
|
|
(struct sockaddr DCPTR) &stDstAddr,
|
|
sizeof(stDstAddr));
|
|
|
|
if (SOCKET_ERROR == intRC)
|
|
{
|
|
/********************************************************************/
|
|
/* Get the last error. */
|
|
/********************************************************************/
|
|
lastError = WSAGetLastError();
|
|
|
|
/********************************************************************/
|
|
/* We expect the connect to return an error of WSAEWOULDBLOCK - */
|
|
/* anything else indicates a genuine error. */
|
|
/********************************************************************/
|
|
if (lastError != WSAEWOULDBLOCK)
|
|
{
|
|
TRC_ERR((TB, _T("Connect failed - GLE:%d"), lastError));
|
|
failure = TRUE;
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* We've done as much as we can at this point - all we can do now is */
|
|
/* wait for the socket connection to complete. */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, _T("Waiting for connect to complete...")));
|
|
|
|
DC_EXIT_POINT:
|
|
if (failure)
|
|
{
|
|
TRC_ALT((TB, _T("Failed to begin socket connection process")));
|
|
|
|
/********************************************************************/
|
|
/* Call the FSM. */
|
|
/********************************************************************/
|
|
TDConnectFSMProc(TD_EVT_ERROR,
|
|
NL_MAKE_DISCONNECT_ERR(NL_ERR_TDSKTCONNECTFAILED));
|
|
}
|
|
|
|
DC_END_FN();
|
|
} /* TDBeginSktConnect */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: TDSetSockOpt */
|
|
/* */
|
|
/* Purpose: Sets a given WinSock socket option. Note that this function */
|
|
/* does not return an error if it fails to set the option as */
|
|
/* TD can still continue successfully despite failing to set */
|
|
/* the options to the desired values. */
|
|
/* */
|
|
/* Params: IN level - the level at which the option is defined (see */
|
|
/* docs for setsockopt). */
|
|
/* IN optName - the socket option for which the value is to be */
|
|
/* set. */
|
|
/* IN value - the value to set the option to. */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CTD::TDSetSockOpt(DCINT level, DCINT optName, DCINT value)
|
|
{
|
|
int intRC;
|
|
DCINT size = sizeof(DCINT);
|
|
#ifdef DC_DEBUG
|
|
DCINT oldVal;
|
|
DCINT newVal;
|
|
#endif /* DC_DEBUG */
|
|
|
|
DC_BEGIN_FN("TDSetSockOpt");
|
|
|
|
#ifdef DC_DEBUG
|
|
/************************************************************************/
|
|
/* For the debug build trace out the current value of the option */
|
|
/* before setting it. */
|
|
/************************************************************************/
|
|
getsockopt(_TD.hSocket, level, optName, (char DCPTR) &oldVal, &size);
|
|
#endif /* DC_DEBUG */
|
|
|
|
/************************************************************************/
|
|
/* Now set the option. */
|
|
/************************************************************************/
|
|
intRC = setsockopt(_TD.hSocket, level, optName, (char DCPTR) &value, size);
|
|
|
|
if (SOCKET_ERROR == intRC)
|
|
{
|
|
TRC_ALT((TB, _T("Failed to set socket option:%d rc:%d (level:%d val:%d)"),
|
|
optName,
|
|
WSAGetLastError(),
|
|
level,
|
|
value));
|
|
DC_QUIT;
|
|
}
|
|
|
|
#ifdef DC_DEBUG
|
|
/************************************************************************/
|
|
/* Get the new value of the option. */
|
|
/************************************************************************/
|
|
getsockopt(_TD.hSocket, level, optName, (char DCPTR) &newVal, &size);
|
|
|
|
TRC_NRM((TB, _T("Mod socket option %d:%d from %d to %d"),
|
|
level,
|
|
optName,
|
|
oldVal,
|
|
newVal));
|
|
#endif /* DC_DEBUG */
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
} /* TDSetSockOpt */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: TDDisconnect */
|
|
/* */
|
|
/* Purpose: Disconnects the transport driver. */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CTD::TDDisconnect(DCVOID)
|
|
{
|
|
int intRC;
|
|
SOCKET socket;
|
|
|
|
DC_BEGIN_FN("TDDisconnect");
|
|
|
|
/************************************************************************/
|
|
/* Kill the timer. */
|
|
/************************************************************************/
|
|
TDKillTimer();
|
|
|
|
/************************************************************************/
|
|
/* Ensure the data-in-TD flag is cleared. */
|
|
/************************************************************************/
|
|
_TD.dataInTD = FALSE;
|
|
|
|
/************************************************************************/
|
|
/* Cancel the outstanding DNS lookup. We can't be sure that the async */
|
|
/* operation has completed already and the message is already sitting */
|
|
/* on our queue (or being processed by the receive thread). If that is */
|
|
/* the case then WSACancelAsyncRequest will fail, but it doesn't */
|
|
/* matter. */
|
|
/************************************************************************/
|
|
intRC = WSACancelAsyncRequest(_TD.hGHBN);
|
|
if (SOCKET_ERROR == intRC) {
|
|
TRC_NRM((TB, _T("Failed to cancel async DNS request")));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Decouple to the sender thread and clear the send queue. */
|
|
/************************************************************************/
|
|
_pCd->CD_DecoupleSyncNotification(CD_SND_COMPONENT, this,
|
|
CD_NOTIFICATION_FUNC(CTD,TDClearSendQueue), 0);
|
|
|
|
#ifdef OS_WINCE
|
|
#if (_WIN32_WCE > 300)
|
|
SetEvent(_TD.hevtAddrChange);
|
|
|
|
if (_TD.hAddrChangeThread)
|
|
{
|
|
WaitForSingleObject(_TD.hAddrChangeThread, INFINITE);
|
|
CloseHandle(_TD.hAddrChangeThread);
|
|
_TD.hAddrChangeThread = NULL;
|
|
}
|
|
ResetEvent(_TD.hevtAddrChange);
|
|
#endif
|
|
#endif
|
|
|
|
if (INVALID_SOCKET == _TD.hSocket)
|
|
{
|
|
TRC_NRM((TB, _T("_TD.hSocket is NULL so just quit")));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Now close the socket. */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, _T("Close the socket")));
|
|
|
|
socket = _TD.hSocket;
|
|
_TD.hSocket = INVALID_SOCKET;
|
|
intRC = closesocket(socket);
|
|
|
|
if (SOCKET_ERROR == intRC)
|
|
{
|
|
TRC_ALT((TB, _T("closesocket rc:%d"), WSAGetLastError()));
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
|
|
DC_END_FN();
|
|
} /* TDDisconnect */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: TDSetTimer */
|
|
/* */
|
|
/* Purpose: Sets the timer. */
|
|
/* */
|
|
/* Returns: TRUE on success and FALSE otherwise. */
|
|
/* */
|
|
/* Params: IN timeInterval - the time interval of the timer. */
|
|
/****************************************************************************/
|
|
DCBOOL DCINTERNAL CTD::TDSetTimer(DCUINT timeInterval)
|
|
{
|
|
DCBOOL rc;
|
|
|
|
DC_BEGIN_FN("TDSetTimer");
|
|
|
|
/************************************************************************/
|
|
/* Set the timer with the passed time interval. */
|
|
/************************************************************************/
|
|
_TD.hTimer = SetTimer(_TD.hWnd, TD_TIMERID, timeInterval, NULL);
|
|
if (_TD.hTimer != 0) {
|
|
// Everything went OK, so set a successful return code.
|
|
rc = TRUE;
|
|
TRC_NRM((TB, _T("Set timer with interval:%u"), timeInterval));
|
|
}
|
|
else {
|
|
TRC_SYSTEM_ERROR("SetTimer");
|
|
rc = FALSE;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return rc;
|
|
} /* TDSetTimer */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: TDKillTimer */
|
|
/* */
|
|
/* Purpose: Cleans up the timer which is used to time-out connect */
|
|
/* and disconnect attempts. */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CTD::TDKillTimer(DCVOID)
|
|
{
|
|
BOOL rc;
|
|
|
|
DC_BEGIN_FN("TDKillTimer");
|
|
|
|
/************************************************************************/
|
|
/* Destroy the connection timeout timer. If we fail to get rid of this */
|
|
/* timer then there's not much we can do - we'll continue to get */
|
|
/* WM_TIMER messages which we'll ignore in retail and assert on in the */
|
|
/* debug version. */
|
|
/************************************************************************/
|
|
if (_TD.hTimer != 0)
|
|
{
|
|
rc = KillTimer(_TD.hWnd, TD_TIMERID);
|
|
_TD.hTimer = 0;
|
|
|
|
TRC_NRM((TB, _T("Timer has %s been killed"), rc ? _T("") : _T(" NOT")));
|
|
}
|
|
else
|
|
{
|
|
TRC_NRM((TB, _T("Timer has not been set")));
|
|
}
|
|
|
|
DC_END_FN();
|
|
} /* TDKillTimer */
|
|
|
|
|
|
/****************************************************************************/
|
|
// TDFlushSendQueue
|
|
//
|
|
// Tries to send all the packets which are waiting in the send queue.
|
|
// Must be called on the sender thread, Can be called through direct calls
|
|
// or through a CD_Decouple call.
|
|
/****************************************************************************/
|
|
void DCINTERNAL CTD::TDFlushSendQueue(ULONG_PTR unused)
|
|
{
|
|
DCUINT bytesSent;
|
|
PTD_SNDBUF_INFO pOldBuf;
|
|
int bytesToSend;
|
|
DCBOOL sentABuffer = FALSE;
|
|
int WSAErr;
|
|
|
|
DC_BEGIN_FN("TDFlushSendQueue");
|
|
|
|
DC_IGNORE_PARAMETER(unused);
|
|
|
|
// Check for rare re-entrancy.
|
|
if (!_TD.inFlushSendQueue) {
|
|
_TD.inFlushSendQueue = TRUE;
|
|
|
|
// Check that there are some buffers waiting to be sent.
|
|
if (_TD.pFQBuf != NULL) {
|
|
|
|
// Run along the send queue and try to send the data.
|
|
// Check for buffers that are inUse because ClearSendQueue could
|
|
// potentially clear some buffers before this call comes in
|
|
//
|
|
while (NULL != _TD.pFQBuf &&
|
|
_TD.pFQBuf->inUse) {
|
|
// Check that the buffer is in use and that the count of
|
|
// bytes waiting to be sent is more than zero.
|
|
|
|
TRC_ASSERT((_TD.pFQBuf->inUse), (TB, _T("Buffer is not in use")));
|
|
TRC_ASSERT((_TD.pFQBuf->bytesLeftToSend > 0),
|
|
(TB, _T("No bytes waiting to be sent")));
|
|
|
|
// Trace out the send buffer information.
|
|
TD_TRACE_SENDINFO(TRC_LEVEL_DBG);
|
|
|
|
TRC_DBG((TB, _T("Sending buffer:%p (waiting:%u)"),
|
|
_TD.pFQBuf,
|
|
_TD.pFQBuf->bytesLeftToSend));
|
|
|
|
// Call WinSock to send the buffer. We expect the call to:
|
|
// - succeed and send all the bytes requested. In this case
|
|
// there is little for us to do.
|
|
// - send some of the bytes we asked to be sent. This
|
|
// indicates that WinSock is applying back-pressure to us,
|
|
// so we update our count of bytes sent for this buffer
|
|
// and then quit. We will get a FD_WRITE later and retry
|
|
// the send.
|
|
// - send none of the bytes that we asked to be sent and
|
|
// return SOCKET_ERROR instead. We then use
|
|
// WSAGetLastError to determine why the call failed. If
|
|
// the reason is WSAEWOULDBLOCK then WinSock has decided
|
|
// to fail the call due to back-pressure - this is fine
|
|
// by us, so we just quit. Once again we will get an
|
|
// FD_WRITE to tell us that back-pressure has been
|
|
// relieved. Any other reason code is a genuine error
|
|
// so we decouple a call into the state table with an
|
|
// error code.
|
|
#ifdef DC_DEBUG
|
|
// Calculate how many bytes we can send and then decrement
|
|
// the count of bytes left to send in this period.
|
|
if (0 == _TD.hThroughputTimer) {
|
|
bytesToSend = (int)_TD.pFQBuf->bytesLeftToSend;
|
|
}
|
|
else {
|
|
bytesToSend = (int) DC_MIN(_TD.pFQBuf->bytesLeftToSend,
|
|
_TD.periodSendBytesLeft);
|
|
TRC_DBG((TB, _T("periodSendBytesLeft:%u"),
|
|
_TD.periodSendBytesLeft));
|
|
if (0 == bytesToSend) {
|
|
TRC_ALT((TB, _T("Constrained SEND network throughput")));
|
|
}
|
|
|
|
_TD.periodSendBytesLeft -= bytesToSend;
|
|
}
|
|
#else
|
|
bytesToSend = (int)_TD.pFQBuf->bytesLeftToSend;
|
|
#endif
|
|
|
|
bytesSent = (DCUINT)send(_TD.hSocket,
|
|
(char *)_TD.pFQBuf->pDataLeftToSend, bytesToSend, 0);
|
|
if (SOCKET_ERROR != bytesSent) {
|
|
TRC_DBG((TB, _T("Sent %u bytes of %u waiting"), bytesSent,
|
|
_TD.pFQBuf->bytesLeftToSend));
|
|
|
|
// Update the performance counter.
|
|
PRF_ADD_COUNTER(PERF_BYTES_SENT, bytesSent);
|
|
|
|
// Update the count of bytesWaiting and shuffle the
|
|
// pointer to the data along as well.
|
|
_TD.pFQBuf->pDataLeftToSend += bytesSent;
|
|
_TD.pFQBuf->bytesLeftToSend -= bytesSent;
|
|
|
|
// Check to determine if we managed to send all the data.
|
|
if (_TD.pFQBuf->bytesLeftToSend == 0) {
|
|
// We managed to send all the data in this buffer -
|
|
// so it is no longer in use. Get a pointer to this
|
|
// buffer.
|
|
pOldBuf = _TD.pFQBuf;
|
|
|
|
// Now update the head of the send queue with the
|
|
// next buffer and reset the next field of the buffer
|
|
// we've just sent.
|
|
_TD.pFQBuf = pOldBuf->pNext;
|
|
|
|
// Finally update the fields in the old buffer.
|
|
pOldBuf->pNext = NULL;
|
|
pOldBuf->inUse = FALSE;
|
|
pOldBuf->pDataLeftToSend = NULL;
|
|
sentABuffer = TRUE;
|
|
|
|
// Update the performance counter.
|
|
PRF_INC_COUNTER(PERF_PKTS_FREED);
|
|
|
|
TRC_DBG((TB, _T("Sent buffer completely - move to next")));
|
|
}
|
|
else {
|
|
// We didn't manage to send all the data so trace and
|
|
// quit.
|
|
TRC_NRM((TB, _T("Didn't send all data in buffer - quit")));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
else {
|
|
WSAErr = WSAGetLastError();
|
|
|
|
if (WSAErr == WSAEWOULDBLOCK || WSAErr == WSAENOBUFS) {
|
|
// WSAEWOULDBLOCK means that the network system is out
|
|
// of buffer space so we should wait until we receive
|
|
// a FD_WRITE notification indicating that more buffer
|
|
// space is available.
|
|
//
|
|
// WSAENOBUFS means that no buffer space is available
|
|
// and indicates a shortage of resources on the
|
|
// system.
|
|
bytesSent = 0;
|
|
PRF_INC_COUNTER(PERF_WINSOCK_SEND_FAIL);
|
|
TRC_NRM((TB, _T("WinSock send returns WSAEWOULDBLOCK")));
|
|
|
|
// We haven't sent any data, time to get out.
|
|
DC_QUIT;
|
|
}
|
|
else {
|
|
bytesSent = 0;
|
|
|
|
// If this is not a WSAEWOULDBLOCK and it is not a
|
|
// WSAENOBUFS error then call the FSM to begin
|
|
// disconnect processing.
|
|
|
|
// Trace out the buffer structure.
|
|
TD_TRACE_SENDINFO(TRC_LEVEL_ALT);
|
|
|
|
// We failed to send any data and the socket returned
|
|
// an error. The connection has probably failed or
|
|
// ended.
|
|
TRC_ALT((TB, _T("Failed to send any data, rc:%d"),
|
|
WSAErr));
|
|
|
|
// Decouple across to the recv side event handler at
|
|
// this point. It will call the TD FSM.
|
|
|
|
_pCd->CD_DecoupleSimpleNotification(CD_RCV_COMPONENT, this,
|
|
CD_NOTIFICATION_FUNC(CTD,TDSendError), 0);
|
|
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
TRC_NRM((TB, _T("No buffers waiting to be sent")));
|
|
}
|
|
}
|
|
else {
|
|
TRC_ABORT((TB, _T("Re-entered TDFlushSendQueue")));
|
|
goto RealExit;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
_TD.inFlushSendQueue = FALSE;
|
|
|
|
// If we previously failed TD_GetPublicBuffer, and we just
|
|
// succeeded in sending a buffer, call the OnBufferAvailable
|
|
// callbacks now.
|
|
TRC_DBG((TB, _T("Sent a buffer? %d, GetBuffer failed? %d"),
|
|
sentABuffer, _TD.getBufferFailed));
|
|
if (sentABuffer && _TD.getBufferFailed) {
|
|
TRC_NRM((TB, _T("Signal buffer available")));
|
|
_pXt->XT_OnTDBufferAvailable();
|
|
_TD.getBufferFailed = FALSE;
|
|
}
|
|
|
|
RealExit:
|
|
DC_END_FN();
|
|
} /* TDFlushSendQueue */
|