767 lines
22 KiB
C
767 lines
22 KiB
C
// 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) 1993 - 2000 Microsoft Corporation.
|
|
// All Rights Reserved.
|
|
//
|
|
// Abstract:
|
|
//
|
|
// The SOCKDUP sample demonstrates the basics of shared socket
|
|
// programming in Winsock 2 using WSADuplicateSocket and WSASocket.
|
|
// The parent and child processes use memory mapped files to exchange
|
|
// the socket protocol information (WSAPROTOCOL_INFO) needed
|
|
// to duplicate the socket in the child process.
|
|
//
|
|
// The server application can function as either the parent
|
|
// or a child. The parent accepts each client connection request
|
|
// and dispatches a child process to handle the actual I/O for
|
|
// the connection. After a child process is successfully created,
|
|
// the parent is free to handle other client connection requests.
|
|
//
|
|
// Written By: Mike Liu
|
|
//
|
|
|
|
#ifdef _IA64_
|
|
#pragma warning(disable:4127 4706 4267)
|
|
#endif
|
|
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#endif
|
|
|
|
#ifndef _CRT_SECURE_NO_DEPRECATE
|
|
#define _CRT_SECURE_NO_DEPRECATE
|
|
#endif
|
|
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#include <wspiapi.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <conio.h>
|
|
|
|
char *DEFAULT_PORT = "8765";
|
|
const char FILE_MAPPING_BASE_NAME[] = "/MAPPED_FILE/WSADuplicateSocket";
|
|
const char PARENT[] = "parent";
|
|
const char CHILD[] = "child";
|
|
static int nChildProcCount = 0;
|
|
SOCKET gsListen = INVALID_SOCKET;
|
|
SOCKET gsAccept = INVALID_SOCKET;
|
|
HANDLE ghParentFileMappingEvent = NULL;
|
|
HANDLE ghChildFileMappingEvent = NULL;
|
|
HANDLE ghMMFileMap = NULL;;
|
|
|
|
void Usage(char *szProgramName)
|
|
{
|
|
fprintf(stderr, "Usage: %s [-e Endpoint] [-i IPAddress]\n\n", szProgramName);
|
|
fprintf(stderr, " Endpoint: port to listen on (default %s)\n", DEFAULT_PORT);
|
|
fprintf(stderr, " IPAddress: IP address to bind to (default INADDR_ANY)\n");
|
|
}
|
|
|
|
/*
|
|
Prints the given socket address in a printable string format.
|
|
*/
|
|
void PrintAddressString(LPSOCKADDR pSockAddr, DWORD dwSockAddrLen)
|
|
{
|
|
// INET6_ADDRSTRLEN is the maximum size of a valid IPv6 address
|
|
// including port,colons,NULL,etc.
|
|
char buf[INET6_ADDRSTRLEN];
|
|
DWORD dwBufSize = 0;
|
|
|
|
memset(buf,0,sizeof(buf));
|
|
dwBufSize = sizeof(buf);
|
|
|
|
// This function converts the pSockAddr to a printable format into buf.
|
|
if (WSAAddressToString(pSockAddr,
|
|
dwSockAddrLen,
|
|
NULL,
|
|
buf,
|
|
&dwBufSize) == SOCKET_ERROR)
|
|
{
|
|
printf("ERROR: WSAAddressToString failed %d \n", WSAGetLastError());
|
|
goto CLEANUP;
|
|
}
|
|
|
|
printf("%s\n", buf);
|
|
|
|
CLEANUP:
|
|
return;
|
|
}
|
|
|
|
BOOL ConsoleCtrlHandler(DWORD dwEvent);
|
|
void DoCleanup(void);
|
|
void DoParent(char *pszIPAdress, char *szPort, char *pszChildProcName);
|
|
void DoChild(char *pszChildFileMappingObj);
|
|
BOOL DispatchChild(SOCKET ClientSock, char *ChildProcName);
|
|
SOCKET GetSocket(char *szFileMappingObj);
|
|
BOOL DoGracefulShutdown(SOCKET sock);
|
|
|
|
int __cdecl main(int argc, char **argv)
|
|
{
|
|
char *pszIPAdress = NULL;
|
|
char *pszPort = DEFAULT_PORT;
|
|
char *pszChildFileMappingObj = NULL;
|
|
WSADATA wsaData;
|
|
BOOL bParent = TRUE;
|
|
int nStatus;
|
|
|
|
if (argc > 1)
|
|
{
|
|
int i;
|
|
|
|
for(i = 1; i < argc; i++)
|
|
{
|
|
if ((argv[i][0] == '-') || (argv[i][0] == '/'))
|
|
{
|
|
switch(tolower(argv[i][1]))
|
|
{
|
|
case 'c':
|
|
bParent = FALSE;
|
|
if (i + 1 < argc)
|
|
pszChildFileMappingObj = argv[++i];
|
|
break;
|
|
case 'i':
|
|
if (i + 1 < argc)
|
|
pszIPAdress = argv[++i];
|
|
break;
|
|
case 'e':
|
|
if (i + 1 < argc)
|
|
pszPort = argv[++i];
|
|
break;
|
|
default:
|
|
Usage(argv[0]);
|
|
exit(1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Usage(argv[0]);
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((nStatus = WSAStartup(0x202,&wsaData)) != 0)
|
|
{
|
|
fprintf(stderr, "\nWinsock 2 DLL initialization failed: %d\n", nStatus);
|
|
WSACleanup();
|
|
exit(-1);
|
|
}
|
|
|
|
if (bParent) // Run as parent
|
|
DoParent(pszIPAdress, pszPort, argv[0]);
|
|
else // Run as child
|
|
DoChild(pszChildFileMappingObj);
|
|
|
|
WSACleanup();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4127)
|
|
|
|
void DoParent(char *pszIPAddress, char *pszPort, char *pszChildProcName)
|
|
{
|
|
int nFromLen;
|
|
SOCKADDR_STORAGE saFrom;
|
|
DWORD dwProcID;
|
|
struct addrinfo hints;
|
|
struct addrinfo *res;
|
|
struct addrinfo *pAddr;
|
|
int i;
|
|
|
|
// Install the CTRL+BREAK and CTRL+C Handler
|
|
if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)ConsoleCtrlHandler,
|
|
TRUE)
|
|
== FALSE)
|
|
fprintf(stderr,"SetConsoleCtrlHandler failed: %d", GetLastError());
|
|
|
|
dwProcID = GetCurrentProcessId();
|
|
|
|
printf("Parent process %lu started ...\n", dwProcID);
|
|
|
|
// prepare the hints for the type of socket we are interested in.
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = AF_UNSPEC;
|
|
hints.ai_protocol = IPPROTO_TCP;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_flags = AI_PASSIVE; // since we're going to bind on this socket.
|
|
|
|
// getaddrinfo is the protocol independent version of GetHostByName.
|
|
// the res contains the result.
|
|
if (getaddrinfo(pszIPAddress,
|
|
pszPort,
|
|
&hints,
|
|
&res) != NO_ERROR)
|
|
{
|
|
fprintf(stderr,"getaddrinfo failed. Error = %d\n", WSAGetLastError());
|
|
goto CLEANUP;
|
|
}
|
|
|
|
printf("getaddrinfo successful.Enumerating the returned addresses ...\n\n");
|
|
|
|
// for each returned interface, create a listening socket.
|
|
gsListen = INVALID_SOCKET;
|
|
for (pAddr = res, i = 1; pAddr != NULL; pAddr = pAddr->ai_next, i++)
|
|
{
|
|
printf("Trying Address : %d from getaddrinfo\n", i);
|
|
PrintAddressString(pAddr->ai_addr, (DWORD) pAddr->ai_addrlen);
|
|
// create a suitable socket for this interface.
|
|
gsListen = WSASocket(pAddr->ai_family,
|
|
pAddr->ai_socktype,
|
|
pAddr->ai_protocol,
|
|
NULL,
|
|
0,
|
|
0);
|
|
if (gsListen != INVALID_SOCKET)
|
|
break;
|
|
|
|
fprintf(stderr,"WSASocket failed. Error = %d\n", WSAGetLastError());
|
|
fprintf(stderr,"Ignoring this address and continuing with the next. \n\n");
|
|
}
|
|
|
|
if (pAddr == NULL)
|
|
{
|
|
fprintf(stderr, "Couldn't find any suitable socket\n");
|
|
goto CLEANUP;
|
|
}
|
|
|
|
gsListen = WSASocket(pAddr->ai_family,
|
|
pAddr->ai_socktype,
|
|
pAddr->ai_protocol,
|
|
(LPWSAPROTOCOL_INFO) NULL,
|
|
0,
|
|
WSA_FLAG_OVERLAPPED);
|
|
|
|
if (gsListen == INVALID_SOCKET)
|
|
{
|
|
fprintf(stderr, "\nWSASocket() failed to obtain the listen socket: %d\n",
|
|
WSAGetLastError());
|
|
goto CLEANUP;
|
|
}
|
|
|
|
// bind() associates a local address and port
|
|
// combination with the socket just created.
|
|
if (bind(gsListen, (struct sockaddr*)pAddr->ai_addr, (int) pAddr->ai_addrlen)
|
|
== SOCKET_ERROR)
|
|
{
|
|
fprintf(stderr, "\nbind() failed: %d\n",
|
|
WSAGetLastError());
|
|
closesocket(gsListen);
|
|
gsListen = INVALID_SOCKET;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
// start listening on the socket for incoming connections
|
|
if (listen(gsListen,5) == SOCKET_ERROR)
|
|
{
|
|
fprintf(stderr, "\nlisten() failed: %d\n",
|
|
WSAGetLastError());
|
|
closesocket(gsListen);
|
|
gsListen = INVALID_SOCKET;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
nFromLen = sizeof(saFrom);
|
|
|
|
printf("Listening on %s:%s\n", pszIPAddress? pszIPAddress : "localhost", pszPort);
|
|
|
|
while(TRUE)
|
|
{
|
|
printf("Waiting for new connection (Type CTRL+C to exit) ...\n\n");
|
|
|
|
gsAccept = WSAAccept(gsListen,
|
|
(struct sockaddr*)&saFrom,
|
|
&nFromLen,
|
|
(LPCONDITIONPROC)NULL,
|
|
0);
|
|
|
|
if(gsAccept == INVALID_SOCKET)
|
|
{
|
|
fprintf(stderr, "\nWSAAccept() failed: %d\n",
|
|
WSAGetLastError());
|
|
break;
|
|
}
|
|
|
|
// Spawn a child process to handle the I/O.
|
|
DispatchChild(gsAccept, pszChildProcName);
|
|
|
|
// Doing a hard shutdown here is a no-no because it
|
|
// will also reset the duplicated child socket.
|
|
/*
|
|
LINGER lingerStruct;
|
|
|
|
lingerStruct.l_onoff = 1;
|
|
lingerStruct.l_linger = 0;
|
|
setsockopt(gsAccept, SOL_SOCKET, SO_LINGER,
|
|
(char *)&lingerStruct, sizeof(lingerStruct));
|
|
*/
|
|
|
|
closesocket(gsAccept);
|
|
gsAccept = INVALID_SOCKET;
|
|
}
|
|
|
|
CLEANUP:
|
|
|
|
if (gsListen != INVALID_SOCKET)
|
|
{
|
|
closesocket(gsListen);
|
|
gsListen = INVALID_SOCKET;
|
|
}
|
|
|
|
if (res != NULL)
|
|
{
|
|
freeaddrinfo(res);
|
|
res = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void DoChild(char *pszChildFileMappingObj)
|
|
{
|
|
SOCKET sockDuplicated = INVALID_SOCKET;
|
|
DWORD dwProcID;
|
|
char szBuf[MAX_PATH+1];
|
|
WSABUF wsaBuf;
|
|
DWORD dwReceived = 0;
|
|
DWORD dwFlags = 0;
|
|
int nStatus;
|
|
|
|
dwProcID = GetCurrentProcessId();
|
|
|
|
printf("Child process %lu started ...\n", dwProcID);
|
|
|
|
if ((sockDuplicated = GetSocket(pszChildFileMappingObj))
|
|
!= INVALID_SOCKET)
|
|
{
|
|
while(TRUE)
|
|
{
|
|
szBuf[0] = '\0';
|
|
wsaBuf.len = MAX_PATH;
|
|
wsaBuf.buf = (char *)szBuf;
|
|
|
|
// Receive the data sent by the other side.
|
|
nStatus = WSARecv(sockDuplicated,
|
|
&wsaBuf,
|
|
1,
|
|
&dwReceived,
|
|
&dwFlags,
|
|
(LPWSAOVERLAPPED) NULL,
|
|
0);
|
|
|
|
if (nStatus == 0) // success
|
|
{
|
|
if (dwReceived == 0) // Client closed connection
|
|
{
|
|
fprintf(stderr, "Client closed connection\n");
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Print whatever is received
|
|
szBuf[dwReceived] = '\0';
|
|
printf("%s", szBuf);
|
|
}
|
|
}
|
|
else // SOCKET_ERROR
|
|
{
|
|
fprintf(stderr, "WSARecv() failed: %d\n", WSAGetLastError());
|
|
break;
|
|
}
|
|
}
|
|
|
|
DoGracefulShutdown(sockDuplicated);
|
|
sockDuplicated = INVALID_SOCKET;
|
|
}
|
|
else
|
|
fprintf(stderr, "\nChild socket cannot be obtained: %d\n",
|
|
WSAGetLastError());
|
|
|
|
printf("\nChild process %lu has finished!!!\n", dwProcID);
|
|
|
|
printf("Press any key to continue ...");
|
|
getchar();
|
|
}
|
|
|
|
#pragma warning(pop)
|
|
|
|
|
|
// The server and client have to rely on
|
|
// certain interprocess communication schemes
|
|
// to exchange the WSAPROTOCOL_INFO needed for
|
|
// duplicating the socket. In this sample,
|
|
// we use momory mapped files.
|
|
BOOL DispatchChild(SOCKET ClientSock, char *pszChildProcName)
|
|
{
|
|
char szChildComandLineBuf[MAX_PATH];
|
|
char szFileMappingObj[MAX_PATH];
|
|
BOOL bResult = TRUE;
|
|
STARTUPINFO siParent;
|
|
PROCESS_INFORMATION piChild;
|
|
char szParentEventName[MAX_PATH];
|
|
char szChildEventName[MAX_PATH];
|
|
|
|
ZeroMemory(&siParent, sizeof(siParent));
|
|
siParent.cb = sizeof(siParent);
|
|
siParent.dwFlags = STARTF_USECOUNTCHARS;
|
|
siParent.dwXCountChars = 10 * MAX_PATH;
|
|
siParent.dwYCountChars = MAX_PATH;
|
|
|
|
// Compose a name for the memory mappled file.
|
|
sprintf_s(szFileMappingObj,
|
|
MAX_PATH,
|
|
"%s%i",
|
|
FILE_MAPPING_BASE_NAME,
|
|
nChildProcCount++);
|
|
|
|
sprintf_s(szParentEventName, MAX_PATH,"%s%s", szFileMappingObj, PARENT);
|
|
sprintf_s(szChildEventName, MAX_PATH,"%s%s", szFileMappingObj, CHILD);
|
|
|
|
// Create an event to signal the child
|
|
// that the protocol info is set
|
|
if ((ghParentFileMappingEvent = CreateEvent(NULL, TRUE, FALSE, szParentEventName)) == NULL)
|
|
{
|
|
fprintf(stderr, "\nCreateEvent() failed: %d\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
// Create an event to for the child to signal the
|
|
// parent that the protocol info can be released
|
|
if ((ghChildFileMappingEvent = CreateEvent(NULL, TRUE, FALSE, szChildEventName)) == NULL)
|
|
{
|
|
fprintf(stderr, "\nCreateEvent() failed: %d\n", GetLastError());
|
|
CloseHandle(ghParentFileMappingEvent);
|
|
ghParentFileMappingEvent = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
// Set up the child process command line options.
|
|
// The memory mapped file name is passed in as
|
|
// one of the options.
|
|
sprintf_s(szChildComandLineBuf,
|
|
MAX_PATH,
|
|
"%s /c %s",
|
|
pszChildProcName,
|
|
szFileMappingObj);
|
|
|
|
if (CreateProcess(NULL,
|
|
szChildComandLineBuf,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
CREATE_NEW_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
&siParent,
|
|
&piChild))
|
|
{
|
|
WSAPROTOCOL_INFO ProtocolInfo;
|
|
int nError;
|
|
LPVOID lpView;
|
|
int nStructLen = sizeof(WSAPROTOCOL_INFO);
|
|
|
|
// Get the protocol information
|
|
// to be used to duplicate the socket
|
|
if (WSADuplicateSocket(ClientSock,
|
|
piChild.dwProcessId,
|
|
&ProtocolInfo) == SOCKET_ERROR)
|
|
{
|
|
fprintf(stderr, "WSADuplicateSocket(): failed. Error = %d\n", WSAGetLastError());
|
|
DoCleanup();
|
|
exit(1);
|
|
}
|
|
|
|
// Set the protocol information in a
|
|
// memory mapped file for the child to use
|
|
ghMMFileMap = CreateFileMapping(INVALID_HANDLE_VALUE,
|
|
NULL,
|
|
PAGE_READWRITE,
|
|
0,
|
|
nStructLen,
|
|
szFileMappingObj);
|
|
|
|
if (ghMMFileMap != NULL)
|
|
{
|
|
if ((nError = GetLastError()) == ERROR_ALREADY_EXISTS)
|
|
fprintf(stderr, "CreateFileMapping(): mappping file already exists\n");
|
|
else
|
|
{
|
|
lpView = MapViewOfFile(ghMMFileMap,
|
|
FILE_MAP_READ | FILE_MAP_WRITE,
|
|
0, 0, 0);
|
|
|
|
if (lpView != NULL)
|
|
{
|
|
memcpy(lpView, &ProtocolInfo, nStructLen);
|
|
UnmapViewOfFile(lpView);
|
|
|
|
// Signal the child the protocol infomation is set
|
|
SetEvent(ghParentFileMappingEvent);
|
|
// Wait the child to signal that the protocol
|
|
// infomation can be released now
|
|
if (WaitForSingleObject(ghChildFileMappingEvent, 2000) == WAIT_OBJECT_0)
|
|
{
|
|
fprintf(stderr, "WaitForSingleObject() failed: %d\n", GetLastError());
|
|
DoCleanup();
|
|
exit(1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "MapViewOfFile() failed: %d\n", GetLastError());
|
|
bResult = FALSE;
|
|
}
|
|
}
|
|
|
|
CloseHandle(ghMMFileMap);
|
|
ghMMFileMap = NULL;
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "CreateFileMapping() failed: %d\n", GetLastError());
|
|
bResult = FALSE;
|
|
}
|
|
|
|
CloseHandle(piChild.hThread);
|
|
CloseHandle(piChild.hProcess);
|
|
}
|
|
else
|
|
{
|
|
printf("\nCreate child process failed!!!\n");
|
|
bResult = FALSE;
|
|
}
|
|
|
|
if (ghParentFileMappingEvent != NULL)
|
|
{
|
|
CloseHandle(ghParentFileMappingEvent);
|
|
ghParentFileMappingEvent = NULL;
|
|
}
|
|
|
|
if (ghChildFileMappingEvent != NULL)
|
|
{
|
|
CloseHandle(ghChildFileMappingEvent);
|
|
ghChildFileMappingEvent = NULL;
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
// Once the protocol information is set by the
|
|
// parent in the memory mapped file, the child
|
|
// is ready to use it to duplicate the socket.
|
|
SOCKET GetSocket(char *szFileMappingObj)
|
|
{
|
|
WSAPROTOCOL_INFO ProtocolInfo;
|
|
SOCKET sockDuplicated = INVALID_SOCKET;
|
|
char szParentEventName[MAX_PATH];
|
|
char szChildEventName[MAX_PATH];
|
|
|
|
sprintf_s(szParentEventName, MAX_PATH,"%s%s", szFileMappingObj, PARENT);
|
|
sprintf_s(szChildEventName, MAX_PATH,"%s%s", szFileMappingObj, CHILD);
|
|
|
|
// Open the events
|
|
if ((ghParentFileMappingEvent = OpenEvent(SYNCHRONIZE, FALSE, szParentEventName)) == 0)
|
|
{
|
|
fprintf(stderr, "OpenEvent() failed: %d\n", GetLastError());
|
|
return INVALID_SOCKET;
|
|
}
|
|
|
|
if ((ghChildFileMappingEvent = OpenEvent(SYNCHRONIZE, FALSE, szChildEventName)) == 0)
|
|
{
|
|
fprintf(stderr, "OpenEvent() failed: %d\n", GetLastError());
|
|
CloseHandle(ghParentFileMappingEvent);
|
|
ghParentFileMappingEvent = NULL;
|
|
return INVALID_SOCKET;
|
|
}
|
|
|
|
// Wait for the parent to signal that the protocol info
|
|
// is ready to be accessed
|
|
if (WaitForSingleObject(ghParentFileMappingEvent, 2000) == WAIT_FAILED)
|
|
{
|
|
fprintf(stderr, "WaitForSingleObject() failed: %d\n", GetLastError());
|
|
DoCleanup();
|
|
exit(1);
|
|
}
|
|
|
|
ghMMFileMap = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE,
|
|
FALSE,
|
|
szFileMappingObj);
|
|
|
|
if (ghMMFileMap != NULL)
|
|
{
|
|
LPVOID lpView = MapViewOfFile(ghMMFileMap,
|
|
FILE_MAP_READ | FILE_MAP_WRITE,
|
|
0, 0, 0);
|
|
|
|
if ((BYTE*) lpView != NULL)
|
|
{
|
|
int nStructLen = sizeof(WSAPROTOCOL_INFO);
|
|
|
|
memcpy(&ProtocolInfo, lpView, nStructLen);
|
|
UnmapViewOfFile(lpView);
|
|
|
|
// Duplicate the socket based on the protocol
|
|
// information stored in the memory mapped file.
|
|
sockDuplicated = WSASocket(FROM_PROTOCOL_INFO,
|
|
FROM_PROTOCOL_INFO,
|
|
FROM_PROTOCOL_INFO,
|
|
&ProtocolInfo,
|
|
0,
|
|
0);
|
|
|
|
// Signal the parent the we are done
|
|
// with the mapped file
|
|
SetEvent(ghChildFileMappingEvent);
|
|
}
|
|
else
|
|
fprintf(stderr, "MapViewOfFile() failed: %d\n", GetLastError());
|
|
}
|
|
else
|
|
fprintf(stderr, "CreateFileMapping() failed: %d\n", GetLastError());
|
|
|
|
if (ghParentFileMappingEvent != NULL)
|
|
{
|
|
CloseHandle(ghParentFileMappingEvent);
|
|
ghParentFileMappingEvent = NULL;
|
|
}
|
|
|
|
if (ghChildFileMappingEvent != NULL)
|
|
{
|
|
CloseHandle(ghChildFileMappingEvent);
|
|
ghChildFileMappingEvent = NULL;
|
|
}
|
|
|
|
if (ghMMFileMap != NULL)
|
|
{
|
|
CloseHandle(ghMMFileMap);
|
|
ghMMFileMap = NULL;
|
|
}
|
|
|
|
return sockDuplicated;
|
|
}
|
|
|
|
|
|
// Handle CTRL+BREAK or CTRL+C events
|
|
BOOL ConsoleCtrlHandler(DWORD dwEvent)
|
|
{
|
|
UNREFERENCED_PARAMETER( dwEvent );
|
|
|
|
DoCleanup();
|
|
printf("Server terminated. Bye!\n");
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void DoCleanup(void)
|
|
{
|
|
// Do a best effort cleanup
|
|
if (ghParentFileMappingEvent != NULL)
|
|
{
|
|
CloseHandle(ghParentFileMappingEvent);
|
|
ghParentFileMappingEvent = NULL;
|
|
}
|
|
if (ghChildFileMappingEvent != NULL)
|
|
{
|
|
CloseHandle(ghChildFileMappingEvent);
|
|
ghChildFileMappingEvent = NULL;
|
|
}
|
|
if (ghMMFileMap != NULL)
|
|
{
|
|
CloseHandle(ghMMFileMap);
|
|
ghMMFileMap = NULL;
|
|
}
|
|
if (gsListen != INVALID_SOCKET)
|
|
{
|
|
closesocket(gsListen);
|
|
gsListen = INVALID_SOCKET;
|
|
}
|
|
if (gsAccept != INVALID_SOCKET)
|
|
{
|
|
closesocket(gsAccept);
|
|
gsAccept = INVALID_SOCKET;
|
|
}
|
|
|
|
WSACleanup();
|
|
}
|
|
|
|
// Do a graceful shutdown of a the given socket sock.
|
|
BOOL DoGracefulShutdown(SOCKET sock)
|
|
{
|
|
BOOL bRetVal = FALSE;
|
|
WSAEVENT hEvent = WSA_INVALID_EVENT;
|
|
long lNetworkEvents = 0;
|
|
int status = 0;
|
|
|
|
hEvent = WSACreateEvent();
|
|
if (hEvent == WSA_INVALID_EVENT)
|
|
{
|
|
fprintf(stderr, "DoGracefulShutdown: WSACreateEvent failed: %d\n",
|
|
WSAGetLastError());
|
|
goto CLEANUP;
|
|
}
|
|
|
|
lNetworkEvents = FD_CLOSE;
|
|
if (WSAEventSelect(sock, hEvent, lNetworkEvents) != 0)
|
|
{
|
|
fprintf(stderr, "DoGracefulShutdown: WSAEventSelect failed: %d\n",
|
|
WSAGetLastError());
|
|
goto CLEANUP;
|
|
}
|
|
|
|
if (shutdown(sock, SD_SEND) != 0)
|
|
{
|
|
fprintf(stderr, "DoGracefulShutdown: shutdown failed: %d\n",
|
|
WSAGetLastError());
|
|
goto CLEANUP;
|
|
}
|
|
|
|
if (WaitForSingleObject(hEvent, INFINITE) != WAIT_OBJECT_0)
|
|
{
|
|
fprintf(stderr, "DoGracefulShutdown: WaitForSingleObject failed: %d\n",
|
|
WSAGetLastError());
|
|
goto CLEANUP;
|
|
}
|
|
|
|
do
|
|
{
|
|
char buf[128];
|
|
|
|
status = recv(sock, buf, sizeof(buf), 0);
|
|
} while (!(status == 0 || status == SOCKET_ERROR));
|
|
|
|
if (closesocket(sock) != 0)
|
|
{
|
|
fprintf(stderr, "DoGracefulShutdown: closesocket failed: %d\n",
|
|
WSAGetLastError());
|
|
goto CLEANUP;
|
|
}
|
|
|
|
printf("Socket %d has been closed gracefully\n", sock);
|
|
sock = INVALID_SOCKET;
|
|
bRetVal = TRUE;
|
|
|
|
CLEANUP:
|
|
|
|
if (hEvent != WSA_INVALID_EVENT)
|
|
{
|
|
WSACloseEvent(hEvent);
|
|
hEvent = WSA_INVALID_EVENT;
|
|
}
|
|
|
|
if (sock != INVALID_SOCKET)
|
|
{
|
|
fprintf(stderr, "DoGracefulShutdown: Can't close socket gracefully. "
|
|
"So, closing it anyway ... \n");
|
|
closesocket(sock);
|
|
sock = INVALID_SOCKET;
|
|
}
|
|
|
|
return bRetVal;
|
|
}
|