📄 iocpserver.cpp
字号:
// clients connecting and then not sending or receiving, the server
// should maintain a timer on each connection. If after some point,
// the server deems a connection is "stale" it can then set linger
// to be abortive and close the connection.
//
/*
LINGER lingerStruct;
lingerStruct.l_onoff = 1;
lingerStruct.l_linger = 0;
nRet = setsockopt(g_sdListen, SOL_SOCKET, SO_LINGER,
(char *)&lingerStruct, sizeof(lingerStruct) );
if( nRet == SOCKET_ERROR ) {
myprintf("setsockopt(SO_LINGER) failed: %d\n", WSAGetLastError());
return(FALSE);
}
*/
freeaddrinfo(addrlocal);
return(TRUE);
}
//
// Worker thread that handles all I/O requests on any socket handle added to the IOCP.
//
DWORD WINAPI WorkerThread (LPVOID WorkThreadContext) {
HANDLE hIOCP = (HANDLE)WorkThreadContext;
BOOL bSuccess = FALSE;
int nRet = 0;
LPWSAOVERLAPPED lpOverlapped = NULL;
PPER_SOCKET_CONTEXT lpPerSocketContext = NULL;
PPER_IO_CONTEXT lpIOContext = NULL;
WSABUF buffRecv;
WSABUF buffSend;
DWORD dwRecvNumBytes = 0;
DWORD dwSendNumBytes = 0;
DWORD dwFlags = 0;
DWORD dwIoSize = 0;
while( TRUE ) {
//
// continually loop to service io completion packets
//
bSuccess = GetQueuedCompletionStatus(hIOCP, &dwIoSize,
(PDWORD_PTR)&lpPerSocketContext,
(LPOVERLAPPED *)&lpOverlapped,
INFINITE);
if( !bSuccess )
myprintf("GetQueuedCompletionStatus() failed: %d\n", GetLastError());
if( lpPerSocketContext == NULL ) {
//
// CTRL-C handler used PostQueuedCompletionStatus to post an I/O packet with
// a NULL CompletionKey (or if we get one for any reason). It is time to exit.
//
return(0);
}
if( g_bEndServer ) {
//
// main thread will do all cleanup needed - see finally block
//
return(0);
}
if( !bSuccess || (bSuccess && (dwIoSize == 0)) ) {
//
// client connection dropped, continue to service remaining (and possibly
// new) client connections
//
CloseClient(lpPerSocketContext, FALSE);
continue;
}
//
// determine what type of IO packet has completed by checking the PER_IO_CONTEXT
// associated with this socket. This will determine what action to take.
//
lpIOContext = (PPER_IO_CONTEXT)lpOverlapped;
switch( lpIOContext->IOOperation ) {
case ClientIoRead:
//
// a read operation has completed, post a write operation to echo the
// data back to the client using the same data buffer.
//
lpIOContext->IOOperation = ClientIoWrite;
lpIOContext->nTotalBytes = dwIoSize;
lpIOContext->nSentBytes = 0;
lpIOContext->wsabuf.len = dwIoSize;
dwFlags = 0;
nRet = WSASend(lpPerSocketContext->Socket, &lpIOContext->wsabuf, 1,
&dwSendNumBytes, dwFlags, &(lpIOContext->Overlapped), NULL);
if( nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()) ) {
myprintf("WSASend() failed: %d\n", WSAGetLastError());
CloseClient(lpPerSocketContext, FALSE);
} else if( g_bVerbose ) {
myprintf("WorkerThread %d: Socket(%d) Recv completed (%d bytes), Send posted\n",
GetCurrentThreadId(), lpPerSocketContext->Socket, dwIoSize);
}
break;
case ClientIoWrite:
//
// a write operation has completed, determine if all the data intended to be
// sent actually was sent.
//
lpIOContext->IOOperation = ClientIoWrite;
lpIOContext->nSentBytes += dwIoSize;
dwFlags = 0;
if( lpIOContext->nSentBytes < lpIOContext->nTotalBytes ) {
//
// the previous write operation didn't send all the data,
// post another send to complete the operation
//
buffSend.buf = lpIOContext->Buffer + lpIOContext->nSentBytes;
buffSend.len = lpIOContext->nTotalBytes - lpIOContext->nSentBytes;
nRet = WSASend (lpPerSocketContext->Socket, &buffSend, 1,
&dwSendNumBytes, dwFlags, &(lpIOContext->Overlapped), NULL);
if( nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()) ) {
myprintf("WSASend() failed: %d\n", WSAGetLastError());
CloseClient(lpPerSocketContext, FALSE);
} else if( g_bVerbose ) {
myprintf("WorkerThread %d: Socket(%d) Send partially completed (%d bytes), Recv posted\n",
GetCurrentThreadId(), lpPerSocketContext->Socket, dwIoSize);
}
} else {
//
// previous write operation completed for this socket, post another recv
//
lpIOContext->IOOperation = ClientIoRead;
dwRecvNumBytes = 0;
dwFlags = 0;
buffRecv.buf = lpIOContext->Buffer,
buffRecv.len = MAX_BUFF_SIZE;
nRet = WSARecv(lpPerSocketContext->Socket, &buffRecv, 1,
&dwRecvNumBytes, &dwFlags, &lpIOContext->Overlapped, NULL);
if( nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()) ) {
myprintf("WSARecv() failed: %d\n", WSAGetLastError());
CloseClient(lpPerSocketContext, FALSE);
} else if( g_bVerbose ) {
myprintf("WorkerThread %d: Socket(%d) Send completed (%d bytes), Recv posted\n",
GetCurrentThreadId(), lpPerSocketContext->Socket, dwIoSize);
}
}
break;
} //switch
} //while
return(0);
}
//
// Allocate a context structures for the socket and add the socket to the IOCP.
// Additionally, add the context structure to the global list of context structures.
//
PPER_SOCKET_CONTEXT UpdateCompletionPort(SOCKET sd, IO_OPERATION ClientIo,
BOOL bAddToList) {
PPER_SOCKET_CONTEXT lpPerSocketContext;
lpPerSocketContext = CtxtAllocate(sd, ClientIo);
if( lpPerSocketContext == NULL )
return(NULL);
g_hIOCP = CreateIoCompletionPort((HANDLE)sd, g_hIOCP, (DWORD_PTR)lpPerSocketContext, 0);
if( g_hIOCP == NULL ) {
myprintf("CreateIoCompletionPort() failed: %d\n", GetLastError());
if( lpPerSocketContext->pIOContext )
xfree(lpPerSocketContext->pIOContext);
xfree(lpPerSocketContext);
return(NULL);
}
//
//The listening socket context (bAddToList is FALSE) is not added to the list.
//All other socket contexts are added to the list.
//
if( bAddToList ) CtxtListAddTo(lpPerSocketContext);
if( g_bVerbose )
myprintf("UpdateCompletionPort: Socket(%d) added to IOCP\n", lpPerSocketContext->Socket);
return(lpPerSocketContext);
}
//
// Close down a connection with a client. This involves closing the socket (when
// initiated as a result of a CTRL-C the socket closure is not graceful). Additionally,
// any context data associated with that socket is free'd.
//
VOID CloseClient (PPER_SOCKET_CONTEXT lpPerSocketContext,
BOOL bGraceful) {
EnterCriticalSection(&g_CriticalSection);
if( lpPerSocketContext ) {
if( g_bVerbose )
myprintf("CloseClient: Socket(%d) connection closing (graceful=%s)\n",
lpPerSocketContext->Socket, (bGraceful?"TRUE":"FALSE"));
if( !bGraceful ) {
//
// force the subsequent closesocket to be abortative.
//
LINGER lingerStruct;
lingerStruct.l_onoff = 1;
lingerStruct.l_linger = 0;
setsockopt(lpPerSocketContext->Socket, SOL_SOCKET, SO_LINGER,
(char *)&lingerStruct, sizeof(lingerStruct) );
}
closesocket(lpPerSocketContext->Socket);
lpPerSocketContext->Socket = INVALID_SOCKET;
CtxtListDeleteFrom(lpPerSocketContext);
lpPerSocketContext = NULL;
} else {
myprintf("CloseClient: lpPerSocketContext is NULL\n");
}
LeaveCriticalSection(&g_CriticalSection);
return;
}
//
// Allocate a socket context for the new connection.
//
PPER_SOCKET_CONTEXT CtxtAllocate(SOCKET sd, IO_OPERATION ClientIO) {
PPER_SOCKET_CONTEXT lpPerSocketContext;
EnterCriticalSection(&g_CriticalSection);
lpPerSocketContext = (PPER_SOCKET_CONTEXT)xmalloc(sizeof(PER_SOCKET_CONTEXT));
if( lpPerSocketContext ) {
lpPerSocketContext->pIOContext = (PPER_IO_CONTEXT)xmalloc(sizeof(PER_IO_CONTEXT));
if( lpPerSocketContext->pIOContext ) {
lpPerSocketContext->Socket = sd;
lpPerSocketContext->pCtxtBack = NULL;
lpPerSocketContext->pCtxtForward = NULL;
lpPerSocketContext->pIOContext->Overlapped.Internal = 0;
lpPerSocketContext->pIOContext->Overlapped.InternalHigh = 0;
lpPerSocketContext->pIOContext->Overlapped.Offset = 0;
lpPerSocketContext->pIOContext->Overlapped.OffsetHigh = 0;
lpPerSocketContext->pIOContext->Overlapped.hEvent = NULL;
lpPerSocketContext->pIOContext->IOOperation = ClientIO;
lpPerSocketContext->pIOContext->pIOContextForward = NULL;
lpPerSocketContext->pIOContext->nTotalBytes = 0;
lpPerSocketContext->pIOContext->nSentBytes = 0;
lpPerSocketContext->pIOContext->wsabuf.buf = lpPerSocketContext->pIOContext->Buffer;
lpPerSocketContext->pIOContext->wsabuf.len = sizeof(lpPerSocketContext->pIOContext->Buffer);
ZeroMemory(lpPerSocketContext->pIOContext->wsabuf.buf, lpPerSocketContext->pIOContext->wsabuf.len);
} else {
xfree(lpPerSocketContext);
myprintf("HeapAlloc() PER_IO_CONTEXT failed: %d\n", GetLastError());
}
} else {
myprintf("HeapAlloc() PER_SOCKET_CONTEXT failed: %d\n", GetLastError());
}
LeaveCriticalSection(&g_CriticalSection);
return(lpPerSocketContext);
}
//
// Add a client connection context structure to the global list of context structures.
//
VOID CtxtListAddTo (PPER_SOCKET_CONTEXT lpPerSocketContext) {
PPER_SOCKET_CONTEXT pTemp;
EnterCriticalSection(&g_CriticalSection);
if( g_pCtxtList == NULL ) {
//
// add the first node to the linked list
//
lpPerSocketContext->pCtxtBack = NULL;
lpPerSocketContext->pCtxtForward = NULL;
g_pCtxtList = lpPerSocketContext;
} else {
//
// add node to head of list
//
pTemp = g_pCtxtList;
g_pCtxtList = lpPerSocketContext;
lpPerSocketContext->pCtxtBack = pTemp;
lpPerSocketContext->pCtxtForward = NULL;
pTemp->pCtxtForward = lpPerSocketContext;
}
LeaveCriticalSection(&g_CriticalSection);
return;
}
//
// Remove a client context structure from the global list of context structures.
//
VOID CtxtListDeleteFrom(PPER_SOCKET_CONTEXT lpPerSocketContext) {
PPER_SOCKET_CONTEXT pBack;
PPER_SOCKET_CONTEXT pForward;
PPER_IO_CONTEXT pNextIO = NULL;
PPER_IO_CONTEXT pTempIO = NULL;
EnterCriticalSection(&g_CriticalSection);
if( lpPerSocketContext ) {
pBack = lpPerSocketContext->pCtxtBack;
pForward = lpPerSocketContext->pCtxtForward;
if( ( pBack == NULL ) && ( pForward == NULL ) ) {
//
// This is the only node in the list to delete
//
g_pCtxtList = NULL;
} else if ( ( pBack == NULL ) && ( pForward != NULL ) ) {
//
// This is the start node in the list to delete
//
pForward->pCtxtBack = NULL;
g_pCtxtList = pForward;
} else if ( ( pBack != NULL ) && ( pForward == NULL ) ) {
//
// This is the end node in the list to delete
//
pBack->pCtxtForward = NULL;
} else if( pBack && pForward ) {
//
// Neither start node nor end node in the list
//
pBack->pCtxtForward = pForward;
pForward->pCtxtBack = pBack;
}
//
// Free all i/o context structures per socket
//
pTempIO = (PPER_IO_CONTEXT)(lpPerSocketContext->pIOContext);
do {
pNextIO = (PPER_IO_CONTEXT)(pTempIO->pIOContextForward);
if( pTempIO ) {
//
//The overlapped structure is safe to free when only the posted i/o has
//completed. Here we only need to test those posted but not yet received
//by PQCS in the shutdown process.
//
if( g_bEndServer )
while( !HasOverlappedIoCompleted((LPOVERLAPPED)pTempIO) ) Sleep(0);
xfree(pTempIO);
pTempIO = NULL;
}
pTempIO = pNextIO;
} while( pNextIO );
xfree(lpPerSocketContext);
lpPerSocketContext = NULL;
} else {
myprintf("CtxtListDeleteFrom: lpPerSocketContext is NULL\n");
}
LeaveCriticalSection(&g_CriticalSection);
return;
}
//
// Free all context structure in the global list of context structures.
//
VOID CtxtListFree() {
PPER_SOCKET_CONTEXT pTemp1, pTemp2;
EnterCriticalSection(&g_CriticalSection);
pTemp1 = g_pCtxtList;
while( pTemp1 ) {
pTemp2 = pTemp1->pCtxtBack;
CloseClient(pTemp1, FALSE);
pTemp1 = pTemp2;
}
LeaveCriticalSection(&g_CriticalSection);
return;
}
//
// Our own printf. This is done because calling printf from multiple
// threads can AV. The standard out for WriteConsole is buffered...
//
int myprintf (const char *lpFormat, ... ) {
int nLen = 0;
int nRet = 0;
char cBuffer[512] ;
va_list arglist ;
HANDLE hOut = NULL;
ZeroMemory(cBuffer, sizeof(cBuffer));
va_start(arglist, lpFormat);
nLen = lstrlen( lpFormat ) ;
nRet = wvsprintf( cBuffer, lpFormat, arglist );
if( nRet >= nLen || GetLastError() == 0 ) {
hOut = GetStdHandle(STD_OUTPUT_HANDLE) ;
if( hOut != INVALID_HANDLE_VALUE )
WriteConsole( hOut, cBuffer, lstrlen(cBuffer), (LPDWORD)&nLen, NULL ) ;
}
return nLen ;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -