|
I was wondering if you have anything that will support Dialog app? Thanks.
-Matt Newman
-Matt Newman
-Sonork ID: 100.11179:BestSnowman
|
|
|
|
|
I have done my own IOCP server by putting toghether many parts and after lots of reading... however after 9 month i found best working exampel from microsoft in april 2001 msdn enjoy: ..K\Samples\NetDS\WinSock\iocp\IocpServerex.Cpp
It basically same as you try to do but with better approach to some IO issues.
|
|
|
|
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (C) 1998 - 2000 Microsoft Corporation. All Rights Reserved.
//
// Module:
// iocpserverex.cpp
//
// Abstract:
// This program is a Winsock echo server program that demonstrates the usage
// of AcceptEx with IOCP. The AcceptEx function accepts a new connection,
// returns the local and remote address, and receives the first block of data
// sent by the client application. The design of this program is based on that
// in the iocpserver.cpp. But it uses overlapped AcceptEx on the IOCP also.
// AcceptEx allows data to be "returned" from an accepted connection.
//
// Usage:
// Start the server and wait for connections on port 6001
// iocpserverex -e:6001
//
// Build:
// Use the headers and libs from the April98 Platform SDK or later.
// Link with ws2_32.lib and mswsock.lib
//
// Author: Wei Hua, Barry Butterklee - Microsoft Developer Support
//
//
#ifdef _IA64_
#pragma warning(disable:4127)
#endif
#include <winsock2.h>
#include <mswsock.h>
#include <stdio.h>
#include <stdlib.h>
#include "iocpserver.h"
unsigned short g_Port = DEFAULT_PORT;
BOOL g_bEndServer = FALSE; // set to TRUE on CTRL-C
BOOL g_bRestart = TRUE; // set to TRUE to CTRL-BRK
BOOL g_bVerbose = FALSE;
HANDLE g_hIOCP = NULL;
SOCKET g_sdListen = INVALID_SOCKET;
HANDLE g_ThreadHandles[MAX_WORKER_THREAD];
HANDLE g_hCleanupEvent = NULL;
PPER_SOCKET_CONTEXT g_pCtxtListenSocket = NULL;
PPER_SOCKET_CONTEXT g_pCtxtList = NULL; // linked list of context info structures
// maintained to allow the the cleanup
// handler to cleanly close all sockets and
// free resources.
CRITICAL_SECTION g_CriticalSection; // guard access to the global context list
void main (int argc, char *argv[])
{
SYSTEM_INFO systemInfo;
WSADATA wsaData;
DWORD dwThreadCount;
int nRet;
if (!ValidOptions(argc, argv))
return;
if (!SetConsoleCtrlHandler(CtrlHandler, TRUE))
{
printf ("SetConsoleCtrlHandler failed to install console handler: %d\n",
GetLastError());
return;
}
GetSystemInfo(&systemInfo);
dwThreadCount = systemInfo.dwNumberOfProcessors * 2;
g_hCleanupEvent = CreateEvent( NULL, TRUE, FALSE, NULL);
if (g_hCleanupEvent == NULL)
{
printf("CreateEvent failed: %d\n",GetLastError());
return;
}
if ((nRet = WSAStartup(0x202, &wsaData)) != 0)
{
printf("WSAStartup failed: %d\n",nRet);
CloseHandle(g_hCleanupEvent);
return;
}
InitializeCriticalSection(&g_CriticalSection);
while (g_bRestart)
{
g_bRestart = FALSE;
g_bEndServer = FALSE;
ResetEvent(g_hCleanupEvent);
__try
{
// notice that we will create more worker threads (dwThreadCount) than
// the thread concurrency limit on the IOCP.
g_hIOCP = CreateIoCompletionPort(
INVALID_HANDLE_VALUE, NULL, 0, 0);
if (NULL == g_hIOCP)
{
printf( "CreateIoCompletionPort failed to create I/O completion port: %d\n",
GetLastError());
__leave;
}
for (DWORD dwCPU=0; dwCPU<dwThreadCount; dwCPU++)
{
// Create worker threads to service the overlapped I/O requests. The decision
// to create 2 worker threads per CPU in the system is a heuristic. Also,
// note that thread handles are closed right away, because we will not need them
// and the worker threads will continue to execute.
HANDLE hThread;
DWORD dwThreadId;
hThread = CreateThread(NULL, 0, WorkerThread, g_hIOCP, 0, &dwThreadId);
if (hThread == NULL)
{
printf("CreateThread failed to create worker thread: %d\n",
GetLastError());
__leave;
}
g_ThreadHandles[dwCPU] = hThread;
}
if (!CreateListenSocket())
__leave;
if (!CreateAcceptSocket(TRUE))
__leave;
WaitForSingleObject(g_hCleanupEvent, INFINITE);
}
__finally
{
g_bEndServer = TRUE;
// Cause worker threads to exit
if (g_hIOCP)
{
for (DWORD i = 0; i < dwThreadCount; i++)
PostQueuedCompletionStatus(g_hIOCP, 0, 0, NULL);
}
//Make sure worker threads exits.
if (WAIT_OBJECT_0 != WaitForMultipleObjects(dwThreadCount, g_ThreadHandles, TRUE, 1000))
printf("WaitForMultipleObjects failed: %d\n", GetLastError());
else
for (DWORD i=0; i<dwThreadCount; i++)
{
if (g_ThreadHandles[i] != INVALID_HANDLE_VALUE)
CloseHandle(g_ThreadHandles[i]);
g_ThreadHandles[i] = INVALID_HANDLE_VALUE;
}
if (g_sdListen != INVALID_SOCKET)
{
closesocket(g_sdListen);
g_sdListen = INVALID_SOCKET;
}
if (g_pCtxtListenSocket)
{
while (!HasOverlappedIoCompleted((LPOVERLAPPED)&g_pCtxtListenSocket->pIOContext->Overlapped))
Sleep(0);
if (g_pCtxtListenSocket->pIOContext->SocketAccept != INVALID_SOCKET)
closesocket(g_pCtxtListenSocket->pIOContext->SocketAccept);
g_pCtxtListenSocket->pIOContext->SocketAccept = INVALID_SOCKET;
//We know there is only one overlapped I/O on the listening socket
if (g_pCtxtListenSocket->pIOContext)
HeapFree(GetProcessHeap(), 0, g_pCtxtListenSocket->pIOContext);
if (g_pCtxtListenSocket)
HeapFree(GetProcessHeap(), 0, g_pCtxtListenSocket);
g_pCtxtListenSocket = NULL;
}
CtxtListFree();
if (g_hIOCP)
{
CloseHandle(g_hIOCP);
g_hIOCP = NULL;
}
} //finally
if (g_bRestart)
{
printf("\niocpserverex is restarting...\n");
}
else
printf("\niocpserverex is exiting...\n");
} //while (g_bRestart)
DeleteCriticalSection(&g_CriticalSection);
WSACleanup();
CloseHandle(g_hCleanupEvent);
SetConsoleCtrlHandler(CtrlHandler, FALSE);
} //main
// Just validate the command line options.
//
BOOL ValidOptions(int argc, char *argv[])
{
BOOL bRet = TRUE;
for (int i=1; i<argc; i++)
{
if ((argv[i][0] =='-') || (argv[i][0] == '/'))
{
switch (tolower(argv[i][1]))
{
case 'e':
if (strlen(argv[i]) > 3)
g_Port = (unsigned short)atoi(&argv[i][3]);
break;
case 'v':
g_bVerbose = TRUE;
break;
case '?':
printf("Usage:\n iocpserver [-p:port] [-v] [-?]\n");
printf(" -e:port\tSpecify echoing port number\n");
printf(" -v\t\tVerbose\n");
printf(" -?\t\tDisplay this help\n");
bRet = FALSE;
break;
default:
printf("Unknown options flag %s\n", argv[i]);
bRet = FALSE;
break;
}
}
}
return(bRet);
}
// Intercept CTRL-C or CTRL-BRK events and cause the server to initiate shutdown.
// CTRL-BRK resets the restart flag, and after cleanup the server restarts.
BOOL WINAPI CtrlHandler (
DWORD dwEvent
)
{
switch (dwEvent)
{
case CTRL_BREAK_EVENT:
g_bRestart = TRUE;
case CTRL_C_EVENT:
case CTRL_LOGOFF_EVENT:
case CTRL_SHUTDOWN_EVENT:
case CTRL_CLOSE_EVENT:
if (g_bVerbose)
printf("CtrlHandler: closing listening socket\n");
g_bEndServer = TRUE;
SetEvent(g_hCleanupEvent);
break;
default:
// unknown type--better pass it on.
return(FALSE);
}
return(TRUE);
}
// Create a socket with all the socket options we need, namely disable buffering
// and set linger.
//
SOCKET CreateSocket(void)
{
int nRet;
int nZero = 0;
LINGER lingerStruct;
SOCKET sdSocket = INVALID_SOCKET;
sdSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (INVALID_SOCKET == sdSocket)
{
printf("WSASocket(sdSocket): %d\n", WSAGetLastError());
return(sdSocket);
}
// Disable send buffering on the socket. Setting SO_SNDBUF
// to 0 causes winsock to stop bufferring sends and perform
// sends directly from our buffers, thereby save one memory copy.
nZero = 0;
nRet = setsockopt(sdSocket, SOL_SOCKET, SO_SNDBUF, (char *)&nZero, sizeof(nZero));
if (SOCKET_ERROR == nRet)
{
printf("setsockopt(SNDBUF): %d\n", WSAGetLastError());
return(sdSocket);
}
// Disable receive buffering on the socket. Setting SO_RCVBUF
// to 0 causes winsock to stop bufferring receive and perform
// receives directly from our buffers, thereby save one memory copy.
nZero = 0;
nRet = setsockopt(sdSocket, SOL_SOCKET, SO_RCVBUF, (char *)&nZero, sizeof(nZero));
if (SOCKET_ERROR == nRet)
{
printf("setsockopt(SO_RCVBUF): %d\n", WSAGetLastError());
return(sdSocket);
}
lingerStruct.l_onoff = 1;
lingerStruct.l_linger = 0;
nRet = setsockopt(sdSocket, SOL_SOCKET, SO_LINGER,
(char *)&lingerStruct, sizeof(lingerStruct));
if (SOCKET_ERROR == nRet)
{
printf("setsockopt(SO_LINGER): %d\n", WSAGetLastError());
return(sdSocket);
}
return(sdSocket);
}
// Create a listening socket, bind, and set up its listening backlog.
//
BOOL CreateListenSocket(void)
{
SOCKADDR_IN si_addrlocal;
int nRet;
LINGER lingerStruct;
lingerStruct.l_onoff = 1;
lingerStruct.l_linger = 0;
g_sdListen = CreateSocket();
if (INVALID_SOCKET == g_sdListen)
{
return(FALSE);
}
si_addrlocal.sin_family = AF_INET;
si_addrlocal.sin_port = htons(g_Port);
si_addrlocal.sin_addr.s_addr = htonl(INADDR_ANY);
nRet = bind(g_sdListen, (struct sockaddr *)&si_addrlocal, sizeof(si_addrlocal));
if (SOCKET_ERROR == nRet)
{
printf("bind: %d\n", WSAGetLastError());
return(FALSE);
}
nRet = listen(g_sdListen, 5);
if (SOCKET_ERROR == nRet)
{
printf("listen: %d\n", WSAGetLastError());
return(FALSE);
}
return(TRUE);
}
// Create a socket and invoke AcceptEx. Only the original call to to this
// function needs to be added to the IOCP.
//
// If the expected behaviour of connecting client applications is to NOT
// send data right away, then only posting one AcceptEx can cause connection
// attempts to be refused if a client connects without sending some initial
// data (notice that the associated iocpclient does not operate this way
// but instead makes a connection and starts sending data write away).
// This is because the IOCP packet does not get delivered without the initial
// data (as implemented in this sample) thus preventing the worker thread
// from posting another AcceptEx and eventually the backlog value set in
// listen() will be exceeded if clients continue to try to connect.
//
// One technique to address this situation is to simply cause AcceptEx
// to return right away upon accepting a connection without returning any
// data. This can be done by setting dwReceiveDataLength=0 when calling AcceptEx.
//
// Another technique to address this situation is to post multiple calls
// to AcceptEx. Posting multiple calls to AcceptEx is similar in concept to
// increasing the backlog value in listen(), though posting AcceptEx is
// dynamic (i.e. during the course of running your application you can adjust
// the number of AcceptEx calls you post). It is important however to keep
// your backlog value in listen() high in your server to ensure that the
// stack can accept connections even if your application does not get enough
// CPU cycles to repost another AcceptEx under stress conditions.
//
// This sample implements neither of these techniques and is therefore
// susceptible to the behaviour described above.
//
BOOL CreateAcceptSocket(BOOL fUpdateIOCP)
{
int nRet;
DWORD dwRecvNumBytes = 0;
//The context for listening socket uses the SockAccept member to store the
//socket for client connection.
if (fUpdateIOCP)
{
g_pCtxtListenSocket = UpdateCompletionPort(g_sdListen, ClientIoAccept, FALSE);
if (g_pCtxtListenSocket == NULL)
{
printf("failed to update listen socket to IOCP\n");
return(FALSE);
}
}
g_pCtxtListenSocket->pIOContext->SocketAccept = CreateSocket();
if (INVALID_SOCKET == g_pCtxtListenSocket->pIOContext->SocketAccept)
{
printf("failed to create new accept socket\n");
return(FALSE);
}
// pay close attention to these parameters and buffer lengths
nRet = AcceptEx(
g_sdListen,
g_pCtxtListenSocket->pIOContext->SocketAccept,
(LPVOID)(g_pCtxtListenSocket->pIOContext->Buffer),
MAX_BUFF_SIZE - (2 * (sizeof(SOCKADDR_IN) + 16)),
sizeof(SOCKADDR_IN) + 16,
sizeof(SOCKADDR_IN) + 16,
&dwRecvNumBytes,
(LPOVERLAPPED) &(g_pCtxtListenSocket->pIOContext->Overlapped)
);
if (nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()))
{
printf("AcceptEx Failed: %d\n", WSAGetLastError());
return(FALSE);
}
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;
LPOVERLAPPED lpOverlapped = NULL;
PPER_SOCKET_CONTEXT lpPerSocketContext = NULL;
PPER_SOCKET_CONTEXT lpAcceptSocketContext = NULL;
PPER_IO_CONTEXT lpIOContext = NULL;
WSABUF buffRecv;
WSABUF buffSend;
DWORD dwRecvNumBytes = 0;
DWORD dwSendNumBytes = 0;
DWORD dwFlags = 0;
DWORD dwIoSize;
while (TRUE)
{
// continually loop to service io completion packets
bSuccess = GetQueuedCompletionStatus(
hIOCP,
&dwIoSize,
(PDWORD_PTR)&lpPerSocketContext,
&lpOverlapped,
INFINITE
);
if (!bSuccess)
printf("GetQueuedCompletionStatus: %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;
}
lpIOContext = (PPER_IO_CONTEXT)lpOverlapped;
//We should never skip the loop and not post another AcceptEx if the current
//completion packet is for previous AcceptEx
if (lpIOContext->IOOperation != ClientIoAccept)
{
if (!bSuccess || (bSuccess && (0 == dwIoSize)) )
{
// 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.
switch (lpIOContext->IOOperation)
{
case ClientIoAccept:
// When the AcceptEx function returns, the socket sAcceptSocket is
// in the default state for a connected socket. The socket sAcceptSocket
// does not inherit the properties of the socket associated with
// sListenSocket parameter until SO_UPDATE_ACCEPT_CONTEXT is set on
// the socket. Use the setsockopt function to set the SO_UPDATE_ACCEPT_CONTEXT
// option, specifying sAcceptSocket as the socket handle and sListenSocket
// as the option value.
nRet = setsockopt(
lpPerSocketContext->pIOContext->SocketAccept,
SOL_SOCKET,
SO_UPDATE_ACCEPT_CONTEXT,
(char *)&g_sdListen,
sizeof(g_sdListen)
);
if (nRet == SOCKET_ERROR)
{
//just warn user here.
printf("setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed to update accept socket\n");
SetEvent(g_hCleanupEvent);
return 0;
}
lpAcceptSocketContext = UpdateCompletionPort(
lpPerSocketContext->pIOContext->SocketAccept,
ClientIoAccept, TRUE);
if (lpAcceptSocketContext == NULL)
{
//just warn user here.
printf("failed to update accept socket to IOCP\n");
SetEvent(g_hCleanupEvent);
return 0;
}
if (dwIoSize)
{
lpAcceptSocketContext->pIOContext->IOOperation = ClientIoWrite;
lpAcceptSocketContext->pIOContext->nTotalBytes = dwIoSize;
lpAcceptSocketContext->pIOContext->nSentBytes = 0;
lpAcceptSocketContext->pIOContext->wsabuf.len = dwIoSize;
CopyMemory(lpAcceptSocketContext->pIOContext->Buffer, lpPerSocketContext->pIOContext->Buffer, sizeof(lpPerSocketContext->pIOContext->Buffer));
lpAcceptSocketContext->pIOContext->wsabuf.buf = lpAcceptSocketContext->pIOContext->Buffer;
nRet = WSASend(
lpPerSocketContext->pIOContext->SocketAccept,
&lpAcceptSocketContext->pIOContext->wsabuf, 1,
&dwSendNumBytes,
0,
&(lpAcceptSocketContext->pIOContext->Overlapped), NULL);
if (SOCKET_ERROR == nRet && (ERROR_IO_PENDING != WSAGetLastError()))
{
printf ("WSASend: %d\n", WSAGetLastError());
CloseClient(lpAcceptSocketContext, FALSE);
}
else if (g_bVerbose)
{
printf("WorkerThread %d: Socket(%d) AcceptEx completed (%d bytes), Send posted\n",
GetCurrentThreadId(), lpPerSocketContext->Socket, dwIoSize);
}
}
else
{
// AcceptEx completes but doesn't read any data so we need to post
// an outstanding overlapped read.
lpAcceptSocketContext->pIOContext->IOOperation = ClientIoRead;
dwRecvNumBytes = 0;
dwFlags = 0;
buffRecv.buf = lpAcceptSocketContext->pIOContext->Buffer,
buffRecv.len = MAX_BUFF_SIZE;
nRet = WSARecv(
lpAcceptSocketContext->Socket,
&buffRecv, 1,
&dwRecvNumBytes,
&dwFlags,
&lpAcceptSocketContext->pIOContext->Overlapped, NULL);
if (SOCKET_ERROR == nRet && (ERROR_IO_PENDING != WSAGetLastError()))
{
printf ("WSARecv: %d\n", WSAGetLastError());
CloseClient(lpAcceptSocketContext, FALSE);
}
}
//Time to post another outstanding AcceptEx
if (!CreateAcceptSocket(FALSE))
{
printf("Please shut down and reboot the server.\n");
SetEvent(g_hCleanupEvent);
return 0;
}
break;
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 (SOCKET_ERROR == nRet && (ERROR_IO_PENDING != WSAGetLastError()))
{
printf("WSASend: %d\n", WSAGetLastError());
CloseClient(lpPerSocketContext, FALSE);
}
else if (g_bVerbose)
{
printf("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 (SOCKET_ERROR == nRet && (ERROR_IO_PENDING != WSAGetLastError()))
{
printf ("WSASend: %d\n", WSAGetLastError());
CloseClient(lpPerSocketContext, FALSE);
}
else if (g_bVerbose)
{
printf("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 (SOCKET_ERROR == nRet && (ERROR_IO_PENDING != WSAGetLastError()))
{
printf ("WSARecv: %d\n", WSAGetLastError());
CloseClient(lpPerSocketContext, FALSE);
}
else if (g_bVerbose)
{
printf("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 (NULL == g_hIOCP)
{
printf("CreateIoCompletionPort: %d\n", GetLastError());
if (lpPerSocketContext->pIOContext)
HeapFree(GetProcessHeap(), 0, lpPerSocketContext->pIOContext);
HeapFree(GetProcessHeap(), 0, 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)
printf("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)
printf("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) );
}
if (lpPerSocketContext->pIOContext->SocketAccept != INVALID_SOCKET)
{
closesocket(lpPerSocketContext->pIOContext->SocketAccept);
lpPerSocketContext->pIOContext->SocketAccept = INVALID_SOCKET;
};
closesocket(lpPerSocketContext->Socket);
CtxtListDeleteFrom(lpPerSocketContext);
lpPerSocketContext = NULL;
}
else
{
printf("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)HeapAlloc(
GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PER_SOCKET_CONTEXT));
if ( lpPerSocketContext)
{
lpPerSocketContext->pIOContext = (PPER_IO_CONTEXT)HeapAlloc(
GetProcessHeap(), HEAP_ZERO_MEMORY, 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);
lpPerSocketContext->pIOContext->SocketAccept = INVALID_SOCKET;
}
else
{
HeapFree(GetProcessHeap(), 0, lpPerSocketContext);
printf("HeapAlloc PER_IO_CONTEXT Failed: %d\n", GetLastError());
}
}
else
{
printf("HeapAlloc PER_SOCKET_CONTEXT Failed: %d\n", GetLastError());
return(NULL);
}
LeaveCriticalSection(&g_CriticalSection);
return(lpPerSocketContext);
}
// Add a client connection context structure to the globnal 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);
HeapFree(GetProcessHeap(), 0, pTempIO);
pTempIO = NULL;
}
pTempIO = pNextIO;
} while (pNextIO);
HeapFree(GetProcessHeap(), 0, lpPerSocketContext);
lpPerSocketContext = NULL;
}
else
{
printf("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;
}
|
|
|
|
|
here is the iocpserver.h
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (C) 1998 Microsoft Corporation. All Rights Reserved.
//
// Module:
// iocpserver.h
//
#ifndef IOCPSERVER_H
#define IOCPSERVER_H
#define DEFAULT_PORT 5001
#define MAX_BUFF_SIZE 2048
#define MAX_WORKER_THREAD 16
typedef enum _IO_OPERATION {
ClientIoRead,
ClientIoWrite,
ClientQoS
} IO_OPERATION, *PIO_OPERATION;
// data to be associated for every I/O operation on a socket
typedef struct _PER_IO_CONTEXT {
WSAOVERLAPPED Overlapped;
WSABUF wsabuf;
int nTotalBytes;
int nSentBytes;
IO_OPERATION IOOperation;
struct _PER_IO_CONTEXT *pIOContextForward;
CHAR Buffer[MAX_BUFF_SIZE];
} PER_IO_CONTEXT, *PPER_IO_CONTEXT;
// For AcceptEx, the IOCP key is the PER_SOCKET_CONTEXT for the listening socket,
// so we need to another field SocketAccept in PER_IO_CONTEXT. When the outstanding
// AcceptEx completes, this field is our connection socket handle.
// data to be associated with every socket added to the IOCP
typedef struct _PER_SOCKET_CONTEXT {
SOCKET Socket;
PER_IO_CONTEXT IOContext[2];
//one for read/write, echo server has either a read or write outstanding; and
//one for SIO_GET_QOS.
struct _PER_SOCKET_CONTEXT *pCtxtBack;
struct _PER_SOCKET_CONTEXT *pCtxtForward;
DWORD dwOutstandingOverlappedIO;
} PER_SOCKET_CONTEXT, *PPER_SOCKET_CONTEXT;
BOOL ValidOptions(int argc, char *argv[]);
BOOL WINAPI CtrlHandler(
DWORD dwEvent
);
BOOL CreateListenSocket(void);
BOOL CreateAcceptSocket(
BOOL fUpdateIOCP
);
DWORD WINAPI WorkerThread (
LPVOID WorkContext
);
PPER_SOCKET_CONTEXT UpdateCompletionPort(
SOCKET sd,
IO_OPERATION ClientIo1,
IO_OPERATION ClientIo2,
BOOL bAddToList
);
// bAddToList is FALSE for listening socket, and TRUE for connection sockets.
// As we maintain the context for listening socket in a global structure, we
// don't need to add it to the list.
VOID CloseClient (
PPER_SOCKET_CONTEXT lpPerSocketContext,
BOOL bGraceful
);
PPER_SOCKET_CONTEXT CtxtAllocate(
SOCKET s,
IO_OPERATION ClientIO1,
IO_OPERATION ClientIO2
);
VOID CtxtListFree(
);
VOID CtxtListAddTo (
PPER_SOCKET_CONTEXT lpPerSocketContext
);
VOID CtxtListDeleteFrom(
PPER_SOCKET_CONTEXT lpPerSocketContext
);
#endif
|
|
|
|
|
Does this IOCP code of microsoft work with any Nt4.0 and higher systems or just windows 2000?
please reply?
if it only works with win 2000, then is it possible to make it so it works with win nt 4.0 and higher systems?
raj
|
|
|
|
|
Would you, please, send us a iocpserver.h
Thank you.
|
|
|
|
|
am i blind or what??
pIOContext is not declared anywhere under ClientIOWrite:
please correct me if i'm wrong
(blah... MS should go hide themselves in a cave and never come out of there :P )
|
|
|
|
|
Just a note, with VC++ 6 SP5 + Processor Pack and the latest Platform SDK, this won't compile as-is.
Add ..\. to the additional include directories (under settings) to make the compiler find the correct buffer.h file.
|
|
|
|
|
I prefer to change
#include "buffer.h" to
#include "..\buffer.h" Regards
Thomas
Finally with Sonork id: 100.10453 Thömmi
Disclaimer: Because of heavy processing requirements, we are currently using some of your unused brain capacity for backup processing. Please ignore any hallucinations, voices or unusual dreams you may experience. Please avoid concentration-intensive tasks until further notice. Thank you.
|
|
|
|
|
Thanks! You saved me hours.
Regards
|
|
|
|
|
Thomas Freudenberg wrote:
#include "..\buffer.h"
While that will work, I think it's the job of the IDE to solve these problems without resorting to changing code.
(Call it a difference in coding style)
I always seem to miss one instance somewhere when I do it by hand, and I always manage to mess something up when I do a global replace... (go figure)
|
|
|
|
|
Your correct, if you set your Tools/Options/Directories to include the buffer.h as the first entry in the Includes and Source this will fix your problemss.
Normski. - Professional Windows Programmer
|
|
|
|
|
Thanks! This was what I was experiencing. Now that I think about it, it makes perfect sense.
|
|
|
|
|
Norm, i suggest litle update for final version.
1. In shutdown process my server keep hanging arround in ThreadpoolFunc, to solve this do next:
1.
In CIOCPServer constructor put:
m_nWorkerCnt =0;
'cos without it i got m_nWorkerCnt=-817623635345 (or something like this)
2.
and in ThreadPoolFunc put and(&&) instead or(||):
for ( BOOL bStayInPool = TRUE; bStayInPool && pThis->m_bTimeToKill == false; )
'cos in shutdown for m_bTimeToKill = true still doasn't leave for loop.
3. For echo mode (to switch between echo and non-echo)
void CIOCPServer::SetEchoMode()
{
m_bEcho = !m_bEcho;
}
Igor Janjetovic
ANOXSoft
|
|
|
|
|
These are the only bug remaining before I *release* a new version of the client/server:
1/ When the server is 5+ clients connected in 'echo' mode and you shut the server down, the server hangs.
2/ When the client is disconnected in 'flood' mode it should automatically connect to server.
Once this 2 issues are resolved I'll repost a newer version, for those you want the latest version here:
click here
Normski. - Professional Windows Programmer
|
|
|
|
|
For bug #1 you have never initialized m_nWorkerCnt so CloseCompletionPort never exits. This is because in debug mode VC++ initializes it to 0xcdcdcdcd. See the code below:
while (m_nWorkerCnt)
{
PostQueuedCompletionStatus(m_hCompletionPort, 0, (DWORD) NULL, NULL);
Sleep(1000);
}
Here are the only 4 occurrence(s) of m_nWorkerCnt in your Dec 12 code.
Searching for 'm_nWorkerCnt'...
X:\Winsockiocp\IOCP_Server\IOCPServer.cpp(482): m_nWorkerCnt++;
X:\Winsockiocp\IOCP_Server\IOCPServer.cpp(654): InterlockedDecrement(&pThis->m_nWorkerCnt);
X:\Winsockiocp\IOCP_Server\IOCPServer.cpp(957): while (m_nWorkerCnt)
X:\Winsockiocp\IOCP_Server\IOCPServer.h(164): LONG m_nWorkerCnt;
4 occurrence(s) have been found.
|
|
|
|
|
John this has been fixed!
Cheers
Normski. - Professional Windows Programmer
|
|
|
|
|
As for Bug # 2: I found the following. Sometimes when the server goes down SocketThreadProc gets a socket error in WSAEnumNetworkEvents with the error being winsock error 10038. Winsock error 10038 means that you tried to perform an operation on an object that is not a socket. Your code prints the error message then exits the thread so it can't reconnect to the server because the network thread is terminated. You should recreate and initialize the socket in this case.
|
|
|
|
|
True and I'll fix this now.
Normski. - Professional Windows Programmer
|
|
|
|
|
For bug #1 use the message "Just a few things more" from strumf. All of his comments work and fix minor bugs.
For bug #2 here is the code:
in CSocketClass::SocketThreadProc(LPVOID lParam)
replace the error handling code with this:
if (nRet == SOCKET_ERROR)
{
CString str;
DWORD dwError = WSAGetLastError();
str.Format("WSAEnumNetworkEvents error %ld\n",dwError);
::PostMessage(pThis->m_hWnd, WM_STATUS_MSG, 0,(LPARAM) AllocBuffer(str));
if ( dwError == 10038){
pThis->OnClose();
continue;
}
break;
}
Also I found a third bug with the buffer that was causing access violations. The problem occurred when two threads processed a CIOCPServer::OnClientWriting on the same client at the same time. One thread would be using the buffer and the second would free it causing the access violation.
Below is the call stack for the first thread (access violations in memmove):
memmove(unsigned char * 0x003d0000, unsigned char * 0x003d0014, unsigned long 0x00000fec) line 171
CBuffer::Delete(unsigned int 0x00000014) line 543 + 34 bytes
CIOCPServer::OnClientWriting(ClientContext * 0x012768c8, unsigned long 0x00000014) line 899
CIOCPServer::ProcessIOMessage(IOType IOWrite, ClientContext * 0x012768c8, unsigned long 0x00000014) line 200 + 22 bytes
CIOCPServer::ThreadPoolFunc(void * 0x012750c4) line 624 + 23 bytes
Here is the call stack from the second thread:
_CrtDbgReport(int 0x00000000, const char * 0x00000000, int 0x00000000, const char * 0x00000000, const char * 0x005eac50) line 415
CDumpContext::OutputString(const char * 0x01d8f8d0) line 36 + 22 bytes
CDumpContext::operator<<(const char * 0x01d8fb0f) line 90
AfxTrace(const char * 0x005c5020 `string') line 58
CBuffer::DeAllocateBuffer(unsigned int 0x00000014) line 306 + 21 bytes
CBuffer::Delete(unsigned int 0x00000000) line 549
CIOCPServer::OnClientWriting(ClientContext * 0x012768c8, unsigned long 0x00000000) line 899
CIOCPServer::ProcessIOMessage(IOType IOWrite, ClientContext * 0x012768c8, unsigned long 0x00000000) line 200 + 22 bytes
CIOCPServer::ThreadPoolFunc(void * 0x012750c4) line 624 + 23 bytes
I took these call stack traces immediatly after I got the access violation in the first thread. If you look carefully the problem occurs because the second thread deallocates the buffer that the first thread is using. A solution for this is the following code;
bool CIOCPServer::OnClientWriting(ClientContext* pContext, DWORD dwIoSize)
{
if ( dwIoSize == 0 ) return true;
// rest of OnClientWriting unchanged
Your line numbers may not be exactly the same because I added the above two bug fixes before I fixed this one, however I did see this error before I modified a single line of code.
I did all of my testing on a dual processor p3 750 as the server and a 1.06 Ghz p3 laptop as the client.
John
|
|
|
|
|
John
You've been a busy guy Thanks for the feedback much appricated I'm posting new version of the whole project on my web site, one people are happy with it I submit the fixes to www.codeproject.com.
Cheers
Normski. - Professional Windows Programmer
|
|
|
|
|
I posted this earlier (message below)
Depending of result of OnClientReading and OnClientWriting functions (true or false) we issue next WSARcv, so you must take care to return true or false (in default project always return true - so per one request you issue 2 WSARcv, one in Onclientreading and one in OnClientWriting (if it works in echo mode), and it seems that some internal buffers get overhelmed) depending of waht you want to do next.
I had the same problemm (2499 times it worked with me), but after properly handling result value it works fine (no - it works great).
Igor Janjetovic
ANOXSoft Co.
|
|
|
|
|
Greetings from chilly North Carolina,
The bad weather has given me good reason to update my IOCP_Server code and TestClient application. Here are the updates...
Server:
* IOCP_Server now supports multiple connections to SQL Server databases through OLEDB
* Added a class that allows for easy storage of server parameter info in '.ini' files
* Added support for WindowsNT/2000/XP Performance monitor (what a pain in the rear!)
TestClient program
* Will automatically cycle through 7 user defined SQL Statements, send to server and display reply
* Allows for dynamic changes to IP address/port (you don't have to restart the app)
* Allows you to run multiple instances on one machine, reports port and number of transactions in NT Taskbar.
Please feel free to contact me a james_a_white@hotmail.com for the code -- ensure that the string 'IOCP' appears somewhere in the subject line. If you have previously contacted me I sent a copy of the updated code to you this morning.
I don't know how to 'post' the code to this site. If someone could help with that it would save me the trouble of having to wade through my junk mail to find requests for source code. This would allow site users to get the code much more quickly.
Cheers!
James
James A. White
|
|
|
|
|
James A. White wrote:
The bad weather has given me good reason to update my IOCP_Server code and TestClient application
You are not Norm Almond
Anyway, I am interested in that code, so please mail it to me.
Regards
Thomas
|
|
|
|
|
No... I am not Norm Almond... he is a *much* better programmer than I am! I have just taken his code and made modifications to it in order to fit the requirements of a project that I am working on. If you are attempting to learn about IO Completion ports I would highly recommend running his code since it is the focus of the article. If you also want to see how I have implemented OLEDB database access for my project and/or want to add features such as easy support for .ini files or perfmon support you can download my code.
Since my code serves as a base to a commercial application that I am developing I am hoping that someone will post either bug fixes or feature enhancements! (As I tell my 4 year old daughter... sharing is caring!).
Cheers!
James
|
|
|
|
|