Leaked source code of windows server 2003
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.
 
 
 
 
 
 

3248 lines
87 KiB

/*==========================================================================
*
* Copyright (C) 2000 Microsoft Corporation. All Rights Reserved.
*
* File: fastsock.c
* Content: new socket management to speed up massive multiplayer games
* History:
*
* Date By Reason
* ========== == ======
* 01/23/2000 aarono created
* 05/08/2000 aarono B#34466 Fix ordering problem in DecRefConnExist
* 07/07/2000 aarono added WSAEHOSTUNREACH for disconnected links in GetPlayerConn
* 08/30/2000 aarono workaround PAST bug MB#43599
* fix MB#43586 Win2K stopping, not handling WSAEWOULDBLOCK on
* receive properly (was dropping link).
* 02/02/2001 aarono B#300219 STRESS: don't break on WSAENOBUFS from winsock
* 02/21/2001 a-aogus don't allow large receives from untrusted sources.
***************************************************************************/
#define INCL_WINSOCK_API_TYPEDEFS 1 // includes winsock 2 fn proto's, for getprocaddress
#include <winsock2.h>
#include "dpsp.h"
#if USE_RSIP
#include "rsip.h"
#elif USE_NATHELP
#include "nathelp.h"
#endif
#include "mmsystem.h"
LPFN_WSAWAITFORMULTIPLEEVENTS g_WSAWaitForMultipleEvents;
LPFN_WSASEND g_WSASend;
LPFN_WSASENDTO g_WSASendTo;
LPFN_WSACLOSEEVENT g_WSACloseEvent;
LPFN_WSACREATEEVENT g_WSACreateEvent;
LPFN_WSAENUMNETWORKEVENTS g_WSAEnumNetworkEvents;
LPFN_WSAEVENTSELECT g_WSAEventSelect;
LPFN_GETSOCKOPT g_getsockopt;
HRESULT FastPlayerEventSelect(LPGLOBALDATA pgd, PPLAYERCONN pConn, BOOL bSelect);
VOID FastAccept(LPGLOBALDATA pgd, LPWSANETWORKEVENTS pNetEvents);
HRESULT ProcessConnEvents(
LPGLOBALDATA pgd,
PPLAYERCONN pConn,
LPWSANETWORKEVENTS pSockEvents,
LPWSANETWORKEVENTS pSockInEvents
);
extern DWORD wsaoDecRef(LPSENDINFO pSendInfo);
PPLAYERCONN CleanPlayerConn(LPGLOBALDATA pgd, PPLAYERCONN pConn, BOOL bHard);
PPLAYERCONN FastCombine(LPGLOBALDATA pgd, PPLAYERCONN pConn, SOCKADDR *psockaddr);
VOID RemoveConnFromPendingList(LPGLOBALDATA pgd, PPLAYERCONN pConn);
/*=============================================================================
PlayerConnnection management:
-----------------------------
For datagram sends, there is one port on each machine used as a target,
and since no connection is required, only 1 socket is used for datagram
sends and receives. Since datagrams are allowed to be dropped, there is
no race between sending from one side to the other until player creation
since its ok to drop the receives that occure before player creation on
this machine occurs. Actually there is a race, but we don't care.
For reliable sends there are numerous race conditions that exist. First
the actual creation of the link between this client and the remote
machine is going to become a race. Since we are connecting to and from
the same ports to create the link, only one link will get set up, either
it will get set up due to a connect to us, or it will get set up by us
connecting to them.
They connect to us:
In the case where they connect to us first, there may be reliable data
arriving at our node that would get thrown out it we indicated it to
the dplay layer, because dplay has not yet created the player. To avoid
this problem we queue any incoming receives and wait until the player
has been created locally before indicating that data to the dplay layer.
We pend any incoming data on a PLAYERCONN structure that we put on the
PendingConnList on the global data. The PLAYERCONN structure can be
fully initialized except we won't know the playerid of the remote player,
as a result of which we cannot put the PLAYERCONN into the PlayerHash
hash table.
Note there is an additional problem of not knowing which player a connect
is from. This means we need to put the queueing in the dplay layer,
not in the dpwsock layer.
We connect to them:
When we go to establish the connection with the remote, we must do it
asynchronously so as not to block in case we are a server. During the
connection process, we first look on the PendingConnList and see if
the remote hasn't already connected to us. If it has, we pick up the
socket from the conn list and immediately indicate any pending data.
If we failed to find the connection in the Pending list, this doesn't
mean they won't still beat us but we are going to try and connect to
them first. We create our connect structure and put it in the hash
table and mark it pending. We also put it on the pending connect list
so any incoming connection could find it. We then issue an asynchronous
connect on the socket.
When the connect completes any pending sends are then sent by the
send thread. If the connect fails because the other side connected
to us first, then we wait for the thread accepting connections to find
the connection structure and send out the pending sends.
Since they may be connecting from an old client, there is no guarantee
that their inbound connection and our outbound connection will use the
same socket. Each player structure must therefore contain both an
inbound and outbound socket.
=============================================================================*/
#ifdef DEBUG
VOID DUMPCONN(PPLAYERCONN pConn, DWORD dwLevel)
{
DPF(8,"Conn %x dwRefCount %d sSocket %d sSocketIn %d dwFlags %x iEventHandle %d\n",
pConn, pConn->dwRefCount, pConn->sSocket, pConn->sSocketIn, pConn->dwFlags, pConn->iEventHandle);
if(dwLevel >= 1 ){
if(pConn->dwFlags & PLYR_NEW_CLIENT){
DEBUGPRINTADDR(8,"NEW CLIENT: Socket",&pConn->IOSock.sockaddr);
}
if(pConn->dwFlags & PLYR_OLD_CLIENT){
DEBUGPRINTADDR(8,"OLD CLIENT: Socket Out",&pConn->IOSock.sockaddr);
DEBUGPRINTADDR(8,"OLD CLIENT: Socket In",&pConn->IOnlySock.sockaddr);
}
}
if(dwLevel >= 2){
DPF(8,"Receive... pReceiveBuffer %x, cbReceiveBuffer %d, cbReceived %d, cbExpected %d\n",
pConn->pReceiveBuffer, pConn->cbReceiveBuffer, pConn->cbReceived, pConn->cbExpected);
}
}
#else
#define DUMPCONN(pConn,Level)
#endif
int myclosesocket(LPGLOBALDATA pgd, SOCKET socket)
{
DWORD lNonBlock=1;
int err;
if(socket==INVALID_SOCKET){
DPF(0,"Closing invalid socket... bad bad bad\n");
DEBUG_BREAK();
}
if(socket==pgd->sSystemStreamSocket){
DPF(0,"Closing listen socket... bad bad bad\n");
DEBUG_BREAK();
}
err = ioctlsocket(socket,FIONBIO,&lNonBlock);
if (SOCKET_ERROR == err)
{
err = WSAGetLastError();
DPF(0,"myclosesocket: could not set non-blocking mode on socket err = %d!",err);
}
return closesocket(socket);
}
// TRUE -> same port and IP addr.
BOOL _inline bSameAddr(SOCKADDR *psaddr, SOCKADDR *psaddr2)
{
SOCKADDR_IN *psaddr_in = (SOCKADDR_IN *)psaddr;
SOCKADDR_IN *psaddr_in2 = (SOCKADDR_IN *)psaddr2;
if( (psaddr_in->sin_port == psaddr_in2->sin_port) &&
!memcmp(&psaddr_in->sin_addr,&psaddr_in2->sin_addr, 4 ))
{
return TRUE;
} else {
return FALSE;
}
}
//
// HashPlayer() - hash a dpid to a 0->PLAYER_HASH_SIZE index.
//
UINT _inline HashPlayer(DPID dpid){
UINT Hash=0;
Hash = ((dpid & 0xFF000000)>>24) ^ ((dpid & 0xFF0000)>>16) ^ ((dpid & 0xFF00)>>8) ^ (dpid & 0xFF);
Hash = Hash % PLAYER_HASH_SIZE;
DPF(8,"Player Hash %d\n",Hash);
return Hash;
}
//
// HashSocket() - hash a socket id, including port
//
UINT _inline HashSocket(SOCKADDR *psockaddr){
unsigned char *pc = (char *)(&(*(SOCKADDR_IN *)(psockaddr)).sin_port);
UINT Hash=0;
Hash = *pc ^ *(pc+1) ^ *(pc+2) ^ *(pc+3) ^ *(pc+4) ^ *(pc+5);
Hash = Hash % SOCKET_HASH_SIZE;
DPF(8,"Socket Hash %d\n",Hash);
return Hash;
}
/*=============================================================================
FastSockInit - Initialize Fask socket processing
Description:
Parameters:
pgd - Service Provider's global data blob for this instance
Return Values:
-----------------------------------------------------------------------------*/
BOOL FastSockInit(LPGLOBALDATA pgd)
{
BOOL bReturn = TRUE;
INT i;
try {
InitializeCriticalSection(&pgd->csFast);
} except ( EXCEPTION_EXECUTE_HANDLER) {
// Catch STATUS_NOMEMORY
DPF(0,"FastSockInit: Couldn't allocate critical section, bailing\n");
bReturn=FALSE;
goto exit;
}
// Start at -1 so we get the Accept handle too.
for(i=-1; i<NUM_EVENT_HANDLES; i++){
pgd->EventHandles[i]=CreateEvent(NULL, FALSE, FALSE, NULL);
if(!pgd->EventHandles[i]){
DPF(0,"FastSockInit: Failed to allocate handles, bailing\n");
for(;i>-1;--i){
CloseHandle(pgd->EventHandles[i]);
}
bReturn = FALSE;
goto err_exit1;
}
}
// could initialize all the listenerlists to 0, but that would be pointless.
pgd->BackStop=INVALID_HANDLE_VALUE;
pgd->nEventSlotsAvail = NUM_EVENT_HANDLES * MAX_EVENTS_PER_HANDLE;
InitBilink(&pgd->InboundPendingList);
DPF(8,"FastSock Init: nEventSlots %d\n",pgd->nEventSlotsAvail);
pgd->bFastSock=TRUE;
exit:
return bReturn;
err_exit1:
DeleteCriticalSection(&pgd->csFast);
return bReturn;
}
/*=============================================================================
FastSockCleanConnList - Release connections
Description:
Parameters:
pgd - Service Provider's global data blob for this instance
Return Values:
-----------------------------------------------------------------------------*/
VOID FastSockCleanConnList(LPGLOBALDATA pgd)
{
PPLAYERCONN pConn,pNextConn;
BILINK *pBilink, *pBilinkWalker;
INT i;
DPF(8,"==>FastSockCleanConnList\n");
DPF(8,"Cleaning up Player ID hash Table\n");
EnterCriticalSection(&pgd->csFast);
for(i=0;i<PLAYER_HASH_SIZE; i++){
pConn=pgd->PlayerHash[i];
pgd->PlayerHash[i]=NULL;
while(pConn)
{
pNextConn=pConn->pNextP;
DPF(8,"Destroying Connection for Playerid %x\n",pConn->dwPlayerID);
DUMPCONN(pConn,3);
CleanPlayerConn(pgd, pConn, TRUE);
DecRefConnExist(pgd, pConn); // dump existence ref.
DecRefConn(pgd,pConn); // dump playerid table ref.
pConn=pNextConn;
}
}
// Clean up socket hash table entries
DPF(8,"Cleaning up Socket hash Table\n");
for(i=0;i<SOCKET_HASH_SIZE; i++)
{
pConn=pgd->SocketHash[i];
pgd->SocketHash[i]=NULL;
while(pConn)
{
pNextConn=pConn->pNextP;
DPF(8,"Destroying Connection for Playerid %x\n",pConn->dwPlayerID);
DUMPCONN(pConn,3);
CleanPlayerConn(pgd, pConn, TRUE);
DecRefConnExist(pgd, pConn); // dump existence ref.
DecRefConn(pgd,pConn); // dump socket table ref.
pConn=pNextConn;
}
}
// Clean up inbound list.
DPF(8,"Cleaning up Inbound Pending List\n");
pBilink=pgd->InboundPendingList.next;
while(pBilink != &pgd->InboundPendingList)
{
pBilinkWalker=pBilink->next;
pConn=CONTAINING_RECORD(pBilink, PLAYERCONN, InboundPendingList);
DPF(8,"Destroying Connection for Playerid %x\n",pConn->dwPlayerID);
DUMPCONN(pConn,3);
CleanPlayerConn(pgd, pConn, TRUE);
DecRefConnExist(pgd, pConn);
//DecRefConn(pgd,pConn); // dump inbound list ref --no, gets handled in CleanPlayerConn for this case
pBilink=pBilinkWalker;
}
InitBilink(&pgd->InboundPendingList);
LeaveCriticalSection(&pgd->csFast);
ASSERT(pgd->nEventSlotsAvail == NUM_EVENT_HANDLES * MAX_EVENTS_PER_HANDLE);
DPF(8,"<==FastSockCleanConnList\n");
}
/*=============================================================================
FastSockFini - Release resources for fast socket processing.
Description:
Parameters:
pgd - Service Provider's global data blob for this instance
Return Values:
-----------------------------------------------------------------------------*/
VOID FastSockFini(LPGLOBALDATA pgd)
{
BOOL bReturn = TRUE;
INT i;
DPF(8,"==>FastSockFini\n");
for(i=-1;i<NUM_EVENT_HANDLES;i++){
CloseHandle(pgd->EventHandles[i]);
}
// Clean up player hash table entries
FastSockCleanConnList(pgd);
DeleteCriticalSection(&pgd->csFast);
pgd->bFastSock=FALSE;
DPF(8,"<==FastSockFini\n");
}
/*=============================================================================
GetEventHandle - Allocate an event handle for the connection
Description:
Parameters:
pgd - Service Provider's global data blob for this instance
pConn - connection to add processing for on the event.
Return Values:
-----------------------------------------------------------------------------*/
BOOL GetEventHandle(LPGLOBALDATA pgd, PPLAYERCONN pConn)
{
UINT i;
int index;
int iEvent=-1; // index of event
int iConn; // index into connections on this event
int bFoundSlot = FALSE;
i=(pgd->iEventAlloc+(NUM_EVENT_HANDLES-1))%NUM_EVENT_HANDLES;
while(pgd->iEventAlloc != i){
if(pgd->EventList[pgd->iEventAlloc].nConn < MAX_EVENTS_PER_HANDLE){
// Found a winner.
iEvent = pgd->iEventAlloc;
iConn=pgd->EventList[iEvent].nConn++;
DPF(8,"GetEventHandle: For Conn %x, using Event index %d, Slot %d\n",pConn,iEvent,iConn);
pgd->EventList[iEvent].pConn[iConn]=pConn;
pConn->iEventHandle=iEvent;
bFoundSlot=TRUE;
pgd->nEventSlotsAvail--;
DPF(8,"GetEventHandle: EventSlots Left %d\n",pgd->nEventSlotsAvail);
if(!pgd->nEventSlotsAvail){
DPF(0,"Out of Event slots, no new connections will be accepted\n");
}
break;
}
pgd->iEventAlloc = (pgd->iEventAlloc+1)%NUM_EVENT_HANDLES;
}
// advance the index so we distribute the load across the handles.
pgd->iEventAlloc = (pgd->iEventAlloc+1)%NUM_EVENT_HANDLES;
DPF(8,"iEventAlloc %d\n",pgd->iEventAlloc);
ASSERT(iEvent != -1);
return bFoundSlot;
}
/*=============================================================================
FreeEventHandle - Remove the event handle for the connection
Description:
Parameters:
pgd - Service Provider's global data blob for this instance
pConn - connection to remove processing for on the event.
Return Values:
-----------------------------------------------------------------------------*/
VOID FreeEventHandle(LPGLOBALDATA pgd, PPLAYERCONN pConn)
{
int iEvent;
int iLastConn;
UINT iConn;
iEvent = pConn->iEventHandle;
if(iEvent == INVALID_EVENT_SLOT){
DPF(1,"WARN: tried to free invalid event\n");
return;
}
for(iConn=0;iConn<pgd->EventList[iEvent].nConn;iConn++){
if(pgd->EventList[iEvent].pConn[iConn]==pConn){
ASSERT(pgd->EventList[iEvent].nConn);
iLastConn = pgd->EventList[iEvent].nConn-1;
// copy the last entry over this entry (could be 0 over 0, but who cares?)
pgd->EventList[iEvent].pConn[iConn]=pgd->EventList[iEvent].pConn[iLastConn];
pgd->EventList[iEvent].nConn--;
ASSERT((INT)(pgd->EventList[iEvent].nConn) >= 0);
pgd->nEventSlotsAvail++;
pConn->iEventHandle = INVALID_EVENT_SLOT;
DPF(8,"FreeEventHandle index %d Slot %d nConn %d on slot Total Slots Left %d\n",iEvent,iConn,pgd->EventList[iEvent].nConn,pgd->nEventSlotsAvail);
return;
}
}
DPF(0,"UH OH, couldn't free event handle!\n");
DEBUG_BREAK();
}
/*=============================================================================
FindPlayerById - Find the connection structure for a player
Description:
Finds a player and returns the connection structure with a reference
Parameters:
pgd - Service Provider's global data blob for this instance
dpid - player id of the player we are trying to find connection for.
Return Values:
PPLAYERCONN - player connection structure
NULL - Didn't find the player connection.
-----------------------------------------------------------------------------*/
PPLAYERCONN FindPlayerById(LPGLOBALDATA pgd, DPID dpid)
{
PPLAYERCONN pConn;
EnterCriticalSection(&pgd->csFast);
pConn = pgd->PlayerHash[HashPlayer(dpid)];
while(pConn && pConn->dwPlayerID != dpid){
pConn = pConn->pNextP;
}
if(pConn){
DPF(8,"FindPlayerById, found %x\n",pConn);
DUMPCONN(pConn, 1);
AddRefConn(pConn);
}
LeaveCriticalSection(&pgd->csFast);
return pConn;
}
/*=============================================================================
FindPlayerBySocket - Find the connection structure for a player
Description:
Finds a player and returns the connection structure with a reference
Parameters:
pgd - Service Provider's global data blob for this instance
psockaddr - socketaddr of the player we are trying to find connection for.
Return Values:
PPLAYERCONN - player connection structure
NULL - Didn't find the player connection.
-----------------------------------------------------------------------------*/
PPLAYERCONN FindPlayerBySocket(LPGLOBALDATA pgd, SOCKADDR *psockaddr)
{
PPLAYERCONN pConn;
EnterCriticalSection(&pgd->csFast);
DEBUGPRINTADDR(8,"FindPlyrBySock",psockaddr);
pConn = pgd->SocketHash[HashSocket(psockaddr)];
while(pConn && !bSameAddr(psockaddr, &pConn->IOSock.sockaddr))
{
DEBUGPRINTADDR(8,"FPBS: doesn't match",&pConn->IOSock.sockaddr);
pConn = pConn->pNextS;
}
if(pConn){
DPF(8,"FindPlayerBySocket, found %x\n",pConn);
DUMPCONN(pConn,1);
AddRefConn(pConn);
}
LeaveCriticalSection(&pgd->csFast);
return pConn;
}
/*=============================================================================
CreatePlayerConn - Create a player connection structure
Description: Fast lock must be held!
Parameters:
pgd - Service Provider's global data blob for this instance
dpid - dpid of the player (if known, else DPID_UNKNOWN)
psockaddr - socket address (if known)
Return Values:
ptr to created player conn, or NULL if we couldn't create (out of mem).
-----------------------------------------------------------------------------*/
PPLAYERCONN CreatePlayerConn(LPGLOBALDATA pgd, DPID dpid, SOCKADDR *psockaddr)
{
PPLAYERCONN pConn;
// Allocate and initialize a Player connection structure
if(dpid != DPID_UNKNOWN && (pConn=FindPlayerById(pgd, dpid)))
{
return pConn; //Player already exists for this id.
}
if(!(pConn=SP_MemAlloc(sizeof(PLAYERCONN)+DEFAULT_RECEIVE_BUFFERSIZE)))
{
return pConn; //NULL
}
if(!GetEventHandle(pgd, pConn)){
SP_MemFree(pConn);
return NULL;
}
pConn->pDefaultReceiveBuffer = (PCHAR)(pConn+1);
pConn->pReceiveBuffer = pConn->pDefaultReceiveBuffer;
pConn->cbReceiveBuffer = DEFAULT_RECEIVE_BUFFERSIZE;
pConn->cbReceived = 0;
pConn->dwRefCount = 1;
pConn->dwPlayerID = dpid;
pConn->sSocket = INVALID_SOCKET;
pConn->sSocketIn = INVALID_SOCKET;
pConn->dwFlags = 0;
pConn->bTrusted = FALSE;
InitBilink(&pConn->PendingConnSendQ);
InitBilink(&pConn->InboundPendingList);
if(psockaddr){
// Don't yet know if this guy can re-use sockets, bang only
// socket we know about so far into both slots.
memcpy(&pConn->IOSock.sockaddr, psockaddr, sizeof(SOCKADDR));
memcpy(&pConn->IOnlySock.sockaddr, psockaddr, sizeof(SOCKADDR));
}
DPF(8,"CreatedPlayerConn %x\n",pConn);
DUMPCONN(pConn,3);
return pConn;
}
/*=============================================================================
DestroyPlayerConn - Remove the connection from any lists, shut down any
active sockets.
Description:
Parameters:
pgd - Service Provider's global data blob for this instance
pConn
Return Values:
Pulls the player Conn off of any hash tables and lists it lives on
and gets rid of its existence count. No guarantee that this will
actually free the object though, that happens when the last reference
is released.
-----------------------------------------------------------------------------*/
PPLAYERCONN CleanPlayerConn(LPGLOBALDATA pgd, PPLAYERCONN pConn, BOOL bHard)
{
LINGER Linger;
int err;
LPREPLYLIST prd;
#ifdef DEBUG
DWORD dwTime;
dwTime=timeGetTime();
#endif
EnterCriticalSection(&pgd->csFast);
DPF(8,"==>CLEANPLAYERCONN %x time %d\n",pConn,dwTime);
DUMPCONN(pConn,3);
// Remove from listening.
FastPlayerEventSelect(pgd, pConn, FALSE);
// Dump event handle.
FreeEventHandle(pgd, pConn);
// Remove from lists.
if(pConn->dwFlags & PLYR_PENDINGLIST)
{
RemoveConnFromPendingList(pgd, pConn);
}
if(pConn->dwFlags & PLYR_DPIDHASH)
{
RemoveConnFromPlayerHash(pgd,pConn);
}
if(pConn->dwFlags & PLYR_SOCKHASH)
{
RemoveConnFromSocketHash(pgd,pConn);
}
// Close all sockets.
// When closing the sockets we want to avoid a bunch of nastiness where data sometimes isn't delivered
// because we close the socket before the data is sent, but we don't want to linger the socket because
// then it gets into a TIME_WAIT state where the same connection cannot be re-established for 4 minutes
// which wrecks havoc with our tests and can cause connection problems since DirectPlay uses a limited
// range (100 ports) between machines. So we set the socket to hard close (avoiding TIME_WAIT) but use
// the "Reply" clean up code path to close the sockets down.
if(pConn->sSocket != INVALID_SOCKET)
{
DPF(8,"Closing Socket %d\n",pConn->sSocket);
Linger.l_onoff=TRUE; Linger.l_linger=0; // avoid TIME_WAIT
if(SOCKET_ERROR == setsockopt( pConn->sSocket,SOL_SOCKET,SO_LINGER,(char FAR *)&Linger,sizeof(Linger)))
{
DPF(0,"DestroyPlayerConn:Couldn't set linger to short for hard close\n");
}
ENTER_DPSP();
prd=SP_MemAlloc(sizeof(REPLYLIST));
if(!prd){
LEAVE_DPSP();
DPF(8,"Closing Socket %d\n",pConn->sSocket);
err=myclosesocket(pgd,pConn->sSocket);
if(err == SOCKET_ERROR){
err=WSAGetLastError();
DPF(8,"Error Closing Socket %x, err=%d\n", pConn->sSocket,err);
}
} else {
// very tricky, overloading the reply close list to close this socket with our own linger...
prd->pNextReply=pgd->pReplyCloseList;
pgd->pReplyCloseList=prd;
prd->sSocket=pConn->sSocket;
prd->tSent=timeGetTime();
prd->lpMessage=NULL;
LEAVE_DPSP();
}
pConn->sSocket=INVALID_SOCKET;
if(pConn->sSocketIn==INVALID_SOCKET){
pConn->dwFlags &= ~(PLYR_CONNECTED|PLYR_ACCEPTED);
} else {
pConn->dwFlags &= ~(PLYR_CONNECTED);
}
}
if(pConn->sSocketIn != INVALID_SOCKET)
{
// may have to close another socket.
DPF(8,"Closing SocketIn %d\n",pConn->sSocketIn);
Linger.l_onoff=TRUE; Linger.l_linger=0; // avoid TIME_WAIT
if(SOCKET_ERROR == setsockopt(pConn->sSocketIn,SOL_SOCKET,SO_LINGER,(char FAR *)&Linger,sizeof(Linger)))
{
DPF(0,"DestroyPlayerConn:Couldn't set linger to short for hard close\n");
}
ENTER_DPSP();
prd=SP_MemAlloc(sizeof(REPLYLIST));
if(!prd){
LEAVE_DPSP();
err=myclosesocket(pgd,pConn->sSocketIn);
if(err == SOCKET_ERROR){
err=WSAGetLastError();
DPF(8,"Error Closing Socket %x, err=%d\n", pConn->sSocketIn,err);
}
} else {
// very tricky, overloading the reply close list to close this socket with our own linger...
prd->pNextReply=pgd->pReplyCloseList;
pgd->pReplyCloseList=prd;
prd->sSocket=pConn->sSocketIn;
prd->tSent=timeGetTime();
prd->lpMessage=NULL;
LEAVE_DPSP();
}
pConn->sSocketIn=INVALID_SOCKET;
pConn->dwFlags &= ~(PLYR_ACCEPTED);
}
// Free extra buffer.
if(pConn->pReceiveBuffer != pConn->pDefaultReceiveBuffer){
SP_MemFree(pConn->pReceiveBuffer);
pConn->pReceiveBuffer = pConn->pDefaultReceiveBuffer;
}
// Dump Send Queue if present.
while(!EMPTY_BILINK(&pConn->PendingConnSendQ)){
PSENDINFO pSendInfo;
pSendInfo=CONTAINING_RECORD(pConn->PendingConnSendQ.next, SENDINFO, PendingConnSendQ);
Delete(&pSendInfo->PendingConnSendQ);
pSendInfo->Status = DPERR_CONNECTIONLOST;
EnterCriticalSection(&pgd->csSendEx);
pSendInfo->RefCount = 1;
LeaveCriticalSection(&pgd->csSendEx);
wsaoDecRef(pSendInfo);
}
DUMPCONN(pConn,3);
#ifdef DEBUG
dwTime = timeGetTime()-dwTime;
if(dwTime > 1000){
DPF(0,"Took way too long in CleanPlayerConn, elapsed %d ms\n",dwTime);
//DEBUG_BREAK(); // removed break due to stress hits
}
#endif
DPF(8,"<==CleanPlayerConn total time %d ms\n",dwTime);
LeaveCriticalSection(&pgd->csFast);
return pConn;
}
/*=============================================================================
DecRefConn - Decrement reference on PlayerConn, when it hits 0, free it.
Description:
Parameters:
pConn - player connection.
Return Values:
decrements the reference on a player conn. If it hits 0, frees it.
-----------------------------------------------------------------------------*/
INT DecRefConn(LPGLOBALDATA pgd, PPLAYERCONN pConn)
{
INT count;
count=InterlockedDecrement(&pConn->dwRefCount);
if(!count){
CleanPlayerConn(pgd, pConn, FALSE);
DPF(8,"Freeing Connection pConn %x\n",pConn);
SP_MemFree(pConn);
}
#ifdef DEBUG
if(count & 0x80000000){
DPF(0,"DecRefConn: Conn refcount for conn %x has gone negative count %x\n",pConn,count);
DUMPCONN(pConn,2);
DEBUG_BREAK();
}
#endif
return count;
}
/*=============================================================================
DecRefConnExist - Dumps Existence ref if not already dumped.
Description:
Parameters:
pConn - player connection.
Return Values:
-----------------------------------------------------------------------------*/
INT DecRefConnExist(LPGLOBALDATA pgd, PPLAYERCONN pConn)
{
INT count;
EnterCriticalSection(&pgd->csFast);
if(!(pConn->dwFlags & PLYR_DESTROYED)){
pConn->dwFlags |= PLYR_DESTROYED;
count=DecRefConn(pgd,pConn);
} else {
count=pConn->dwRefCount;
}
LeaveCriticalSection(&pgd->csFast);
return count;
}
/*=============================================================================
AddConnToPlayerHash - puts a connection in the player hash table.
Description: The fast lock must be held!
Parameters:
pgd - Service Provider's global data blob for this instance
pConn - connection to put in the hash table.
Return Values:
None.
-----------------------------------------------------------------------------*/
HRESULT AddConnToPlayerHash(LPGLOBALDATA pgd, PPLAYERCONN pConn)
{
PPLAYERCONN pConn2;
INT i;
HRESULT hr=DP_OK;
#ifdef DEBUG
if(pConn->dwPlayerID == DPID_UNKNOWN){
DEBUG_BREAK();
}
#endif
ASSERT(!(pConn->dwFlags & PLYR_DPIDHASH));
if(!(pConn->dwFlags & PLYR_DPIDHASH)){
if(pConn2 = FindPlayerById(pgd, pConn->dwPlayerID)){
DPF(0,"AddConnToPlayerHash: Player in %x id %d already exists, pConn=%x\n",pConn,pConn->dwPlayerID,pConn2);
DecRefConn(pgd, pConn2);
hr=DPERR_GENERIC;
goto exit;
}
DPF(8,"Adding Conn %x to Player ID Hash\n",pConn);
DUMPCONN(pConn,1);
// add a reference for being in the player hash table.
AddRefConn(pConn);
i=HashPlayer(pConn->dwPlayerID);
ASSERT(i<PLAYER_HASH_SIZE);
pConn->pNextP = pgd->PlayerHash[i];
pgd->PlayerHash[i] = pConn;
pConn->dwFlags |= PLYR_DPIDHASH;
} else {
DPF(1,"WARNING:tried to add Conn %x to Player Hash again\n",pConn);
}
exit:
return hr;
}
/*=============================================================================
RemoveConnFromPlayerHash - pull a connection from the player hash table.
Description:
Parameters:
pgd - Service Provider's global data blob for this instance
pConn - connection to put in the hash table.
Return Values:
PPLAYERCONN - removed from hash, here it is.
NULL - couldn't find it.
-----------------------------------------------------------------------------*/
PPLAYERCONN RemoveConnFromPlayerHash(LPGLOBALDATA pgd, PPLAYERCONN pConnIn)
{
PPLAYERCONN pConn=NULL,pConnPrev;
INT i;
if(pConnIn->dwFlags & PLYR_DPIDHASH){
i=HashPlayer(pConnIn->dwPlayerID);
EnterCriticalSection(&pgd->csFast);
pConn = pgd->PlayerHash[i];
pConnPrev = CONTAINING_RECORD(&pgd->PlayerHash[i], PLAYERCONN, pNextP); // sneaky
while(pConn && pConn != pConnIn){
pConnPrev = pConn;
pConn = pConn->pNextP;
}
if(pConn){
DPF(8,"Removing Conn %x from Player ID Hash\n",pConn);
DUMPCONN(pConn,1);
pConnPrev->pNextP = pConn->pNextP;
pConn->dwFlags &= ~(PLYR_DPIDHASH);
i=DecRefConn(pgd, pConn); // remove reference for player hash table
ASSERT(i);
}
LeaveCriticalSection(&pgd->csFast);
}
return pConn;
}
/*=============================================================================
AddConnToSocketHash - puts a connection in the socket hash table.
Description:
Parameters:
pgd - Service Provider's global data blob for this instance
pConn - connection to put in the hash table.
Return Values:
None.
-----------------------------------------------------------------------------*/
HRESULT AddConnToSocketHash(LPGLOBALDATA pgd, PPLAYERCONN pConn)
{
PPLAYERCONN pConn2;
INT i;
HRESULT hr=DP_OK;
EnterCriticalSection(&pgd->csFast);
if(!(pConn->dwFlags & PLYR_SOCKHASH)){
if(pConn2 = FindPlayerBySocket(pgd, &pConn->IOSock.sockaddr)){
DecRefConn(pgd, pConn2);
DPF(0,"AddConnToPlayerHash: Player in %x id %d already exists, pConn=%x\n",pConn,pConn->dwPlayerID,pConn2);
hr=DPERR_GENERIC;
goto exit;
}
DPF(8,"Adding Conn %x to Socket Hash\n",pConn);
DUMPCONN(pConn,1);
// add a reference for being in the socket hash.
AddRefConn(pConn);
i=HashSocket(&pConn->IOSock.sockaddr);
ASSERT(i<SOCKET_HASH_SIZE);
pConn->pNextS = pgd->SocketHash[i];
pgd->SocketHash[i] = pConn;
pConn->dwFlags |= PLYR_SOCKHASH;
} else {
DPF(0,"WARNING: tried to add pConn %x to socket hash again\n",pConn);
DEBUG_BREAK();
}
exit:
LeaveCriticalSection(&pgd->csFast);
return hr;
}
/*=============================================================================
RemoveConnFromSockHash - pull a connection from the socket hash table.
Description:
Parameters:
pgd - Service Provider's global data blob for this instance
pConn - connection to put in the hash table.
Return Values:
PPLAYERCONN - removed from hash, here it is.
NULL - couldn't find it.
-----------------------------------------------------------------------------*/
PPLAYERCONN RemoveConnFromSocketHash(LPGLOBALDATA pgd, PPLAYERCONN pConnIn)
{
PPLAYERCONN pConn=NULL,pConnPrev;
UINT i;
if(pConnIn->dwFlags & PLYR_SOCKHASH){
i=HashSocket(&pConnIn->IOSock.sockaddr);
DPF(8,"Removing Player %x from Socket Hash\n",pConnIn);
EnterCriticalSection(&pgd->csFast);
pConn = pgd->SocketHash[i];
pConnPrev = CONTAINING_RECORD(&pgd->SocketHash[i], PLAYERCONN, pNextS); // sneaky
while(pConn && pConn!=pConnIn){
pConnPrev = pConn;
pConn = pConn->pNextS;
}
if(pConn){
pConnPrev->pNextS = pConn->pNextS;
pConn->dwFlags &= ~(PLYR_SOCKHASH);
i=DecRefConn(pgd, pConn); // remove reference for socket hash table
ASSERT(i);
}
LeaveCriticalSection(&pgd->csFast);
}
return pConn;
}
/*=============================================================================
FindConnInPendingList - finds a connection from the pending list
Description:
Parameters:
pgd - Service Provider's global data blob for this instance
pConn - connection to put in the hash table.
Return Values:
PPLAYERCONN - pointer ot connection if found, and adds a reference.
-----------------------------------------------------------------------------*/
PPLAYERCONN FindConnInPendingList(LPGLOBALDATA pgd, SOCKADDR *psaddr)
{
PPLAYERCONN pConnWalker=NULL, pConn=NULL;
BILINK *pBilink;
EnterCriticalSection(&pgd->csFast);
pBilink=pgd->InboundPendingList.next;
while(pBilink != &pgd->InboundPendingList){
pConnWalker=CONTAINING_RECORD(pBilink, PLAYERCONN, InboundPendingList);
if(bSameAddr(psaddr, &pConnWalker->IOnlySock.sockaddr)){
AddRefConn(pConnWalker);
pConn=pConnWalker;
break;
}
pBilink=pBilink->next;
}
if(pConn){
DPF(8,"Found Conn %x in Pending List\n",pConn);
DUMPCONN(pConn,3);
AddRefConn(pConn);
}
LeaveCriticalSection(&pgd->csFast);
return pConn;
}
/*=============================================================================
AddConnToPendingList - puts a connection on the pending list
Description:
The Pending list keeps the list of connections that we have received
from, but haven't received any information on yet. Until we receive
from one of these connections, we don't have any way to know exactly
who the connection is from. When we receive from one of these with
a return address in the message, we can make an association with
an outbound connection (if present) and can put this node in the
socket hash table at that time.
Parameters:
pgd - Service Provider's global data blob for this instance
pConn - connection to put in the hash table.
Return Values:
None.
-----------------------------------------------------------------------------*/
HRESULT AddConnToPendingList(LPGLOBALDATA pgd, PPLAYERCONN pConn)
{
PPLAYERCONN pConn2;
INT i;
HRESULT hr=DP_OK;
EnterCriticalSection(&pgd->csFast);
if(pConn2 = FindConnInPendingList(pgd, &pConn->IOnlySock.sockaddr)){
// OPTIMIZATION: should we remove the socket from the list here, it must be old.
DPF(0,"AddConnToPendingList: Player in %x id %d already exists, pConn=%x\n",pConn,pConn->dwPlayerID,pConn2);
DecRefConn(pgd, pConn2);
hr=DPERR_GENERIC;
goto exit;
}
InsertAfter(&pConn->InboundPendingList,&pgd->InboundPendingList);
AddRefConn(pConn);
pConn->dwFlags |= PLYR_PENDINGLIST;
DPF(8,"Added Conn %x to PendingList\n",pConn);
exit:
LeaveCriticalSection(&pgd->csFast);
return hr;
}
/*=============================================================================
RemoveConnFromPendingList
Description:
Parameters:
pgd - Service Provider's global data blob for this instance
pConn - connection to put in the hash table.
Return Values:
None.
-----------------------------------------------------------------------------*/
VOID RemoveConnFromPendingList(LPGLOBALDATA pgd, PPLAYERCONN pConn)
{
if(pConn->dwFlags & PLYR_PENDINGLIST)
{
ASSERT(!EMPTY_BILINK(&pConn->InboundPendingList));
Delete(&pConn->InboundPendingList);
pConn->dwFlags &= ~(PLYR_PENDINGLIST);
DecRefConn(pgd, pConn);
DPF(8,"Removed Conn %x From Pending List\n",pConn);
}
}
/*=============================================================================
GetPlayerConn - Finds or creates a player conn and starts connecting it.
Description:
Parameters:
pgd - Service Provider's global data blob for this instance
dpid - dpid of the player (if known)
psockaddr - socket address (if known)
Return Values:
if found, creates a reference.
-----------------------------------------------------------------------------*/
PPLAYERCONN GetPlayerConn(LPGLOBALDATA pgd, DPID dpid, SOCKADDR *psockaddr)
{
PPLAYERCONN pConn=NULL;
SOCKET sSocket;
SOCKADDR_IN saddr;
INT rc,err;
DWORD dwSize;
BOOL bTrue=TRUE;
u_long lNonBlock = 1; // passed to ioctlsocket to make socket non-blocking
u_long lBlock = 0; // passed to ioctlsocket to make socket blocking
BOOL bCreated=FALSE;
EnterCriticalSection(&pgd->csFast);
// Do we already know this player by id?
if(dpid != DPID_UNKNOWN) {
if (pConn=FindPlayerById(pgd, dpid))
{
DPF(8,"GetPlayerConn: Found Con for dpid %x pConn %x\n",dpid,pConn);
goto exit; //Player already exists for this id.
}
}
if(pConn=FindPlayerBySocket(pgd, psockaddr)){
if(pConn->dwFlags & (PLYR_CONNECTED|PLYR_CONN_PENDING))
{
DPF(8,"GetPlayerConn: Found Conn by socketaddr pConn %x\n",pConn);
if(dpid != DPID_UNKNOWN){
pConn->dwPlayerID=dpid;
AddConnToPlayerHash(pgd, pConn);
}
DUMPCONN(pConn,1);
goto exit;
}
} else {
// do we already know this player by connection?
if(psockaddr && (pConn=FindConnInPendingList(pgd, psockaddr)) )
{
//NOTE: I think we always catch this case in FastCombine
// We may get it before the combine happens?
if(!(pConn->dwFlags & PLYR_CONNECTED|PLYR_CONN_PENDING)){ //only once...
// hey, this is a bi-directional socket, so make it so.
ASSERT(pConn->dwPlayerID == DPID_UNKNOWN);
if((dpid != DPID_UNKNOWN) && (pConn->dwPlayerID == DPID_UNKNOWN))
{
ASSERT(! (pConn->dwFlags & PLYR_DPIDHASH));
pConn->dwPlayerID = dpid;
AddConnToPlayerHash(pgd, pConn);
}
if(!(pConn->dwFlags & PLYR_SOCKHASH)){
AddConnToSocketHash(pgd, pConn);
}
ASSERT(pConn->sSocketIn != INVALID_SOCKET);
ASSERT(pConn->sSocket == INVALID_SOCKET);
pConn->sSocket = pConn->sSocketIn;
pConn->sSocketIn = INVALID_SOCKET;
if(pConn->dwFlags & PLYR_ACCEPTED){
pConn->dwFlags |= (PLYR_CONNECTED | PLYR_NEW_CLIENT);
} else {
ASSERT(pConn->dwFlags & PLYR_ACCEPT_PENDING);
pConn->dwFlags |= (PLYR_CONN_PENDING | PLYR_NEW_CLIENT);
}
RemoveConnFromPendingList(pgd,pConn); // found a home, don't need on pending list anymore
}
DPF(8,"GetPlayerConn FoundConn in Pending List pConn %x\n",pConn);
DUMPCONN(pConn,3);
goto exit;
}
}
// Have critical section...
// Doesn't already exist, so create one.
if(!pConn){
DPF(8,"GetPlayerConn: No Conn Found, creating\n");
pConn = CreatePlayerConn(pgd, dpid, psockaddr);
if(!pConn){
DPF(8, "CreatePlayerConn Failed\n");
goto exit;
}
if(dpid != DPID_UNKNOWN)AddConnToPlayerHash(pgd, pConn);
if(psockaddr)AddConnToSocketHash(pgd,pConn);
bCreated=TRUE;
AddRefConn(pConn); // need ref to return to caller.
} else {
// already have ref to return to caller as result of find.
}
// have critical section and a pConn, maybe created (see bCreated), and a return ref.
ASSERT(pConn->sSocket == INVALID_SOCKET);
ASSERT(!(pConn->dwFlags & (PLYR_CONN_PENDING|PLYR_CONNECTED)));
//if(pgd->bSeparateIO && !(pgd->SystemStreamPortOut))
ASSERT(pgd->bSeparateIO);
{
// Workaround Win9x < Millennium sockets bug. Can't use same port for
// inbound/outbound traffic, because Win9x will not accept connections in
// some cases. So we create a socket for outbound traffic (and re-use
// that port for all outbound traffic).
rc=CreateSocket(pgd, &sSocket, SOCK_STREAM, 0, INADDR_ANY, &err, FALSE);
if(rc != DP_OK){
DPF(0,"Couldn't create Outbound socket on Win9x < Millennium platform, rc=%x , wserr=%d\n",rc, err);
goto err_exit;
}
dwSize = sizeof(saddr);
err=getsockname(sSocket, (SOCKADDR *)&saddr, &dwSize);
if(err){
DPF(0,"Couldn't get socket name?\n");
DEBUG_BREAK();
}
//pgd->SystemStreamPortOut = saddr.sin_port;
//DPF(2,"System stream out port is now %d.",ntohs(pgd->SystemStreamPortOut));
DPF(2,"Stream out socket %x port is now %d.",sSocket, ntohs(saddr.sin_port));
bTrue = SetSharedPortAccess(sSocket);
if (! bTrue)
{
DPF(0,"Failed to to set shared mode on socket - continue\n");
}
}
/*
else
{
// Normal execution path.
sSocket = socket(AF_INET, SOCK_STREAM, 0);
// Bind it to our system address (so we only use one address for a change)
memset(&saddr,0,sizeof(SOCKADDR_IN));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
if(pgd->bSeparateIO && pgd->SystemStreamPortOut){
saddr.sin_port = pgd->SystemStreamPortOut; // part of Win9x Hack. (see above)
} else {
saddr.sin_port = pgd->SystemStreamPort;
}
ASSERT(pgd->SystemStreamPort);
DPF(7,"Using port %d.",ntohs(saddr.sin_port));
// Set socket for address re-use
bTrue = SetSharedPortAccess(sSocket);
if (! bTrue)
{
DPF(0,"Failed to to set shared mode on socket - continue\n");
}
rc = bind(sSocket, (SOCKADDR *)&saddr, sizeof(saddr));
if(rc){
err = WSAGetLastError();
DPF(0,"Failed to bind socket to port %d, error=%d.",ntohs(saddr.sin_port),err);
goto err_exit; // sends will fail until player killed.
}
}
*/
// turn ON keepalive
if (SOCKET_ERROR == setsockopt(sSocket, SOL_SOCKET, SO_KEEPALIVE, (CHAR FAR *)&bTrue, sizeof(bTrue)))
{
err = WSAGetLastError();
DPF(0,"Failed to turn ON keepalive - continue : err = %d\n",err);
}
ASSERT(bTrue);
// turn off nagling - always, avoids race on closing socket w/o linger, otherwise must linger socket.
DPF(5, "Turning nagling off on outbound socket");
if (SOCKET_ERROR == setsockopt(sSocket, IPPROTO_TCP, TCP_NODELAY, (CHAR FAR *)&bTrue, sizeof(bTrue)))
{
err = WSAGetLastError();
DPF(0,"Failed to turn off naggling - continue : err = %d\n",err);
}
// update connection info.
pConn->dwFlags |= PLYR_CONN_PENDING;
pConn->sSocket = sSocket;
//
// Now Connect this puppy
//
// set socket to non-blocking
rc = ioctlsocket(sSocket,FIONBIO,&lNonBlock);
if (SOCKET_ERROR == rc)
{
err = WSAGetLastError();
DPF(0,"could not set non-blocking mode on socket err = %d!",err);
DPF(0,"will revert to synchronous behavior. bummer");
}
FastPlayerEventSelect(pgd,pConn, TRUE);
DEBUGPRINTADDR(4, "Fast connecting socket:", psockaddr);
rc = connect(sSocket,psockaddr,sizeof(SOCKADDR));
if(SOCKET_ERROR == rc)
{
err = WSAGetLastError();
if(err == WSAEISCONN || err == WSAEADDRINUSE || err == WSAEACCES){
// must be an accept about to happen
DPF(8,"Hey, we're already connected! got extended error %d on connect\n",err);
pConn->dwFlags |= PLYR_ACCEPT_PENDING;
} else if (err == WSAEWOULDBLOCK) {
// this is what we should normally get.
DPF(8,"Conn is pending connection %x\n",pConn);
} else if (err == WSAEHOSTUNREACH) {
DEBUGPRINTADDR(8,"Can't reach host, not connecting\n",psockaddr);
goto err_exit;
} else if (err == WSAENOBUFS) {
DEBUGPRINTADDR(8,"Winsock out of memory, not connecting\n",psockaddr);
goto err_exit;
} else {
DPF(0,"Trying to connect UH OH, very bad things, err=%d\n",err);
DEBUG_BREAK();
goto err_exit; // sends will fail until player deleted.
}
} else {
// Very unlikely, but WOO HOO, connected.
DPF(0,"Very surprising, connect didn't return pending on async call?");
pConn->dwFlags &= ~(PLYR_CONN_PENDING);
pConn->dwFlags |= PLYR_CONNECTED;
}
exit:
LeaveCriticalSection(&pgd->csFast);
DPF(8,"<===GetPlayerConn %x\n",pConn);
return pConn;
err_exit:
pConn->dwFlags &= ~(PLYR_CONN_PENDING);
if(bCreated){
// better blow it away.
DPF(0,"GetPlayerConn: Severe error connection Conn we made, so blowing it away.\n");
CleanPlayerConn(pgd,pConn,TRUE); // clean up
DecRefConnExist(pgd,pConn); // dump existence
DecRefConn(pgd,pConn); // bye bye...(ref we had for caller)
} else {
if(pConn) {
DecRefConn(pgd,pConn);
}
}
pConn=NULL; // Connection Lost.
goto exit;
}
/*=============================================================================
FastPlayerEventSelect - start listening for events for a player.
Description:
Starts the appropriate events waiting for a signal for a
PlayerConn structure.
Parameters:
pgd - Service Provider's global data blob for this instance
pConn - Player to start waiting for events on
Return Values:
-----------------------------------------------------------------------------*/
HRESULT FastPlayerEventSelect(LPGLOBALDATA pgd, PPLAYERCONN pConn, BOOL bSelect)
{
HRESULT hr=DP_OK;
DWORD lEvents;
INT rc;
UINT err;
DPF(8,"FastPlayerEventSelect pConn %x, bSelect %d\n",pConn, bSelect);
DUMPCONN(pConn,0);
if(pConn->sSocket != INVALID_SOCKET){
if(bSelect){
lEvents = FD_READ|FD_CLOSE;
if(pConn->dwFlags & PLYR_CONN_PENDING){
lEvents |= FD_CONNECT;
}
if(!EMPTY_BILINK(&pConn->PendingConnSendQ)){
lEvents |= FD_WRITE;
}
} else {
lEvents = 0;
}
DPF(8,"Selecting %x on IO Socket...\n",lEvents);
DEBUGPRINTADDR(8,"IO Socket",&pConn->IOSock.sockaddr);
rc=g_WSAEventSelect(pConn->sSocket, pgd->EventHandles[pConn->iEventHandle],lEvents);
if(rc == SOCKET_ERROR){
err = WSAGetLastError();
DPF(0,"FastPlayerEventSelect: failed to do select on 2-way socket extended error=%d\n",err);
hr=DPERR_GENERIC;
}
}
if(pConn->sSocketIn != INVALID_SOCKET){
if(bSelect){
lEvents = FD_READ|FD_CLOSE;
} else {
lEvents = 0;
}
DPF(8,"Selecting %x on IOnly Socket...\n",lEvents);
DEBUGPRINTADDR(8,"IOnly Socket",&pConn->IOnlySock.sockaddr);
rc=g_WSAEventSelect(pConn->sSocketIn, pgd->EventHandles[pConn->iEventHandle], lEvents);
if(rc == SOCKET_ERROR){
err = WSAGetLastError();
DPF(0,"FastPlayerEventSelect: failed to do select on receive-only socket extended error=%d\n",err);
hr=DPERR_GENERIC;
}
}
DPF(8,"<==FastPlayerEventSelect pConn %x\n",pConn);
return hr;
}
/*=============================================================================
FastStreamReceiveThreadProc - version of stream recevie thread proc
that uses Winsock 2.0 functions for
greater speed.
Description:
Parameters:
pgd - Service Provider's global data blob for this instance
pConn - connection to put in the hash table.
Return Values:
PPLAYERCONN - removed from hash, here it is.
NULL - couldn't find it.
-----------------------------------------------------------------------------*/
DWORD WINAPI FastStreamReceiveThreadProc(LPVOID pvCast)
{
IDirectPlaySP * pISP = (IDirectPlaySP *)pvCast;
HRESULT hr;
UINT i,j;
UINT err;
INT rc;
DWORD dwDataSize = sizeof(GLOBALDATA);
LPGLOBALDATA pgd;
WSANETWORKEVENTS NetEvents1, NetEvents2;
LPWSANETWORKEVENTS pNetEvents1, pNetEvents2;
DWORD Event;
PPLAYERCONN pConn;
DWORD nConn;
// get the global data
hr = pISP->lpVtbl->GetSPData(pISP,(LPVOID *)&pgd,&dwDataSize,DPGET_LOCAL);
if (FAILED(hr) || (dwDataSize != sizeof(GLOBALDATA) ))
{
DPF_ERR("FastStreamReceiveThreadProc: couldn't get SP data from DirectPlay - failing");
ExitThread(0);
return 0;
}
pgd->pISP = pISP; // why not do this somewhere easier? -- because old dplay didn't.
listen(pgd->sSystemStreamSocket, 200);
err = g_WSAEventSelect(pgd->sSystemStreamSocket, pgd->hAccept, FD_ACCEPT);
if(err){
err = WSAGetLastError();
DPF(0,"FastStreamReceiveThreadProc: Event select for accept socket failed err=%d\n",err);
ExitThread(0);
return 0;
}
while (1)
{
if (pgd->bShutdown)
{
DPF(2,"FastStreamReceiveThreadProc: detected shutdown - bailing");
goto CLEANUP_EXIT;
}
Event=WaitForMultipleObjectsEx(NUM_EVENT_HANDLES+1, &pgd->hAccept, FALSE, 2500, TRUE);
if(Event != WAIT_TIMEOUT)
{
i = Event - WAIT_OBJECT_0;
if( i <= NUM_EVENT_HANDLES)
{
DPF(8,"GotSignal on iEvent %d Event %x\n", i, pgd->EventHandles[i]);
// Go to the signalled object and look for events on its sockets.
if(i != 0){
i--; // go from hAccept based index to table index.
EnterCriticalSection(&pgd->csFast);
// loop through the connections tied to this event and
// see if there is any work to do for this connection.
nConn=pgd->EventList[i].nConn;
for (j=0;j<nConn;j++){
pConn = pgd->EventList[i].pConn[j];
if(pConn){
AddRefConn(pConn); // lock it down.
pConn->bCombine=FALSE;
// Check for events on the connection.
NetEvents1.lNetworkEvents=0;
NetEvents2.lNetworkEvents=0;
pNetEvents1=NULL;
pNetEvents2=NULL;
if(pConn->sSocket != INVALID_SOCKET){
rc=g_WSAEnumNetworkEvents(pConn->sSocket, 0, &NetEvents1);
if(NetEvents1.lNetworkEvents){
pNetEvents1 = &NetEvents1;
}
}
if(pConn->sSocketIn != INVALID_SOCKET){
rc=g_WSAEnumNetworkEvents(pConn->sSocketIn, 0, &NetEvents2);
if(NetEvents2.lNetworkEvents){
pNetEvents2 = &NetEvents2;
}
}
if(pNetEvents1 || pNetEvents2){
DPF(8,"Found Events on Connection %x\n",pConn);
// There are events for this connection, deal with it!
hr=ProcessConnEvents(pgd, pConn, pNetEvents1, pNetEvents2); // can drop csFast
if(FAILED(hr)){
if(hr==DPERR_CONNECTIONLOST){
CleanPlayerConn(pgd, pConn, TRUE);
DecRefConnExist(pgd, pConn); // destory existence ref.
} else {
DPF(0,"Unexpected error processing connection events err=%x\n",hr);
}
}
}
if(pConn->bCombine || (nConn != pgd->EventList[i].nConn)){
// list changed, re-scan.
nConn = pgd->EventList[i].nConn;
j=0;
}
DecRefConn(pgd, pConn); // set it free
}
}
LeaveCriticalSection(&pgd->csFast);
} else {
// it's the accept socket, someone just connected to us. Yeah!
do{
rc = g_WSAEnumNetworkEvents(pgd->sSystemStreamSocket,0,&NetEvents1);
if(NetEvents1.lNetworkEvents & FD_ACCEPT)
{
EnterCriticalSection(&pgd->csFast);
FastAccept(pgd, &NetEvents1);
LeaveCriticalSection(&pgd->csFast);
}
}while(NetEvents1.lNetworkEvents & FD_ACCEPT);
}
}
}
}// while TRUE
CLEANUP_EXIT:
return 0;
} // StreamReceiveThreadProc
/*=============================================================================
FastHandleMessage - Indicate a message to the DirectPlay layer.
Description:
Parameters:
pgd - Service Provider's global data blob for this instance
pConn - connection to put in the hash table.
Return Values:
-----------------------------------------------------------------------------*/
HRESULT FastHandleMessage(LPGLOBALDATA pgd, PPLAYERCONN *ppConn)
{
PPLAYERCONN pConn = *ppConn;
if(SP_MESSAGE_TOKEN(pConn->pReceiveBuffer)==TOKEN && pConn->cbReceived != SPMESSAGEHEADERLEN)
{
ASSERT(pgd->AddressFamily == AF_INET);
if(pConn->dwFlags & PLYR_NEW_CLIENT){
IP_SetAddr((LPVOID)pConn->pReceiveBuffer, &pConn->IOSock.sockaddr_in);
} else {
IP_SetAddr((LPVOID)pConn->pReceiveBuffer, &pConn->IOnlySock.sockaddr_in);
}
if( !(pConn->dwFlags & PLYR_OLD_CLIENT) && // already combined
(pConn->sSocket == INVALID_SOCKET) && // just checking
!(pConn->lNetEventsSocketIn & FD_CLOSE) // don't bother if we're gonna blow it away
){
LPMESSAGEHEADER phead = (LPMESSAGEHEADER)pConn->pReceiveBuffer;
pConn=*ppConn=FastCombine(pgd, pConn, &(phead->sockaddr));
}
LeaveCriticalSection(&pgd->csFast);
#if DUMPBYTES
{
PCHAR pBuf;
UINT buflen;
UINT i=0;
pBuf = pConn->pReceiveBuffer+sizeof(MESSAGEHEADER);
buflen = pConn->cbReceived-sizeof(MESSAGEHEADER);
while (((i + 16) < buflen) && (i < 4*16)){
DPF(9, "%08x %08x %08x %08x",*(PUINT)(&pBuf[i]),*(PUINT)(&pBuf[i+4]),*(PUINT)(&pBuf[i+8]),*(PUINT)(&pBuf[i+12]));
i += 16;
}
}
#endif
pgd->pISP->lpVtbl->HandleMessage(pgd->pISP,
pConn->pReceiveBuffer+sizeof(MESSAGEHEADER),
pConn->cbReceived-sizeof(MESSAGEHEADER),
pConn->pReceiveBuffer);
EnterCriticalSection(&pgd->csFast);
if(pConn->pReceiveBuffer != pConn->pDefaultReceiveBuffer){
DPF(8,"Releasing big receive buffer of size %d\n",pConn->cbReceiveBuffer);
SP_MemFree(pConn->pReceiveBuffer);
}
pConn->cbReceived=0;
pConn->cbExpected=0;
pConn->cbReceiveBuffer = DEFAULT_RECEIVE_BUFFERSIZE;
pConn->pReceiveBuffer=pConn->pDefaultReceiveBuffer;
if(pConn->pReceiveBuffer != (PCHAR)(pConn+1)){
DEBUG_BREAK();
}
}
return DP_OK;
}
/*=============================================================================
FastReceive - Receive data on a connection.
Description:
First receives a message header, then receives the message.
When a full message is received, it is indicated.
Parameters:
pgd - Service Provider's global data blob for this instance
pConn - connection to put in the hash table.
Return Values:
-----------------------------------------------------------------------------*/
HRESULT FastReceive(LPGLOBALDATA pgd, PPLAYERCONN *ppConn)
{
PPLAYERCONN pConn;
PCHAR pBuffer; // receive buffer pointer.
DWORD cbBuffer; // receive buffer size.
SOCKET sSocket; // socket to receive on
INT err; // sockets error.
DWORD cbReceived; // actual bytes received on this recv call.
DWORD cbMessageSize=0; // size of the message to receive
HRESULT hr;
pConn=*ppConn;
if(pConn->cbExpected == 0){
// all messages have a header, let get that first.
pConn->cbExpected = SPMESSAGEHEADERLEN;
}
// point to place in buffer we're going to receive latest data.
// don't get more than we expect, or we would have to be smart
// about setting up messages.
pBuffer = pConn->pReceiveBuffer+pConn->cbReceived;
cbBuffer = pConn->cbExpected-pConn->cbReceived;
if(cbBuffer > pConn->cbReceiveBuffer){
DPF(0,"Receive would overrun buffer\n");
DEBUG_BREAK();
}
if(pConn->dwFlags & PLYR_NEW_CLIENT){
// new client does bi-directional on socket.
sSocket = pConn->sSocket;
} else {
// old clients have separate receive socket.
sSocket = pConn->sSocketIn;
}
ASSERT(sSocket != INVALID_SOCKET);
DPF(8,"Attempting to receive %d bytes", cbBuffer);
DEBUGPRINTSOCK(8,">>> receiving data on socket - ",&sSocket);
cbReceived = recv(sSocket, pBuffer, cbBuffer, 0); // <----- Receive that data!
if(cbReceived == 0){
// remote side has shutdown connection gracefully
DEBUGPRINTSOCK(8,"<<< received notification on socket - ",&sSocket);
DEBUGPRINTSOCK(5,"Remote side has shutdown connection gracefully - ",&sSocket);
hr = DPERR_CONNECTIONLOST;
goto ERROR_EXIT;
} else if (cbReceived == SOCKET_ERROR){
err = WSAGetLastError();
if(err == WSAEWOULDBLOCK){
DPF(1,"WARN: Got WSAEWOULDBLOCK on non-blocking receive, round and round we go...\n");
goto exit;
}
DEBUGPRINTSOCK(8,"<<< received notification on socket - ",&sSocket);
DPF(0,"STREAMRECEIVEE: receive error - err = %d",err);
hr = DPERR_CONNECTIONLOST;
goto ERROR_EXIT;
}
DPF(5, "received %d bytes", cbReceived);
pConn->cbReceived += cbReceived;
if(pConn->cbReceived == SPMESSAGEHEADERLEN){
// got the header, set up for the body of the message.
if(VALID_DPWS_MESSAGE(pConn->pReceiveBuffer))
{
cbMessageSize = SP_MESSAGE_SIZE(pConn->pReceiveBuffer);
} else {
// Bad data. Shut this baby down!
DPF(2,"got invalid message - token = 0x%08x",SP_MESSAGE_TOKEN(pConn->pReceiveBuffer));
hr = DPERR_CONNECTIONLOST;
goto ERROR_EXIT;
}
}
if(cbMessageSize)
{
pConn->cbExpected = cbMessageSize;
if(cbMessageSize > DEFAULT_RECEIVE_BUFFERSIZE){
if(!pConn->bTrusted){
// until the connection is trusted, don't allow big messages to come in.
DPF(0,"Rejecting large receive size %d, on untrusted connection pConn %x, dropping link",cbMessageSize);
hr=DPERR_CONNECTIONLOST;
goto ERROR_EXIT;
}
pConn->pReceiveBuffer = SP_MemAlloc(cbMessageSize);
if(!pConn->pReceiveBuffer){
DPF(0,"Failed to allocate receive buffer for message - out of memory");
hr=DPERR_CONNECTIONLOST;
goto ERROR_EXIT;
}
pConn->cbReceiveBuffer = cbMessageSize;
// copy header into new message buffer
memcpy(pConn->pReceiveBuffer, pConn->pDefaultReceiveBuffer, SPMESSAGEHEADERLEN);
}
}
if(pConn->cbExpected == pConn->cbReceived)
{
// hey, got a whole message, send it up.
hr = FastHandleMessage(pgd, ppConn); // <---- INDICATE THE MESSAGE
#ifdef DEBUG
if(pConn != *ppConn){
DPF(8,"Connections pConn %x pNewConn %x combined\n",pConn, *ppConn);
}
#endif
pConn = *ppConn;
if(FAILED(hr)){
goto ERROR_EXIT;
}
}
exit:
return DP_OK;
ERROR_EXIT:
return hr;
}
/*=============================================================================
QueueSendOnConn - queue a send on a connection until we know it is ok to send.
Description:
Note: can only have 1 send outstanding to winsock per socket because of a winsock bug.
Parameters:
pgd - Service Provider's global data blob for this instance
pConn - connection to put in the hash table.
pSendInfo - send to queue.
Return Values:
PPLAYERCONN - removed from hash, here it is.
NULL - couldn't find it.
-----------------------------------------------------------------------------*/
VOID QueueSendOnConn(LPGLOBALDATA pgd, PPLAYERCONN pConn, PSENDINFO pSendInfo)
{
EnterCriticalSection(&pgd->csFast);
InsertBefore(&pSendInfo->PendingConnSendQ, &pConn->PendingConnSendQ);
LeaveCriticalSection(&pgd->csFast);
}
/*=============================================================================
QueueNextSend - Move a pending send into the real sendq.
Description:
Parameters:
pgd - Service Provider's global data blob for this instance
pConn - connection to put in the hash table.
pSendInfo - send to queue.
Return Values:
PPLAYERCONN - removed from hash, here it is.
NULL - couldn't find it.
-----------------------------------------------------------------------------*/
VOID QueueNextSend(LPGLOBALDATA pgd,PPLAYERCONN pConn)
{
BILINK *pBilink;
EnterCriticalSection(&pgd->csFast);
DPF(8,"==>QueueNextSend pConn %x",pConn);
while(!EMPTY_BILINK(&pConn->PendingConnSendQ) && !pConn->bSendOutstanding)
{
PSENDINFO pSendInfo;
pBilink=pConn->PendingConnSendQ.next;
pSendInfo=CONTAINING_RECORD(pBilink, SENDINFO, PendingConnSendQ);
Delete(pBilink);
DPF(8,"QueueNextSend: Queuing pConn %x pSendInfo %x\n",pConn,pSendInfo);
QueueForSend(pgd,pSendInfo);
}
DPF(8,"<==QueueNextSend pConn %x",pConn);
LeaveCriticalSection(&pgd->csFast);
}
/*=============================================================================
FastCombine - see if this socket should be made bidirectional
Description:
There are 3 cases where we want to combine connections.
1. We have accepted the connection, we receive data and that
data tells us the back connection is an existing outbound
connection. In this case we combine the connections
a. The return address is the same as the from address,
in this case it is a NEW client, and we mark it so
and will use the same connection for outbound traffic
b. The return address is different thant the from address
in this case it is an OLD client, and we mark it so
and will need to establish the outbound connection
later.
2. We receive data on a connection and there is no outbound
connection yet, but inbound and outbound connections are
different. We know that it is an "old" client and
eventually we will have to connect back. We mark the
connection as OLD and when the connection back is
made it will use this same Connection since it will match
up on the target address.
Parameters:
pgd - Service Provider's global data blob for this instance
pConn - connection to put in the hash table.
psockaddr - outbound socket address.
Return Values:
PPLAYERCONN pConn - if this isn't the same as the pConn on the way
in, then the connections have been combined.
- the old connection will disappear when decrefed.
- a reference is added for the returned connection
if it is not the original.
Note: csFast Held across this call. Nothing here is supposed to block...?
-----------------------------------------------------------------------------*/
PPLAYERCONN FastCombine(LPGLOBALDATA pgd, PPLAYERCONN pConn, SOCKADDR *psockaddr_in)
{
PPLAYERCONN pConnFind=NULL;
SOCKADDR sockaddr,*psockaddr;
// See if there is already a player with this target address...
DPF(8,"==>FastCombine pConn %x\n",pConn);
DEBUGPRINTADDR(8,"==>FastCombine saddr",psockaddr_in);
#if USE_RSIP
if(pgd->sRsip != INVALID_SOCKET){
HRESULT hr;
hr=rsipQueryLocalAddress(pgd, TRUE, psockaddr_in, &sockaddr);
if(hr==DP_OK){
psockaddr=&sockaddr;
} else {
psockaddr=psockaddr_in;
}
} else {
psockaddr=psockaddr_in;
}
#elif USE_NATHELP
if(pgd->pINatHelp){
HRESULT hr;
hr=IDirectPlayNATHelp_QueryAddress(
pgd->pINatHelp,
&pgd->INADDRANY,
psockaddr_in,
&sockaddr,
sizeof(SOCKADDR_IN),
DPNHQUERYADDRESS_TCP|DPNHQUERYADDRESS_CACHENOTFOUND
);
if(hr==DP_OK){
psockaddr=&sockaddr;
} else {
psockaddr=psockaddr_in;
}
} else {
psockaddr=psockaddr_in;
}
#else
psockaddr=psockaddr_in;
#endif
if(!pConn->bCombine) // don't combine more than once, we can't handle it.
{
pConnFind=FindPlayerBySocket(pgd, psockaddr);
if(pConnFind){
// We already have a connection to this guy. See if the back-connection
// is dead, if it is merge the two.
ASSERT(pConnFind != pConn);
if(!(pConnFind->dwFlags & (PLYR_ACCEPTED|PLYR_ACCEPT_PENDING))){
// In order to get here, the client must be an old style client.
// Otherwise, the connect for the outbound would have failed, since
// we would be re-using the address.
// Already correctly in the socket hash.
// Old conn gets pulled from pending list by CleanPlayerConn.
ASSERT(pConnFind->sSocketIn == INVALID_SOCKET);
DPF(8,"FastCombine: Merging Connections pConn %x, pConnFound %x\n",pConn,pConnFind);
DUMPCONN(pConn,3);
DUMPCONN(pConnFind,3);
//
// Merge the receive socket into the outbound player connection.
//
// copy socket information
pConnFind->sSocketIn = pConn->sSocketIn;
memcpy(&pConnFind->IOnlySock.sockaddr, &pConn->IOnlySock.sockaddr, sizeof(SOCKADDR));
// copy over receive data.
pConnFind->cbExpected = pConn->cbExpected;
pConnFind->cbReceived = pConn->cbReceived;
if(pConn->pReceiveBuffer != pConn->pDefaultReceiveBuffer){
pConnFind->pReceiveBuffer = pConn->pReceiveBuffer;
pConnFind->cbReceiveBuffer = pConn->cbReceiveBuffer;
pConn->pReceiveBuffer = pConn->pDefaultReceiveBuffer;
} else {
ASSERT(pConn->cbReceiveBuffer == DEFAULT_RECEIVE_BUFFERSIZE);
memcpy(pConnFind->pReceiveBuffer, pConn->pReceiveBuffer, pConn->cbReceived);
}
pConnFind->dwFlags |= (PLYR_ACCEPTED | PLYR_OLD_CLIENT);
// point events to the correct connection. Overrides old conn's select.
// Do this first so we don't drop any events.
pConnFind->lNetEventsSocketIn=pConn->lNetEventsSocketIn;
pConnFind->lNetEventsSocket=pConn->lNetEventsSocket;
FastPlayerEventSelect(pgd, pConnFind, TRUE);
// clean up old connection, but don't close the socket
pConn->dwFlags &= ~(PLYR_ACCEPTED);
pConn->sSocketIn = INVALID_SOCKET;
ASSERT(pConn->sSocket==INVALID_SOCKET);
CleanPlayerConn(pgd, pConn, FALSE);
pConn->bCombine=TRUE; // tell receive thread, to reload.
DecRefConnExist(pgd, pConn); // destroy existence ref
DPF(8,"MergedConn pConnFound%x\n",pConnFind);
DUMPCONN(pConnFind,3);
pConnFind->bCombine=TRUE; // to prevent re-combine.
if(pConn->bTrusted){
pConnFind->bTrusted=TRUE;
}
// leaves 1 reference for the caller.
return pConnFind;
}
DecRefConn(pgd, pConnFind); // found but can't combine... dump find ref.
}
} else {
DPF(0,"Called Fast Combine with already combined connection, may be bad\n");
}
// if we got here, we didn't combine.
if(bSameAddr(psockaddr,&pConn->IOnlySock.sockaddr)){
// If destination and source sockets match
// Promote this one to do both
pConn->sSocket = pConn->sSocketIn;
pConn->sSocketIn = INVALID_SOCKET;
pConn->dwFlags |= (PLYR_NEW_CLIENT | PLYR_CONNECTED);
if(!(pConn->dwFlags & PLYR_SOCKHASH)){// Hmmm, maybe always already in it.
AddConnToSocketHash(pgd, pConn);
}
RemoveConnFromPendingList(pgd, pConn);
DPF(8,"FastCombine: Promoted Connection to Bi-Directional %x\n",pConn);
DUMPCONN(pConn,3);
} else {
ASSERT(!(pConn->dwFlags & PLYR_NEW_CLIENT));
pConn->dwFlags |= PLYR_OLD_CLIENT;
// Remove from inbound hash, change to outbound hash.
memcpy(&pConn->IOSock.sockaddr, psockaddr, sizeof(SOCKADDR));
if(pConnFind && (pConnFind->dwFlags & (PLYR_CONNECTED|PLYR_CONN_PENDING))){
// already connecting...
} else {
// ok this is the back connection.
RemoveConnFromPendingList(pgd, pConn);
RemoveConnFromSocketHash(pgd, pConn);
AddConnToSocketHash(pgd, pConn);
}
DPF(8,"FastCombine: Connection is Old Client %x\n",pConn);
DUMPCONN(pConn,3);
}
FastPlayerEventSelect(pgd,pConn,TRUE); // make sure we're listening to all the right things.
DPF(8,"<==FastCombine\n");
return pConn;
}
/*=============================================================================
FastDropInbound - drop the inbound port for an old style client.
Description:
Parameters:
pgd - Service Provider's global data blob for this instance
pConn - connection to put in the hash table.
Return Values:
-----------------------------------------------------------------------------*/
VOID FastDropInbound(LPGLOBALDATA pgd, PPLAYERCONN pConn)
{
LINGER Linger;
int err;
LPREPLYLIST prd;
#ifdef DEBUG
DWORD dwTime;
dwTime=timeGetTime();
#endif
// See if there is already a player with this target address...
DPF(8, "==>FastDropInbound pConn %x",pConn);
pConn->dwFlags &= ~(PLYR_OLD_CLIENT|PLYR_ACCEPTED|PLYR_ACCEPT_PENDING);
if (pConn->sSocketIn != INVALID_SOCKET)
{
// Hard close inbound socket to avoid TIME_WAIT.
BOOL bNoLinger=TRUE;
if (SOCKET_ERROR == setsockopt( pConn->sSocketIn,SOL_SOCKET,SO_DONTLINGER,(char FAR *)&bNoLinger,sizeof(bNoLinger)))
{
DPF(0, "FastDropInbound:Couldn't set linger to \"don't linger\".");
}
}
RemoveConnFromPendingList(pgd, pConn);
if(pConn->sSocketIn != INVALID_SOCKET)
{
err=g_WSAEventSelect(pConn->sSocketIn, 0, 0);
if (err)
{
err=GetLastError();
DPF(8, "Error trying to deselect sSocketIn %d.",pConn->sSocketIn);
}
else
{
DPF(8, "Deselected socket %d.",pConn->sSocketIn);
}
ENTER_DPSP();
prd=SP_MemAlloc(sizeof(REPLYLIST));
if (!prd)
{
LEAVE_DPSP();
DPF(1, "Closing Socket %d immediately.", pConn->sSocketIn);
myclosesocket(pgd,pConn->sSocketIn);
}
else
{
DPF(4, "Beginning delayed socket %d close.", pConn->sSocketIn);
// very tricky, overloading the reply close list to close this socket with our own linger...
prd->pNextReply=pgd->pReplyCloseList;
pgd->pReplyCloseList=prd;
prd->sSocket=pConn->sSocketIn;
prd->tSent=timeGetTime();
prd->lpMessage=NULL;
LEAVE_DPSP();
}
pConn->sSocketIn = INVALID_SOCKET;
}
//memset(&pConn->IOnlySock,0,sizeof(pConn->IOnlySock));
//
// reset receive information.
//
// Free extra buffer.
if (pConn->pReceiveBuffer != pConn->pDefaultReceiveBuffer)
{
SP_MemFree(pConn->pReceiveBuffer);
}
pConn-> cbReceiveBuffer=DEFAULT_RECEIVE_BUFFERSIZE;
pConn->cbReceived=0;
pConn->cbExpected=0;
#ifdef DEBUG
dwTime = timeGetTime()-dwTime;
if(dwTime > 1000)
{
DPF(0, "Took way too long in FastDropInbound, elapsed %d ms.",dwTime);
//DEBUG_BREAK(); // removed break due to stress hits.
}
#endif
// Will shut down select for inbound.(done before close now.
//FastPlayerEventSelect(pgd,pConn,TRUE);
DPF(8, "<==FastDropInbound");
}
/*=============================================================================
ProcessConnEvents - handle the events on a connection.
Description:
Parameters:
pgd - Service Provider's global data blob for this instance
pConn - connection to put in the hash table.
pSockEvents - socket events on the bi-directional socket OR NULL
pSockInEvents - socket events for the inbound only socket OR NULL
Return Values:
PPLAYERCONN - removed from hash, here it is.
NULL - couldn't find it.
-----------------------------------------------------------------------------*/
HRESULT ProcessConnEvents(
LPGLOBALDATA pgd,
PPLAYERCONN pConn,
LPWSANETWORKEVENTS pSockEvents,
LPWSANETWORKEVENTS pSockInEvents
)
{
WSANETWORKEVENTS SockEvents;
HRESULT hr=DP_OK;
INT err;
PPLAYERCONN pConnIn; // connection passed by FastStreamReceiveThreadProc;
pConnIn=pConn;
DPF(8,"==>ProcessConnEvents pConn %x\n",pConn);
// store in the conn, so downstream routines know what we're processing.
if(pSockEvents){
pConn->lNetEventsSocket=pSockEvents->lNetworkEvents;
} else {
pConn->lNetEventsSocket=0;
}
if(pSockInEvents){
pConn->lNetEventsSocketIn=pSockInEvents->lNetworkEvents;
} else {
pConn->lNetEventsSocketIn=0;
}
if(pSockEvents){
DPF(8,"SockEvents %x pConn %x\n",pSockEvents->lNetworkEvents,pConn);
if(pSockEvents->lNetworkEvents & FD_READ){
// Keep reading until all the readings done.
ASSERT(!pSockInEvents);
ASSERT(!(pConn->dwFlags & PLYR_OLD_CLIENT));
pConn->dwFlags |= (PLYR_NEW_CLIENT|PLYR_ACCEPTED);
do {
// will indicate data if whole message received.
hr=FastReceive(pgd, &pConn); // can drop csFast
if(hr!=DP_OK){
goto exit;
}
err=g_WSAEnumNetworkEvents(pConn->sSocket, 0, &SockEvents);
if(err==SOCKET_ERROR){
err = WSAGetLastError();
DPF(8,"Error on EnumNetworkEvents, LastError = %d\n",err);
goto exit;
} else {
DPF(8,"ProcessConnEvents, Polling Sock NetEvents pConn %d Events %x\n",pConn, SockEvents.lNetworkEvents);
}
if(SockEvents.lNetworkEvents & FD_CLOSE && !(pSockEvents->lNetworkEvents & FD_CLOSE)){
pSockEvents->lNetworkEvents |= FD_CLOSE;
pSockEvents->iErrorCode[FD_CLOSE_BIT] = SockEvents.iErrorCode[FD_CLOSE_BIT];
}
} while (SockEvents.lNetworkEvents & FD_READ);
}
if(pSockEvents->lNetworkEvents & FD_WRITE){
// connection succeeded, send any pending sends now
QueueNextSend(pgd,pConn);
pConn->dwFlags |= PLYR_CONNECTED;
pConn->dwFlags &= ~(PLYR_CONN_PENDING);
g_WSAEventSelect(pConn->sSocket, pgd->EventHandles[pConn->iEventHandle], FD_READ|FD_CLOSE);
}
if(pSockEvents->lNetworkEvents & FD_CONNECT)
{
// Check the connect status.
if(pSockEvents->iErrorCode[FD_CONNECT_BIT]){
DPF(0,"Connect Error %d\n",pSockEvents->iErrorCode[FD_CONNECT_BIT]);
hr=DPERR_CONNECTIONLOST;
goto exit;
}
// don't want to know about connect any more...
pConn->dwFlags |= PLYR_CONNECTED;
pConn->dwFlags &= ~(PLYR_CONN_PENDING);
g_WSAEventSelect(pConn->sSocket, pgd->EventHandles[pConn->iEventHandle], FD_READ|FD_WRITE|FD_CLOSE);
}
if(pSockEvents->lNetworkEvents & FD_CLOSE)
{
DPF(8,"Outbound (Maybe I/O) Connection Closed\n");
hr=DPERR_CONNECTIONLOST;
goto exit;
}
}
if(pSockInEvents)
{
ASSERT(!(pConn->dwFlags & PLYR_NEW_CLIENT));
DPF(8,"SockEvents (IOnly) %x pConn %x\n",pSockInEvents->lNetworkEvents, pConn);
// Need to read first, don't want to drop the data on close
if(pSockInEvents->lNetworkEvents & FD_READ)
{
do{
// Careful here, we may combine connections changing pConn.
hr=FastReceive(pgd, &pConn); // can drop csFast
if(hr!=DP_OK)
{
FastDropInbound(pgd, pConn);
hr=DP_OK;
goto exit;
}
if(pConn->sSocketIn == INVALID_SOCKET){
// new pConn may be bi-directional.
if(pConn->sSocket == INVALID_SOCKET){
hr=DPERR_CONNECTIONLOST;
goto exit;
} else {
hr=DP_OK;
goto exit;
}
}
err=g_WSAEnumNetworkEvents(pConn->sSocketIn, 0, &SockEvents);
if(err==SOCKET_ERROR){
err = WSAGetLastError();
DPF(8,"Error on EnumNetworkEvents, LastError = %d\n",err);
goto exit;
} else {
DPF(8,"ProcessConnEvents, Polling SockIn NetEvents pConn %x Events %x\n",pConn,SockEvents.lNetworkEvents);
}
if((SockEvents.lNetworkEvents & FD_CLOSE) && !(pSockInEvents->lNetworkEvents & FD_CLOSE)){
pSockInEvents->lNetworkEvents |= FD_CLOSE;
pSockInEvents->iErrorCode[FD_CLOSE_BIT] = SockEvents.iErrorCode[FD_CLOSE_BIT];
}
} while (SockEvents.lNetworkEvents & FD_READ);
}
if(pSockInEvents->lNetworkEvents & FD_CLOSE)
{
if(pConn->sSocket == INVALID_SOCKET){
DPF(8,"ProcessConn Events, Got Close on Inbound Only, returning ConnectionLost\n");
ASSERT(!(pConn->dwFlags & (PLYR_CONN_PENDING|PLYR_CONNECTED)));
hr=DPERR_CONNECTIONLOST;
} else {
DPF(8,"ProcessConn Events, Got Close on I/O Connection dropping inbound only.\n");
FastDropInbound(pgd, pConn);
hr=DP_OK;
}
goto exit;
}
}
exit:
pConn->lNetEventsSocket=0;
pConn->lNetEventsSocketIn=0;
if(pConn != pConnIn){
// During a call to FastReceive, connections were combined and
// we were given a reference, now we drop that reference.
DecRefConn(pgd, pConn);
}
DPF(8,"<==ProcessConnEvents hr=0x%x\n",hr);
return hr;
}
/*=============================================================================
FastAccept - accept a connection
Description:
Parameters:
pgd - Service Provider's global data blob for this instance
pNetEvents - socket events on the accept socket
Return Values:
Note: csFast held across this call. Nothing here should block.
-----------------------------------------------------------------------------*/
VOID FastAccept(LPGLOBALDATA pgd, LPWSANETWORKEVENTS pNetEvents)
{
SOCKADDR sockaddr;
INT addrlen = sizeof(sockaddr);
SOCKET sSocket;
PPLAYERCONN pConn;
UINT err; // last error
DPF(8,"==>FastAccept\n");
sSocket = accept(pgd->sSystemStreamSocket,&sockaddr,&addrlen);
if (INVALID_SOCKET == sSocket)
{
err = WSAGetLastError();
DPF(2,"FastAccept: stream accept error - err = %d socket = %d",err,(DWORD)sSocket);
DEBUG_BREAK();
} else {
// All our sockets have KEEPALIVE...
BOOL bTrue = TRUE;
DEBUGPRINTADDR(5,"FastAccept - accepted connection from",&sockaddr);
// turn ON keepalive
if (SOCKET_ERROR == setsockopt(sSocket, SOL_SOCKET, SO_KEEPALIVE, (CHAR FAR *)&bTrue, sizeof(bTrue)))
{
err = WSAGetLastError();
DPF(0,"Failed to turn ON keepalive - continue : err = %d\n",err);
}
// add the new socket to our receive q
// need to allocate a connection structure, lets see if someone is waiting on this accept
pConn=FindPlayerBySocket(pgd, &sockaddr);
if(pConn){
if(pConn->sSocket == INVALID_SOCKET){
// we found the connection because a connect is waiting for it.
ASSERT(pConn->dwFlags & PLYR_ACCEPT_PENDING);
ASSERT(pConn->dwFlags & PLYR_NEW_CLIENT);
ASSERT(bSameAddr(&sockaddr, &pConn->IOSock.sockaddr));
pConn->sSocket = sSocket;
pConn->dwFlags &= ~(PLYR_ACCEPT_PENDING);
pConn->dwFlags |= (PLYR_CONNECTED|PLYR_ACCEPTED);
FastPlayerEventSelect(pgd,pConn,TRUE);
DPF(8,"Found Pending Connection, now connected\n");
DUMPCONN(pConn,3);
} else {
if(TRUE /*pgd->bSeparateIO*/){
// more work for Win9x < Mill
// 8/30/00 ao - now we turn this on in all cases because we need to allow
// for a NATed client to have different inbound and outbound connections to
// workaround the NAT PAST bug where it picks a random from port for
// the outbound link when we haven't received before sending on an ASSIGNED port.
DPF(0,"New client connecting back to me, but I treat as old for compat\n");
pConn->sSocketIn=sSocket;
pConn->dwFlags |= PLYR_ACCEPTED|PLYR_OLD_CLIENT;
pConn->bCombine=TRUE;
FastPlayerEventSelect(pgd,pConn,TRUE);
} else {
DPF(0,"Nice race, already have a connection pConn %x, re-use\n", pConn);
closesocket(sSocket);
}
}
DecRefConn(pgd, pConn); // remove reference from FindPlayerBySocket().
} else {
if(pConn=FindConnInPendingList(pgd, &sockaddr)){
// This guy's in the pending list, blow old conn away.
DPF(8,"Found Accept for Player in Pending List, blow away old one\n");
CleanPlayerConn(pgd, pConn, TRUE);
DecRefConnExist(pgd, pConn); // dump existence ref.
DecRefConn(pgd, pConn); // dump our ref.
}
//
// No connection, we need to create one.
//
// make sure we have room.
if(pgd->nEventSlotsAvail && (pConn = CreatePlayerConn(pgd, DPID_UNKNOWN, &sockaddr))){
DPF(8,"Creating new Connection for Accept %x\n",pConn);
// put on pending list...
pConn->sSocketIn = sSocket;
AddConnToPendingList(pgd, pConn);
pConn->dwFlags |= PLYR_ACCEPTED;
FastPlayerEventSelect(pgd, pConn, TRUE);
} else {
// No room for more accept events ... blow this socket out!
LINGER Linger;
DPF(0,"FastAccept: VERY BAD, Out of Event Slots, can't accept any new connections, killing this one\n");
Linger.l_onoff=FALSE;
Linger.l_linger=0;
if( SOCKET_ERROR == setsockopt( sSocket,SOL_SOCKET,SO_LINGER,(char FAR *)&Linger, sizeof(Linger) ) ){
err = WSAGetLastError();
DPF(0,"Couldn't set linger on socket, can't kill now, so it will be an orphan...bad.bad.bad\n");
} else {
// don't need to shutdown the socket, since we don't have any data on it.
myclosesocket(pgd,sSocket);
}
}
}
}
DPF(8,"<==FastAccept\n");
}
/*=============================================================================
FastInternalReliableSend - reliable send using fast socket code.
Description:
Parameters:
Return Values:
-----------------------------------------------------------------------------*/
HRESULT FastInternalReliableSend(LPGLOBALDATA pgd, LPDPSP_SENDDATA psd, SOCKADDR *lpSockAddr)
{
HRESULT hr=DP_OK;
SOCKET sSocket = INVALID_SOCKET;
UINT err;
PPLAYERCONN pConn=NULL;
LPSENDINFO pSendInfo=NULL;
PCHAR pBuffer=NULL;
DPID idPlayerTo;
DPF(6, "FastInternalReliableSend: Parameters: (0x%x, 0x%x, 0x%x)",
pgd, psd, lpSockAddr);
EnterCriticalSection(&pgd->csFast);
if(psd->idPlayerTo){
idPlayerTo=psd->idPlayerTo;
} else {
idPlayerTo=DPID_UNKNOWN;
}
pConn = GetPlayerConn(pgd, idPlayerTo, lpSockAddr); // adds a ref
if(!pConn){
hr=DPERR_CONNECTIONLOST;
goto exit;
}
// Always go async, since we are on a non-blocking mode socket.
{
// make this puppy asynchronous.... malloc ICK!
pSendInfo = pgd->pSendInfoPool->Get(pgd->pSendInfoPool);
pBuffer = SP_MemAlloc(psd->dwMessageSize);
if(!pSendInfo || !pBuffer){
hr=DPERR_OUTOFMEMORY;
goto CLEANUP_EXIT;
}
SetReturnAddress(psd->lpMessage,pgd->sSystemStreamSocket,SERVICE_SADDR_PUBLIC(pgd));
memcpy(pBuffer, psd->lpMessage, psd->dwMessageSize);
pSendInfo->SendArray[0].buf = pBuffer;
pSendInfo->SendArray[0].len = psd->dwMessageSize;
pSendInfo->iFirstBuf = 0;
pSendInfo->cBuffers = 1;
pSendInfo->sSocket = pConn->sSocket;
//CommonInitForSend
pSendInfo->pConn = pConn;
pSendInfo->dwMessageSize= psd->dwMessageSize;
pSendInfo->dwUserContext= 0;
pSendInfo->RefCount = 3; // one for completion, 1 for this routine, 1 for async completion of send.
pSendInfo->pgd = pgd;
pSendInfo->lpISP = pgd->pISP;
pSendInfo->Status = DP_OK;
pSendInfo->idTo = psd->idPlayerTo;
pSendInfo->idFrom = psd->idPlayerFrom;
pSendInfo->dwSendFlags = psd->dwFlags|DPSEND_ASYNC;
pSendInfo->dwFlags = SI_RELIABLE | SI_INTERNALBUFF;
EnterCriticalSection(&pgd->csSendEx);
InsertBefore(&pSendInfo->PendingSendQ,&pgd->PendingSendQ);
pgd->dwBytesPending += psd->dwMessageSize;
pgd->dwMessagesPending += 1;
LeaveCriticalSection(&pgd->csSendEx);
// End CommonInit for Send.
if((pConn->dwFlags & PLYR_CONNECTED) && EMPTY_BILINK(&pConn->PendingConnSendQ) && !pConn->bSendOutstanding){
QueueForSend(pgd, pSendInfo); // send it
} else {
QueueSendOnConn(pgd, pConn, pSendInfo);
}
wsaoDecRef(pSendInfo);
}
// success
hr = DP_OK;
exit:
if(pConn){
DecRefConn(pgd,pConn);
}
LeaveCriticalSection(&pgd->csFast);
DPF(6, "FastInternalReliableSend: Returning: [0x%lx] (exit)", hr);
return hr;
CLEANUP_EXIT:
if(pConn){
DecRefConn(pgd, pConn); // balance Get
}
LeaveCriticalSection(&pgd->csFast);
if(pBuffer){
SP_MemFree(pBuffer);
}
if(pSendInfo){
SP_MemFree(pSendInfo);
}
DPF(6, "FastInternalReliableSend: Returning: [0x%lx] (cleanup exit)", hr);
return hr;
}
/*=============================================================================
FastInternalReliableSendEx - reliable send using fast socket code.
Description:
Parameters:
Return Values:
-----------------------------------------------------------------------------*/
HRESULT FastInternalReliableSendEx(LPGLOBALDATA pgd, LPDPSP_SENDEXDATA psd, LPSENDINFO pSendInfo, SOCKADDR *lpSockAddr)
{
HRESULT hr=DP_OK;
SOCKET sSocket = INVALID_SOCKET;
UINT err;
PPLAYERCONN pConn=NULL;
PCHAR pBuffer=NULL;
DPID idPlayerTo;
UINT i;
DWORD dwOffset;
DPF(6, "FastInternalReliableSendEx: Parameters: (0x%x, 0x%x, 0x%x, 0x%x)",
pgd, psd, pSendInfo, lpSockAddr);
EnterCriticalSection(&pgd->csFast);
if(psd->idPlayerTo){
idPlayerTo=psd->idPlayerTo;
} else {
idPlayerTo=DPID_UNKNOWN;
}
pConn = GetPlayerConn(pgd, idPlayerTo, lpSockAddr); // adds a ref
if(!pConn){
hr=DPERR_CONNECTIONLOST;
goto exit;
}
// SECURITY: we will trust anyone we send data to.
pConn->bTrusted = TRUE;
// Always go async, since we are on a non-blocking mode socket.
{
// make this puppy asynchronous.... malloc ICK!
if(!(psd->dwFlags & DPSEND_ASYNC))
{
pBuffer = SP_MemAlloc(psd->dwMessageSize+sizeof(MESSAGEHEADER));
if(!pBuffer){
hr=DPERR_OUTOFMEMORY;
goto CLEANUP_EXIT;
}
}
pSendInfo->sSocket = pConn->sSocket;
//CommonInitForSend
pSendInfo->pConn = pConn;
pSendInfo->dwMessageSize = psd->dwMessageSize;
pSendInfo->dwUserContext = (DWORD_PTR)psd->lpDPContext;
pSendInfo->RefCount = 3; // one for completion, 1 for this routine, 1 for async completion of send.
pSendInfo->pgd = pgd;
pSendInfo->lpISP = pgd->pISP;
pSendInfo->Status = DP_OK;
pSendInfo->idTo = psd->idPlayerTo;
pSendInfo->idFrom = psd->idPlayerFrom;
pSendInfo->dwSendFlags = psd->dwFlags|DPSEND_ASYNC;
pSendInfo->iFirstBuf = 0;
if(psd->dwFlags & DPSEND_ASYNC) {
pSendInfo->dwFlags = SI_RELIABLE;
pSendInfo->cBuffers = psd->cBuffers+1;
} else {
// in sync case we need to copy the buffers since the upper layer
// is expecting ownership back immediately. Sync sends can't expect
// thrilling performance anyway so this should not show up in perf.
// copy message into one contiguous buffer.
dwOffset=0;
for( i = 0 ; i < psd->cBuffers+1 ; i++)
{
memcpy(pBuffer+dwOffset, pSendInfo->SendArray[i].buf, pSendInfo->SendArray[i].len);
dwOffset += pSendInfo->SendArray[i].len;
}
pSendInfo->dwFlags = SI_RELIABLE | SI_INTERNALBUFF;
pSendInfo->cBuffers = 1;
pSendInfo->SendArray[0].buf = pBuffer;
pSendInfo->SendArray[0].len = psd->dwMessageSize+sizeof(MESSAGEHEADER);
}
EnterCriticalSection(&pgd->csSendEx);
InsertBefore(&pSendInfo->PendingSendQ,&pgd->PendingSendQ);
pgd->dwBytesPending += psd->dwMessageSize;
pgd->dwMessagesPending += 1;
LeaveCriticalSection(&pgd->csSendEx);
// End CommonInit for Send.
if((pConn->dwFlags & PLYR_CONNECTED) && EMPTY_BILINK(&pConn->PendingConnSendQ) && !pConn->bSendOutstanding){
QueueForSend(pgd, pSendInfo); // send it
} else {
QueueSendOnConn(pgd, pConn, pSendInfo);
}
wsaoDecRef(pSendInfo);
}
// success
if(psd->dwFlags & DPSEND_ASYNC)
{
hr = DPERR_PENDING;
}else {
hr = DP_OK;
}
exit:
if(pConn){
DecRefConn(pgd,pConn);
}
LeaveCriticalSection(&pgd->csFast);
DPF(6, "FastInternalReliableSendEx: Returning: [0x%lx] (exit)", hr);
return hr;
CLEANUP_EXIT:
if(pConn){
DecRefConn(pgd, pConn); // balance Get
}
LeaveCriticalSection(&pgd->csFast);
if(pBuffer){
SP_MemFree(pBuffer);
}
DPF(6, "FastInternalReliableSendEx: Returning: [0x%lx] (cleanup exit)", hr);
return hr;
}
/*=============================================================================
FastReply - reliable reply using fast socket code.
Description:
Parameters:
Return Values:
-----------------------------------------------------------------------------*/
HRESULT FastReply(LPGLOBALDATA pgd, LPDPSP_REPLYDATA prd, DPID dwPlayerID)
{
HRESULT hr=DP_OK;
SOCKET sSocket = INVALID_SOCKET;
UINT err;
PPLAYERCONN pConn=NULL;
LPSENDINFO pSendInfo=NULL;
PCHAR pBuffer=NULL;
SOCKADDR *psaddr;
LPMESSAGEHEADER phead;
DPF(8,"==>FastReply\n");
phead=(LPMESSAGEHEADER)prd->lpSPMessageHeader;
psaddr=&phead->sockaddr;
if(dwPlayerID == 0){
dwPlayerID = DPID_UNKNOWN;
}
EnterCriticalSection(&pgd->csFast);
pConn = GetPlayerConn(pgd, dwPlayerID, psaddr); // adds a ref
if(!pConn){
hr = DPERR_CONNECTIONLOST;
goto exit;
}
// make this puppy asynchronous.... malloc ICK!
pSendInfo = pgd->pSendInfoPool->Get(pgd->pSendInfoPool);
pBuffer = SP_MemAlloc(prd->dwMessageSize);
if(!pSendInfo || !pBuffer){
hr=DPERR_OUTOFMEMORY;
goto CLEANUP_EXIT;
}
SetReturnAddress(prd->lpMessage,pgd->sSystemStreamSocket,SERVICE_SADDR_PUBLIC(pgd));
memcpy(pBuffer, prd->lpMessage, prd->dwMessageSize);
pSendInfo->SendArray[0].buf = pBuffer;
pSendInfo->SendArray[0].len = prd->dwMessageSize;
pSendInfo->iFirstBuf = 0;
pSendInfo->cBuffers = 1;
pSendInfo->sSocket = pConn->sSocket;
//CommonInitForSend
pSendInfo->pConn = pConn;
pSendInfo->dwMessageSize= prd->dwMessageSize;
pSendInfo->dwUserContext= 0;
pSendInfo->RefCount = 3; // one for send routine, one for completion, 1 for this routine
pSendInfo->pgd = pgd;
pSendInfo->lpISP = pgd->pISP;
pSendInfo->Status = DP_OK;
pSendInfo->idTo = dwPlayerID;
pSendInfo->idFrom = 0;
pSendInfo->dwSendFlags = DPSEND_GUARANTEE|DPSEND_ASYNC;
pSendInfo->Status = DP_OK;
pSendInfo->dwFlags = SI_RELIABLE | SI_INTERNALBUFF;
EnterCriticalSection(&pgd->csSendEx);
InsertBefore(&pSendInfo->PendingSendQ,&pgd->PendingSendQ);
pgd->dwBytesPending += prd->dwMessageSize;
pgd->dwMessagesPending += 1;
LeaveCriticalSection(&pgd->csSendEx);
DPF(9,"pConn->dwFlags & PLYR_CONNECTED = %x",pConn->dwFlags & PLYR_CONNECTED);
DPF(9,"EMPTY_BILINK PendingConnSendQ = %x",EMPTY_BILINK(&pConn->PendingConnSendQ));
DPF(9,"!pConn->bSendOutstanding = %x",!pConn->bSendOutstanding);
// End CommonInit for Send.
if((pConn->dwFlags & PLYR_CONNECTED) && EMPTY_BILINK(&pConn->PendingConnSendQ) && !pConn->bSendOutstanding){
DPF(9,"==>QueueForSend");
QueueForSend(pgd, pSendInfo); // send it
} else {
DPF(9,"==>QueueSendOnConn");
QueueSendOnConn(pgd, pConn, pSendInfo);
}
wsaoDecRef(pSendInfo);
// success
hr = DP_OK;
exit:
if(pConn){
DecRefConn(pgd, pConn);
}
LeaveCriticalSection(&pgd->csFast);
DPF(8,"<==Fast Reply\n");
return hr;
CLEANUP_EXIT:
if(pConn){
DecRefConn(pgd, pConn); // balance Get
}
LeaveCriticalSection(&pgd->csFast);
if(pBuffer){
SP_MemFree(pBuffer);
}
if(pSendInfo){
SP_MemFree(pSendInfo);
}
return hr;
}