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 1024
class 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);
else
sprintf(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_MSG
DECLARE_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
/
// CWebServerApp
BEGIN_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_MSG
ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()
/
// CWebServerApp construction
CWebServerApp::CWebServerApp()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}
/
// The one and only CWebServerApp object
CWebServerApp theApp;
/
// CWebServerApp initialization
BOOL 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 _AFXDLL
Enable3dControls(); // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif
SetRegistryKey("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 500
class 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_MSG
DECLARE_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 About
class 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_MSG
DECLARE_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 dialog
CWebServerDlg::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 Win32
m_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 handlers
BOOL 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 dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
m_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 painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int 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 icon
dc.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;
else
return 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 Thread
unsigned int ThreadA_ID;
HANDLE ThreadC; // Cleanup Thread
unsigned 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);
}
else
LogMessage(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);
}
else
LogMessage(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);
}
else
pGenericServer->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);
}
else
return THREADEXIT_SUCCESS;//不是超时返回,说明服务关闭了。
}
return THREADEXIT_SUCCESS;
}