【網絡編程】HTTP網絡編程

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(&ltime);//獲得計算機系統當前的日歷時間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(&ltime);//將時間數值變換成本地時間,考慮到本地時區和夏令時標志;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*)&ltime);newtime = gmtime(&ltime);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;
}	

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/73416.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/73416.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/73416.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

用Scrum敏捷的視角看《哪吒2》的創作

去年我們公司邀請Scrum中文網的老師培訓了敏捷開發課程&#xff0c;讓我對敏捷有了更深入的理解。前陣子我參加了scrum中文網的一個直播&#xff0c;老師分享了敏捷在個人領域或生活其他領域的應用&#xff0c;很有意思。因為我學習敏捷&#xff0c;除了應用到本身軟件研發的工…

Docker+Flask 實戰:打造高并發微服務架構

DockerFlask 實戰&#xff1a;打造高并發微服務架構 今天我們要深入探討一個非常熱門且實用的主題&#xff1a;基于 Docker 部署 Python Flask 應用。Docker 作為當下最流行的容器化技術&#xff0c;已經廣泛應用于各種開發和部署場景&#xff0c;尤其是在微服務架構中。而 Fl…

Linux find 命令完全指南

find 是 Linux 系統最強大的文件搜索工具&#xff0c;支持 嵌套遍歷、條件篩選、執行動作。以下通過場景分類解析核心用法&#xff0c;涵蓋高效搜索、文件管理及高級技巧&#xff1a; 一、基礎搜索模式 1. 按文件名搜索&#xff08;精確/模糊匹配&#xff09; <BASH> f…

【量化策略】趨勢跟蹤策略

【量化策略】趨勢跟蹤策略 &#x1f680;量化軟件開通 &#x1f680;量化實戰教程 技術背景與應用場景 在金融市場中&#xff0c;趨勢跟蹤策略是一種基于市場趨勢進行交易的量化投資方法。該策略的核心思想是“順勢而為”&#xff0c;即認為市場價格會沿著一定的方向持續移…

AI自動化、資本短視、三輸與破局

當前AI應用中的一個深層矛盾&#xff1a;工程師使用AI將很專業的任務變成小白可以操作的工作&#xff0c;然后資本方給小白很少的錢把工程師裁掉了&#xff0c;然而小白不懂底層&#xff0c;出問題幾乎無法修復。由此&#xff0c;技術普及與專業能力之間的斷層引發了"三輸…

Python數據分析之數據可視化

Python 數據分析重點知識點 本系列不同其他的知識點講解&#xff0c;力求通過例子讓新同學學習用法&#xff0c;幫助老同學快速回憶知識點 可視化系列&#xff1a; Python基礎數據分析工具數據處理與分析數據可視化機器學習基礎 四、數據可視化 圖表類型與選擇 根據數據特…

簡述計算機網絡中的七層模型和四層模型

在計算機網絡中&#xff0c;網絡協議棧的設計通常采用分層結構來處理不同的通信任務。常見的分層結構有OSI七層模型和TCP/IP四層模型。雖然它們的層次數量不同&#xff0c;但本質上都在解決如何有效地進行計算機間通信。本文將分別介紹這兩種結構的功能和各層的協議。 一、OSI七…

2025高頻面試算法總結篇【持續更新中】

文章目錄 遞歸&回溯131. 分割回文串面試題 08.12. 八皇后 動態規劃72編輯距離5. 最長回文子串279. 完全平方數300. 最長遞增子序列 遞歸&回溯 131. 分割回文串 回溯思路&#xff1a; 臨界條件&#xff1a; if (start s.length) > 保存 循環遍歷這個字串 for (int…

【大模型學習】第二十二章 什么是對抗生成網絡

目錄 一、背景介紹 二、生活化例子說明什么是對抗生成網絡 三、技術細節詳解 &#xff08;一&#xff09;基本概念 &#xff08;二&#xff09;訓練機制 &#xff08;三&#xff09;損失函數 一、背景介紹 對抗生成網絡&#xff08;Generative Adversarial Networks, GANs…

攝像頭模塊ISP處理流程

攝像頭模塊的ISP&#xff08;圖像信號處理器&#xff09;處理流程是對圖像傳感器輸出的原始信號進行系統性優化的過程&#xff0c;主要分為以下關鍵步驟及對應功能模塊&#xff1a; 一、原始信號輸入與預處理 ?傳感器信號捕獲? CMOS/CCD傳感器將光信號轉換為模擬電信號&…

linux系統安裝和激活conda

安裝 wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.shbash ./Miniconda3-latest-Linux-x86_64.sh回車到最后按照輸入yes&#xff0c;之后按提示操作。 激活 conda activate如果沒有反應或者返回&#xff1a; bash: conda: command not found則…

(全)2024下半年真題 系統架構設計師 綜合知識 答案解析02

系統架構設計師第二版教程VIP課程https://edu.csdn.net/course/detail/40283 面向對象技術 在UML用例圖中&#xff0c;參與者之間存在 關系。 A. 聚合 B. 包含 C. 繼承 D. 擴展 答案&#xff1a;C 解析&#xff1a;用例圖描述了一組用例、參與者以及它們之間的關系…

【學習筆記】《逆向工程核心原理》03.abex‘crackme-2、函數的調用約定、視頻講座-Tut.ReverseMe1

文章目錄 abexcrackme-21. Visual Basic文件的特征1.1. VB專用引擎1.2. 本地代碼與偽代碼1.3. 事件處理程序1.4. 未文檔化的結構體 2. 開始調試2.1. 間接調用2.2. RT_MainStruct結構體2.3. ThunRTMain()函數 3. 分析crackme3.1. 檢索字符串3.2. 查找字符串地址3.3. 生成Serial的…

深入解析Go語言Channel:源碼剖析與并發讀寫機制

文章目錄 Channel的內部結構Channel的創建過程有緩沖Channel的并發讀寫機制同時讀寫的可能性發送操作的實現接收操作的實現 并發讀寫的核心機制解析互斥鎖保護環形緩沖區等待隊列直接傳遞優化Goroutine調度 實例分析&#xff1a;有緩沖Channel的并發讀寫性能優化與最佳實踐緩沖…

初識Linux(14)Ext系列?件系統

之前談論的都是已打開文件在操作系統的中的管理&#xff0c;但是還有更多的文件沒有被打開&#xff0c;被存在磁盤中&#xff0c;如何管理這些磁盤中的文件&#xff0c;就是本篇的學習目標。 目錄 1.理解硬件 磁盤結構 扇區的讀寫 CHS地址定位 磁盤的邏輯結構 2. 引??件…

電機控制常見面試問題(十二)

文章目錄 一.電機鎖相環1.理解鎖相環2.電機控制中的鎖相環應用3.數字鎖相環&#xff08;DPLL&#xff09; vs 模擬鎖相環&#xff08;APLL&#xff09;4.鎖相環設計的關鍵技術挑戰5.總結 二、磁鏈觀測1.什么是磁鏈&#xff1f;2.為什么要觀測磁鏈&#xff1f;3.怎么觀測磁鏈&am…

Android `%d` 與 `1$%d` 格式化的區別

在 Android 開發中&#xff0c;我們經常需要對字符串進行格式化處理&#xff0c;比如動態填充數字、日期、字符等。 其中&#xff0c;%d 和 1$%d 都是格式化占位符&#xff0c;但它們在使用上有一些不同。 本文將詳細解析這兩者的區別&#xff0c;并結合 Kotlin 代碼示例幫助你…

SpringBoot中使用kaptcha生成驗證碼

簡介 kaptcha是谷歌開源的簡單實用的驗證碼生成工具。通過設置參數&#xff0c;可以自定義驗證碼大小、顏色、顯示的字符等等。 Maven引入依賴 <!-- https://mvnrepository.com/artifact/pro.fessional/kaptcha --><dependency><groupId>pro.fessional<…

如何在PHP中實現數據加密與解密:保護敏感信息

如何在PHP中實現數據加密與解密&#xff1a;保護敏感信息 在現代Web開發中&#xff0c;數據安全是一個至關重要的議題。無論是用戶的個人信息、支付數據&#xff0c;還是其他敏感信息&#xff0c;都需要在存儲和傳輸過程中進行加密&#xff0c;以防止數據泄露和惡意攻擊。PHP作…

單元測試、系統測試、集成測試、回歸測試的步驟、優點、缺點、注意點梳理說明

單元測試、系統測試、集成測試、回歸測試的梳理說明 單元測試 步驟&#xff1a; 編寫測試用例&#xff0c;覆蓋代碼的各個分支和邊界條件。使用測試框架&#xff08;如JUnit、NUnit&#xff09;執行測試。檢查測試結果&#xff0c;確保代碼按預期運行。修復發現的缺陷并重新測…