13.1 HTTP 簡介
HTTP(Hyper Text Transfer Protocol,超文本傳輸協議)是用于從萬維網(WWW:World Wide Web) 服務器(簡稱Web 服務器)傳輸超文本到本地瀏覽器的傳送協議,基于TCP/IP 通信協 議來傳遞數據 (HTML 文件、圖片文件、查詢結果等)。
13.2 HTTP 的工作原理
HTTP協議工作于客戶端/服務器端架構上。瀏覽器作為HTTP 客戶端通過URL 向 HTTP 服務器端即Web 服務器發送所有請求。
Web服務器有Apache 服務器、IIS 服務器 (Internet Information Services) 等。 Web服務器根據接收到的請求向客戶端發送響應信息。
HTTP 的默認端口號為80,但是你也可以改為8080或者其他端口。 HTTP的注意事項如下3點:
- (1)HTTP 是無連接:無連接的含義是限制每次連接只處理一個請求。服務器處理完客 戶的請求,并收到客戶的應答后即斷開連接。采用這種方式可以節省傳輸時間。
- (2)HTTP 是媒體獨立的:這意味著,只要客戶端和服務器知道如何處理數據內容,任 何類型的數據都可以通過HTTP 發送。客戶端以及服務器指定使用適合的MIME-type內容類 型。
- (3)HTTP 是無狀態的:HTTP 協議是無狀態協議。無狀態是指協議對于事務處理沒有記 憶能力。缺少狀態意味著如果后續處理需要前面的信息,則它必須重傳,這樣可能導致每次連 接傳送的數據量增大。另一方面,在服務器不需要先前信息時它的應答較快。
我們來看一下HTTP 協議通信流程,如圖13-1所示。
13.3 HTTP 的特點
HTTP 協議的主要特點可概括如下:
- (1)支持客戶/服務器模式。
- (2)簡單快速:客戶向服務器請求服務時,只需傳送請求方法和路徑。請求方法常用的 有 GET 、HEAD 、POST 。每種方法規定了客戶與服務器聯系的類型不同。HTTP 協議簡單, 使得HTTP 服務器的程序規模小,因而通信速度很快。
- (3)靈活: HTTP 允許傳輸任意類型的數據對象。正在傳輸的類型由Content-Type 加 以 標記 。
- (4)無連接:無連接的含義是限制每次連接只處理一個請求。服務器處理完客戶的請求, 并收到客戶的應答后即斷開連接。采用這種方式可以節省傳輸時間。
- (5)無狀態: HTTP 協議是無狀態協議。無狀態是指協議對于事務處理沒有記憶能力。 缺少狀態意味著如果后續處理需要前面的信息,則它必須重傳,這樣可能導致每次連接傳送的 數據量增大。另一方面,在服務器不需要先前信息時它的應答就較快。
13.4 HTTP 的消息結構
-
HTTP 是基于客戶端/服務器端 (C/S) 的架構模型,通過一個可靠的鏈接來交換信息,是一個無狀態的請求/響應協議。
-
一 個HTTP 客戶端是一個應用程序(Web 瀏覽器或其他任何客戶端),通過連接到服務 器達到向服務器發送一個或多個HTTP 請求的目的。
-
一 個HTTP 服務器同樣也是一個應用程序(通常是一個Web 服務,如Apache Web服務 器或IS 服務器等),接收客戶端的請求并向客戶端發送HTTP 響應數據。
-
HTTP使用統一資源標識符(Uniform Resource Identifiers,URI)來傳輸數據和建立連接。
-
一旦建立連接后,數據消息就通過類似 Internet 郵件所使用的格式[RFC5322]和多用途 Internet郵件擴展 (MIME)[RFC2045] 來傳送。
13.5 客戶端請求消息
客戶端發送一個HTTP 請求到服務器的請求消息由請求行(request line)、請求頭部(也 稱請求頭)、空行和請求數據4部分組成。圖13-2給出了請求報文的一般格式。
HTTP 協議定義了8種請求方法(或者叫“動作”),表明對 Request-URI 指定的資源 的不同操作方式,具體如下:
- (1)OPTIONS: 返回服務器針對特定資源所支持的HTTP 請求方法。也可以利用向Web 服務器發送’*'的請求來測試服務器的功能性。
- (2)HEAD: 向服務器索要與GET 請求相一致的響應,只不過響應體將不會被返回。這 一方法可以在不必傳輸整個響應內容的情況下就獲取包含在響應消息頭中的元信息。
- (3)GET: 向特定的資源發出請求。
- (4)POST: 向指定資源提交數據進行處理請求(例如,提交表單或者上傳文件)。數據 被包含在請求體中。POST 請求可能會導致新資源的創建和/或已有資源的修改。
- (5)PUT: 向指定資源位置上傳其最新內容。
- (6)DELETE: 請求服務器刪除 Request-URI 所標識的資源。
- (7)TRACE: 回顯服務器收到的請求,主要用于測試或診斷。
- (8)CONNECT:HTTP/1.1 協議中預留給能夠將連接改為管道方式的代理服務器。
雖然HTTP 的請求方式有8種,但是我們在實際應用中常用的也就是get 和post, 其他請 求方式也都可以通過這兩種方式間接地實現。
13.6 服務器響應消息
HTTP 響應也由4個部分組成,分別是狀態行、消息報頭(也稱響應頭)、空行和響應正文,如圖13-3所示。
下面給出一個典型的使用GET來傳遞數據的實例。
客戶端請求:
服務器端響應:
輸出結果
圖13-4演示請求和響應HTTP報文的操作。
13.7 HTTP 狀態碼
當瀏覽者訪問一個網頁時,瀏覽者的瀏覽器會向網頁所在服務器發出請求。當瀏覽器接收 并顯示網頁前,此網頁所在的服務器會返回一個包含HTTP 狀態碼的信息頭(server header), 用以響應瀏覽器的請求。
HTTP狀態碼的英文為HTTP Status Code。下面是常見的HTTP 狀態碼:
- 200:請求成功。
- 301:資源(網頁等)被永久轉移到其他URL。
- 404:請求的資源(網頁等)不存在。
- 500:內部服務器錯誤。
13.8 HTTP 狀態碼分類
HTTP狀態碼由3個十進制數字組成,第一個十進制數字定義狀態碼的類型,后兩個數字沒有分類的作用。HTTP 狀態碼共分為5種類型,如表13-1所示。
分類 | 分類描述 |
1** | 信息,服務器收到請求,需要請求者繼續執行操作 |
2** | 成功,操作被成功接收并處理 |
3** | 重定向,需要進一步的操作以完成請求 |
4** | 客戶端錯誤,請求包含語法錯誤或無法完成請求 |
5** | 服務器錯誤,服務器在處理請求的過程中發生了錯誤 |
13.9 實現HTTP 服務器
前面對 HTTP 協議進行了簡單的介紹。下面我們利用前面的網絡技術來實現一個 HTTP 服務器。我們學了好多服務器技術,比如CAsynSocket 、CSocket 、WSAAsyncSelect等。這里 我們選擇WSAAsyncSelect 技 術 。
WSAAsyncSelect 模型是Windows socket的一個異步I/O 模 型,利用這個模型,應用程序可在一個套接字上接收以Windows 消息為基礎的網絡事件通知。 Windows sockets應用程序在創建套接字后,調用WSAAsyncSelect 函數注冊感興趣的網絡事 件,當該事件發生時Windows 窗口收到消息,應用程序就可以對接收到的網絡事件進行處理。 利用 WSAAsyncSelect 函數,將socket 消息發送到 hWnd 窗口上,然后在那里處理相應的 FD_READ 、FD_WRITE等消息。更多關于WSAAsyncSelect 的知識,我們前面章節已經介紹 過 了 。
為了便于學習,我們的HTTP 實現的功能并不多,主要實現了HTTP 最基本的一些功能。 大家可以把這個例子作為原型,完善其功能。
我們的HTTP 服務器基于異步選擇模型WSAAsyncSelect。通過前面章節的學習應該知道, 這個模型需要一個Windows 窗口,因此我們的程序是一個基于對話框的程序。
13.9.2 界面設計
- (1)新建一個對話框工程,工程名是WebServer。
- (2)切換到資源視圖,打開對話框編輯器,放置按鈕,如圖13-5所示。
從控件標題我們大致能知道其含義了。其中,服務器的根目錄主要是放置網頁文件的,比 如 index.htm。
log.h
//zww
/****************************************************************************************
* ///
* Original Filename: Log.h
*
*
* Comments:
* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
****************************************************************************************/
#if !defined(AFX_LOG_H__82043CA7_5940_4F7E_A316_7F91096B2008__INCLUDED_)
#define AFX_LOG_H__82043CA7_5940_4F7E_A316_7F91096B2008__INCLUDED_#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000#include <string>using namespace std;#define MAX_MSG_SIZE 1024class CLog
{
public:BOOL ClearLog(const char*);BOOL LogMessage(const char*, const char*, const char* = NULL, long = NULL);CLog();virtual ~CLog();
private:FILE *m_f;char szLogFilePath[MAX_PATH];char szMessage[MAX_MSG_SIZE];char szDT[128];struct tm *newtime;time_t ltime;CRITICAL_SECTION cs;
};#endif // !defined(AFX_LOG_H__82043CA7_5940_4F7E_A316_7F91096B2008__INCLUDED_)
log.cpp
//zww
/****************************************************************************************
* ///
* Original Filename: Log.cpp
*
* History:
* Created/Modified by Date Main Purpose/Changes
* Souren M. Abeghyan 2001/05/25 Implementation of the CLog class.
*
* Comments:
* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
****************************************************************************************/
#include "stdafx.h"
#include "Log.h"#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif//
// Construction/Destruction
//CLog::CLog()
{InitializeCriticalSection(&cs);
}CLog::~CLog()
{DeleteCriticalSection(&cs);
}BOOL CLog::LogMessage(const char *szFolder, const char *szMsg, const char *szMsg1, long nNumber)
{EnterCriticalSection(&cs);time(<ime);//獲得計算機系統當前的日歷時間if((!strlen(szFolder)) || (!strlen(szMsg)))return FALSE;if(!GetWindowsDirectory(szLogFilePath, MAX_PATH)){LeaveCriticalSection(&cs);return FALSE;}if(szLogFilePath[0] != '\\')strcat(szLogFilePath, "\\");strcat(szLogFilePath, szFolder);m_f = fopen(szLogFilePath, "a");if(m_f != NULL) {newtime = localtime(<ime);//將時間數值變換成本地時間,考慮到本地時區和夏令時標志;strftime(szDT, 128, // 格式化顯示日期時間"%a, %d %b %Y %H:%M:%S", newtime);if(szMsg1 != NULL)sprintf(szMessage, "%s - %s.\t[%s]\t[%d]\n", szDT, szMsg, szMsg1, nNumber);elsesprintf(szMessage, "%s - %s.\t[%d]\n", szDT, szMsg, nNumber);int n = fwrite(szMessage, sizeof(char), strlen(szMessage), m_f);if(n != strlen(szMessage)){LeaveCriticalSection(&cs);fclose(m_f);return FALSE;}fclose(m_f);LeaveCriticalSection(&cs);return TRUE;}LeaveCriticalSection(&cs);return FALSE;
}BOOL CLog::ClearLog(const char *szFolder)
{if(!strlen(szFolder))return FALSE;if(!GetWindowsDirectory(szLogFilePath, MAX_PATH))return FALSE;if(szLogFilePath[0] != '\\')strcat(szLogFilePath, "\\");strcat(szLogFilePath, szFolder);return DeleteFile(szLogFilePath);
}
WebServer.h
//zww
// WebServer.h : main header file for the WEBSERVER application
//#if !defined(AFX_WEBSERVER_H__3F794A8E_734C_4694_8F25_F7CC03C513E5__INCLUDED_)
#define AFX_WEBSERVER_H__3F794A8E_734C_4694_8F25_F7CC03C513E5__INCLUDED_#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000#ifndef __AFXWIN_H__#error include 'stdafx.h' before including this file for PCH
#endif#include "resource.h" // main symbols/
// CWebServerApp:
// See WebServer.cpp for the implementation of this class
//class CWebServerApp : public CWinApp
{
public:CWebServerApp();// Overrides// ClassWizard generated virtual function overrides//{{AFX_VIRTUAL(CWebServerApp)public:virtual BOOL InitInstance();//}}AFX_VIRTUAL// Implementation//{{AFX_MSG(CWebServerApp)// NOTE - the ClassWizard will add and remove member functions here.// DO NOT EDIT what you see in these blocks of generated code !//}}AFX_MSGDECLARE_MESSAGE_MAP()
};///{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.#endif // !defined(AFX_WEBSERVER_H__3F794A8E_734C_4694_8F25_F7CC03C513E5__INCLUDED_)
WebServer.cpp
// WebServer.cpp : Defines the class behaviors for the application.
// zww#include "stdafx.h"
#include "WebServer.h"
#include "WebServerDlg.h"#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif/
// CWebServerAppBEGIN_MESSAGE_MAP(CWebServerApp, CWinApp)//{{AFX_MSG_MAP(CWebServerApp)// NOTE - the ClassWizard will add and remove mapping macros here.// DO NOT EDIT what you see in these blocks of generated code!//}}AFX_MSGON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()/
// CWebServerApp constructionCWebServerApp::CWebServerApp()
{// TODO: add construction code here,// Place all significant initialization in InitInstance
}/
// The one and only CWebServerApp objectCWebServerApp theApp;/
// CWebServerApp initializationBOOL CWebServerApp::InitInstance()
{AfxEnableControlContainer();// Standard initialization// If you are not using these features and wish to reduce the size// of your final executable, you should remove from the following// the specific initialization routines you do not need.#ifdef _AFXDLLEnable3dControls(); // Call this when using MFC in a shared DLL
#elseEnable3dControlsStatic(); // Call this when linking to MFC statically
#endifSetRegistryKey("AcID_Soft");//彈出對話框CWebServerDlg dlg;m_pMainWnd = &dlg;int nResponse = dlg.DoModal();if (nResponse == IDOK){// TODO: Place code here to handle when the dialog is// dismissed with OK}else if (nResponse == IDCANCEL){// TODO: Place code here to handle when the dialog is// dismissed with Cancel}// Since the dialog has been closed, return FALSE so that we exit the// application, rather than start the application's message pump.return FALSE;
}
WebServerDlg.h
//zww
// WebServerDlg.h : header file
//#if !defined(AFX_WEBSERVERDLG_H__8F4AD293_7AED_4ED5_A263_0ED2ED7032C8__INCLUDED_)
#define AFX_WEBSERVERDLG_H__8F4AD293_7AED_4ED5_A263_0ED2ED7032C8__INCLUDED_#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000/
// CWebServerDlg dialog#include "HTTPServer.h"#define TIMER_ID_1 1
#define TIMER_TO_1 500class CWebServerDlg : public CDialog
{
public:UINT nTimerID;BOOL m_bRun;
// Construction
public:void RestoreSettings();void SaveSettings();CWebServerDlg(CWnd* pParent = NULL); // standard constructor// Dialog Data//{{AFX_DATA(CWebServerDlg)enum { IDD = IDD_WEBSERVER_DIALOG };CStatic m_nVisitors;CStatic m_nBytesRecv;CStatic m_nBytesSent;CStatic m_nRequests;CStatic m_nActiveConn;CString m_szHomeDir;CString m_szDefIndex;int m_Port;int m_PTO;CString m_szStatus;//}}AFX_DATA// ClassWizard generated virtual function overrides//{{AFX_VIRTUAL(CWebServerDlg)protected:virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support//}}AFX_VIRTUAL// Implementation
protected:HICON m_hIcon;CHTTPServer WebServer;// Generated message map functions//{{AFX_MSG(CWebServerDlg)virtual BOOL OnInitDialog();afx_msg void OnSysCommand(UINT nID, LPARAM lParam);afx_msg void OnPaint();afx_msg HCURSOR OnQueryDragIcon();afx_msg void OnStart();afx_msg void OnStop();afx_msg void OnClose();virtual void OnOK();afx_msg void OnTimer(UINT nIDEvent);afx_msg void OnReset();afx_msg void OnHomedirbrowse();//}}AFX_MSGDECLARE_MESSAGE_MAP()
};//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.#endif // !defined(AFX_WEBSERVERDLG_H__8F4AD293_7AED_4ED5_A263_0ED2ED7032C8__INCLUDED_)
WebServerDlg.cpp
// WebServerDlg.cpp : implementation file
// zww#include "stdafx.h"
#include "WebServer.h"
#include "WebServerDlg.h"#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif/
// CAboutDlg dialog used for App Aboutclass CAboutDlg : public CDialog
{
public:CAboutDlg();// Dialog Data//{{AFX_DATA(CAboutDlg)enum { IDD = IDD_ABOUTBOX };//}}AFX_DATA// ClassWizard generated virtual function overrides//{{AFX_VIRTUAL(CAboutDlg)protected:virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support//}}AFX_VIRTUAL// Implementation
protected://{{AFX_MSG(CAboutDlg)//}}AFX_MSGDECLARE_MESSAGE_MAP()
};CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{//{{AFX_DATA_INIT(CAboutDlg)//}}AFX_DATA_INIT
}void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{CDialog::DoDataExchange(pDX);//{{AFX_DATA_MAP(CAboutDlg)//}}AFX_DATA_MAP
}BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)//{{AFX_MSG_MAP(CAboutDlg)// No message handlers//}}AFX_MSG_MAP
END_MESSAGE_MAP()/
// CWebServerDlg dialogCWebServerDlg::CWebServerDlg(CWnd* pParent /*=NULL*/): CDialog(CWebServerDlg::IDD, pParent)
{//{{AFX_DATA_INIT(CWebServerDlg)//初始化參數m_szHomeDir = _T("");m_szDefIndex = _T("");m_Port = 81;m_PTO = 10;m_szStatus = _T("");//}}AFX_DATA_INIT// Note that LoadIcon does not require a subsequent DestroyIcon in Win32m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}void CWebServerDlg::DoDataExchange(CDataExchange* pDX)
{CDialog::DoDataExchange(pDX);//{{AFX_DATA_MAP(CWebServerDlg)DDX_Control(pDX, IDC_VISITORS, m_nVisitors);DDX_Control(pDX, IDC_BYTESRECV, m_nBytesRecv);DDX_Control(pDX, IDC_BYTESENT, m_nBytesSent);DDX_Control(pDX, IDC_REQUESTS, m_nRequests);DDX_Control(pDX, IDC_ACTIVECONN, m_nActiveConn);DDX_Text(pDX, IDC_HOMEDIR, m_szHomeDir);DDX_Text(pDX, IDC_DEFINDEXFILE, m_szDefIndex);DDX_Text(pDX, IDC_PORT, m_Port);DDV_MinMaxInt(pDX, m_Port, 1, 65535);DDX_Text(pDX, IDC_PTO, m_PTO);DDV_MinMaxInt(pDX, m_PTO, 0, 10000);DDX_Text(pDX, IDC_STATUS, m_szStatus);//}}AFX_DATA_MAP
}BEGIN_MESSAGE_MAP(CWebServerDlg, CDialog)//{{AFX_MSG_MAP(CWebServerDlg)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_BN_CLICKED(IDC_START, OnStart)ON_BN_CLICKED(IDC_STOP, OnStop)ON_WM_CLOSE()ON_WM_TIMER()ON_BN_CLICKED(IDC_RESET, OnReset)ON_BN_CLICKED(IDC_HOMEDIRBROWSE, OnHomedirbrowse)//}}AFX_MSG_MAP
END_MESSAGE_MAP()/
// CWebServerDlg message handlersBOOL CWebServerDlg::OnInitDialog()
{CDialog::OnInitDialog();// Add "About..." menu item to system menu.// IDM_ABOUTBOX must be in the system command range.ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);ASSERT(IDM_ABOUTBOX < 0xF000);CMenu* pSysMenu = GetSystemMenu(FALSE);if (pSysMenu != NULL){CString strAboutMenu;strAboutMenu.LoadString(IDS_ABOUTBOX);if (!strAboutMenu.IsEmpty()){pSysMenu->AppendMenu(MF_SEPARATOR);pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);}}// Set the icon for this dialog. The framework does this automatically// when the application's main window is not a dialogSetIcon(m_hIcon, TRUE); // Set big iconSetIcon(m_hIcon, FALSE); // Set small icon// TODO: Add extra initialization herem_bRun = FALSE;//保存設置RestoreSettings();//啟動服務OnStart();return TRUE; // return TRUE unless you set the focus to a control
}void CWebServerDlg::OnSysCommand(UINT nID, LPARAM lParam)
{if ((nID & 0xFFF0) == IDM_ABOUTBOX){CAboutDlg dlgAbout;dlgAbout.DoModal();}else{CDialog::OnSysCommand(nID, lParam);}
}// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.void CWebServerDlg::OnPaint()
{if (IsIconic()){CPaintDC dc(this); // device context for paintingSendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);// Center icon in client rectangleint cxIcon = GetSystemMetrics(SM_CXICON);int cyIcon = GetSystemMetrics(SM_CYICON);CRect rect;GetClientRect(&rect);int x = (rect.Width() - cxIcon + 1) / 2;int y = (rect.Height() - cyIcon + 1) / 2;// Draw the icondc.DrawIcon(x, y, m_hIcon);}else{CDialog::OnPaint();}
}// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CWebServerDlg::OnQueryDragIcon()
{return (HCURSOR) m_hIcon;
}//啟動服務子程序
void CWebServerDlg::OnStart()
{//如果已經在運行if(m_bRun){AfxMessageBox("服務器已經在運行");return;}UpdateData();//否則啟動服務m_bRun = WebServer.Start(LPCTSTR(m_szHomeDir), LPCTSTR(m_szDefIndex), m_Port, m_PTO * 1000);if(m_bRun)nTimerID = SetTimer(TIMER_ID_1, TIMER_TO_1, NULL);m_szStatus = "程序服務中...";UpdateData(FALSE);
}//停止服務
void CWebServerDlg::OnStop()
{if(m_bRun){//調用Shutdown來關閉服務BOOL bResult = WebServer.Shutdown();KillTimer(nTimerID);m_bRun = FALSE;m_szStatus = "目前沒有提供服務.";UpdateData(FALSE);return;}AfxMessageBox("服務沒有啟動");
}//退出程序
void CWebServerDlg::OnClose()
{//保存設置SaveSettings();OnStop();CDialog::OnClose();
}void CWebServerDlg::OnOK()
{SaveSettings();OnStop();CDialog::OnOK();
}//定時器處理程序,主要是顯示服務器數據
void CWebServerDlg::OnTimer(UINT nIDEvent)
{StatisticsTag st;CString szTemp;WebServer.GetStats(st);szTemp.Format("%d", st.nClientsConnected);m_nActiveConn.SetWindowText(szTemp);szTemp.Format("%.1f", st.nTotalRecv);m_nBytesRecv.SetWindowText(szTemp);szTemp.Format("%.1f", st.nTotalSent);m_nBytesSent.SetWindowText(szTemp);szTemp.Format("%d", st.nTotalHits);m_nRequests.SetWindowText(szTemp);szTemp.Format("%d", st.nVisitors);m_nVisitors.SetWindowText(szTemp);CDialog::OnTimer(nIDEvent);
}//重新啟動服務
void CWebServerDlg::OnReset()
{OnStop();WebServer.Reset();UpdateData();OnStart();
}//設置服務根目錄
void CWebServerDlg::OnHomedirbrowse()
{BROWSEINFO bi;char folder_name[MAX_PATH];char dir_name[MAX_PATH];LPMALLOC lpMalloc; bi.hwndOwner = GetSafeHwnd();bi.pidlRoot = NULL;bi.pszDisplayName = folder_name;bi.lpszTitle = "請選擇目錄";bi.ulFlags = BIF_EDITBOX | BIF_STATUSTEXT;bi.lpfn = NULL;bi.lParam = NULL;bi.iImage = NULL;LPITEMIDLIST pidl = SHBrowseForFolder(&bi);if(pidl){SHGetPathFromIDList(pidl, dir_name);m_szHomeDir = dir_name;UpdateData(FALSE);}if(!SHGetMalloc(&lpMalloc) && (lpMalloc != NULL)) { if(pidl != NULL) { lpMalloc->Free(pidl); } lpMalloc->Release(); }
}//保存設置
void CWebServerDlg::SaveSettings()
{CWinApp* pApp = AfxGetApp();UpdateData();pApp->WriteProfileString("Settings", "Server Root", m_szHomeDir);pApp->WriteProfileString("Settings", "Defindex", m_szDefIndex);pApp->WriteProfileInt("Settings", "Port", m_Port);pApp->WriteProfileInt("Settings", "PTO", m_PTO);
}//獲取設置
void CWebServerDlg::RestoreSettings()
{CWinApp* pApp = AfxGetApp();//默認服務器根目錄是"C:\\ServerRoot"m_szHomeDir = pApp->GetProfileString("Settings", "Server Root", "C:\\ServerRoot");m_szDefIndex = pApp->GetProfileString("Settings", "Defindex", "index.htm");m_Port = pApp->GetProfileInt("Settings", "Port", 80);m_PTO = pApp->GetProfileInt("Settings", "PTO", 10);UpdateData(FALSE);
}
HTTPServer.h
//zww
/****************************************************************************************
* ///
* Original Filename: HTTPServer.h* Comments:
* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
****************************************************************************************/
#if !defined(AFX_HTTPSERVER_H__177A41DC_432E_4819_A614_8A0D66ABF434__INCLUDED_)
#define AFX_HTTPSERVER_H__177A41DC_432E_4819_A614_8A0D66ABF434__INCLUDED_#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000#include "GenericServer.h"#define ERROR404 "/fnf.html"
#define ERROR501 "/mna.html"#define SERVERNAME "AcIDSoftWebServer/0.1b"typedef map<string, string> MIMETYPES;class CHTTPServer : public CGenericServer
{
public:CHTTPServer();virtual ~CHTTPServer();BOOL Start(string, string, int, int);BOOL IsComplete(string);BOOL ParseRequest(string, string&, BOOL&);int GotConnection(char*, int);int DataSent(DWORD);
private:string m_HomeDir;string m_DefIndex;MIMETYPES MimeTypes;};#endif // !defined(AFX_HTTPSERVER_H__177A41DC_432E_4819_A614_8A0D66ABF434__INCLUDED_)
HTTPServer.cpp
/****************************************************************************************
* ///
* Original Filename: HTTPServer.cpp
*
* History:
* Created/Modified by Date Main Purpose/Changes
* Souren M. Abeghyan 2001/05/25 Implementation of the CHTTPServer class
*
* Comments:
* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
****************************************************************************************/
#include "stdafx.h"
#include "WebServer.h"
#include "HTTPServer.h"
// zww
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif//
// Construction/Destruction
//CHTTPServer::CHTTPServer()
{//// 初始化 MIME 類型//MimeTypes["doc"] = "application/msword";MimeTypes["bin"] = "application/octet-stream";MimeTypes["dll"] = "application/octet-stream";MimeTypes["exe"] = "application/octet-stream";MimeTypes["pdf"] = "application/pdf";MimeTypes["p7c"] = "application/pkcs7-mime";MimeTypes["ai"] = "application/postscript";MimeTypes["eps"] = "application/postscript";MimeTypes["ps"] = "application/postscript";MimeTypes["rtf"] = "application/rtf";MimeTypes["fdf"] = "application/vnd.fdf";MimeTypes["arj"] = "application/x-arj";MimeTypes["gz"] = "application/x-gzip";MimeTypes["class"] = "application/x-java-class";MimeTypes["js"] = "application/x-javascript";MimeTypes["lzh"] = "application/x-lzh";MimeTypes["lnk"] = "application/x-ms-shortcut";MimeTypes["tar"] = "application/x-tar";MimeTypes["hlp"] = "application/x-winhelp";MimeTypes["cert"] = "application/x-x509-ca-cert";MimeTypes["zip"] = "application/zip";MimeTypes["cab"] = "application/x-compressed";MimeTypes["arj"] = "application/x-compressed";MimeTypes["aif"] = "audio/aiff";MimeTypes["aifc"] = "audio/aiff";MimeTypes["aiff"] = "audio/aiff";MimeTypes["au"] = "audio/basic";MimeTypes["snd"] = "audio/basic";MimeTypes["mid"] = "audio/midi";MimeTypes["rmi"] = "audio/midi";MimeTypes["mp3"] = "audio/mpeg";MimeTypes["vox"] = "audio/voxware";MimeTypes["wav"] = "audio/wav";MimeTypes["ra"] = "audio/x-pn-realaudio";MimeTypes["ram"] = "audio/x-pn-realaudio";MimeTypes["bmp"] = "image/bmp";MimeTypes["gif"] = "image/gif";MimeTypes["jpeg"] = "image/jpeg";MimeTypes["jpg"] = "image/jpeg";MimeTypes["tif"] = "image/tiff";MimeTypes["tiff"] = "image/tiff";MimeTypes["xbm"] = "image/xbm";MimeTypes["wrl"] = "model/vrml";MimeTypes["htm"] = "text/html";MimeTypes["html"] = "text/html";MimeTypes["c"] = "text/plain";MimeTypes["cpp"] = "text/plain";MimeTypes["def"] = "text/plain";MimeTypes["h"] = "text/plain";MimeTypes["txt"] = "text/plain";MimeTypes["rtx"] = "text/richtext";MimeTypes["rtf"] = "text/richtext";MimeTypes["java"] = "text/x-java-source";MimeTypes["css"] = "text/css";MimeTypes["mpeg"] = "video/mpeg";MimeTypes["mpg"] = "video/mpeg";MimeTypes["mpe"] = "video/mpeg";MimeTypes["avi"] = "video/msvideo";MimeTypes["mov"] = "video/quicktime";MimeTypes["qt"] = "video/quicktime";MimeTypes["shtml"] = "wwwserver/html-ssi";MimeTypes["asa"] = "wwwserver/isapi";MimeTypes["asp"] = "wwwserver/isapi";MimeTypes["cfm"] = "wwwserver/isapi";MimeTypes["dbm"] = "wwwserver/isapi";MimeTypes["isa"] = "wwwserver/isapi";MimeTypes["plx"] = "wwwserver/isapi";MimeTypes["url"] = "wwwserver/isapi";MimeTypes["cgi"] = "wwwserver/isapi";MimeTypes["php"] = "wwwserver/isapi";MimeTypes["wcgi"] = "wwwserver/isapi";
}CHTTPServer::~CHTTPServer()
{}int CHTTPServer::GotConnection(char*, int)
{return 0;
}int CHTTPServer::DataSent(DWORD)
{return 0;
}//啟動服務,重要函數,分析根目錄
BOOL CHTTPServer::Start(string HomeDir, string DefIndex, int Port, int PersTO)
{m_HomeDir = HomeDir;m_DefIndex = DefIndex;if(m_HomeDir.substr(m_HomeDir.size() - 1, 1) != "\\")m_HomeDir += "\\";return Run(Port, PersTO);
}//是否完成
BOOL CHTTPServer::IsComplete(string szRequest)
{if(szRequest.substr(szRequest.size() - 4, 4) == "\r\n\r\n")return TRUE;elsereturn FALSE;
}//分析請求數據
BOOL CHTTPServer::ParseRequest(string szRequest, string &szResponse, BOOL &bKeepAlive)
{////string szMethod;string szFileName;string szFileExt;string szStatusCode("200 OK");string szContentType("text/html");string szConnectionType("close");string szNotFoundMessage;string szDateTime;char pResponseHeader[2048];fpos_t lengthActual = 0, length = 0;char *pBuf = NULL;int n;//// 檢查提交方法//n = szRequest.find(" ", 0);if(n != string::npos){szMethod = szRequest.substr(0, n);if(szMethod == "GET"){//// 獲取文件名// int n1 = szRequest.find(" ", n + 1);if(n != string::npos){szFileName = szRequest.substr(n + 1, n1 - n - 1);if(szFileName == "/"){szFileName = m_DefIndex;}}else{LogMessage(LOGFILENAME, "No 'space' found in Request String #1", "ParseRequest");return FALSE;}}else{szStatusCode = "501 Not Implemented";szFileName = ERROR501;}}else{LogMessage(LOGFILENAME, "No 'space' found in Request String #2", "ParseRequest");return FALSE;}//// 分析鏈接類型//n = szRequest.find("\nConnection: Keep-Alive", 0);if(n != string::npos)bKeepAlive = TRUE;//// 分析內容類型//int nPointPos = szFileName.rfind(".");if(nPointPos != string::npos){szFileExt = szFileName.substr(nPointPos + 1, szFileName.size());strlwr((char*)szFileExt.c_str());MIMETYPES::iterator it;it = MimeTypes.find(szFileExt);if(it != MimeTypes.end())szContentType = (*it).second;}//得到目前的時間//char szDT[128];struct tm *newtime;time_t ltime;time((time_t*)<ime);newtime = gmtime(<ime);strftime(szDT, 128,"%a, %d %b %Y %H:%M:%S GMT", newtime);//// 讀取文件//FILE *f;f = fopen((m_HomeDir + szFileName).c_str(), "r+b");if(f != NULL) {// 獲得文件大小fseek(f, 0, SEEK_END);fgetpos(f, &lengthActual);fseek(f, 0, SEEK_SET);pBuf = new char[lengthActual + 1];length = fread(pBuf, 1, lengthActual, f);fclose(f);//// 返回響應//sprintf(pResponseHeader, "HTTP/1.0 %s\r\nDate: %s\r\nServer: %s\r\nAccept-Ranges: bytes\r\nContent-Length: %d\r\nConnection: %s\r\nContent-Type: %s\r\n\r\n",szStatusCode.c_str(), szDT, SERVERNAME, (int)length, bKeepAlive ? "Keep-Alive" : "close", szContentType.c_str());}else{//// 如果文件沒有找到 //f = fopen((m_HomeDir + ERROR404).c_str(), "r+b");if(f != NULL) {// 獲取文件大小fseek(f, 0, SEEK_END);fgetpos(f, &lengthActual);fseek(f, 0, SEEK_SET);pBuf = new char[lengthActual + 1];length = fread(pBuf, 1, lengthActual, f);fclose(f);szNotFoundMessage = string(pBuf, length);delete pBuf;pBuf = NULL;}szStatusCode = "404 Resource not found";sprintf(pResponseHeader, "HTTP/1.0 %s\r\nContent-Length: %d\r\nContent-Type: text/html\r\nDate: %s\r\nServer: %s\r\n\r\n%s",szStatusCode.c_str(), szNotFoundMessage.size(), szDT, SERVERNAME, szNotFoundMessage.c_str());bKeepAlive = FALSE; }szResponse = string(pResponseHeader);if(pBuf)szResponse += string(pBuf, length);delete pBuf;pBuf = NULL;return TRUE;
}
GernericServer.h
//zww
/****************************************************************************************
* ///
* Original Filename: genericserver.h
*
*
* Comments:
* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
****************************************************************************************/
#if !defined(AFX_GENERICSERVER_H__6C21FFA2_485A_11D5_9692_0050BA8CD8A0__INCLUDED_)
#define AFX_GENERICSERVER_H__6C21FFA2_485A_11D5_9692_0050BA8CD8A0__INCLUDED_#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000#define THREADKILL_TO 1000
#define THREADWAIT_TO 30000
#define TICK 100
#define THREADEXIT_SUCCESS 0x1234
#define SERVERPORT 80
#define MAX_BUFFER 100000
#define SENDBLOCK 200000
#define LOGFILENAME "UMServer.log"#pragma warning(disable:4786)#include <winsock2.h>
#include <string>
#include <vector>
#include <deque>
#include <map>
#include <algorithm>
#include <list>
#include <functional>
#include <process.h>#include "Log.h"using namespace std;struct NewConnectionTag;struct ThreadTag
{HANDLE hThread;unsigned int threadID;
};struct IDCompare : binary_function<ThreadTag, unsigned int, bool>
{bool operator()(const ThreadTag& _X, const unsigned int& _Y) const{return (_X.threadID == _Y); }
};typedef list<ThreadTag> THREADLIST;
typedef list<HANDLE> HANDLELIST;
typedef vector<string> STRVECT;struct StatisticsTag
{long nClientsConnected;long nTotalHits;double nTotalSent;double nTotalRecv;double nErrosCount;long nVisitors;
};class CGenericServer : public CLog
{
public:CGenericServer();virtual ~CGenericServer();
public: void GetStats(StatisticsTag&);void Reset();BOOL Run(int, int);BOOL Shutdown();
protected: virtual int GotConnection(char*, int) = 0;virtual int DataSent(DWORD) = 0;virtual BOOL IsComplete(string) = 0;virtual BOOL ParseRequest(string, string&, BOOL&) = 0;
private: static UINT __stdcall AcceptThread(LPVOID);static UINT __stdcall ClientThread(LPVOID);static UINT __stdcall HelperThread(LPVOID);BOOL AddClient(SOCKET, char*, int);void CleanupThread(WSAEVENT, SOCKET, NewConnectionTag*, DWORD);void CleanupThread(WSAEVENT, WSAEVENT, SOCKET);
private: HANDLE ThreadA; // Accept Threadunsigned int ThreadA_ID;HANDLE ThreadC; // Cleanup Threadunsigned int ThreadC_ID;WSAEVENT ShutdownEvent;HANDLE ThreadLaunchedEvent;THREADLIST ThreadList;HANDLELIST HandleList;StatisticsTag Stats;CRITICAL_SECTION cs;CRITICAL_SECTION _cs;int ServerPort;int PersistenceTO;BOOL bRun;
protected:STRVECT Visitors;
};struct NewConnectionTag
{CGenericServer *pGenericServer;SOCKET s;
};#endif // !defined(AFX_GENERICSERVER_H__6C21FFA2_485A_11D5_9692_0050BA8CD8A0__INCLUDED_)
GernericServer.cpp
/****************************************************************************************
* ///
* Original Filename: genericserver.cpp
*
* History:
* Created/Modified by Date Main Purpose/Changes
* Souren M. Abeghyan 2001/05/25 Implementation of the CGenericServer class.
*
* Comments:
* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
****************************************************************************************/#include "stdafx.h"
#include "GenericServer.h"#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
// zww
//
// Construction/Destruction
//CGenericServer::CGenericServer()
{bRun = FALSE;
}CGenericServer::~CGenericServer()
{}//獲取狀態
void CGenericServer::GetStats(StatisticsTag &st)
{st.nTotalRecv = Stats.nTotalRecv;st.nTotalSent = Stats.nTotalSent;st.nTotalHits = Stats.nTotalHits;st.nVisitors = Visitors.size();EnterCriticalSection(&_cs);st.nClientsConnected = ThreadList.size();LeaveCriticalSection(&_cs);
}//增加新的連接
BOOL CGenericServer::AddClient(SOCKET s, char* ClientAddress, int port)
{GotConnection(ClientAddress, port);STRVECT::iterator it;it = find(Visitors.begin(), Visitors.end(), ClientAddress);if(it == Visitors.end())Visitors.push_back(ClientAddress);InterlockedIncrement(&Stats.nTotalHits);ThreadTag Thread;HANDLE hThread;unsigned int threadID;EnterCriticalSection(&cs);NewConnectionTag *NewConn = new NewConnectionTag;NewConn->pGenericServer = this;NewConn->s = s;hThread = (HANDLE)_beginthreadex(NULL, 0, ClientThread, NewConn, 0, &threadID);if(hThread){Thread.threadID = threadID;Thread.hThread = hThread;ThreadList.push_back(Thread);}elseLogMessage(LOGFILENAME, "_beginthreadex(...) failure", "AddClient", errno);LeaveCriticalSection(&cs);return TRUE;
}//啟動服務器
BOOL CGenericServer::Run(int Port, int PersTO)
{if(bRun){LogMessage(LOGFILENAME, "_beginthreadex(...) failure, for Launch Thread", "Run", errno);return FALSE;}ServerPort = Port;PersistenceTO = PersTO;InitializeCriticalSection(&cs);InitializeCriticalSection(&_cs);Reset();ThreadLaunchedEvent = CreateEvent(NULL, FALSE, TRUE, NULL);// 啟動接收線程ResetEvent(ThreadLaunchedEvent);ThreadA = (HANDLE)_beginthreadex(NULL, 0, AcceptThread, this, 0, &ThreadA_ID);if(!ThreadA){LogMessage(LOGFILENAME, "_beginthreadex(...) failure, for Launch Thread", "Run", errno);return FALSE;}if(WaitForSingleObject(ThreadLaunchedEvent, THREADWAIT_TO) != WAIT_OBJECT_0){LogMessage(LOGFILENAME, "Unable to get response from Accept Thread withing specified Timeout ->", "Run", THREADWAIT_TO);CloseHandle(ThreadLaunchedEvent);return FALSE;}// 啟動幫助線程ResetEvent(ThreadLaunchedEvent);ThreadC = (HANDLE)_beginthreadex(NULL, 0, HelperThread, this, 0, &ThreadC_ID);if(!ThreadC){LogMessage(LOGFILENAME, "_beginthreadex(...) failure, for Helper Thread", "Run", errno);return FALSE;}if(WaitForSingleObject(ThreadLaunchedEvent, THREADWAIT_TO) != WAIT_OBJECT_0){LogMessage(LOGFILENAME, "Unable to get response from Helper Thread within specified Timeout ->", "Run", THREADWAIT_TO);CloseHandle(ThreadLaunchedEvent);return FALSE;}CloseHandle(ThreadLaunchedEvent);bRun = TRUE;return TRUE;
}//關閉服務
BOOL CGenericServer::Shutdown()
{if(!bRun)return FALSE;BOOL bResult = TRUE;HANDLE hArray[2];hArray[0] = ThreadA;hArray[1] = ThreadC;//// 關閉接收和helper線程//SetEvent(ShutdownEvent);DWORD n = WaitForMultipleObjects(2, hArray, TRUE, THREADKILL_TO);if(n == WAIT_TIMEOUT || n == WAIT_FAILED){LogMessage(LOGFILENAME, "WaitForMultipleObjects(...) timed out", "Shutdown");if(!TerminateThread(ThreadA, THREADEXIT_SUCCESS))LogMessage(LOGFILENAME, "TerminateThread(.ThreadA.) failure, probably it is already terminated", "Shutdown", GetLastError());if(!TerminateThread(ThreadC, THREADEXIT_SUCCESS))LogMessage(LOGFILENAME, "TerminateThread(.ThreadC.) failure, probably it is already terminated", "Shutdown", GetLastError());bResult = FALSE;}CloseHandle(ThreadA); CloseHandle(ThreadC); //// 所有的客戶線程都結束//THREADLIST::iterator it;while(ThreadList.size()){Sleep(100);}DeleteCriticalSection(&cs);DeleteCriticalSection(&_cs);bRun = FALSE;return bResult;
}void CGenericServer::Reset()
{//// Reset statistic values//Stats.nClientsConnected = 0;Stats.nErrosCount = 0;Stats.nTotalSent = 0;Stats.nTotalRecv = 0;Stats.nTotalHits = 0;Stats.nVisitors = 0;
}//清除線程
void CGenericServer::CleanupThread(WSAEVENT Event, SOCKET s, NewConnectionTag* pNewConn, DWORD dwThreadID)
{if(Event)WSACloseEvent(Event);closesocket(s);EnterCriticalSection(&cs);delete pNewConn;LeaveCriticalSection(&cs);THREADLIST::iterator it;it = find_if(ThreadList.begin(), ThreadList.end(), bind2nd(IDCompare(), dwThreadID));if(it != ThreadList.end()){EnterCriticalSection(&_cs);HANDLE aaa = (*it).hThread;HandleList.push_back((*it).hThread);ThreadList.erase(it);LeaveCriticalSection(&_cs);}elseLogMessage(LOGFILENAME, "Thread not found in the list", "ClientThread");
}//清除線程
void CGenericServer::CleanupThread(WSAEVENT Event, WSAEVENT ShutdownEvent, SOCKET s)
{if(Event)WSACloseEvent(Event);if(ShutdownEvent)WSACloseEvent(ShutdownEvent);if(s)closesocket(s);WSACleanup();
}UINT __stdcall CGenericServer::AcceptThread(LPVOID pParam)
{CGenericServer *pGenericServer = (CGenericServer*)pParam;SOCKET s; // 主線程WORD wVersionRequested;WSADATA wsaData;sockaddr_in saLocal;WSAEVENT Handles[2];WSANETWORKEVENTS NetworkEvents;sockaddr ClientAddr;INT addrlen = sizeof(ClientAddr);sockaddr_in sain;char cAddr[50];int result;saLocal.sin_family = AF_INET;saLocal.sin_port = htons(pGenericServer->ServerPort);saLocal.sin_addr.s_addr = INADDR_ANY;wVersionRequested = MAKEWORD(2, 2);result = WSAStartup(wVersionRequested, &wsaData);if(result != 0){pGenericServer->LogMessage(LOGFILENAME, "WSAStartup(...) failure", "AcceptThread", result);return THREADEXIT_SUCCESS;}if( LOBYTE(wsaData.wVersion) != 2 ||HIBYTE(wsaData.wVersion) != 2) {pGenericServer->LogMessage(LOGFILENAME, "Requested Socket version not exist", "AcceptThread");pGenericServer->CleanupThread(NULL, NULL, NULL);return THREADEXIT_SUCCESS; }s = WSASocket(AF_INET, SOCK_STREAM, 0, (LPWSAPROTOCOL_INFO)NULL, 0, WSA_FLAG_OVERLAPPED);if(s == INVALID_SOCKET){pGenericServer->LogMessage(LOGFILENAME, "WSASocket(...) failure", "AcceptThread", WSAGetLastError());pGenericServer->CleanupThread(NULL, NULL, NULL);return THREADEXIT_SUCCESS;}//// 綁定//result = ::bind(s, (struct sockaddr *)&saLocal, sizeof(saLocal));if(result == SOCKET_ERROR){pGenericServer->LogMessage(LOGFILENAME, "bind(...) failure", "AcceptThread", WSAGetLastError());pGenericServer->CleanupThread(NULL, NULL, s);return THREADEXIT_SUCCESS;} //// 偵聽//result = listen(s, SOMAXCONN);if(result == SOCKET_ERROR){pGenericServer->LogMessage(LOGFILENAME, "listen(...) failure", "AcceptThread", WSAGetLastError());pGenericServer->CleanupThread(NULL, NULL, s);return THREADEXIT_SUCCESS;} pGenericServer->ShutdownEvent = WSACreateEvent();if(pGenericServer->ShutdownEvent == WSA_INVALID_EVENT){pGenericServer->LogMessage(LOGFILENAME, "WSACreateEvent(...) failure for ShutdownEvent", "AcceptThread", WSAGetLastError());pGenericServer->CleanupThread(NULL, NULL, NULL, s);return THREADEXIT_SUCCESS;} WSAEVENT Event = WSACreateEvent();if(Event == WSA_INVALID_EVENT){pGenericServer->LogMessage(LOGFILENAME, "WSACreateEvent(...) failure for Event", "AcceptThread", WSAGetLastError());pGenericServer->CleanupThread(NULL, pGenericServer->ShutdownEvent, s);return THREADEXIT_SUCCESS;} Handles[0] = pGenericServer->ShutdownEvent;Handles[1] = Event;result = WSAEventSelect(s, Event, FD_ACCEPT);if(result == SOCKET_ERROR){pGenericServer->LogMessage(LOGFILENAME, "WSAEventSelect(...) failure", "AcceptThread", WSAGetLastError());pGenericServer->CleanupThread(Event, pGenericServer->ShutdownEvent, s);return THREADEXIT_SUCCESS;}SetEvent(pGenericServer->ThreadLaunchedEvent);for(;;){DWORD EventCaused = WSAWaitForMultipleEvents(2,Handles, FALSE, WSA_INFINITE, FALSE);if(EventCaused == WAIT_FAILED || EventCaused == WAIT_OBJECT_0){if(EventCaused == WAIT_FAILED)pGenericServer->LogMessage(LOGFILENAME, "WaitForMultipleObjects(...) failure", "AcceptThread", GetLastError());pGenericServer->CleanupThread(Event, pGenericServer->ShutdownEvent, s);return THREADEXIT_SUCCESS;}result = WSAEnumNetworkEvents(s, Event, &NetworkEvents);if(result == SOCKET_ERROR) {pGenericServer->LogMessage(LOGFILENAME, "WSAEnumNetworkEvents(...) failure", "AcceptThread", WSAGetLastError());pGenericServer->CleanupThread(Event, pGenericServer->ShutdownEvent, s);return THREADEXIT_SUCCESS;}if(NetworkEvents.lNetworkEvents == FD_ACCEPT){SOCKET ClientSocket = WSAAccept(s, &ClientAddr, &addrlen, NULL, NULL);memcpy(&sain, &ClientAddr, addrlen);sprintf(cAddr, "%d.%d.%d.%d", sain.sin_addr.S_un.S_un_b.s_b1, sain.sin_addr.S_un.S_un_b.s_b2, sain.sin_addr.S_un.S_un_b.s_b3, sain.sin_addr.S_un.S_un_b.s_b4);if(INVALID_SOCKET == ClientSocket){pGenericServer->LogMessage(LOGFILENAME, "WSAAccept(...) failure", "AcceptThread", WSAGetLastError());// 有一個文件錯誤continue; }else{if(!pGenericServer->AddClient(ClientSocket, cAddr, sain.sin_port)){pGenericServer->LogMessage(LOGFILENAME, "AddClient(...) failure", "AcceptThread");continue; // I think there is no reason to shutdown whole server if just one connection failed}}}}pGenericServer->CleanupThread(Event, pGenericServer->ShutdownEvent, s);return THREADEXIT_SUCCESS;
} //客戶端線程
unsigned __stdcall CGenericServer::ClientThread(LPVOID pParam)
{NewConnectionTag *pNewConn = (NewConnectionTag*)pParam;CGenericServer *pGenericServer = pNewConn->pGenericServer;SOCKET s = pNewConn->s;int result;WSAEVENT EventArray[2];WSANETWORKEVENTS NetworkEvents;BOOL bResend = FALSE;WSABUF Buffer;DWORD NumberOfBytesSent;DWORD dwBytesSent;BOOL bKeepAlive = FALSE;string szRequest;string szResponse;WSAEVENT Event = WSACreateEvent();if(Event == WSA_INVALID_EVENT){pGenericServer->LogMessage(LOGFILENAME, "WSACreateEvent(...) failure", "ClientThread", WSAGetLastError());pGenericServer->CleanupThread(NULL, s, pNewConn, GetCurrentThreadId());return THREADEXIT_SUCCESS;}result = WSAEventSelect(s, Event, FD_READ | FD_WRITE | FD_CLOSE);if(result == SOCKET_ERROR){pGenericServer->LogMessage(LOGFILENAME, "WSAEventSelect(...) failure", "ClientThread", WSAGetLastError());pGenericServer->CleanupThread(Event, s, pNewConn, GetCurrentThreadId());return THREADEXIT_SUCCESS;}EventArray[0] = Event;EventArray[1] = pGenericServer->ShutdownEvent;for(;;){DWORD EventCaused = WSAWaitForMultipleEvents(2,EventArray, FALSE, pGenericServer->PersistenceTO ? pGenericServer->PersistenceTO : WSA_INFINITE, FALSE);if(WSA_WAIT_FAILED == EventCaused){pGenericServer->LogMessage(LOGFILENAME, "WSAWaitForMultipleEvents(...) failure", "ClientThread", WSAGetLastError());pGenericServer->CleanupThread(Event, s, pNewConn, GetCurrentThreadId());return THREADEXIT_SUCCESS;}//// 檢查是否服務器任務已經完成 //if(EventCaused == 1 || EventCaused == WSA_WAIT_TIMEOUT){pGenericServer->CleanupThread(Event, s, pNewConn, GetCurrentThreadId());return THREADEXIT_SUCCESS;}//// 分析什么網絡事件產生//result = WSAEnumNetworkEvents(s, Event, &NetworkEvents);if(result == SOCKET_ERROR){pGenericServer->LogMessage(LOGFILENAME, "WSAEnumNetworkEvents(...) failure", "ClientThread", WSAGetLastError());continue; }//// 其他情況//if(!NetworkEvents.lNetworkEvents)continue;//// 處理事件// if(NetworkEvents.lNetworkEvents & FD_READ){//// 不需要接收接傳入的數據,只需要傳給繼承類//DWORD NumberOfBytesRecvd;WSABUF Buffers;DWORD dwBufferCount = 1;char szBuffer[MAX_BUFFER];DWORD Flags = 0;Buffers.buf = szBuffer;Buffers.len = MAX_BUFFER;result = WSARecv(s,&Buffers,dwBufferCount,&NumberOfBytesRecvd,&Flags,NULL,NULL);if(result != SOCKET_ERROR){pGenericServer->Stats.nTotalRecv += (double)NumberOfBytesRecvd / 1024;//// 檢測是否獲得完整的請求//szRequest += string(szBuffer, NumberOfBytesRecvd);if(!pGenericServer->IsComplete(szRequest))continue;if(!pGenericServer->ParseRequest(szRequest, szResponse, bKeepAlive)){pGenericServer->CleanupThread(Event, s, pNewConn, GetCurrentThreadId());return THREADEXIT_SUCCESS;}//// 發送響應倒客戶端//NumberOfBytesSent = 0;dwBytesSent = 0;do{Buffer.len = (szResponse.size() - dwBytesSent) >= SENDBLOCK ? SENDBLOCK : szResponse.size() - dwBytesSent; Buffer.buf = (char*)((DWORD)szResponse.c_str() + dwBytesSent);result = WSASend(s, &Buffer, 1, &NumberOfBytesSent,0, 0, NULL);if(SOCKET_ERROR != result)dwBytesSent += NumberOfBytesSent;}while((dwBytesSent < szResponse.size()) && SOCKET_ERROR != result);if(WSAGetLastError() == WSAEWOULDBLOCK){bResend = TRUE;continue;}if(SOCKET_ERROR != result){pGenericServer->Stats.nTotalSent += (double)dwBytesSent / 1024;pGenericServer->DataSent(dwBytesSent);}else{pGenericServer->LogMessage(LOGFILENAME, "WSASend(...) failure", "ClientThread, Primary Send", WSAGetLastError());bKeepAlive = FALSE;}if(!bKeepAlive) {pGenericServer->CleanupThread(Event, s, pNewConn, GetCurrentThreadId());return THREADEXIT_SUCCESS;}//// 完成請求,清除緩沖區//szRequest.erase(0, string::npos);}elsepGenericServer->LogMessage(LOGFILENAME, "WSARecv(...) failure", "ClientThread", WSAGetLastError());}if((NetworkEvents.lNetworkEvents & FD_WRITE) && bResend){//// 發送響應倒客戶端//do{Buffer.len = (szResponse.size() - dwBytesSent) >= SENDBLOCK ? SENDBLOCK : szResponse.size() - dwBytesSent; Buffer.buf = (char*)((DWORD)szResponse.c_str() + dwBytesSent);result = WSASend(s, &Buffer, 1, &NumberOfBytesSent,0, 0, NULL);if(SOCKET_ERROR != result) dwBytesSent += NumberOfBytesSent;}while((dwBytesSent < szResponse.size()) && SOCKET_ERROR != result);if(WSAGetLastError() == WSAEWOULDBLOCK){bResend = TRUE;continue;}if(SOCKET_ERROR != result){pGenericServer->Stats.nTotalSent += (double)dwBytesSent / 1024;pGenericServer->DataSent(dwBytesSent);}else{pGenericServer->LogMessage(LOGFILENAME, "WSASend(...) failure", "ClientThread, Primary Send", WSAGetLastError());bKeepAlive = FALSE;}if(!bKeepAlive){pGenericServer->CleanupThread(Event, s, pNewConn, GetCurrentThreadId());return THREADEXIT_SUCCESS;}bResend = FALSE;//// 清除緩沖區//szRequest.erase(0, string::npos);} if(NetworkEvents.lNetworkEvents & FD_CLOSE){pGenericServer->CleanupThread(Event, s, pNewConn, GetCurrentThreadId());return THREADEXIT_SUCCESS;}}return THREADEXIT_SUCCESS; //
} UINT __stdcall CGenericServer::HelperThread(LPVOID pParam)
{CGenericServer *pGenericServer = (CGenericServer*)pParam;HANDLELIST::iterator it;SetEvent(pGenericServer->ThreadLaunchedEvent);for(;;){if(WaitForSingleObject(pGenericServer->ShutdownEvent, TICK) == WAIT_TIMEOUT){EnterCriticalSection(&pGenericServer->_cs);while(pGenericServer->HandleList.size()){HANDLE h = pGenericServer->HandleList.front(); DWORD n = WaitForSingleObject(h, THREADKILL_TO);if(n == WAIT_TIMEOUT){pGenericServer->LogMessage(LOGFILENAME, "WaitForSingleObject(...) timed out", "HelperThread");if(!TerminateThread(h, THREADEXIT_SUCCESS))pGenericServer->LogMessage(LOGFILENAME, "TerminateThread(.h.) failure, probably it is already terminated", "HelperThread", GetLastError());}CloseHandle(h);pGenericServer->HandleList.pop_front();}LeaveCriticalSection(&pGenericServer->_cs);}elsereturn THREADEXIT_SUCCESS;//不是超時返回,說明服務關閉了。}return THREADEXIT_SUCCESS;
}