⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 srvctrl.c

📁 Web服务器V1.01,Win Socket底层开发
💻 C
字号:
/********************************************************************
** WebSrv -- SrvCtrl.c
**
** Steven Lee	2002.11
********************************************************************/

#include <Windows.h>
#include <string.h>
#include <TCHAR.h>
#include <stdio.h>

#ifdef	_DEBUG
//#define	_LOG_USERS
//#define	_DEBUG_VIEW_REQUEST
//#define	_DEBUG_VIEW_RESPONSE
//#define	_DEBUG_VIEW_RESHEAD
#endif

#include "SrvCtrl.h"
#include "WinSockEx.h"
#include "WebSrvDefs.h"

#define	MAX_MSG_NUM		16

SERVERINFOS		g_server;							//server messages
DWORD			g_dwMaxUsers;						//max users number
DWORD			g_dwCurUsers = 0;					//current users number
TCHAR			g_szWebRoot[MAX_PATH];				//server root directory
HANDLE			g_hLogFile = INVALID_HANDLE_VALUE;	//server logfile

void InitSrvCtrl()
{
	g_server.bTerminate = FALSE;
	g_server.hSrvSocket = INVALID_SOCKET;
	g_server.nPort = DEFAULT_SRV_PORT;
	g_server.hSrvThread = NULL;

	g_dwMaxUsers = DEFAULT_MAX_USERS;
	g_dwCurUsers = 0;

	_tcscpy( g_szWebRoot,_T(".\\wwwroot") );

	g_hLogFile = CreateFile( _T(".\\WebSrv.log"),GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS,
		FILE_ATTRIBUTE_NORMAL,NULL );
}

void ReleaseSrvCtrl()
{
	if( g_hLogFile != INVALID_HANDLE_VALUE )
		CloseHandle( g_hLogFile );
}

BOOL WINAPI ConsoleCtrlEvent( DWORD dwCtrlType )
{
	switch( dwCtrlType )
	{
		case CTRL_C_EVENT:
		case CTRL_BREAK_EVENT:
		case CTRL_CLOSE_EVENT:
		case CTRL_LOGOFF_EVENT:
		case CTRL_SHUTDOWN_EVENT:
			StopServer(5000);
			ReleaseWinSocket();
			ReleaseSrvCtrl();
			break;
	}
	return TRUE;
}

BOOL StartServer(LPTSTR lpszError)
{
	DWORD		dwThrdID;

	g_server.hSrvSocket = CreateSrvSocket( g_server.nPort,lpszError );
	if( g_server.hSrvSocket == INVALID_SOCKET )
		return FALSE;

	g_server.hSrvThread = CreateThread( NULL,0,(LPTHREAD_START_ROUTINE)SrvThrdProc,
		(LPVOID)&g_server,0,&dwThrdID );
	if( g_server.hSrvThread == NULL )
	{
		if( lpszError != NULL )
			_stprintf( lpszError,_T("create server thread failed\n") );
		CloseSrvSocket( g_server.hSrvSocket );

		return FALSE;
	}

	g_server.bTerminate = FALSE;

	LogServerMessage( _T("Server Started"),INVALID_SOCKET );

	return TRUE;
}

BOOL StopServer(DWORD dwTimeOut)
{
	g_server.bTerminate = TRUE;

	if( g_server.hSrvThread != NULL )
	{
		if( WaitForSingleObject( g_server.hSrvThread,dwTimeOut ) != WAIT_OBJECT_0 )
		{
			TerminateThread( g_server.hSrvThread,1 );
			
			return FALSE;
		}
		
		CloseHandle( g_server.hSrvThread );
		g_server.hSrvThread = NULL;
	}
	
	LogServerMessage( _T("Server Stopped"),INVALID_SOCKET );

	return TRUE;
}

DWORD WINAPI SrvThrdProc(LPVOID lpParam)
{
	LPSERVERINFOS		lpSrvInfos = (LPSERVERINFOS)lpParam;
	fd_set				fds;
	struct timeval		timeout;
	int					nErr;
	SOCKET				hSocket;
	struct sockaddr_in	client;
	int					nAddrLen = sizeof(struct sockaddr_in);
	HANDLE				hClientThrd;
	DWORD				dwCltThrdId;

	timeout.tv_sec = 0;
	timeout.tv_usec = 500;

	while( !lpSrvInfos->bTerminate )
	{
		FD_ZERO(&fds);
		FD_SET(lpSrvInfos->hSrvSocket,&fds);

		nErr = select( lpSrvInfos->hSrvSocket + 1,&fds,NULL,NULL,&timeout );
		if( nErr == WSAEINVAL )
			continue;
		else if( nErr == SOCKET_ERROR )
			return 1;

		if( FD_ISSET(lpSrvInfos->hSrvSocket,&fds) )
		{
			hSocket = accept( lpSrvInfos->hSrvSocket,(struct sockaddr*)&client,&nAddrLen );
			if( hSocket == INVALID_SOCKET )
			{
				_tprintf( _T("socket accept failed!existing...\n") );
				break;
			}

			if( (g_dwMaxUsers > 0) && (g_dwCurUsers >= g_dwMaxUsers) )
			{
				closesocket( hSocket );
				continue;
			}

			hClientThrd = CreateThread( NULL,0,(LPTHREAD_START_ROUTINE)CltThrdProc,
				(LPVOID)hSocket,0,&dwCltThrdId );
			if( hClientThrd == NULL )
			{
				_tprintf( _T("cannot create client thread.\n") );
				break;
			}

			CloseHandle( hClientThrd );
		}
	}
	
	TerminateSrvSocket( lpSrvInfos->hSrvSocket );
	lpSrvInfos->hSrvSocket = INVALID_SOCKET;
	lpSrvInfos->bTerminate = TRUE;
	Sleep(1000);
	
	return 0;
}

DWORD WINAPI CltThrdProc(LPVOID lpParam)
{
	SOCKET			hSocket = (SOCKET)lpParam;
	fd_set			fds;
	int				nErr;
	int				nRecvLen;
	struct timeval	timeout;

	TCHAR			szRecvBuf[MAX_SOCKET_BUF];

	timeout.tv_sec = 0;
	timeout.tv_usec = 1000;

	g_dwCurUsers++;

#ifdef	_LOG_USERS
	LogServerMessage( _T("User Log In"),hSocket );
#endif

	while( !g_server.bTerminate )
	{
		FD_ZERO(&fds);
		FD_SET(hSocket,&fds);

		nErr = select( hSocket + 1,&fds,NULL,NULL,&timeout );
		if( nErr == WSAEINVAL )
			continue;
		else if( nErr == SOCKET_ERROR )
			break;

		if( FD_ISSET(hSocket,&fds) )
		{
			ZeroMemory( szRecvBuf,MAX_SOCKET_BUF );
			nRecvLen = ReadFromSocket( hSocket,szRecvBuf,
				MAX_SOCKET_BUF * sizeof(TCHAR),NULL );
			if( nRecvLen <= 0 )
				break;
			
#ifdef	_DEBUG_VIEW_REQUEST
			printf( "\nRequest Message:\n" );
			_tprintf( szRecvBuf );
#endif
			
			if( !SendSrvResponse( hSocket,szRecvBuf ) )
				break;
		}
	}

#ifdef	_LOG_USERS
	LogServerMessage( _T("User Log Out"),hSocket );
#endif

	closesocket( hSocket );

	g_dwCurUsers--;
	
	return 0;
}

BOOL ServerRunning()
{
	return !g_server.bTerminate;
}

UINT SetServerPort(UINT nPort)
{
	if( (g_server.hSrvSocket == INVALID_SOCKET) && (nPort > 0) )
		g_server.nPort = nPort;

	return g_server.nPort;
}

int SetMaxUsers(int dwMax)
{
	if( dwMax > 0 )
		g_dwMaxUsers = dwMax;
	
	return g_dwMaxUsers;
}

BOOL GetHTTPFileName(LPCTSTR lpszHeader,LPSTR lpszHTTPFile,SOCKET s)
{
	LPSTR		lpStart,lpEnd;
	TCHAR		lpStrTmp[MAX_PATH];
	int			i;

	if( _tcsnicmp( lpszHeader,_T("Get"),3 ) != 0 )
	{
		LogServerMessage( _T("Unknown HTTP Message Format."),s );
		return FALSE;
	}

	lpStart = _tcschr( lpszHeader,_T('/') );
	lpEnd = _tcschr( lpStart,_T(' ') );
	ZeroMemory( lpStrTmp,MAX_PATH * sizeof(TCHAR) );
	_tcsncpy( lpStrTmp,lpStart,lpEnd - lpStart );
		
	if( _tcslen( lpStrTmp ) == 1 )
		_stprintf( lpszHTTPFile,_T("%s\\Index.html"),g_szWebRoot );
	else
	{
		for( i=0;i<(lpEnd - lpStart);i++ )
			if( lpStrTmp[i] == _T('/') )
				lpStrTmp[i] = _T('\\');
			
		_stprintf( lpszHTTPFile,_T("%s%s"),g_szWebRoot,lpStrTmp );
	}

	return TRUE;
}

BOOL GetHTTPVersion(LPCTSTR lpszHeader,int* lpnMajor,int* lpnMinor)
{
	LPTSTR		lpStart,lpEnd;
	TCHAR		szNumber[10];

	lpStart = _tcsrchr( lpszHeader,_T('/') );
	lpEnd = _tcsrchr( lpszHeader,_T('.') );

	if( lpStart && lpEnd )
	{
		lpStart++;
		ZeroMemory( szNumber,10 * sizeof(TCHAR) );
		_tcsncpy( szNumber,lpStart,lpEnd - lpStart );
		*lpnMajor = atoi( szNumber );
		lpEnd++;
		*lpnMinor = atoi( lpEnd );

		return TRUE;
	}

	return FALSE;
}

BOOL ReqHTTPFileExists(LPTSTR lpszFile)
{
	LPTSTR		lpExt;

	if( GetFileAttributes(lpszFile) != (DWORD)-1 )
		return TRUE;

	lpExt = _tcsrchr( lpszFile,_T('.') );
	if( lpExt != NULL )
	{
		if( _tcsicmp(lpExt,_T(".htm")) == 0 )
			_tcscat( lpszFile,_T("l") );
		else if( (_tcsicmp(lpExt,_T(".html")) == 0) && (_tcslen(lpszFile) > 0) )
			lpszFile[_tcslen(lpszFile) - 1] = _T('\0');

		return GetFileAttributes(lpszFile) != (DWORD)-1;
	}

	return FALSE;
}

BOOL SendSrvResponse(SOCKET hSocket,LPTSTR pszRecv)
{
	const TCHAR	seps[] = _T("\r\n");
	TCHAR		szResponse[MAX_SOCKET_BUF];
	TCHAR		szHTTPFile[MAX_PATH];
	PCHAR		strTok;
	BOOL		bFileOK = FALSE;
	HANDLE		hFile;
	DWORD		dwFileSize,dwToRead,dwRead;
	HTTPHEADER	httpHead;
	int			nVerMajor,nVerMinor;

	strTok = _tcstok( pszRecv,seps );
	if( (!GetHTTPFileName( strTok,szHTTPFile,hSocket )) 
		|| (!GetHTTPVersion(strTok,&nVerMajor,&nVerMinor) ) )
		httpHead.httpRes = RES_BADREQ;
	else
	{
		if( !HTTPVersionSupport( nVerMajor,nVerMinor ) )
			httpHead.httpRes = RES_NOTSUPPORT;
		else if( !ReqHTTPFileExists( szHTTPFile ) )
			httpHead.httpRes = RES_NOTFOUND;
		else
		{
			httpHead.httpRes = RES_OK;
			bFileOK = TRUE;
		}
	}

	if( bFileOK )
	{
		GetContentType( szHTTPFile,httpHead.szContentType );
		hFile = CreateFile( szHTTPFile,GENERIC_READ,FILE_SHARE_READ | FILE_SHARE_WRITE,
			NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL );
		if( hFile == INVALID_HANDLE_VALUE )
			return FALSE;
		dwFileSize = GetFileSize( hFile,NULL );
		if( dwFileSize == INVALID_FILE_SIZE )
		{
			CloseHandle( hFile );
			return FALSE;
		}
		httpHead.dwData = dwFileSize;
		
		FillHTTPResHeader(szResponse,MAX_SOCKET_BUF,&httpHead);
		SendToSocket( hSocket,szResponse,_tcslen(szResponse) * sizeof(TCHAR),NULL );
		
		dwToRead = MAX_SOCKET_BUF;
		while( dwFileSize > 0 )
		{
			ZeroMemory( szResponse,MAX_SOCKET_BUF * sizeof(TCHAR) );
			if( !ReadFile( hFile,szResponse,dwToRead * sizeof(TCHAR),&dwRead,NULL ) )
			{
				LogServerMessage( _T("Error when read file."),INVALID_SOCKET );
				CloseHandle( hFile );
				return FALSE;
			}
			SendToSocket( hSocket,szResponse,dwRead,NULL );

			dwFileSize -= dwRead;
		}

		CloseHandle( hFile );
	}
	else
	{
		if( FillHTTPResHeader( szResponse,MAX_SOCKET_BUF,&httpHead ) > 0 )
			SendToSocket( hSocket,szResponse,strlen(szResponse),NULL );
	}

#ifdef	_DEBUG_VIEW_RESPONSE
	printf( "\nResponse Message:\n" );
	printf( szResponse );
	printf( "\n" );
#endif
	
	return FALSE;
}

DWORD FillHTTPResHeader(LPTSTR lpszResponse,int nSize,LPHTTPHEADER lpHeader)
{
	static const TCHAR* lpcsResMsg[] = {
		_T("200 OK\r\n"),
		_T("301 Moved Permanently\r\n"),
		_T("400 Bad Request\r\n"),
		_T("404 Not Found\r\n"),
		_T("505 HTTP Version Not Supported\r\n")
	};

	TCHAR	szBuffer[MAX_PATH];
	
	ZeroMemory( lpszResponse,nSize * sizeof(TCHAR) );
	_tcscpy( lpszResponse,_T("HTTP/1.1 ") );
	_tcscat( lpszResponse,lpcsResMsg[lpHeader->httpRes] );
	_tcscat( lpszResponse,_T("Steven Lee--WebSrv/1.01\r\n") );
	_tcscat( lpszResponse,_T("Connection: close\r\n") );

	if( lpHeader->httpRes == RES_OK )
	{
		_tcscat( lpszResponse,_T("Accept-Ranges: bytes\r\n") );
		_stprintf( szBuffer,_T("Content-Length: %d\r\n"),lpHeader->dwData );
		_tcscat( lpszResponse,szBuffer );
		_stprintf( szBuffer,_T("Content-Type: %s\r\n\r\n"),lpHeader->szContentType );
		_tcscat( lpszResponse,szBuffer );
	}

#ifdef	_DEBUG_VIEW_RESHEAD
	_tprintf( _T("%s\n"),lpszResponse );
#endif
	
	return _tcslen( lpszResponse );
}

#define	N_CONTENT_TYPE	7

void GetContentType(LPCTSTR lpszReqFile,LPTSTR lpszType)
{
	static const TCHAR* lpcsType[N_CONTENT_TYPE * 2] = {
		_T("htm"),_T("text/html"),
		_T("html"),_T("text/html"),
		_T("txt"),_T("text/html"),
		_T("jpg"),_T("image/jpeg"),
		_T("bmp"),_T("image/bitmap"),
		_T("gif"),_T("image/gif"),
		_T("swf"),_T("application/x-shockwave-flash")
	};
	
	int		i;
	LPTSTR	lpszExtPos = _tcsrchr( lpszReqFile,_T('.') );

	_tcscpy( lpszType,_T("*/*") );
	lpszExtPos++;
	for(i=0;i<N_CONTENT_TYPE;i++)
		if( _tcsicmp(lpszExtPos,lpcsType[i * 2]) == 0 )
		{
			_tcscpy( lpszType,lpcsType[i * 2 + 1] );
			break;
		}
}

const DWORD GetUsersNumber()
{
	return g_dwCurUsers;
}

void LogServerMessage(LPCTSTR szLog,SOCKET s)
{
	TCHAR				szBuffer[MAX_PATH];
	SYSTEMTIME			st;
	TCHAR				szIP[20];
	DWORD				dwWritten;
	
	if( g_hLogFile == INVALID_HANDLE_VALUE )
		return;

	GetLocalTime( &st );
	_stprintf( szBuffer,_T("[%d:%d:%d-%d]--"),st.wHour,
		st.wMinute,st.wSecond,st.wMilliseconds );

	_tcscat( szBuffer,szLog );

	if( s != INVALID_SOCKET )
	{
		if( GetSocketIP( s,szIP,20 * sizeof(TCHAR) ) )
		{
			_tcscat( szBuffer,_T("--From ") );
			_tcscat( szBuffer,szIP );
		}
	}

	_tcscat( szBuffer,_T("\r\n") );

	WriteFile( g_hLogFile,szBuffer,_tcslen(szBuffer) * sizeof(TCHAR),&dwWritten,NULL );
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -