//
// Sample: Overlapped IPv4/IPv6 Server
//
// Files:
// overserver.cpp - this file
// resolve.cpp - Common name resolution routines
// resolve.h - Header file for name resolution routines
//
// Description:
// This sample illustrates simple overlapped IO for TCP and UDP for
// both IPv4 and IPv6. This sample uses the getaddrinfo/getnameinfo
// APIs which allows this application to be IP agnostic. That is the
// desired address family (AF_INET or AF_INET6) can be determined
// simply from the string address passed via the -l command.
//
// For TCP, this sample creates a listening socket for each address family
// available. For each socket, a number of AcceptEx are posted.
// We use one event handle for each overlapped operation. This means
// that we assign an individual I/O operation to each worker thread
// at a time (not on a socket by socket basis). As such, it is possible
// that some reads on a socket may be handled by one worker thread and
// some other reads and writes for the same socket may be handled by
// another thread.
//
// Once an AcceptEx completes, a new socket object is created which
// initiates a number of overlapped receives on the accepted connection.
// Once a read completes, a write is posted back to that client and the
// read is re-posted.
//
// For UDP, a socket is created for each IP address family; however,
// a number of reads are immediately posted on each instead of accepts.
// Once the UDP read is completed, a send back to the source is posted
// and the receive is re-posted.
//
// For example:
// If this sample is called with the following command lines:
// overserver.exe -l fe80::2efe:1234 -e 5150
// overserver.exe -l ::
// Then the server creates an IPv6 socket as an IPv6 address was
// provided.
//
// On the other hand, with the following command line:
// overserver.exe -l 7.7.7.1 -e 5150
// overserver.exe -l 0.0.0.0
// Then the server creates an IPv4 socket.
//
// Calling this sample with no arguments creates both IPv4 and IPv6
// (if installed) servers.
//
// Compile:
// cl -o overserver.exe overserver.cpp resolve.cpp ws2_32.lib
//
// Usage:
// overserver.exe [options]
// -a 4|6 Address family, 4 = IPv4, 6 = IPv6 [default = IPv4]
// -b size Size of send/recv buffer in bytes
// -e port Port number
// -l addr Local address to bind to [default INADDR_ANY for IPv4 or INADDR6_ANY for IPv6]
// -p proto Which protocol to use [default = TCP]
// tcp Use TCP protocol
// udp Use UDP protocol
//
#include
#include
#include
#include
#include
#include
#include "resolve.h"
#define DEFAULT_BUFFER_SIZE 4096 // default buffer size
#define DEFAULT_OVERLAPPED_COUNT 5 // default number of overlapped recvs to post
int gAddressFamily = AF_UNSPEC, // default to unspecified
gSocketType = SOCK_STREAM, // default to TCP socket type
gProtocol = IPPROTO_TCP, // default to TCP protocol
gBufferSize = DEFAULT_BUFFER_SIZE,
gOverlappedCount = DEFAULT_OVERLAPPED_COUNT;
char *gBindAddr = NULL, // local interface to bind to
*gBindPort = "5150"; // local port to bind to
struct _SOCKET_OBJ;
struct _THREAD_OBJ;
//
// This is our per I/O data. It describes a single I/O operation.
//
typedef struct _BUFFER_OBJ
{
WSAOVERLAPPED ol; // Overlapped structure
SOCKET sclient; // Used for AcceptEx client socket
char *buf; // Buffer for send/recv/AcceptEx
int buflen; // Length of the buffer
int operation; // Type of operation submitted
#define OP_ACCEPT 0 // AcceptEx
#define OP_READ 1 // WSARecv/WSARecvFrom
#define OP_WRITE 2 // WSASend?WSASendTo
struct _SOCKET_OBJ *Socket; // SOCKET_OBJ that this I/O belongs to
struct _THREAD_OBJ *Thread; // THREAD_OBJ this I/O is assigned to
SOCKADDR_STORAGE addr; // Remote address (UDP)
int addrlen; // Remote address length
struct _BUFFER_OBJ *next,
*prev;
} BUFFER_OBJ;
//
// This is our per handle data. One of these structures is allocated for
// each socket created by our server.
//
typedef struct _SOCKET_OBJ
{
SOCKET s; // Socket handle for client connection
int af, // Address family of socket (AF_INET or AF_INET6)
bClosing; // Indicates socket is closing
volatile LONG OutstandingOps; // Number of outstanding overlapped ops
BUFFER_OBJ **PendingAccepts; // Array of pending AcceptEx calls (listening socket only)
// Pointers to Microsoft specific extensions (listening socket only)
LPFN_ACCEPTEX lpfnAcceptEx;
LPFN_GETACCEPTEXSOCKADDRS lpfnGetAcceptExSockaddrs;
CRITICAL_SECTION SockCritSec; // Synchronize access to this SOCKET_OBJ
struct _SOCKET_OBJ *next; // Used to chain SOCKET_OBJ together
} SOCKET_OBJ;
//
// Allocated for each thread spawned. Each overlapped I/O issued on a socket is
// assigned to one of the threads in the thread pool.
//
typedef struct _THREAD_OBJ
{
BUFFER_OBJ *BufferList; // Linked list of all sockets allocated
int EventCount; // How many events are in the array to wait on?
HANDLE Event; // Used to signal new clients assigned
// to this thread
HANDLE Thread; // Handle to the curren thread
HANDLE Handles[MAXIMUM_WAIT_OBJECTS]; // Array of socket's event handles
CRITICAL_SECTION ThreadCritSec; // Protect access to SOCKET_OBJ lists
struct _THREAD_OBJ *next; // Next thread object in list
} THREAD_OBJ;
THREAD_OBJ *gChildThreads=NULL; // List of thread objects allocated
int gChildThreadsCount=0; // Number of child threads created
CRITICAL_SECTION gThreadListCritSec;
//
// Statistics counters
//
volatile LONG gBytesRead=0,
gBytesSent=0,
gStartTime=0,
gBytesReadLast=0,
gBytesSentLast=0,
gStartTimeLast=0,
gTotalConnections=0,
gCurrentConnections=0;
//
// Prototypes
//
void AssignIoToThread(BUFFER_OBJ *buf);
void RemoveBufferFromThread(SOCKET_OBJ *sock, BUFFER_OBJ *buf);
void InsertBufferObj(BUFFER_OBJ **head, BUFFER_OBJ *obj);
//
// Function: usage
//
// Description:
// Prints usage information and exits the process.
//
void usage(char *progname)
{
fprintf(stderr, "usage: %s [-a 4|6] [-e port] [-l local-addr] [-p udp|tcp]\n",
progname);
fprintf(stderr, " -a 4|6 Address family, 4 = IPv4, 6 = IPv6 [default = IPv4]\n"
" -b size Buffer size for send/recv [default = %d]\n"
" -e port Port number [default = %s]\n"
" -l addr Local address to bind to [default INADDR_ANY for IPv4 or INADDR6_ANY for IPv6]\n"
" -p tcp|udp Which protocol to use [default = TCP]\n",
gBufferSize,
gBindPort
);
ExitProcess(-1);
}
//
// Function: GetBufferObj
//
// Description:
// Allocate a BUFFER_OBJ. Each send, receive, and accept posted by a
// by the server uses one of these objects. That is, there is one BUFFER_OBJ
// allocated per I/O operation. After the I/O is initiated it is assigned to
// one of the completion threads. To increase performance, a look aside list
// may be used to cache freed BUFFER_OBJ.
//
BUFFER_OBJ *GetBufferObj(SOCKET_OBJ *sock, int buflen)
{
BUFFER_OBJ *newobj=NULL;
// Allocate the object
newobj = (BUFFER_OBJ *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BUFFER_OBJ));
if (newobj == NULL)
{
fprintf(stderr, "GetBufferObj: HeapAlloc failed: %d\n", GetLastError());
ExitProcess(-1);
}
// Allocate the buffer
newobj->buf = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BYTE) *buflen);
if (newobj->buf == NULL)
{
fprintf(stderr, "GetBufferObj: HeapAlloc failed: %d\n", GetLastError());
ExitProcess(-1);
}
newobj->buflen = buflen;
newobj->addrlen = sizeof(newobj->addr);
newobj->Socket = sock;
// Create the event that is to be signed upon completion
newobj->ol.hEvent = WSACreateEvent();
if (newobj->ol.hEvent == NULL)
{
fprintf(stderr, "GetBufferObj: WSACreateEvent failed: %d\n", WSAGetLastError());
ExitProcess(-1);
}
return newobj;
}
//
// Function: FreeBufferObj
//
// Description:
// Free the buffer object.
//
void FreeBufferObj(BUFFER_OBJ *obj)
{
// Close the event
WSACloseEvent(obj->ol.hEvent);
obj->ol.hEvent = NULL;
// Free the buffers
HeapFree(GetProcessHeap(), 0, obj->buf);
HeapFree(GetProcessHeap(), 0, obj);
}
//
// Function: GetSocketObj
//
// Description:
// Allocate a socket object and initialize its members. A socket object is
// allocated for each socket created (either by socket or accept).
//
SOCKET_OBJ *GetSocketObj(SOCKET s, int af)
{
SOCKET_OBJ *sockobj=NULL;
sockobj = (SOCKET_OBJ *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SOCKET_OBJ));
if (sockobj == NULL)
{
fprintf(stderr, "GetSocketObj: HeapAlloc failed: %d\n", GetLastError());
ExitProcess(-1);
}
// Initialize the members
sockobj->s = s;
sockobj->af = af;
InitializeCriticalSection(&sockobj->SockCritSec);
return sockobj;
}
//
// Function: FreeSocketObj
//
// Description:
// Frees a socket object along.
//
void FreeSocketObj(SOCKET_OBJ *obj)
{
BUFFER_OBJ *ptr=NULL,
*tmp=NULL;
if (obj->OutstandingOps != 0)
{
return;
}
if (obj->s != INVALID_SOCKET)
{
closesocket(obj->s);
obj->s = INVALID_SOCKET;
}
DeleteCriticalSection(&obj->SockCritSec);
HeapFree(GetProcessHeap(), 0, obj);
}
//
// Function: GetThreadObj
//
// Description:
// Allocate a thread object and initializes its members.
//
THREAD_OBJ *GetThreadObj()
{
THREAD_OBJ *thread=NULL;
thread = (THREAD_OBJ *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(THREAD_OBJ));
if (thread == NULL)
{
fprintf(stderr, "GetThreadObj: HeapAlloc failed: %d\n", GetLastError());
ExitProcess(-1);
}
// Create an event that the thread will wait on. When signaled, the thread
// rebuilds the array of event handles.
thread->Event = WSACreateEvent();
if (thread->Event == NULL)
{
fprintf(stderr, "GetThreadObj: WSACreateEvent failed: %d\n", WSAGetLastError());
ExitProcess(-1);
}
// The first event is always the thread's event
thread->Handles[0] = thread->Event;
thread->EventCount = 1;
InitializeCriticalSection(&thread->ThreadCritSec);
return thread;
}
//
// Function: FreeThreadObj
//
// Description:
// Free a thread object and is member fields.
//
void FreeThreadObj(THREAD_OBJ *thread)
{
WSACloseEvent(thread->Event);
CloseHandle(thread->Thread);
DeleteCriticalSection(&thread->ThreadCritSec);
HeapFree(GetProcessHeap(), 0, thread);
}
//
// Function: InsertBufferObjToThread
//
// Description:
// Insert a buffer object into the list of pending buffers objects for
// the given thread object. If the buffer can fit in the thread's queue,
// NO_ERROR is returned. If the thread is already waiting on the maximum
// allowable events, then SOCKET_ERROR is returned.
//
int InsertBufferObjToThread(THREAD_OBJ *thread, BUFFER_OBJ *buf)
{
int ret;
EnterCriticalSection(&thread->ThreadCritSec);
// See if the thread is full
if (thread->EventCount < MAXIMUM_WAIT_OBJECTS-1)
{
InsertBufferObj(&thread->BufferList, buf);
thread->Handles[thread->EventCount++] = buf->ol.hEvent;
ret = NO_ERROR;
}
else
{
ret = SOCKET_ERROR;
}
LeaveCriticalSection(&thread->ThreadCritSec);
return ret;
}
//
// Function: RenumberEvents
//
// Description:
// This routine goes through the list of pending buffers within a thread
// and rebuilds the array of event handles that the thread waits on. When
// a new connection is accepted and several receive operations are posted,
// they are assigned to a thread and the thread is signaled to indicate
// new I/O has been placed in its queue. The thread needs to reinitialize
// its array so that it may be signaled for completion on that new I/O.
//
void RenumberEvents(THREAD_OBJ *thread)
{
BUFFER_OBJ *bptr=NULL;
int i;
//
// If index 0 is signaled then rebuild the array of event
// handles to wait on
EnterCriticalSection(&thread->ThreadCritSec);
i = 0;
bptr = thread->BufferList;
thread->EventCount = 1;
while (bptr)
{
thread->Handles[thread->EventCount++] = bptr->ol.hEvent;
i++;
bptr = bptr->next;
}
LeaveCriticalSection(&thread->ThreadCritSec);
}
//
// Function: InsertBufferObj
//
// Description:
// This routine inserts a BUFFER_OBJ into a list of BUFFER_OBJs.
// First the end of the list is found and then the new buffer is
// added to the end.
//
void InsertBufferObj(BUFFER_OBJ **head, BUFFER_OBJ *obj)
{
BUFFER_OBJ *end=NULL,
*ptr=NULL;
// Find the end of the list
ptr = *head;
if (ptr)
{
while (ptr->next)
ptr = ptr->next;
end = ptr;
}
obj->next = NULL;
obj->prev = end;
if (end == NULL)
{
// List is empty
*head = obj;
}
else
{
// Put new object at the end
end->next = obj;
obj->prev = end;
}
}
//
// Function: RemoveBufferObj
//
// Description:
// Remove a BUFFER_OBJ from the list.
//
BUFFER_OBJ *RemoveBufferObj(BUFFER_OBJ **head, BUFFER_OBJ *buf)
{
// Make sure list isn't empty
if (*head != NULL)
{
// Fix up the next and prev pointers
if (buf->prev)
buf->prev->next = buf->next;
if (buf->next)
buf->next->prev = buf->prev;
if (*head == buf)
(*head) = buf->next;
}
return buf;
}
//
// Function: ValidateArgs
//
// Description:
// Parses the command line arguments and sets up some global
// variables.
//
void ValidateArgs(int argc, char **argv)
{
int i;
for(i=1; i < argc ;i++)
{
if (((argv[i][0] != '/') && (argv[i][0] != '-')) || (strlen(argv[i]) < 2))
usage(argv[0]);
else
{
switch (tolower(argv[i][1]))
{
case 'a': // address family - IPv4 or IPv6
if (i+1 >= argc)
usage(argv[0]);
if (argv[i+1][0] == '4')
gAddressFamily = AF_INET;
else if (argv[i+1][0] == '6')
gAddressFamily = AF_INET6;
else
usage(argv[0]);
i++;
break;
case 'b': // buffer size for send/recv
if (i+1 >= argc)
usage(argv[0]);
gBufferSize = atol(argv[++i]);
break;
case 'e': // endpoint - port number
if (i+1 >= argc)
usage(argv[0]);
gBindPort = argv[++i];
break;
case 'l': // local address for binding
if (i+1 >= argc)
usage(argv[0]);
gBindAddr = argv[++i];
break;
case 'o': // overlapped count
if (i+1 >= argc)
usage(argv[0]);
gOverlappedCount = atol(argv[++i]);
break;
case 'p': // protocol - TCP or UDP
if (i+1 >= argc)
usage(argv[0]);
if (_strnicmp(argv[i+1], "tcp", 3) == 0)
{
gProtocol = IPPROTO_TCP;
gSocketType = SOCK_STREAM;
}
else if (_strnicmp(argv[i+1], "udp", 3) == 0)
{
gProtocol = IPPROTO_UDP;
gSocketType = SOCK_DGRAM;
}
else
usage(argv[0]);
i++;
break;
default:
usage(argv[0]);
break;
}
}
}
}
//
// Function: PrintStatistics
//
// Description:
// Print the send/recv statistics for the server
//
void PrintStatistics()
{
ULONG bps, tick, elapsed;
tick = GetTickCount();
elapsed = (tick - gStartTime) / 1000;
if (elapsed == 0)
return;
printf("\n");
bps = gBytesSent / elapsed;
printf("Average BPS sent: %lu [%lu]\n", bps, gBytesSent);
bps = gBytesRead / elapsed;
printf("Average BPS read: %lu [%lu]\n", bps, gBytesRead);
elapsed = (tick - gStartTimeLast) / 1000;
if (elapsed == 0)
return;
bps = gBytesSentLast / elapsed;
printf("Current BPS sent: %lu\n", bps);
bps = gBytesReadLast / elapsed;
printf("Current BPS read: %lu\n", bps);
printf("Total Connections : %lu\n", gTotalConnections);
printf("Current Connections: %lu\n", gCurrentConnections);
InterlockedExchange(&gBytesSentLast, 0);
InterlockedExchange(&gBytesReadLast, 0);
gStartTimeLast = tick;
}
//
// Function: PostRecv
//
// Description:
// Post an overlapped receive operation on the socket.
//
int PostRecv(BUFFER_OBJ *recvobj)
{
WSABUF wbuf;
DWORD bytes,
flags;
int rc=NO_ERROR;
EnterCriticalSection(&recvobj->Socket->SockCritSec);
recvobj->operation = OP_READ;
wbuf.buf = recvobj->buf;
wbuf.len = recvobj->buflen;
flags = 0;
if (gProtocol == IPPROTO_TCP)
{
rc = WSARecv(
recvobj->Socket->s,
&wbuf,
1,
&bytes,
&flags,
&recvobj->ol,
NULL
);
}
else
{
rc = WSARecvFrom(
recvobj->Socket->s,
&wbuf,
1,
&bytes,
&flags,
(SOCKADDR *)&recvobj->addr,
&recvobj->addrlen,
&recvobj->ol,
NULL
);
}
if (rc == SOCKET_ERROR)
{
rc = NO_ERROR;
if (WSAGetLastError() != WSA_IO_PENDING)
{
fprintf(stderr, "PostRecv: WSARecv* failed: %d\n", WSAGetLastError());
rc = SOCKET_ERROR;
}
}
// Increment outstanding overlapped operations
InterlockedIncrement(&recvobj->Socket->OutstandingOps);
LeaveCriticalSection(&recvobj->Socket->SockCritSec);
return NO_ERROR;
}
//
// Function: PostSend
//
// Description:
// Post an overlapped send operation on the socket.
//
int PostSend(BUFFER_OBJ *sendobj)
{
WSABUF wbuf;
DWORD bytes;
int rc;
rc = NO_ERROR;
sendobj->operation = OP_WRITE;
wbuf.buf = sendobj->buf;
wbuf.len = sendobj->buflen;
EnterCriticalSection(&sendobj->Socket->SockCritSec);
if (gProtocol == IPPROTO_TCP)
{
rc = WSASend(
sendobj->Socket->s,
&wbuf,
1,
&bytes,
0,
&sendobj->ol,
NULL
);
}
else
{
rc = WSASendTo(
sendobj->Socket->s,
&wbuf,
1,
&bytes,
0,
(SOCKADDR *)&sendobj->addr,
sendobj->addrlen,
&sendobj->ol,
NULL
);
}
if (rc == SOCKET_ERROR)
{
rc = NO_ERROR;
if (WSAGetLastError() != WSA_IO_PENDING)
{
fprintf(stderr, "PostSend: WSASend* failed: %d\n", WSAGetLastError());
rc = SOCKET_ERROR;
}
}
// Increment the outstanding operation count
InterlockedIncrement(&sendobj->Socket->OutstandingOps);
LeaveCriticalSection(&sendobj->Socket->SockCritSec);
return rc;
}
//
// Function: PostAccept
//
// Description:
// Post an overlapped accept on a listening socket.
//
int PostAccept(BUFFER_OBJ *acceptobj)
{
DWORD bytes;
int rc=NO_ERROR;
acceptobj->operation = OP_ACCEPT;
EnterCriticalSection(&acceptobj->Socket->SockCritSec);
// Create the client socket for an incoming connection
acceptobj->sclient = socket(acceptobj->Socket->af, SOCK_STREAM, IPPROTO_TCP);
if (acceptobj->sclient == INVALID_SOCKET)
{
fprintf(stderr, "PostAccept: socket failed: %d\n", WSAGetLastError());
return -1;
}
rc = acceptobj->Socket->lpfnAcceptEx(
acceptobj->Socket->s,
acceptobj->sclient,
acceptobj->buf,
acceptobj->buflen - ((sizeof(SOCKADDR_STORAGE) + 16) * 2),
sizeof(SOCKADDR_STORAGE) + 16,
sizeof(SOCKADDR_STORAGE) + 16,
&bytes,
&acceptobj->ol
);
if (rc == FALSE)
{
rc = NO_ERROR;
if (WSAGetLastError() != WSA_IO_PENDING)
{
fprintf(stderr, "PostAccept: AcceptEx failed: %d\n",
WSAGetLastError());
rc = SOCKET_ERROR;
}
}
// Increment the outstanding overlapped count for this socket
InterlockedIncrement(&acceptobj->Socket->OutstandingOps);
LeaveCriticalSection(&acceptobj->Socket->SockCritSec);
return rc;
}
//
// Function: HandleIo
//
// Description:
// This function handles the IO on a socket. First, the events signaled
// on the socket are enuemrated, then the appropriate handler routine
// for the event is called.
//
void HandleIo(BUFFER_OBJ *buf)
{
SOCKET_OBJ *sock=NULL,
*clientobj=NULL; // New client object for accepted connections
BUFFER_OBJ *recvobj=NULL, // Used to post new receives on accepted connections
&n, bsp; *sendobj=NULL; // Used to post sends for data received
DWORD bytes,
flags;
BOOL bFreeSocketObj;
int error,
rc;
// Extract the SOCKET_OBJ from the BUFFER_OBJ for easy reference
sock = buf->Socket;
error = NO_ERROR;
bFreeSocketObj = FALSE;
InterlockedDecrement(&sock->OutstandingOps);
// Get the results of the overlapped operation that completed
rc = WSAGetOverlappedResult(
sock->s,
&buf->ol,
&bytes,
FALSE,
&flags
);
if (rc == FALSE)
{
error = WSAGetLastError();
fprintf(stderr, "HandleIo: WSAGetOverlappedResult failed: %d\n", error);
if (gProtocol == IPPROTO_TCP)
{
// An error occured on a TCP socket, so remove this I/O and if no
// more I/O is outstanding, free the socket object. Otherwise,
// wait for the remaining I/O on this socket to complete as well.
RemoveBufferFromThread(sock, buf);
FreeBufferObj(buf);
if (InterlockedDecrement(&sock->OutstandingOps) == 0)
{
printf("Freeing socket obj in GetOverlapepdResult\n");
FreeSocketObj(sock);
}
return;
}
}
if (buf->operation == OP_ACCEPT)
{
SOCKADDR_STORAGE *LocalSockaddr=NULL,
*RemoteSockaddr=NULL;
int LocalSockaddrLen,
RemoteSockaddrLen;
// Update the counters
InterlockedExchangeAdd(&gBytesRead, bytes);
InterlockedExchangeAdd(&gBytesReadLast, bytes);
InterlockedIncrement(&gTotalConnections);
InterlockedIncrement(&gCurrentConnections);
// Retrieve the client's address
sock->lpfnGetAcceptExSockaddrs(
buf->buf,
buf->buflen - ((sizeof(SOCKADDR_STORAGE) + 16) * 2),
sizeof(SOCKADDR_STORAGE) + 16,
sizeof(SOCKADDR_STORAGE) + 16,
(SOCKADDR **)&LocalSockaddr,
&LocalSockaddrLen,
(SOCKADDR **)&RemoteSockaddr,
&RemoteSockaddrLen
);
// Under high connection stress this print slows things down
/*
printf("Received connection from: ");
PrintAddress((SOCKADDR *)RemoteSockaddr, RemoteSockaddrLen);
printf("\n");
*/
// Create a new SOCKET_OBJ for client socket
clientobj = GetSocketObj(buf->sclient, buf->Socket->af);
// Echo back any data received with the AcceptEx call
sendobj = GetBufferObj(clientobj, gBufferSize);
// Copy the data from the accept buffer to the send buffer
sendobj->buflen = bytes;
memcpy(sendobj->buf, buf->buf, bytes);
// Assign the send operation to a thread
AssignIoToThread(sendobj);
// Initiate the overlapped send
if (PostSend(sendobj) != NO_ERROR)
{
// In the event of an error, clean up the socket object
RemoveBufferFromThread(clientobj, sendobj);
FreeBufferObj(sendobj);
closesocket(clientobj->s);
clientobj->s = INVALID_SOCKET;
FreeSocketObj(clientobj);
}
PostAccept(buf);
}
else if ((buf->operation == OP_READ) && (error == NO_ERROR))
{
//
// Receive compeleted successfully
//
if ((bytes > 0) || (gProtocol == IPPROTO_UDP))
{
// Increment the counters
InterlockedExchangeAdd(&gBytesRead, bytes);
InterlockedExchangeAdd(&gBytesReadLast, bytes);
// Create a buffer to send
sendobj = buf;
sendobj->buflen = bytes;
// Initiate the send
if (PostSend(sendobj) != NO_ERROR)
{
// In the event of an error, clean up the socket object
RemoveBufferFromThread(sock, sendobj);
FreeBufferObj(sendobj);
closesocket(sock->s);
sock->s = INVALID_SOCKET;
bFreeSocketObj = TRUE;
}
}
else
{
// Graceful close
sock->bClosing = TRUE;
// Free the completed operation
RemoveBufferFromThread(sock, buf);
FreeBufferObj(buf);
// Check to see if there are more outstanding operations. If so, wait
// for them to complete; otherwise, clean up the socket object.
EnterCriticalSection(&sock->SockCritSec);
if (sock->OutstandingOps == 0)
{
closesocket(sock->s);
bFreeSocketObj = TRUE;
}
LeaveCriticalSection(&sock->SockCritSec);
}
}
else if ((buf->operation == OP_READ) && (error != NO_ERROR) && (gProtocol == IPPROTO_UDP))
{
// If a UDP receive failed, we really don't care. Just re-post it - that is
// we probably got an ICMP error.
if (PostRecv(buf) != NO_ERROR)
{
// In the event of an error, clean up the socket object
RemoveBufferFromThread(sock, buf);
FreeBufferObj(buf);
closesocket(sock->s);
sock->s = INVALID_SOCKET;
bFreeSocketObj = TRUE;
}
}
else if (buf->operation == OP_WRITE)
{
// Increment the counters
InterlockedExchangeAdd(&gBytesSent, bytes);
InterlockedExchangeAdd(&gBytesSentLast, bytes);
// See if the socket is closing, if so check to see if there are any outstanding
// operations. If not, clean up the connection; othewise wait for them
// to complete.
EnterCriticalSection(&sock->SockCritSec);
if (sock->bClosing && (sock->OutstandingOps == 0))
{
RemoveBufferFromThread(sock, buf);
closesocket(sock->s);
FreeBufferObj(buf);
bFreeSocketObj = TRUE;
}
else
{
buf->buflen = gBufferSize;
// Free the send op that just completed
if (PostRecv(buf) != NO_ERROR)
{
RemoveBufferFromThread(sock, buf);
FreeBufferObj(buf);
}
}
LeaveCriticalSection(&sock->SockCritSec);
}
if (bFreeSocketObj)
{
FreeSocketObj(sock);
}

最新评论
void AssignIoToThread(BUFFER_OBJ *buf);
void RemoveBufferFromThread(SOCKET_OBJ *sock, BUFFER_OBJ *buf);
查看全部评论……(共2条)